<div dir="ltr"><div><div class="gmail_extra"><div class="gmail_quote">On Fri, Jan 6, 2017 at 3:59 PM, Marcelo Tosatti <span dir="ltr"><<a href="mailto:mtosatti@redhat.com" target="_blank">mtosatti@redhat.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><br>
<br>
Cache Allocation Technology is a feature on selected recent Intel Xeon<br>
processors which allows control over L3 cache allocation.<br>
<br>
Kernel support has been merged to the upstream kernel, via a filesystem<br>
resctrlfs.<br>
<br>
On top of that, a userspace utility, resctrltool has been written<br>
to facilitate writing applications and using the filesystem<br>
interface (see the rationale at<br>
<a href="http://www.mail-archive.com/linux-kernel@vger.kernel.org/msg1300792.html" rel="noreferrer" target="_blank">http://www.mail-archive.com/<wbr>linux-kernel@vger.kernel.org/<wbr>msg1300792.html</a>).<br>
<br>
This patch adds a new option to systemd, RDTCacheReservation,<br>
to allow configuration of CAT via resctrltool.<br>
<br>
See the first hunk of the patch for a description of the option</blockquote><div><br></div><div>This really doesn't look pretty, neither the approach nor the implementation...<br><br>Is the option actually so complex that calling resctrltool is the only way to adjust it? What about writing to the resctrlfs directly?<br><br></div><div>Also, it would be nicer to submit the patches as GitHub pull requests instead.<br></div><div> <br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div>
<br>
+static char* convert_rdt_back(char *argm[], int argmidx)<br>
+{<br>
+       int i, ret;<br>
+       char *buf, *str;<br>
+       int size = 0;<br>
+       int printcomma = 0;<br>
+       int printsemicolon = 0;<br>
+       int localmode = 0;<br>
+<br>
+       for (i=0; i < argmidx; i++) {<br>
+               /* ',', ';', '=' */<br>
+               size += strlen(argm[i]) + 3;<br>
+       }<br>
+<br>
+       buf = malloc(size);<br>
+       if (buf == NULL)<br>
+               return NULL;<br>
+       memset(buf, 0, size);<br>
+<br>
+       str = buf;<br>
+<br>
+       /* cache-id specified */<br>
+       for (i=0; i < argmidx; i++) {<br>
+               if (strlen(argm[i]) == 10) {<br>
+                       if (strcmp(argm[i], "--cache-id") == 0)<br>
+                               localmode = 1;<br>
+               }<br>
+       }<br>
+<br>
+       for (i=0; i<argmidx; i++) {<br>
+               char *cur = argm[i];<br>
+<br>
+               if (strlen(cur) == 0)<br>
+                       return buf;<br>
+<br>
+               if (strlen(cur) == 6) {<br>
+                       if (strcmp(cur, "--type") == 0) {<br>
+                               ret = sprintf(str, "type=");<br>
+                               str = str + ret;<br>
+                               printcomma = 1;<br>
+                               continue;<br>
+                       }<br></div>
+ etc.<br><div>
+       }<br>
+<br>
+       return buf;<br>
+}<br>
+<br></div></blockquote><div><br></div><div>Converting from foo;bar to argv[] and back is a bit horrible. It seems to me that it would be *much* simpler and easier to maintain if you stored only actual parameters in the unit (e.g. as an array of {type,size,cache_id}) and only generated argv at the moment it was necessary, i.e. right before spawning resctrltool. After all, that's the only place you need it. *And* that way you wouldn't need the above function at all.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div>
<br>
Index: systemd/src/core/execute.c<br>
==============================<wbr>==============================<wbr>=======<br>
--- systemd.orig/src/core/execute.<wbr>c<br>
+++ systemd/src/core/execute.c<br>
@@ -36,6 +36,10 @@<br>
 #include <sys/un.h><br>
 #include <unistd.h><br>
 #include <utmpx.h><br>
+#include <fcntl.h><br>
+#include <sys/select.h><br>
+#include <sys/types.h><br>
+#include <sys/wait.h><br>
<br>
 #ifdef HAVE_PAM<br>
 #include <security/pam_appl.h><br>
@@ -2201,6 +2205,161 @@ static int apply_working_directory(const<br>
         return 0;<br>
 }<br>
<br>
+<br>
+static int fork_exec_resctrltool(Unit *u, char *argv[])<br>
+{<br>
+        int outpipe[2];<br>
+        int errpipe[2];<br>
+        pid_t cpid;<br>
+<br>
+        pipe(outpipe);<br>
+        pipe(errpipe);<br>
+        cpid = fork();<br>
+<br>
+        if(cpid == 0) {<br>
+                int r;<br>
+<br>
+                dup2(errpipe[1], STDERR_FILENO);<br>
+                dup2(outpipe[1], STDOUT_FILENO);<br>
+<br>
+                r = execve(argv[0], argv, NULL);<br>
+                if (r == -1) {<br>
+                        log_open();<br>
+                        log_unit_error_errno(u, r, "Failed to execve resctrltool, ignoring: %m");<br>
+                        log_close();<br>
+                        return -errno;<br>
+                }<br>
+        } else {<br>
+                char buffer[100];<br>
+                fd_set fdset;<br>
+                int count;<br>
+                int ret;<br>
+                int nfds;<br>
+                int status = 0;<br>
+                int retst;<br>
+<br>
+                ret = waitpid(cpid, &status, 0);<br>
+                if (ret == -1) {<br>
+                        log_open();<br>
+                        log_unit_error_errno(u, ret, "Failed to waitpid resctrltool, ignoring: %m");<br>
+                        log_close();<br>
+                        return -errno;<br>
+                }<br>
+<br>
+                if (!WIFEXITED(status)) {<br>
+                        log_open();<br>
+                        log_unit_error_errno(u, ret, "resctrltool exits abnormally, ignoring: %m");<br>
+                        log_close();<br>
+                        return -1;<br>
+                } else {<br>
+                        retst = WEXITSTATUS(status);<br>
+<br>
+                        if (retst == 0) {<br>
+                               return 0;<br>
+                        }<br>
+                }<br>
+<br>
+                /*<br>
+                 * error, read stderr and stdout and log.<br>
+                 */<br>
+<br>
+                FD_ZERO(&fdset);<br>
+                FD_SET(outpipe[0], &fdset);<br>
+                FD_SET(errpipe[0], &fdset);<br>
+<br>
+                if (outpipe[0] > errpipe[0])<br>
+                        nfds = outpipe[0] + 1;<br>
+                else<br>
+                        nfds = errpipe[0] + 1;<br>
+<br>
+                ret = select(nfds, &fdset, NULL, NULL, NULL);<br>
+                if (ret == -1) {<br>
+                        log_open();<br>
+                        log_unit_error_errno(u, ret, "select error, ignoring RDTCacheReservation: %m");<br>
+                        log_close();<br>
+                        return -1;<br>
+                }<br>
+<br>
+                if (FD_ISSET(outpipe[0], &fdset)) {<br>
+                        count = read(outpipe[0], buffer, sizeof(buffer)-1);<br>
+                        if (count >= 0) {<br>
+                                buffer[count] = 0;<br>
+                                log_open();<br>
+                                log_unit_error(u, "resctrltool stdout: %s", buffer);<br>
+                                log_close();<br>
+                        } else {<br>
+                                log_open();<br>
+                                log_unit_error(u, "resctrltool io error\n");<br>
+                                log_close();<br>
+                        }<br>
+                }<br>
+<br>
+                if (FD_ISSET(errpipe[0], &fdset)) {<br>
+                        count = read(errpipe[0], buffer, sizeof(buffer)-1);<br>
+                        if (count >= 0) {<br>
+                                buffer[count] = 0;<br>
+                                log_open();<br>
+                                log_unit_error(u, "resctrltool stderr: %s", buffer);<br>
+                                log_close();<br>
+                        } else {<br>
+                                log_open();<br>
+                                log_unit_error(u, "resctrltool io error\n");<br>
+                                log_close();<br>
+                        }<br>
+                }<br>
+<br>
+                return retst;<br>
+        }<br>
+<br>
+    return -1;<br>
+}<br>
+<br></div></blockquote><div><br></div><div>It rather looks to me like your pipes are leaking?<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div>
+static int setup_rdt_cat(Unit *u, const ExecContext *c, pid_t pid)<br>
+{<br>
+        int ret, i;<br>
+        const char *arg[5];<br>
+        char pidstr[100];<br>
+        char *name = u->id;<br>
+        const char *largm[ARGMSIZE + 4];<br></div></blockquote><div><br></div><div>What's the difference between arg and largm?<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div>
+<br>
+        sprintf(pidstr, "%d", pid);<br></div></blockquote><div><br></div><div>Process IDs are unsigned.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div>
+<br>
+        arg[0] = "/usr/bin/resctrltool";<br>
+        arg[1] = "delete";<br>
+        arg[2] = name;<br>
+        arg[3] = 0;<br>
+<br>
+        ret = fork_exec_resctrltool(u, (char **)arg);<br>
+        /* we want to ignore 'reservation does not exist'<br>
+         * errors, so skip error checking entirely.<br>
+         * any other errors will be caught when trying<br>
+         * to execute create below<br>
+         */<br>
+<br>
+        memset(largm, 0, sizeof(largm));<br>
+        largm[0] = "/usr/bin/resctrltool";<br>
+        largm[1] = "create";<br>
+        largm[2] = "--name";<br>
+        largm[3] = name;<br>
+        for (i = 0; i < c->argmidx; i++)<br>
+                largm[i+4] = c->argm[i];<br>
+<br>
+        ret = fork_exec_resctrltool(u, (char **)largm);<br>
+        if (ret)<br>
+                return ret;<br>
+<br>
+        arg[0] = "/usr/bin/resctrltool";<br>
+        arg[1] = "assign";<br>
+        arg[2] = pidstr;<br>
+        arg[3] = name;<br>
+        arg[4] = 0;<br>
+        ret = fork_exec_resctrltool(u, (char **)arg);<br></div></blockquote><div><div><br></div>Three calls per process seems excessive; couldn't resctrltool have a dedicated subcommand for delete+create+assign?<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"></blockquote><br><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div>
<br>
+#define STATE_NEXT_TYPE 0<br>
+#define STATE_NEXT_SIZE 1<br>
+#define STATE_NEXT_CACHEID_OR_END 2<br>
+<br>
+static void free_argm(ExecContext *c)<br>
+{<br>
+        int i;<br>
+<br>
+        for (i=0; i < c->argmidx; i++) {<br>
+                free(c->argm[i]);<br>
+                c->argm[i] = NULL;<br>
+        }<br>
+        c->argmidx = 0;<br>
+}<br>
+<br>
+int config_parse_rdt_cache_<wbr>reservation(const char* unit,<br>
+                                       const char *filename,<br>
+                                       unsigned line,<br>
+                                       const char *section,<br>
+                                       unsigned section_line,<br>
+                                       const char *lvalue,<br>
+                                       int ltype,<br>
+                                       const char *rvalue,<br>
+                                       void *data,<br>
+                                       void *userdata) {<br>
+<br>
+        ExecContext *c = data;<br>
+        int curstate = STATE_NEXT_TYPE;<br>
+        char *buf = (char *)rvalue;<br>
+<br>
+        assert(filename);<br>
+        assert(lvalue);<br>
+        assert(rvalue);<br>
+        assert(data);<br>
+<br>
+        if (isempty(rvalue)) {<br>
+                /* An empty assignment resets the list */<br>
+                free_argm(c);<br>
+                return 0;<br>
+        }<br>
+<br>
+        do {<br>
+            if (c->argmidx > ARGMSIZE - 3) {<br>
+                free_argm(c);<br>
+                log_syntax(unit, LOG_ERR, filename, line, ENOMEM,<br>
+                           "argmidx overflow, ignoring: %s", rvalue);<br>
+                return 0;<br>
+            }<br>
+<br>
+            switch (curstate) {<br>
+            case STATE_NEXT_TYPE: {<br>
+                char *string, *buf2;<br>
+<br>
+                buf = strstr(buf, "type=");<br>
+                if (buf == NULL) {<br>
+                    free_argm(c);<br>
+                    log_syntax(unit, LOG_ERR, filename, line, EINVAL,<br>
+                           "type= not found when expected, ignoring: %s", rvalue);<br>
+                    return 0;<br>
+                }<br>
+<br>
+                buf2 = strstr(buf, ",");<br>
+                if (buf2 == NULL) {<br>
+                    free_argm(c);<br>
+                    log_syntax(unit, LOG_ERR, filename, line, EINVAL,<br>
+                           ", not found when parsing type, ignoring: %s", rvalue);<br>
+                    return 0;<br>
+                }<br>
+                if (strlen(buf) <= 5) {<br>
+                    free_argm(c);<br>
+                    log_syntax(unit, LOG_ERR, filename, line, EINVAL,<br>
+                           "invalid type, ignoring: %s", rvalue);<br>
+                    return 0;<br>
+                }<br>
+                /* skip type= */<br>
+                buf = buf + 5;<br>
+                if (strlen(buf) < 4) {<br>
+                    free_argm(c);<br>
+                    log_syntax(unit, LOG_ERR, filename, line, EINVAL,<br>
+                           "invalid type, ignoring: %s", rvalue);<br>
+                    return 0;<br>
+                }<br>
+                string = strndup(buf, 4);<br>
+                if (string == NULL) {<br>
+                    free_argm(c);<br>
+                    log_syntax(unit, LOG_ERR, filename, line, ENOMEM,<br>
+                           "enomem for strndup, ignoring: %s", rvalue);<br>
+                    return 0;<br>
+                }<br>
+<br>
+                c->argm[c->argmidx++] = strdup("--type");<br>
+                c->argm[c->argmidx++] = string;<br>
+<br>
+                /* skip {code,data,both}, */<br>
+                buf = buf + 5;<br>
+                curstate = STATE_NEXT_SIZE;<br>
+                break;<br>
+            }<br>
+            case STATE_NEXT_SIZE: {<br>
+                char *string, *buf2, *commabuf, *dotcommabuf;<br>
+                int len, skip = 0;<br>
+<br>
+                buf = strstr(buf, "size=");<br>
+                if (buf == NULL) {<br>
+                    free_argm(c);<br>
+                    log_syntax(unit, LOG_ERR, filename, line, EINVAL,<br>
+                           "size= not found when expected, ignoring: %s", rvalue);<br>
+                    return 0;<br>
+                }<br>
+                if (strlen(buf) <= 5) {<br>
+                    free_argm(c);<br>
+                    log_syntax(unit, LOG_ERR, filename, line, EINVAL,<br>
+                           "invalid size, ignoring: %s", rvalue);<br>
+                    return 0;<br>
+                }<br>
+                /* skip size= */<br>
+                buf = buf + 5;<br>
+<br>
+                commabuf = strstr(buf, ",");<br>
+                dotcommabuf = strstr(buf, ";");<br>
+<br>
+                buf2 = NULL;<br>
+<br>
+                /* the end, neither , or ; follow */<br>
+                if (commabuf == NULL && dotcommabuf == NULL) {<br>
+                    buf2 = buf + strlen(buf);<br>
+                    skip = 0;<br>
+                /* only ',': cache-id must follow */<br>
+                } else if (commabuf && dotcommabuf == NULL) {<br>
+                    buf2 = commabuf;<br>
+                    skip = 1;<br>
+                /* trailing ';' at the end */<br>
+                } else if (commabuf == NULL && dotcommabuf) {<br>
+                    buf2 = dotcommabuf;<br>
+                    skip = 1;<br>
+                } else if (commabuf && dotcommabuf) {<br>
+                    if (commabuf > dotcommabuf)<br>
+                        buf2 = dotcommabuf;<br>
+                    else<br>
+                        buf2 = commabuf;<br>
+                    skip = 1;<br>
+                }<br>
+                len = buf2 - buf;<br>
+                string = strndup(buf, len);<br>
+                if (string == NULL) {<br>
+                    free_argm(c);<br>
+                    log_syntax(unit, LOG_ERR, filename, line, ENOMEM,<br>
+                           "enomem for strndup, ignoring: %s", rvalue);<br>
+                    return 0;<br>
+                }<br>
+<br>
+                c->argm[c->argmidx++] = strdup("--size");<br>
+                c->argm[c->argmidx++] = string;<br>
+                curstate = STATE_NEXT_CACHEID_OR_END;<br>
+<br>
+                buf = buf2;<br>
+                buf = buf + skip;<br>
+                break;<br>
+            }<br>
+            case STATE_NEXT_CACHEID_OR_END: {<br>
+                char *buf2, *string, *dotcommabuf;<br>
+                int len, skip = 0;<br>
+<br>
+                if (strlen(buf) < 4)<br>
+                    break;<br>
+<br>
+                if (strncmp(buf, "type", 4) == 0) {<br>
+                    curstate = STATE_NEXT_TYPE;<br>
+                    break;<br>
+                }<br>
+<br>
+                buf = strstr(buf, "cache-id=");<br>
+                if (buf == NULL) {<br>
+                    free_argm(c);<br>
+                    log_syntax(unit, LOG_ERR, filename, line, EINVAL,<br>
+                           "cache-id= not found when expected, ignoring: %s", rvalue);<br>
+                    return 0;<br>
+                }<br>
+                if (strlen(buf) <= 9) {<br>
+                    free_argm(c);<br>
+                    log_syntax(unit, LOG_ERR, filename, line, EINVAL,<br>
+                           "invalid cache-id=, ignoring: %s", rvalue);<br>
+                    return 0;<br>
+                }<br>
+                /* skip cache-id= */<br>
+                buf = buf + 9;<br>
+<br>
+                dotcommabuf = strstr(buf, ";");<br>
+                /* the end */<br>
+                if (dotcommabuf == NULL) {<br>
+                    buf2 = buf + strlen(buf);<br>
+                    skip = 0;<br>
+                } else {<br>
+                    buf2 = dotcommabuf;<br>
+                    skip = 1;<br>
+                }<br>
+                len = buf2 - buf;<br>
+                string = strndup(buf, len);<br>
+                if (string == NULL) {<br>
+                    free_argm(c);<br>
+                    log_syntax(unit, LOG_ERR, filename, line, ENOMEM,<br>
+                           "enomem for strndup, ignoring: %s", rvalue);<br>
+                    return 0;<br>
+                }<br>
+<br>
+                c->argm[c->argmidx++] = strdup("--cache-id");<br>
+                c->argm[c->argmidx++] = string;<br>
+<br>
+                buf = buf + skip;<br>
+                curstate = STATE_NEXT_TYPE;<br>
+                break;<br>
+            }<br>
+            default:<br>
+                break;<br>
+            }<br>
+        } while (strlen(buf) > 4);<br>
+<br>
+        c->argm[c->argmidx] = 0;<br>
+<br>
+        c->rdt_cache_reservation_set = true;<br>
+<br>
+        return 0;<br>
+}<br></div></blockquote><div><br></div><div>This looks like it could be simplified *a lot* by using the strv functions, e.g. strv_split(buf, ";") and just nicely iterating over each reservation.<br><br></div><div>Also, again, don't generate the command line until you actually need it -- isolate it to the "call resctrltool" function, and store raw parameters until then.<br></div><div>
<br>
                 r = sd_bus_message_append(m, "v", "i", oa);<br>
+        } else if (streq(field, "RDTCacheReservation")) {<br>
+                int argmidx = 0;<br>
+#define ARGMSIZE 100<br>
+#define STATE_NEXT_TYPE 0<br>
+#define STATE_NEXT_SIZE 1<br>
+#define STATE_NEXT_CACHEID_OR_END 2<br>
+                int curstate = STATE_NEXT_TYPE;<br>
+                char *buf = (char *)eq;<br>
+<br>
+                r = sd_bus_message_open_container(<wbr>m, 'v', "as");<br>
+                if (r < 0)<br>
+                        return r;<br>
+<br>
+                r = sd_bus_message_open_container(<wbr>m, 'a', "s");<br>
+                if (r < 0)<br>
+                        return r;<br>
+<br>
+                do {<br>
+                        if (argmidx > ARGMSIZE - 3) {<br>
+                                log_error("argmidx overflow");<br>
+                                return -EINVAL;<br>
+                        }<br>
+<br>
+                        switch (curstate) {<br>
+                        case STATE_NEXT_TYPE: {<br>
+                        char *string, *buf2;<br>
+<br><br>
<br></div><div>Didn't you already do the same parsing earlier? (With the same memory leaks and all.)<br><br></div><div>Overall, even though I'm not a committer, IMHO this needs a lot of improvement before getting close to mergeable.<br clear="all"></div></div><br>-- <br><div class="gmail_signature"><div dir="ltr">Mantas Mikulėnas <<a href="mailto:grawity@gmail.com" target="_blank">grawity@gmail.com</a>></div></div>
</div></div></div>