mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	ipc: optimize semget/shmget/msgget for lots of keys
ipc_findkey() used to scan all objects to look for the wanted key.  This
is slow when using a high number of keys.  This change adds an rhashtable
of kern_ipc_perm objects in ipc_ids, so that one lookup cease to be O(n).
This change gives a 865% improvement of benchmark reaim.jobs_per_min on a
56 threads Intel(R) Xeon(R) CPU E5-2695 v3 @ 2.30GHz with 256G memory [1]
Other (more micro) benchmark results, by the author: On an i5 laptop, the
following loop executed right after a reboot took, without and with this
change:
    for (int i = 0, k=0x424242; i < KEYS; ++i)
        semget(k++, 1, IPC_CREAT | 0600);
                 total       total          max single  max single
   KEYS        without        with        call without   call with
      1            3.5         4.9   µs            3.5         4.9
     10            7.6         8.6   µs            3.7         4.7
     32           16.2        15.9   µs            4.3         5.3
    100           72.9        41.8   µs            3.7         4.7
   1000        5,630.0       502.0   µs             *           *
  10000    1,340,000.0     7,240.0   µs             *           *
  31900   17,600,000.0    22,200.0   µs             *           *
 *: unreliable measure: high variance
The duration for a lookup-only usage was obtained by the same loop once
the keys are present:
                 total       total          max single  max single
   KEYS        without        with        call without   call with
      1            2.1         2.5   µs            2.1         2.5
     10            4.5         4.8   µs            2.2         2.3
     32           13.0        10.8   µs            2.3         2.8
    100           82.9        25.1   µs             *          2.3
   1000        5,780.0       217.0   µs             *           *
  10000    1,470,000.0     2,520.0   µs             *           *
  31900   17,400,000.0     7,810.0   µs             *           *
Finally, executing each semget() in a new process gave, when still
summing only the durations of these syscalls:
creation:
                 total       total
   KEYS        without        with
      1            3.7         5.0   µs
     10           32.9        36.7   µs
     32          125.0       109.0   µs
    100          523.0       353.0   µs
   1000       20,300.0     3,280.0   µs
  10000    2,470,000.0    46,700.0   µs
  31900   27,800,000.0   219,000.0   µs
lookup-only:
                 total       total
   KEYS        without        with
      1            2.5         2.7   µs
     10           25.4        24.4   µs
     32          106.0        72.6   µs
    100          591.0       352.0   µs
   1000       22,400.0     2,250.0   µs
  10000    2,510,000.0    25,700.0   µs
  31900   28,200,000.0   115,000.0   µs
[1] http://lkml.kernel.org/r/20170814060507.GE23258@yexl-desktop
Link: http://lkml.kernel.org/r/20170815194954.ck32ta2z35yuzpwp@debix
Signed-off-by: Guillaume Knispel <guillaume.knispel@supersonicimagine.com>
Reviewed-by: Marc Pardo <marc.pardo@supersonicimagine.com>
Cc: Davidlohr Bueso <dave@stgolabs.net>
Cc: Kees Cook <keescook@chromium.org>
Cc: Manfred Spraul <manfred@colorfullife.com>
Cc: Alexey Dobriyan <adobriyan@gmail.com>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: "Peter Zijlstra (Intel)" <peterz@infradead.org>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Cc: Serge Hallyn <serge@hallyn.com>
Cc: Andrey Vagin <avagin@openvz.org>
Cc: Guillaume Knispel <guillaume.knispel@supersonicimagine.com>
Cc: Marc Pardo <marc.pardo@supersonicimagine.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
			
			
This commit is contained in:
		
							parent
							
								
									e4243b8062
								
							
						
					
					
						commit
						0cfb6aee70
					
				
					 8 changed files with 130 additions and 50 deletions
				
			
		| 
						 | 
					@ -3,6 +3,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <linux/spinlock.h>
 | 
					#include <linux/spinlock.h>
 | 
				
			||||||
#include <linux/uidgid.h>
 | 
					#include <linux/uidgid.h>
 | 
				
			||||||
 | 
					#include <linux/rhashtable.h>
 | 
				
			||||||
#include <uapi/linux/ipc.h>
 | 
					#include <uapi/linux/ipc.h>
 | 
				
			||||||
#include <linux/refcount.h>
 | 
					#include <linux/refcount.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -22,6 +23,8 @@ struct kern_ipc_perm {
 | 
				
			||||||
	unsigned long	seq;
 | 
						unsigned long	seq;
 | 
				
			||||||
	void		*security;
 | 
						void		*security;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct rhash_head khtnode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct rcu_head rcu;
 | 
						struct rcu_head rcu;
 | 
				
			||||||
	refcount_t refcount;
 | 
						refcount_t refcount;
 | 
				
			||||||
} ____cacheline_aligned_in_smp __randomize_layout;
 | 
					} ____cacheline_aligned_in_smp __randomize_layout;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,15 +8,18 @@
 | 
				
			||||||
#include <linux/nsproxy.h>
 | 
					#include <linux/nsproxy.h>
 | 
				
			||||||
#include <linux/ns_common.h>
 | 
					#include <linux/ns_common.h>
 | 
				
			||||||
#include <linux/refcount.h>
 | 
					#include <linux/refcount.h>
 | 
				
			||||||
 | 
					#include <linux/rhashtable.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct user_namespace;
 | 
					struct user_namespace;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct ipc_ids {
 | 
					struct ipc_ids {
 | 
				
			||||||
	int in_use;
 | 
						int in_use;
 | 
				
			||||||
	unsigned short seq;
 | 
						unsigned short seq;
 | 
				
			||||||
 | 
						bool tables_initialized;
 | 
				
			||||||
	struct rw_semaphore rwsem;
 | 
						struct rw_semaphore rwsem;
 | 
				
			||||||
	struct idr ipcs_idr;
 | 
						struct idr ipcs_idr;
 | 
				
			||||||
	int next_id;
 | 
						int next_id;
 | 
				
			||||||
 | 
						struct rhashtable key_ht;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct ipc_namespace {
 | 
					struct ipc_namespace {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										10
									
								
								ipc/msg.c
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								ipc/msg.c
									
									
									
									
									
								
							| 
						 | 
					@ -1011,7 +1011,7 @@ SYSCALL_DEFINE5(msgrcv, int, msqid, struct msgbuf __user *, msgp, size_t, msgsz,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void msg_init_ns(struct ipc_namespace *ns)
 | 
					int msg_init_ns(struct ipc_namespace *ns)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	ns->msg_ctlmax = MSGMAX;
 | 
						ns->msg_ctlmax = MSGMAX;
 | 
				
			||||||
	ns->msg_ctlmnb = MSGMNB;
 | 
						ns->msg_ctlmnb = MSGMNB;
 | 
				
			||||||
| 
						 | 
					@ -1019,7 +1019,7 @@ void msg_init_ns(struct ipc_namespace *ns)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	atomic_set(&ns->msg_bytes, 0);
 | 
						atomic_set(&ns->msg_bytes, 0);
 | 
				
			||||||
	atomic_set(&ns->msg_hdrs, 0);
 | 
						atomic_set(&ns->msg_hdrs, 0);
 | 
				
			||||||
	ipc_init_ids(&ns->ids[IPC_MSG_IDS]);
 | 
						return ipc_init_ids(&ns->ids[IPC_MSG_IDS]);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_IPC_NS
 | 
					#ifdef CONFIG_IPC_NS
 | 
				
			||||||
| 
						 | 
					@ -1027,6 +1027,7 @@ void msg_exit_ns(struct ipc_namespace *ns)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	free_ipcs(ns, &msg_ids(ns), freeque);
 | 
						free_ipcs(ns, &msg_ids(ns), freeque);
 | 
				
			||||||
	idr_destroy(&ns->ids[IPC_MSG_IDS].ipcs_idr);
 | 
						idr_destroy(&ns->ids[IPC_MSG_IDS].ipcs_idr);
 | 
				
			||||||
 | 
						rhashtable_destroy(&ns->ids[IPC_MSG_IDS].key_ht);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1058,11 +1059,12 @@ static int sysvipc_msg_proc_show(struct seq_file *s, void *it)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void __init msg_init(void)
 | 
					int __init msg_init(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	msg_init_ns(&init_ipc_ns);
 | 
						const int err = msg_init_ns(&init_ipc_ns);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ipc_init_proc_interface("sysvipc/msg",
 | 
						ipc_init_proc_interface("sysvipc/msg",
 | 
				
			||||||
				"       key      msqid perms      cbytes       qnum lspid lrpid   uid   gid  cuid  cgid      stime      rtime      ctime\n",
 | 
									"       key      msqid perms      cbytes       qnum lspid lrpid   uid   gid  cuid  cgid      stime      rtime      ctime\n",
 | 
				
			||||||
				IPC_MSG_IDS, sysvipc_msg_proc_show);
 | 
									IPC_MSG_IDS, sysvipc_msg_proc_show);
 | 
				
			||||||
 | 
						return err;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -54,16 +54,28 @@ static struct ipc_namespace *create_ipc_ns(struct user_namespace *user_ns,
 | 
				
			||||||
	ns->user_ns = get_user_ns(user_ns);
 | 
						ns->user_ns = get_user_ns(user_ns);
 | 
				
			||||||
	ns->ucounts = ucounts;
 | 
						ns->ucounts = ucounts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = mq_init_ns(ns);
 | 
						err = sem_init_ns(ns);
 | 
				
			||||||
	if (err)
 | 
						if (err)
 | 
				
			||||||
		goto fail_put;
 | 
							goto fail_put;
 | 
				
			||||||
 | 
						err = msg_init_ns(ns);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							goto fail_destroy_sem;
 | 
				
			||||||
 | 
						err = shm_init_ns(ns);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							goto fail_destroy_msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sem_init_ns(ns);
 | 
						err = mq_init_ns(ns);
 | 
				
			||||||
	msg_init_ns(ns);
 | 
						if (err)
 | 
				
			||||||
	shm_init_ns(ns);
 | 
							goto fail_destroy_shm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return ns;
 | 
						return ns;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fail_destroy_shm:
 | 
				
			||||||
 | 
						shm_exit_ns(ns);
 | 
				
			||||||
 | 
					fail_destroy_msg:
 | 
				
			||||||
 | 
						msg_exit_ns(ns);
 | 
				
			||||||
 | 
					fail_destroy_sem:
 | 
				
			||||||
 | 
						sem_exit_ns(ns);
 | 
				
			||||||
fail_put:
 | 
					fail_put:
 | 
				
			||||||
	put_user_ns(ns->user_ns);
 | 
						put_user_ns(ns->user_ns);
 | 
				
			||||||
	ns_free_inum(&ns->ns);
 | 
						ns_free_inum(&ns->ns);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										11
									
								
								ipc/sem.c
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								ipc/sem.c
									
									
									
									
									
								
							| 
						 | 
					@ -183,14 +183,14 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it);
 | 
				
			||||||
#define sc_semopm	sem_ctls[2]
 | 
					#define sc_semopm	sem_ctls[2]
 | 
				
			||||||
#define sc_semmni	sem_ctls[3]
 | 
					#define sc_semmni	sem_ctls[3]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void sem_init_ns(struct ipc_namespace *ns)
 | 
					int sem_init_ns(struct ipc_namespace *ns)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	ns->sc_semmsl = SEMMSL;
 | 
						ns->sc_semmsl = SEMMSL;
 | 
				
			||||||
	ns->sc_semmns = SEMMNS;
 | 
						ns->sc_semmns = SEMMNS;
 | 
				
			||||||
	ns->sc_semopm = SEMOPM;
 | 
						ns->sc_semopm = SEMOPM;
 | 
				
			||||||
	ns->sc_semmni = SEMMNI;
 | 
						ns->sc_semmni = SEMMNI;
 | 
				
			||||||
	ns->used_sems = 0;
 | 
						ns->used_sems = 0;
 | 
				
			||||||
	ipc_init_ids(&ns->ids[IPC_SEM_IDS]);
 | 
						return ipc_init_ids(&ns->ids[IPC_SEM_IDS]);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_IPC_NS
 | 
					#ifdef CONFIG_IPC_NS
 | 
				
			||||||
| 
						 | 
					@ -198,15 +198,18 @@ void sem_exit_ns(struct ipc_namespace *ns)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	free_ipcs(ns, &sem_ids(ns), freeary);
 | 
						free_ipcs(ns, &sem_ids(ns), freeary);
 | 
				
			||||||
	idr_destroy(&ns->ids[IPC_SEM_IDS].ipcs_idr);
 | 
						idr_destroy(&ns->ids[IPC_SEM_IDS].ipcs_idr);
 | 
				
			||||||
 | 
						rhashtable_destroy(&ns->ids[IPC_SEM_IDS].key_ht);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void __init sem_init(void)
 | 
					int __init sem_init(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	sem_init_ns(&init_ipc_ns);
 | 
						const int err = sem_init_ns(&init_ipc_ns);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ipc_init_proc_interface("sysvipc/sem",
 | 
						ipc_init_proc_interface("sysvipc/sem",
 | 
				
			||||||
				"       key      semid perms      nsems   uid   gid  cuid  cgid      otime      ctime\n",
 | 
									"       key      semid perms      nsems   uid   gid  cuid  cgid      otime      ctime\n",
 | 
				
			||||||
				IPC_SEM_IDS, sysvipc_sem_proc_show);
 | 
									IPC_SEM_IDS, sysvipc_sem_proc_show);
 | 
				
			||||||
 | 
						return err;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										12
									
								
								ipc/shm.c
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								ipc/shm.c
									
									
									
									
									
								
							| 
						 | 
					@ -72,14 +72,14 @@ static void shm_destroy(struct ipc_namespace *ns, struct shmid_kernel *shp);
 | 
				
			||||||
static int sysvipc_shm_proc_show(struct seq_file *s, void *it);
 | 
					static int sysvipc_shm_proc_show(struct seq_file *s, void *it);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void shm_init_ns(struct ipc_namespace *ns)
 | 
					int shm_init_ns(struct ipc_namespace *ns)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	ns->shm_ctlmax = SHMMAX;
 | 
						ns->shm_ctlmax = SHMMAX;
 | 
				
			||||||
	ns->shm_ctlall = SHMALL;
 | 
						ns->shm_ctlall = SHMALL;
 | 
				
			||||||
	ns->shm_ctlmni = SHMMNI;
 | 
						ns->shm_ctlmni = SHMMNI;
 | 
				
			||||||
	ns->shm_rmid_forced = 0;
 | 
						ns->shm_rmid_forced = 0;
 | 
				
			||||||
	ns->shm_tot = 0;
 | 
						ns->shm_tot = 0;
 | 
				
			||||||
	ipc_init_ids(&shm_ids(ns));
 | 
						return ipc_init_ids(&shm_ids(ns));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
| 
						 | 
					@ -95,7 +95,7 @@ static void do_shm_rmid(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
 | 
				
			||||||
	if (shp->shm_nattch) {
 | 
						if (shp->shm_nattch) {
 | 
				
			||||||
		shp->shm_perm.mode |= SHM_DEST;
 | 
							shp->shm_perm.mode |= SHM_DEST;
 | 
				
			||||||
		/* Do not find it any more */
 | 
							/* Do not find it any more */
 | 
				
			||||||
		shp->shm_perm.key = IPC_PRIVATE;
 | 
							ipc_set_key_private(&shm_ids(ns), &shp->shm_perm);
 | 
				
			||||||
		shm_unlock(shp);
 | 
							shm_unlock(shp);
 | 
				
			||||||
	} else
 | 
						} else
 | 
				
			||||||
		shm_destroy(ns, shp);
 | 
							shm_destroy(ns, shp);
 | 
				
			||||||
| 
						 | 
					@ -106,13 +106,15 @@ void shm_exit_ns(struct ipc_namespace *ns)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	free_ipcs(ns, &shm_ids(ns), do_shm_rmid);
 | 
						free_ipcs(ns, &shm_ids(ns), do_shm_rmid);
 | 
				
			||||||
	idr_destroy(&ns->ids[IPC_SHM_IDS].ipcs_idr);
 | 
						idr_destroy(&ns->ids[IPC_SHM_IDS].ipcs_idr);
 | 
				
			||||||
 | 
						rhashtable_destroy(&ns->ids[IPC_SHM_IDS].key_ht);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int __init ipc_ns_init(void)
 | 
					static int __init ipc_ns_init(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	shm_init_ns(&init_ipc_ns);
 | 
						const int err = shm_init_ns(&init_ipc_ns);
 | 
				
			||||||
	return 0;
 | 
						WARN(err, "ipc: sysv shm_init_ns failed: %d\n", err);
 | 
				
			||||||
 | 
						return err;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pure_initcall(ipc_ns_init);
 | 
					pure_initcall(ipc_ns_init);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										100
									
								
								ipc/util.c
									
									
									
									
									
								
							
							
						
						
									
										100
									
								
								ipc/util.c
									
									
									
									
									
								
							| 
						 | 
					@ -83,27 +83,46 @@ struct ipc_proc_iface {
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static int __init ipc_init(void)
 | 
					static int __init ipc_init(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	sem_init();
 | 
						int err_sem, err_msg;
 | 
				
			||||||
	msg_init();
 | 
					
 | 
				
			||||||
 | 
						err_sem = sem_init();
 | 
				
			||||||
 | 
						WARN(err_sem, "ipc: sysv sem_init failed: %d\n", err_sem);
 | 
				
			||||||
 | 
						err_msg = msg_init();
 | 
				
			||||||
 | 
						WARN(err_msg, "ipc: sysv msg_init failed: %d\n", err_msg);
 | 
				
			||||||
	shm_init();
 | 
						shm_init();
 | 
				
			||||||
	return 0;
 | 
					
 | 
				
			||||||
 | 
						return err_msg ? err_msg : err_sem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
device_initcall(ipc_init);
 | 
					device_initcall(ipc_init);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct rhashtable_params ipc_kht_params = {
 | 
				
			||||||
 | 
						.head_offset		= offsetof(struct kern_ipc_perm, khtnode),
 | 
				
			||||||
 | 
						.key_offset		= offsetof(struct kern_ipc_perm, key),
 | 
				
			||||||
 | 
						.key_len		= FIELD_SIZEOF(struct kern_ipc_perm, key),
 | 
				
			||||||
 | 
						.locks_mul		= 1,
 | 
				
			||||||
 | 
						.automatic_shrinking	= true,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * ipc_init_ids	- initialise ipc identifiers
 | 
					 * ipc_init_ids	- initialise ipc identifiers
 | 
				
			||||||
 * @ids: ipc identifier set
 | 
					 * @ids: ipc identifier set
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Set up the sequence range to use for the ipc identifier range (limited
 | 
					 * Set up the sequence range to use for the ipc identifier range (limited
 | 
				
			||||||
 * below IPCMNI) then initialise the ids idr.
 | 
					 * below IPCMNI) then initialise the keys hashtable and ids idr.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
void ipc_init_ids(struct ipc_ids *ids)
 | 
					int ipc_init_ids(struct ipc_ids *ids)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						int err;
 | 
				
			||||||
	ids->in_use = 0;
 | 
						ids->in_use = 0;
 | 
				
			||||||
	ids->seq = 0;
 | 
						ids->seq = 0;
 | 
				
			||||||
	ids->next_id = -1;
 | 
						ids->next_id = -1;
 | 
				
			||||||
	init_rwsem(&ids->rwsem);
 | 
						init_rwsem(&ids->rwsem);
 | 
				
			||||||
 | 
						err = rhashtable_init(&ids->key_ht, &ipc_kht_params);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							return err;
 | 
				
			||||||
	idr_init(&ids->ipcs_idr);
 | 
						idr_init(&ids->ipcs_idr);
 | 
				
			||||||
 | 
						ids->tables_initialized = true;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_PROC_FS
 | 
					#ifdef CONFIG_PROC_FS
 | 
				
			||||||
| 
						 | 
					@ -147,28 +166,20 @@ void __init ipc_init_proc_interface(const char *path, const char *header,
 | 
				
			||||||
 * Returns the locked pointer to the ipc structure if found or NULL
 | 
					 * Returns the locked pointer to the ipc structure if found or NULL
 | 
				
			||||||
 * otherwise. If key is found ipc points to the owning ipc structure
 | 
					 * otherwise. If key is found ipc points to the owning ipc structure
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Called with ipc_ids.rwsem held.
 | 
					 * Called with writer ipc_ids.rwsem held.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static struct kern_ipc_perm *ipc_findkey(struct ipc_ids *ids, key_t key)
 | 
					static struct kern_ipc_perm *ipc_findkey(struct ipc_ids *ids, key_t key)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct kern_ipc_perm *ipc;
 | 
						struct kern_ipc_perm *ipcp = NULL;
 | 
				
			||||||
	int next_id;
 | 
					 | 
				
			||||||
	int total;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (total = 0, next_id = 0; total < ids->in_use; next_id++) {
 | 
						if (likely(ids->tables_initialized))
 | 
				
			||||||
		ipc = idr_find(&ids->ipcs_idr, next_id);
 | 
							ipcp = rhashtable_lookup_fast(&ids->key_ht, &key,
 | 
				
			||||||
 | 
										      ipc_kht_params);
 | 
				
			||||||
		if (ipc == NULL)
 | 
					 | 
				
			||||||
			continue;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (ipc->key != key) {
 | 
					 | 
				
			||||||
			total++;
 | 
					 | 
				
			||||||
			continue;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (ipcp) {
 | 
				
			||||||
		rcu_read_lock();
 | 
							rcu_read_lock();
 | 
				
			||||||
		ipc_lock_object(ipc);
 | 
							ipc_lock_object(ipcp);
 | 
				
			||||||
		return ipc;
 | 
							return ipcp;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return NULL;
 | 
						return NULL;
 | 
				
			||||||
| 
						 | 
					@ -221,13 +232,13 @@ int ipc_addid(struct ipc_ids *ids, struct kern_ipc_perm *new, int size)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	kuid_t euid;
 | 
						kuid_t euid;
 | 
				
			||||||
	kgid_t egid;
 | 
						kgid_t egid;
 | 
				
			||||||
	int id;
 | 
						int id, err;
 | 
				
			||||||
	int next_id = ids->next_id;
 | 
						int next_id = ids->next_id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (size > IPCMNI)
 | 
						if (size > IPCMNI)
 | 
				
			||||||
		size = IPCMNI;
 | 
							size = IPCMNI;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (ids->in_use >= size)
 | 
						if (!ids->tables_initialized || ids->in_use >= size)
 | 
				
			||||||
		return -ENOSPC;
 | 
							return -ENOSPC;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	idr_preload(GFP_KERNEL);
 | 
						idr_preload(GFP_KERNEL);
 | 
				
			||||||
| 
						 | 
					@ -246,6 +257,15 @@ int ipc_addid(struct ipc_ids *ids, struct kern_ipc_perm *new, int size)
 | 
				
			||||||
		       (next_id < 0) ? 0 : ipcid_to_idx(next_id), 0,
 | 
							       (next_id < 0) ? 0 : ipcid_to_idx(next_id), 0,
 | 
				
			||||||
		       GFP_NOWAIT);
 | 
							       GFP_NOWAIT);
 | 
				
			||||||
	idr_preload_end();
 | 
						idr_preload_end();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (id >= 0 && new->key != IPC_PRIVATE) {
 | 
				
			||||||
 | 
							err = rhashtable_insert_fast(&ids->key_ht, &new->khtnode,
 | 
				
			||||||
 | 
										     ipc_kht_params);
 | 
				
			||||||
 | 
							if (err < 0) {
 | 
				
			||||||
 | 
								idr_remove(&ids->ipcs_idr, id);
 | 
				
			||||||
 | 
								id = err;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if (id < 0) {
 | 
						if (id < 0) {
 | 
				
			||||||
		spin_unlock(&new->lock);
 | 
							spin_unlock(&new->lock);
 | 
				
			||||||
		rcu_read_unlock();
 | 
							rcu_read_unlock();
 | 
				
			||||||
| 
						 | 
					@ -377,6 +397,20 @@ static int ipcget_public(struct ipc_namespace *ns, struct ipc_ids *ids,
 | 
				
			||||||
	return err;
 | 
						return err;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * ipc_kht_remove - remove an ipc from the key hashtable
 | 
				
			||||||
 | 
					 * @ids: ipc identifier set
 | 
				
			||||||
 | 
					 * @ipcp: ipc perm structure containing the key to remove
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * ipc_ids.rwsem (as a writer) and the spinlock for this ID are held
 | 
				
			||||||
 | 
					 * before this function is called, and remain locked on the exit.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static void ipc_kht_remove(struct ipc_ids *ids, struct kern_ipc_perm *ipcp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (ipcp->key != IPC_PRIVATE)
 | 
				
			||||||
 | 
							rhashtable_remove_fast(&ids->key_ht, &ipcp->khtnode,
 | 
				
			||||||
 | 
									       ipc_kht_params);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * ipc_rmid - remove an ipc identifier
 | 
					 * ipc_rmid - remove an ipc identifier
 | 
				
			||||||
| 
						 | 
					@ -391,10 +425,25 @@ void ipc_rmid(struct ipc_ids *ids, struct kern_ipc_perm *ipcp)
 | 
				
			||||||
	int lid = ipcid_to_idx(ipcp->id);
 | 
						int lid = ipcid_to_idx(ipcp->id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	idr_remove(&ids->ipcs_idr, lid);
 | 
						idr_remove(&ids->ipcs_idr, lid);
 | 
				
			||||||
 | 
						ipc_kht_remove(ids, ipcp);
 | 
				
			||||||
	ids->in_use--;
 | 
						ids->in_use--;
 | 
				
			||||||
	ipcp->deleted = true;
 | 
						ipcp->deleted = true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * ipc_set_key_private - switch the key of an existing ipc to IPC_PRIVATE
 | 
				
			||||||
 | 
					 * @ids: ipc identifier set
 | 
				
			||||||
 | 
					 * @ipcp: ipc perm structure containing the key to modify
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * ipc_ids.rwsem (as a writer) and the spinlock for this ID are held
 | 
				
			||||||
 | 
					 * before this function is called, and remain locked on the exit.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void ipc_set_key_private(struct ipc_ids *ids, struct kern_ipc_perm *ipcp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						ipc_kht_remove(ids, ipcp);
 | 
				
			||||||
 | 
						ipcp->key = IPC_PRIVATE;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int ipc_rcu_getref(struct kern_ipc_perm *ptr)
 | 
					int ipc_rcu_getref(struct kern_ipc_perm *ptr)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return refcount_inc_not_zero(&ptr->refcount);
 | 
						return refcount_inc_not_zero(&ptr->refcount);
 | 
				
			||||||
| 
						 | 
					@ -485,7 +534,7 @@ void ipc64_perm_to_ipc_perm(struct ipc64_perm *in, struct ipc_perm *out)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * ipc_obtain_object
 | 
					 * ipc_obtain_object_idr
 | 
				
			||||||
 * @ids: ipc identifier set
 | 
					 * @ids: ipc identifier set
 | 
				
			||||||
 * @id: ipc id to look for
 | 
					 * @id: ipc id to look for
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
| 
						 | 
					@ -499,6 +548,9 @@ struct kern_ipc_perm *ipc_obtain_object_idr(struct ipc_ids *ids, int id)
 | 
				
			||||||
	struct kern_ipc_perm *out;
 | 
						struct kern_ipc_perm *out;
 | 
				
			||||||
	int lid = ipcid_to_idx(id);
 | 
						int lid = ipcid_to_idx(id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (unlikely(!ids->tables_initialized))
 | 
				
			||||||
 | 
							return ERR_PTR(-EINVAL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	out = idr_find(&ids->ipcs_idr, lid);
 | 
						out = idr_find(&ids->ipcs_idr, lid);
 | 
				
			||||||
	if (!out)
 | 
						if (!out)
 | 
				
			||||||
		return ERR_PTR(-EINVAL);
 | 
							return ERR_PTR(-EINVAL);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										21
									
								
								ipc/util.h
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								ipc/util.h
									
									
									
									
									
								
							| 
						 | 
					@ -15,8 +15,8 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define SEQ_MULTIPLIER	(IPCMNI)
 | 
					#define SEQ_MULTIPLIER	(IPCMNI)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void sem_init(void);
 | 
					int sem_init(void);
 | 
				
			||||||
void msg_init(void);
 | 
					int msg_init(void);
 | 
				
			||||||
void shm_init(void);
 | 
					void shm_init(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct ipc_namespace;
 | 
					struct ipc_namespace;
 | 
				
			||||||
| 
						 | 
					@ -30,17 +30,17 @@ static inline void mq_put_mnt(struct ipc_namespace *ns) { }
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_SYSVIPC
 | 
					#ifdef CONFIG_SYSVIPC
 | 
				
			||||||
void sem_init_ns(struct ipc_namespace *ns);
 | 
					int sem_init_ns(struct ipc_namespace *ns);
 | 
				
			||||||
void msg_init_ns(struct ipc_namespace *ns);
 | 
					int msg_init_ns(struct ipc_namespace *ns);
 | 
				
			||||||
void shm_init_ns(struct ipc_namespace *ns);
 | 
					int shm_init_ns(struct ipc_namespace *ns);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void sem_exit_ns(struct ipc_namespace *ns);
 | 
					void sem_exit_ns(struct ipc_namespace *ns);
 | 
				
			||||||
void msg_exit_ns(struct ipc_namespace *ns);
 | 
					void msg_exit_ns(struct ipc_namespace *ns);
 | 
				
			||||||
void shm_exit_ns(struct ipc_namespace *ns);
 | 
					void shm_exit_ns(struct ipc_namespace *ns);
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
static inline void sem_init_ns(struct ipc_namespace *ns) { }
 | 
					static inline int sem_init_ns(struct ipc_namespace *ns) { return 0; }
 | 
				
			||||||
static inline void msg_init_ns(struct ipc_namespace *ns) { }
 | 
					static inline int msg_init_ns(struct ipc_namespace *ns) { return 0; }
 | 
				
			||||||
static inline void shm_init_ns(struct ipc_namespace *ns) { }
 | 
					static inline int shm_init_ns(struct ipc_namespace *ns) { return 0; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline void sem_exit_ns(struct ipc_namespace *ns) { }
 | 
					static inline void sem_exit_ns(struct ipc_namespace *ns) { }
 | 
				
			||||||
static inline void msg_exit_ns(struct ipc_namespace *ns) { }
 | 
					static inline void msg_exit_ns(struct ipc_namespace *ns) { }
 | 
				
			||||||
| 
						 | 
					@ -79,7 +79,7 @@ struct ipc_ops {
 | 
				
			||||||
struct seq_file;
 | 
					struct seq_file;
 | 
				
			||||||
struct ipc_ids;
 | 
					struct ipc_ids;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ipc_init_ids(struct ipc_ids *);
 | 
					int ipc_init_ids(struct ipc_ids *);
 | 
				
			||||||
#ifdef CONFIG_PROC_FS
 | 
					#ifdef CONFIG_PROC_FS
 | 
				
			||||||
void __init ipc_init_proc_interface(const char *path, const char *header,
 | 
					void __init ipc_init_proc_interface(const char *path, const char *header,
 | 
				
			||||||
		int ids, int (*show)(struct seq_file *, void *));
 | 
							int ids, int (*show)(struct seq_file *, void *));
 | 
				
			||||||
| 
						 | 
					@ -104,6 +104,9 @@ int ipc_get_maxid(struct ipc_ids *);
 | 
				
			||||||
/* must be called with both locks acquired. */
 | 
					/* must be called with both locks acquired. */
 | 
				
			||||||
void ipc_rmid(struct ipc_ids *, struct kern_ipc_perm *);
 | 
					void ipc_rmid(struct ipc_ids *, struct kern_ipc_perm *);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* must be called with both locks acquired. */
 | 
				
			||||||
 | 
					void ipc_set_key_private(struct ipc_ids *, struct kern_ipc_perm *);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* must be called with ipcp locked */
 | 
					/* must be called with ipcp locked */
 | 
				
			||||||
int ipcperms(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp, short flg);
 | 
					int ipcperms(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp, short flg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue