-
Notifications
You must be signed in to change notification settings - Fork 3.8k
linux: support hierarchies and cgroup2 for mem constraint lookup #2323
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
ofrobots
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
still revieweing, but left some initial comments.
d9ebec0 to
4b958dd
Compare
src/unix/linux-core.c
Outdated
| * remainder of the string (excluding the whitespace). | ||
| * Note that we assume that `start` ends with "\n\0". | ||
| */ | ||
| static const char* uv__mountinfo_next_field(const char* start, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would suggest replacing this function with strsep. Since that is not portable, you can implement your own version, or use a public domain copy. It would be better to have a copy of strsep rathan than a function that is functionally equivalent but subtly different.
|
This looks great at the first glance, thanks! |
src/unix/linux-core.c
Outdated
| */ | ||
| static int uv__read_cgroups_proc_files(uv__cgroups_subsystem_info_t* info, const char* subsystem) { | ||
| FILE* fp; | ||
| /* Should be big enough to fit a single line. */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What would happen in the case when a single line is bigger than the buffer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you move buf definition to inside the loop to make the scope clearer?
|
I think this code can be rewritten in terms of I'll re-review once those changes are done. |
e3cd9e4 to
8a95058
Compare
src/unix/linux-core.c
Outdated
| return 0; | ||
| } | ||
| hierarchy_path_inner = hierarchy_path + strlen(root); | ||
| snprintf(info->path, CGROUPS_BUF_SIZE, "%s/%s", mount_point, hierarchy_path_inner); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I get a gcc format-truncation warning here on gcc v8.3.0.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This does not seem to work on testcase from #2315.
Details will follow.
Upd: cgroups_version is determined correctly, info.path is corrupted.
Also, the max/high logic seems strange.
|
On two subsequent calls to
(yes, that whole block is the value of Upd: even the first call seems corrupted on user session: length= |
|
Overall, this seems to work after those issues would be fixed. |
|
@kjin is on vacation this week + next. This will be picked back up once he's back. |
|
Re: |
@kjin which system are you using? And did you specify |
|
@ChALkeR I'm using Debian stretch. I did specify |
|
Ok, I was able to get it to reproduce on a local Linux machine. |
aa9ec9d to
92aed98
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ofrobots @ChALkeR I think I've addressed all the issues except:
I'm using built-inEDIT: This is a non-issue since cgroups is a Linux-only feature, we don't need to supportstrsepand do not provide an implementation myself. I would like to be able to conditionally implement it for systems without it but am not sure how. Looking at https://unixpapa.com/incnote/string.html (which contains a public domain implementation), they useHAVE_STRSEP, but this doesn't seem to be defined (at least on Debian/Ubuntu) even whenstrsepis.strsepfor all systemsI'm not sure what the best practice is on what to do if a line read from a file is too long for a statically-sized buffer. My instinct is to make it dynamic and double its size for every too-long line but want to be more certain before going ahead to do it.EDIT: After talking to @ofrobots, it seems like usinggetline()is the way to go here.
I'd appreciate guidance on these issues. Thanks!
|
Ok, I've rebased and resolved the conflict -- which occurred due to part of |
|
Hi @bnoordhuis, do you mind reviewing this PR, or at least resolving the change request? It seems like quite a few people have been hoping to seeing this land. If you're busy, it would be good to perhaps assign a different reviewer to help move this forward? Thanks! |
|
@vtjnash maybe? |
|
If this PR remains stuck on the change-requested flag even after a third reviewer, I'd be inclined to open a new one, but I'd like to get blessing for that by an owner of libuv first. |
|
The next reviewer is welcome to dismiss my review if the comments have been addressed; IIRC, they were predominantly about string handling? I'm afraid I don't have time to review it myself. |
src/unix/linux-core.c
Outdated
| if (buf != NULL) | ||
| free(buf); | ||
| if (root != NULL) | ||
| uv__free(root); | ||
| if (mount_point != NULL) | ||
| uv__free(mount_point); | ||
| if (hierarchy_path != NULL) | ||
| uv__free(hierarchy_path); | ||
| if (subsystem_search_string != NULL) | ||
| uv__free(subsystem_search_string); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| if (buf != NULL) | |
| free(buf); | |
| if (root != NULL) | |
| uv__free(root); | |
| if (mount_point != NULL) | |
| uv__free(mount_point); | |
| if (hierarchy_path != NULL) | |
| uv__free(hierarchy_path); | |
| if (subsystem_search_string != NULL) | |
| uv__free(subsystem_search_string); | |
| free(buf); | |
| uv__free(root); | |
| uv__free(mount_point); | |
| uv__free(hierarchy_path); | |
| uv__free(subsystem_search_string); |
src/unix/linux-core.c
Outdated
| continue; | ||
| strsep(&field_ptr, " "); /* mount options */ | ||
| /* A hyphen marks the end of variable-length optional fields. */ | ||
| while ('-' != field_ptr[0]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| while ('-' != field_ptr[0]) | |
| while (field_ptr != NULL && '-' != field_ptr[0]) |
src/unix/linux-core.c
Outdated
| while (feof(fp) == 0) { | ||
| if (getline(&buf, &buf_size, fp) < 0) { | ||
| if (feof(fp) != 0) | ||
| break; | ||
| rc = UV_EIO; | ||
| goto cleanup; | ||
| } | ||
| buf_strlen = strlen(buf); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: from the docs for getline, seems more typical usage would look like:
| while (feof(fp) == 0) { | |
| if (getline(&buf, &buf_size, fp) < 0) { | |
| if (feof(fp) != 0) | |
| break; | |
| rc = UV_EIO; | |
| goto cleanup; | |
| } | |
| buf_strlen = strlen(buf); | |
| while ((buf_strlen = getline(&buf, &buf_size, fp)) != -1) { | |
| ... | |
| } | |
| if (ferror(fp)) { | |
| rc = UV_ERR(errno); | |
| goto cleanup; | |
| } |
(with appropriate reindentation)
src/unix/linux-core.c
Outdated
|
|
||
| char filename[UV__PATH_MAX]; | ||
| char buf[32]; /* Large enough to hold an encoded uint64_t. */ | ||
| uint64_t rc; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rc is now undefined on some code paths
| uint64_t rc; | |
| uint64_t rc = 0; |
|
Most of @bnoordhuis comments were addressed, but a few seem to have been missed. I'll dismiss his review (and substitute my own approval), once my comments are addressed. |
|
@vtjnash Thank you for the review -- I haven't forgotten about this, but unfortunately won't be able to circle back until next week. |
|
@vtjnash OK, I've addressed your comments, both the news ones and the ones you left on Ben's review. I think there is one open question I had regarding wrapping terminal output when it's in a comment (context). Also, apologies, I hadn't realized that you had already looked at this a year ago! (looking at the timestamps of some of your comments) |
|
@kjin Thanks for picking this up again. Cgroups v2 support is valuable IMO but I'd like to cut down the amount of parsing libuv has to do; C is not a great language for that. You parse Would Let me know if you don't have the time or inclination to return to this and I'll try to adopt it. edit: another thing: I think it should fall back to |
|
Hi @bnoordhuis, thanks for your input. I'll have bandwidth in around ~2 weeks (after American Thanksgiving) to work on this. If you (or anyone else) feel inclined to move this forward before then, please go ahead -- I want to make sure I'm not blocking anything critical. But I haven't forgotten about this PR, and I'll post back here again when I'm starting to work on it. |
|
This issue has been automatically marked as stale because it has not had recent activity. Thank you for your contributions. |
|
Since this has been marked as stale, and I don't have the necessary permissions to push here, I've opened a new pull request where I've rebased these changes and addressed one of the remaining review comments: #3712 |
|
@maleadt Thank you for moving this forward. A lot of changes have happened since I opened this PR that have brought me far away from the Node.js ecosystem. I'm sorry that I couldn't address this in a more timely manner, but glad to see that you volunteered to take it over! |
This PR adds support for arbitrary mount locations and hierarchies for cgroups, as well as memory limits from cgroups v2. Fixes #2315
It follows this procedure for resolving
{ cgroups_version, path }for thememorysubsystem:/proc/self/mountinfoto get three fields:memorysubsystem (usually/sys/fs/cgroup/memoryfor cgroups v1, and/sys/fs/cgroupfor cgroups v2)/, but could be/docker/${CONTAINER_PID}for docker containers, or any other value)memorysubsystem is controlled by cgroups v1 or v2 (->cgroups_version)/proc/self/cgroupto get a "hierarchy path" for the given subsystem. This comes from a line of the form${ID}:memory:${HIERARCHY}in cgroups v1 and0::${HIERARCHY}in cgroups v2. It should start with the root string mentioned above, and replacing this with the mount point should yield the "true path" to the memory parameter files (->path). For example:/, the mount_point was/sys/fs/cgroup2, and the hierarchy path read here was/foo/bar, then the true path should be/sys/fs/cgroup2/foo/bar/docker/abcdef0, the mount_point was/sys/fs/cgroup/memory, and the hierarchy path read here was/docker/abcdef0/foo/bar, then the true path should be/sys/fs/cgroup/memory/foo/barIf the attempt to get this true path fails or any assumptions above are violated, we should act as if cgroups is not present and just return
0to signify no known upper memory limit. Otherwise, we use the information above to read eithermemory.limit_in_bytes(for v1) ormemory.high/memory.max(for v2) from the correct location.