diff options
Diffstat (limited to 'util-linux/uevent.c')
-rw-r--r-- | util-linux/uevent.c | 50 |
1 files changed, 29 insertions, 21 deletions
diff --git a/util-linux/uevent.c b/util-linux/uevent.c index 57d9328ef..db11746d0 100644 --- a/util-linux/uevent.c +++ b/util-linux/uevent.c | |||
@@ -15,7 +15,7 @@ | |||
15 | //kbuild:lib-$(CONFIG_UEVENT) += uevent.o | 15 | //kbuild:lib-$(CONFIG_UEVENT) += uevent.o |
16 | 16 | ||
17 | //usage:#define uevent_trivial_usage | 17 | //usage:#define uevent_trivial_usage |
18 | //usage: "[PROG [ARGS]]" | 18 | //usage: "[PROG ARGS]" |
19 | //usage:#define uevent_full_usage "\n\n" | 19 | //usage:#define uevent_full_usage "\n\n" |
20 | //usage: "uevent runs PROG for every netlink notification." | 20 | //usage: "uevent runs PROG for every netlink notification." |
21 | //usage: "\n""PROG's environment contains data passed from the kernel." | 21 | //usage: "\n""PROG's environment contains data passed from the kernel." |
@@ -26,22 +26,26 @@ | |||
26 | #include "common_bufsiz.h" | 26 | #include "common_bufsiz.h" |
27 | #include <linux/netlink.h> | 27 | #include <linux/netlink.h> |
28 | 28 | ||
29 | #define BUFFER_SIZE 16*1024 | ||
30 | |||
31 | #define env ((char **)bb_common_bufsiz1) | 29 | #define env ((char **)bb_common_bufsiz1) |
32 | #define INIT_G() do { setup_common_bufsiz(); } while (0) | 30 | #define INIT_G() do { setup_common_bufsiz(); } while (0) |
33 | enum { | 31 | enum { |
34 | MAX_ENV = COMMON_BUFSIZE / sizeof(char*) - 1, | 32 | MAX_ENV = COMMON_BUFSIZE / sizeof(char*) - 1, |
35 | /* sizeof(env[0]) instead of sizeof(char*) | 33 | // ^^^sizeof(env[0]) instead of sizeof(char*) |
36 | * makes gcc-6.3.0 emit "strict-aliasing" warning. | 34 | // makes gcc-6.3.0 emit "strict-aliasing" warning. |
37 | */ | 35 | |
36 | // socket receive buffer of 2MiB proved to be too small: | ||
37 | // http://lists.busybox.net/pipermail/busybox/2019-December/087665.html | ||
38 | // udevd seems to use a whooping 128MiB. | ||
39 | // The socket receive buffer size is just a resource limit. | ||
40 | // The buffers are allocated lazily so the memory is not wasted. | ||
41 | KERN_RCVBUF = 128 * 1024 * 1024, | ||
42 | |||
43 | // Might be made smaller: the kernel v5.4 passes up to 32 environment | ||
44 | // variables with a total of 2kb on each event. | ||
45 | // On top of that the action string and device path are added. | ||
46 | USER_RCVBUF = 16 * 1024, | ||
38 | }; | 47 | }; |
39 | 48 | ||
40 | #ifndef SO_RCVBUFFORCE | ||
41 | #define SO_RCVBUFFORCE 33 | ||
42 | #endif | ||
43 | enum { RCVBUF = 2 * 1024 * 1024 }; | ||
44 | |||
45 | int uevent_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 49 | int uevent_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
46 | int uevent_main(int argc UNUSED_PARAM, char **argv) | 50 | int uevent_main(int argc UNUSED_PARAM, char **argv) |
47 | { | 51 | { |
@@ -57,7 +61,8 @@ int uevent_main(int argc UNUSED_PARAM, char **argv) | |||
57 | // Reproducer: | 61 | // Reproducer: |
58 | // uevent mdev & | 62 | // uevent mdev & |
59 | // find /sys -name uevent -exec sh -c 'echo add >"{}"' ';' | 63 | // find /sys -name uevent -exec sh -c 'echo add >"{}"' ';' |
60 | fd = create_and_bind_to_netlink(NETLINK_KOBJECT_UEVENT, /*groups:*/ 1 << 0, RCVBUF); | 64 | reopen: |
65 | fd = create_and_bind_to_netlink(NETLINK_KOBJECT_UEVENT, /*groups:*/ 1 << 0, KERN_RCVBUF); | ||
61 | 66 | ||
62 | for (;;) { | 67 | for (;;) { |
63 | char *netbuf; | 68 | char *netbuf; |
@@ -69,17 +74,20 @@ int uevent_main(int argc UNUSED_PARAM, char **argv) | |||
69 | // for a new uevent notification to come in. | 74 | // for a new uevent notification to come in. |
70 | // We use a fresh mmap so that buffer is not allocated | 75 | // We use a fresh mmap so that buffer is not allocated |
71 | // until kernel actually starts filling it. | 76 | // until kernel actually starts filling it. |
72 | netbuf = mmap(NULL, BUFFER_SIZE, | 77 | netbuf = xmmap_anon(USER_RCVBUF); |
73 | PROT_READ | PROT_WRITE, | ||
74 | MAP_PRIVATE | MAP_ANON, | ||
75 | /* ignored: */ -1, 0); | ||
76 | if (netbuf == MAP_FAILED) | ||
77 | bb_simple_perror_msg_and_die("mmap"); | ||
78 | 78 | ||
79 | // Here we block, possibly for a very long time | 79 | // Here we block, possibly for a very long time |
80 | len = safe_read(fd, netbuf, BUFFER_SIZE - 1); | 80 | len = safe_read(fd, netbuf, USER_RCVBUF - 1); |
81 | if (len < 0) | 81 | if (len < 0) { |
82 | if (errno == ENOBUFS) { | ||
83 | // Ran out of socket receive buffer | ||
84 | bb_simple_error_msg("uevent overrun"); | ||
85 | close(fd); | ||
86 | munmap(netbuf, USER_RCVBUF); | ||
87 | goto reopen; | ||
88 | } | ||
82 | bb_simple_perror_msg_and_die("read"); | 89 | bb_simple_perror_msg_and_die("read"); |
90 | } | ||
83 | end = netbuf + len; | 91 | end = netbuf + len; |
84 | *end = '\0'; | 92 | *end = '\0'; |
85 | 93 | ||
@@ -108,7 +116,7 @@ int uevent_main(int argc UNUSED_PARAM, char **argv) | |||
108 | while (env[idx]) | 116 | while (env[idx]) |
109 | bb_unsetenv(env[idx++]); | 117 | bb_unsetenv(env[idx++]); |
110 | } | 118 | } |
111 | munmap(netbuf, BUFFER_SIZE); | 119 | munmap(netbuf, USER_RCVBUF); |
112 | } | 120 | } |
113 | 121 | ||
114 | return 0; // not reached | 122 | return 0; // not reached |