ipc: Determine maximal size of a shared memory area at runtime.
authorAndre Noll <maan@systemlinux.org>
Wed, 10 Aug 2011 17:50:38 +0000 (19:50 +0200)
committerAndre Noll <maan@systemlinux.org>
Fri, 16 Sep 2011 19:41:47 +0000 (21:41 +0200)
During command dispatch, the afs process allocates shm areas for the
query result and passes the identifiers via the local socket to the
child process of para_server which is executing the command. If the
write to the (non-blocking) local socket fails, for example because
the call would block, afs closes the connection to the child process
immediately to avoid deadlocks.

Therefore the maximal output size of an (afs) command depends
linearly on the size of the shared memory areas, so it is desirable
to allocate areas as large as possible. Currently, we use the SHMMAX
if it is defined and fall back to the safe default value of 64K
otherwise. However, this default is much smaller than the typical limit
of 32M on Linux. Moreover, the maximal size of a shared memory area
(shmmax) can be set at any time on most if not all operating systems,
so runtime detection of shmmax is to be preferred.

Unfortunately the way to obtain shmmax varies between operating
systems. On Linux, the value is available as the contents of a file
in /proc while on {Free,Net}BSD and Darwin sysctlbyname() must be
called. Moreover, BSD and Darwin use different identifiers for the
sysctlbyname() call.

So any code that determines shmmax at runtime and works on all
of the above systems must necessarily be ugly. This patch tries to
concentrate all ugliness in the new shm_get_shmmax() function, so that
the changes outside of ipc.c consist only of replacements SHMMAX ->
shm_get_shmmax() and of the removal of the default SHMMAX setting.

The new function only determines shmmax once when it is called for
the first time.

afs.c
aft.c
attribute.c
blob.c
ipc.c
ipc.h
mood.c
playlist.c

diff --git a/afs.c b/afs.c
index 755537da9e436ab8705222925fa3c737f648eab1..955b8f207d5305418674f6a0f2e30f904d110517 100644 (file)
--- a/afs.c
+++ b/afs.c
@@ -537,7 +537,7 @@ static int activate_mood_or_playlist(char *arg, int *num_admissible)
 static void com_select_callback(int fd, const struct osl_object *query)
 {
        struct para_buffer pb = {
-               .max_size = SHMMAX,
+               .max_size = shm_get_shmmax(),
                .private_data = &fd,
                .max_size_handler = pass_buffer_as_shm
        };
diff --git a/aft.c b/aft.c
index f4080233855009be5423b3bc9325b861d5f20b0c..e91d734fea7d70e74350cdcfdc3c4def9bce8a05 100644 (file)
--- a/aft.c
+++ b/aft.c
@@ -1037,7 +1037,7 @@ static int make_status_items(struct audio_file_data *afd,
                .flags = LS_FLAG_FULL_PATH | LS_FLAG_ADMISSIBLE_ONLY,
                .mode = LS_MODE_VERBOSE,
        };
-       struct para_buffer pb = {.max_size = SHMMAX - 1};
+       struct para_buffer pb = {.max_size = shm_get_shmmax() - 1};
        time_t current_time;
        int ret;
 
@@ -1048,7 +1048,7 @@ static int make_status_items(struct audio_file_data *afd,
        free(status_items);
        status_items = pb.buf;
        memset(&pb, 0, sizeof(pb));
-       pb.max_size = SHMMAX - 1;
+       pb.max_size = shm_get_shmmax() - 1;
        pb.flags = PBF_SIZE_PREFIX;
        ret = print_list_item(&d, &opts, &pb, current_time);
        if (ret < 0) {
@@ -1343,7 +1343,7 @@ static void com_ls_callback(int fd, const struct osl_object *query)
 {
        struct ls_options *opts = query->data;
        char *p, *pattern_start = (char *)query->data + sizeof(*opts);
-       struct para_buffer b = {.max_size = SHMMAX,
+       struct para_buffer b = {.max_size = shm_get_shmmax(),
                .flags = (opts->mode == LS_MODE_PARSER)? PBF_SIZE_PREFIX : 0,
                .max_size_handler = pass_buffer_as_shm, .private_data = &fd};
        int i = 0, ret;
@@ -1665,7 +1665,7 @@ static void com_add_callback(int fd, const struct osl_object *query)
        char afsi_buf[AFSI_SIZE];
        uint32_t flags = read_u32(buf + CAB_FLAGS_OFFSET);
        struct afs_info default_afsi = {.last_played = 0};
-       struct para_buffer msg = {.max_size = SHMMAX,
+       struct para_buffer msg = {.max_size = shm_get_shmmax(),
                .max_size_handler = pass_buffer_as_shm, .private_data = &fd};
        uint16_t afhi_offset, chunks_offset;
 
@@ -2080,7 +2080,7 @@ static void com_touch_callback(int fd, const struct osl_object *query)
 {
        struct touch_action_data tad = {.cto = query->data,
                .pb = {
-                       .max_size = SHMMAX,
+                       .max_size = shm_get_shmmax(),
                        .private_data = &fd,
                        .max_size_handler = pass_buffer_as_shm
                }
@@ -2226,7 +2226,7 @@ static void com_rm_callback(int fd, const struct osl_object *query)
 {
        struct com_rm_action_data crd = {.flags = *(uint32_t *)query->data,
                .pb = {
-                       .max_size = SHMMAX,
+                       .max_size = shm_get_shmmax(),
                        .private_data = &fd,
                        .max_size_handler = pass_buffer_as_shm
                }
@@ -2371,7 +2371,7 @@ static void com_cpsi_callback(int fd, const struct osl_object *query)
        struct cpsi_action_data cad = {
                .flags = *(unsigned *)query->data,
                .pb = {
-                       .max_size = SHMMAX,
+                       .max_size = shm_get_shmmax(),
                        .private_data = &fd,
                        .max_size_handler = pass_buffer_as_shm
                }
@@ -2543,7 +2543,7 @@ static int check_audio_file(struct osl_row *row, void *data)
 void aft_check_callback(int fd, __a_unused const struct osl_object *query)
 {
        struct para_buffer pb = {
-               .max_size = SHMMAX,
+               .max_size = shm_get_shmmax(),
                .private_data = &fd,
                .max_size_handler = pass_buffer_as_shm
        };
index 42fa421cbbfff275afb9a6d1691911483159bbe7..f36f4e7d91f7aae22fe9e039dd86ce30e0f3538b 100644 (file)
@@ -149,7 +149,7 @@ static void com_lsatt_callback(int fd, const struct osl_object *query)
        struct lsatt_action_data laad = {
                .flags = *(unsigned *) query->data,
                .pb = {
-                       .max_size = SHMMAX,
+                       .max_size = shm_get_shmmax(),
                        .private_data = &fd,
                        .max_size_handler = pass_buffer_as_shm
                }
@@ -295,7 +295,7 @@ static void com_addatt_callback(int fd, const struct osl_object *query)
        char *p;
        int ret = 1, ret2 = 0;
        struct para_buffer pb = {
-               .max_size = SHMMAX,
+               .max_size = shm_get_shmmax(),
                .private_data = &fd,
                .max_size_handler = pass_buffer_as_shm
        };
@@ -378,7 +378,7 @@ static void com_mvatt_callback(int fd, const struct osl_object *query)
        struct osl_object obj = {.data = old, .size = size};
        struct osl_row *row;
        struct para_buffer pb = {
-               .max_size = SHMMAX,
+               .max_size = shm_get_shmmax(),
                .private_data = &fd,
                .max_size_handler = pass_buffer_as_shm
        };
@@ -448,7 +448,7 @@ static void com_rmatt_callback(int fd, const struct osl_object *query)
        struct remove_attribute_action_data raad = {
                .num_removed = 0,
                .pb = {
-                       .max_size = SHMMAX,
+                       .max_size = shm_get_shmmax(),
                        .private_data = &fd,
                        .max_size_handler = pass_buffer_as_shm
                }
diff --git a/blob.c b/blob.c
index 25aa2a6dc323ed99efba3a78d247c25b27205da1..f98c7e5fc3f73d7b5a62e42e171c168a1a63a6af 100644 (file)
--- a/blob.c
+++ b/blob.c
@@ -130,7 +130,7 @@ static void com_lsblob_callback(struct osl_table *table,
        struct lsblob_action_data lbad = {
                .flags = *(uint32_t *)query->data,
                .pb = {
-                       .max_size = SHMMAX,
+                       .max_size = shm_get_shmmax(),
                        .private_data = &fd,
                        .max_size_handler = pass_buffer_as_shm
                }
@@ -261,7 +261,7 @@ static void com_rmblob_callback(struct osl_table *table, int fd,
        struct rmblob_data rmbd = {
                .num_removed = 0,
                .pb = {
-                       .max_size = SHMMAX,
+                       .max_size = shm_get_shmmax(),
                        .private_data = &fd,
                        .max_size_handler = pass_buffer_as_shm
                }
diff --git a/ipc.c b/ipc.c
index c1069ad9af067a026c794663be96518351eec8ee..674d1cb01e5fae9c5ae2ecb8f8ceec92fa9faa62 100644 (file)
--- a/ipc.c
+++ b/ipc.c
@@ -9,6 +9,10 @@
 #include "para.h"
 #include "error.h"
 #include "ipc.h"
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+
 #include <sys/ipc.h>
 #include <sys/shm.h>
 #include <sys/sem.h>
@@ -171,3 +175,45 @@ int shm_detach(void *addr)
        int ret = shmdt(addr);
        return ret < 0? -ERRNO_TO_PARA_ERROR(errno) : 1;
 }
+
+# if defined __FreeBSD__ || defined __NetBSD__
+#      define SYSCTL_SHMMAX_VARIABLE "kern.ipc.shmmax"
+# elif defined __APPLE__
+#      define SYSCTL_SHMMAX_VARIABLE "kern.sysv.shmmax"
+# else
+#      undef SYSCTL_SHMMAX_VARIABLE
+# endif
+
+size_t shm_get_shmmax(void)
+{
+       static size_t shmmax;
+
+       if (shmmax > 0) /* only dance once */
+               return shmmax;
+#ifdef __linux__ /* get it from proc fs */
+       {
+               int fd = open("/proc/sys/kernel/shmmax", O_RDONLY);
+               if (fd >= 0) {
+                       char buf[100] = "";
+                       int ret = read(fd, buf, sizeof(buf) - 1);
+                       if (ret > 0) {
+                               buf[ret] = '\0';
+                               shmmax = strtoul(buf, NULL, 10);
+                       }
+               }
+       }
+#elif defined SYSCTL_SHMMAX_VARIABLE
+       {
+               size_t len = sizeof(shmmax);
+               sysctlbyname(SYSCTL_SHMMAX_VARIABLE, &shmmax, &len, NULL, 0);
+       }
+#elif defined SHMMAX
+       shmmax = SHMMAX;
+#endif
+       if (shmmax == 0) {
+               PARA_WARNING_LOG("unable to determine shmmax\n");
+               shmmax = 65535; /* last ressort */
+       }
+       PARA_INFO_LOG("shmmax: %zu\n", shmmax);
+       return shmmax;
+}
diff --git a/ipc.h b/ipc.h
index 71e09ec1d1a7a21ea5a7fcab5bfb47835c70e464..c8d31c0c991641a1f71627c855705bdf7decd5c6 100644 (file)
--- a/ipc.h
+++ b/ipc.h
@@ -11,7 +11,4 @@ int shm_new(size_t size);
 int shm_attach(int id, enum shm_attach_mode mode, void **result);
 int shm_detach(void *addr);
 int shm_destroy(int id);
-
-#ifndef SHMMAX
-#define SHMMAX 65535
-#endif
+size_t shm_get_shmmax(void);
diff --git a/mood.c b/mood.c
index 93461ee845c558ec30c0da78bf548939f1247857..d9ae48bf967e61b0c4c0ca499d75f0b47eb47b96 100644 (file)
--- a/mood.c
+++ b/mood.c
@@ -422,7 +422,7 @@ out:
 void mood_check_callback(int fd, __a_unused const struct osl_object *query)
 {
        struct para_buffer pb = {
-               .max_size = SHMMAX,
+               .max_size = shm_get_shmmax(),
                .private_data = &fd,
                .max_size_handler = pass_buffer_as_shm
        };
index 2d2f23b4eb9c6df6b2d9ff9157a9716f09c1fcc6..806e49d55f68502fb38daffc33620cb075bbd1b3 100644 (file)
@@ -131,7 +131,7 @@ static int check_playlist(struct osl_row *row, void *data)
 void playlist_check_callback(int fd, __a_unused const struct osl_object *query)
 {
        struct para_buffer pb = {
-               .max_size = SHMMAX,
+               .max_size = shm_get_shmmax(),
                .private_data = &fd,
                .max_size_handler = pass_buffer_as_shm
        };