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 755537d..955b8f2 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 f408023..e91d734 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 42fa421..f36f4e7 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 25aa2a6..f98c7e5 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 c1069ad..674d1cb 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 71e09ec..c8d31c0 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 93461ee..d9ae48b 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 2d2f23b..806e49d 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
        };