<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>