ipc: allow boot time extension of IPCMNI from 32k to 16M
authorWaiman Long <longman@redhat.com>
Tue, 14 May 2019 22:46:29 +0000 (15:46 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 15 May 2019 02:52:52 +0000 (19:52 -0700)
The maximum number of unique System V IPC identifiers was limited to
32k.  That limit should be big enough for most use cases.

However, there are some users out there requesting for more, especially
those that are migrating from Solaris which uses 24 bits for unique
identifiers.  To satisfy the need of those users, a new boot time kernel
option "ipcmni_extend" is added to extend the IPCMNI value to 16M.  This
is a 512X increase which should be big enough for users out there that
need a large number of unique IPC identifier.

The use of this new option will change the pattern of the IPC
identifiers returned by functions like shmget(2).  An application that
depends on such pattern may not work properly.  So it should only be
used if the users really need more than 32k of unique IPC numbers.

This new option does have the side effect of reducing the maximum number
of unique sequence numbers from 64k down to 128.  So it is a trade-off.

The computation of a new IPC id is not done in the performance critical
path.  So a little bit of additional overhead shouldn't have any real
performance impact.

Link: http://lkml.kernel.org/r/20190329204930.21620-1-longman@redhat.com
Signed-off-by: Waiman Long <longman@redhat.com>
Acked-by: Manfred Spraul <manfred@colorfullife.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Davidlohr Bueso <dbueso@suse.de>
Cc: "Eric W . Biederman" <ebiederm@xmission.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Kees Cook <keescook@chromium.org>
Cc: "Luis R. Rodriguez" <mcgrof@kernel.org>
Cc: Matthew Wilcox <willy@infradead.org>
Cc: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Documentation/admin-guide/kernel-parameters.txt
ipc/ipc_sysctl.c
ipc/util.c
ipc/util.h

index 80e40de..d1d1da9 100644 (file)
        ip=             [IP_PNP]
                        See Documentation/filesystems/nfs/nfsroot.txt.
 
+       ipcmni_extend   [KNL] Extend the maximum number of unique System V
+                       IPC identifiers from 32,768 to 16,777,216.
+
        irqaffinity=    [SMP] Set the default irq affinity mask
                        The argument is a cpu list, as described above.
 
index 49f9bf4..73b7782 100644 (file)
@@ -120,7 +120,8 @@ static int proc_ipc_sem_dointvec(struct ctl_table *table, int write,
 static int zero;
 static int one = 1;
 static int int_max = INT_MAX;
-static int ipc_mni = IPCMNI;
+int ipc_mni = IPCMNI;
+int ipc_mni_shift = IPCMNI_SHIFT;
 
 static struct ctl_table ipc_kern_table[] = {
        {
@@ -246,3 +247,12 @@ static int __init ipc_sysctl_init(void)
 }
 
 device_initcall(ipc_sysctl_init);
+
+static int __init ipc_mni_extend(char *str)
+{
+       ipc_mni = IPCMNI_EXTEND;
+       ipc_mni_shift = IPCMNI_EXTEND_SHIFT;
+       pr_info("IPCMNI extended to %d.\n", ipc_mni);
+       return 0;
+}
+early_param("ipcmni_extend", ipc_mni_extend);
index 095274a..cf5d108 100644 (file)
@@ -109,7 +109,7 @@ static const struct rhashtable_params ipc_kht_params = {
  * @ids: ipc identifier set
  *
  * Set up the sequence range to use for the ipc identifier range (limited
- * below IPCMNI) then initialise the keys hashtable and ids idr.
+ * below ipc_mni) then initialise the keys hashtable and ids idr.
  */
 void ipc_init_ids(struct ipc_ids *ids)
 {
@@ -225,7 +225,7 @@ static inline int ipc_idr_alloc(struct ipc_ids *ids, struct kern_ipc_perm *new)
                                0, GFP_NOWAIT);
        }
        if (idx >= 0)
-               new->id = SEQ_MULTIPLIER * new->seq + idx;
+               new->id = (new->seq << IPCMNI_SEQ_SHIFT) + idx;
        return idx;
 }
 
@@ -253,8 +253,8 @@ int ipc_addid(struct ipc_ids *ids, struct kern_ipc_perm *new, int limit)
        /* 1) Initialize the refcount so that ipc_rcu_putref works */
        refcount_set(&new->refcount, 1);
 
-       if (limit > IPCMNI)
-               limit = IPCMNI;
+       if (limit > ipc_mni)
+               limit = ipc_mni;
 
        if (ids->in_use >= limit)
                return -ENOSPC;
@@ -737,7 +737,7 @@ static struct kern_ipc_perm *sysvipc_find_ipc(struct ipc_ids *ids, loff_t pos,
        if (total >= ids->in_use)
                return NULL;
 
-       for (; pos < IPCMNI; pos++) {
+       for (; pos < ipc_mni; pos++) {
                ipc = idr_find(&ids->ipcs_idr, pos);
                if (ipc != NULL) {
                        *new_pos = pos + 1;
index e272be6..9746886 100644 (file)
 #include <linux/err.h>
 #include <linux/ipc_namespace.h>
 
-#define IPCMNI 32768  /* <= MAX_INT limit for ipc arrays (including sysctl changes) */
-#define SEQ_MULTIPLIER (IPCMNI)
+/*
+ * The IPC ID contains 2 separate numbers - index and sequence number.
+ * By default,
+ *   bits  0-14: index (32k, 15 bits)
+ *   bits 15-30: sequence number (64k, 16 bits)
+ *
+ * When IPCMNI extension mode is turned on, the composition changes:
+ *   bits  0-23: index (16M, 24 bits)
+ *   bits 24-30: sequence number (128, 7 bits)
+ */
+#define IPCMNI_SHIFT           15
+#define IPCMNI_EXTEND_SHIFT    24
+#define IPCMNI                 (1 << IPCMNI_SHIFT)
+#define IPCMNI_EXTEND          (1 << IPCMNI_EXTEND_SHIFT)
+
+#ifdef CONFIG_SYSVIPC_SYSCTL
+extern int ipc_mni;
+extern int ipc_mni_shift;
+
+#define IPCMNI_SEQ_SHIFT       ipc_mni_shift
+#define IPCMNI_IDX_MASK                ((1 << ipc_mni_shift) - 1)
+
+#else /* CONFIG_SYSVIPC_SYSCTL */
+
+#define ipc_mni                        IPCMNI
+#define IPCMNI_SEQ_SHIFT       IPCMNI_SHIFT
+#define IPCMNI_IDX_MASK                ((1 << IPCMNI_SHIFT) - 1)
+#endif /* CONFIG_SYSVIPC_SYSCTL */
 
 void sem_init(void);
 void msg_init(void);
@@ -96,9 +122,9 @@ struct pid_namespace *ipc_seq_pid_ns(struct seq_file *);
 #define IPC_MSG_IDS    1
 #define IPC_SHM_IDS    2
 
-#define ipcid_to_idx(id) ((id) % SEQ_MULTIPLIER)
-#define ipcid_to_seqx(id) ((id) / SEQ_MULTIPLIER)
-#define IPCID_SEQ_MAX min_t(int, INT_MAX/SEQ_MULTIPLIER, USHRT_MAX)
+#define ipcid_to_idx(id)  ((id) & IPCMNI_IDX_MASK)
+#define ipcid_to_seqx(id) ((id) >> IPCMNI_SEQ_SHIFT)
+#define IPCID_SEQ_MAX    (INT_MAX >> IPCMNI_SEQ_SHIFT)
 
 /* must be called with ids->rwsem acquired for writing */
 int ipc_addid(struct ipc_ids *, struct kern_ipc_perm *, int);
@@ -123,8 +149,8 @@ static inline int ipc_get_maxidx(struct ipc_ids *ids)
        if (ids->in_use == 0)
                return -1;
 
-       if (ids->in_use == IPCMNI)
-               return IPCMNI - 1;
+       if (ids->in_use == ipc_mni)
+               return ipc_mni - 1;
 
        return ids->max_idx;
 }
@@ -216,10 +242,10 @@ void free_ipcs(struct ipc_namespace *ns, struct ipc_ids *ids,
 
 static inline int sem_check_semmni(struct ipc_namespace *ns) {
        /*
-        * Check semmni range [0, IPCMNI]
+        * Check semmni range [0, ipc_mni]
         * semmni is the last element of sem_ctls[4] array
         */
-       return ((ns->sem_ctls[3] < 0) || (ns->sem_ctls[3] > IPCMNI))
+       return ((ns->sem_ctls[3] < 0) || (ns->sem_ctls[3] > ipc_mni))
                ? -ERANGE : 0;
 }