summaryrefslogtreecommitdiffstats
path: root/ipc
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-11-17 19:56:17 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2017-11-17 19:56:17 -0500
commitfa7f578076a8814caa5371e9f4949e408140766d (patch)
tree8021be6f956e8a259523c394a01183797ce04422 /ipc
parent2dcd9c71c1ffa9a036e09047f60e08383bb0abb6 (diff)
parentd1b069f5febc850556cf49e9bb9092d3179c5be5 (diff)
Merge branch 'akpm' (patches from Andrew)
Merge more updates from Andrew Morton: - a bit more MM - procfs updates - dynamic-debug fixes - lib/ updates - checkpatch - epoll - nilfs2 - signals - rapidio - PID management cleanup and optimization - kcov updates - sysvipc updates - quite a few misc things all over the place * emailed patches from Andrew Morton <akpm@linux-foundation.org>: (94 commits) EXPERT Kconfig menu: fix broken EXPERT menu include/asm-generic/topology.h: remove unused parent_node() macro arch/tile/include/asm/topology.h: remove unused parent_node() macro arch/sparc/include/asm/topology_64.h: remove unused parent_node() macro arch/sh/include/asm/topology.h: remove unused parent_node() macro arch/ia64/include/asm/topology.h: remove unused parent_node() macro drivers/pcmcia/sa1111_badge4.c: avoid unused function warning mm: add infrastructure for get_user_pages_fast() benchmarking sysvipc: make get_maxid O(1) again sysvipc: properly name ipc_addid() limit parameter sysvipc: duplicate lock comments wrt ipc_addid() sysvipc: unteach ids->next_id for !CHECKPOINT_RESTORE initramfs: use time64_t timestamps drivers/watchdog: make use of devm_register_reboot_notifier() kernel/reboot.c: add devm_register_reboot_notifier() kcov: update documentation Makefile: support flag -fsanitizer-coverage=trace-cmp kcov: support comparison operands collection kcov: remove pointless current != NULL check kernel/panic.c: add TAINT_AUX ...
Diffstat (limited to 'ipc')
-rw-r--r--ipc/sem.c1
-rw-r--r--ipc/shm.c1
-rw-r--r--ipc/util.c97
-rw-r--r--ipc/util.h26
4 files changed, 75 insertions, 50 deletions
diff --git a/ipc/sem.c b/ipc/sem.c
index a5cff0e109ab..87bd38f38dc3 100644
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -515,6 +515,7 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params)
515 sma->sem_nsems = nsems; 515 sma->sem_nsems = nsems;
516 sma->sem_ctime = ktime_get_real_seconds(); 516 sma->sem_ctime = ktime_get_real_seconds();
517 517
518 /* ipc_addid() locks sma upon success. */
518 retval = ipc_addid(&sem_ids(ns), &sma->sem_perm, ns->sc_semmni); 519 retval = ipc_addid(&sem_ids(ns), &sma->sem_perm, ns->sc_semmni);
519 if (retval < 0) { 520 if (retval < 0) {
520 call_rcu(&sma->sem_perm.rcu, sem_rcu_free); 521 call_rcu(&sma->sem_perm.rcu, sem_rcu_free);
diff --git a/ipc/shm.c b/ipc/shm.c
index 7733d768666d..7acda23430aa 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -601,6 +601,7 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
601 shp->shm_file = file; 601 shp->shm_file = file;
602 shp->shm_creator = current; 602 shp->shm_creator = current;
603 603
604 /* ipc_addid() locks shp upon success. */
604 error = ipc_addid(&shm_ids(ns), &shp->shm_perm, ns->shm_ctlmni); 605 error = ipc_addid(&shm_ids(ns), &shp->shm_perm, ns->shm_ctlmni);
605 if (error < 0) 606 if (error < 0)
606 goto no_id; 607 goto no_id;
diff --git a/ipc/util.c b/ipc/util.c
index 79b30eee32cd..ff045fec8d83 100644
--- a/ipc/util.c
+++ b/ipc/util.c
@@ -116,13 +116,16 @@ int ipc_init_ids(struct ipc_ids *ids)
116 int err; 116 int err;
117 ids->in_use = 0; 117 ids->in_use = 0;
118 ids->seq = 0; 118 ids->seq = 0;
119 ids->next_id = -1;
120 init_rwsem(&ids->rwsem); 119 init_rwsem(&ids->rwsem);
121 err = rhashtable_init(&ids->key_ht, &ipc_kht_params); 120 err = rhashtable_init(&ids->key_ht, &ipc_kht_params);
122 if (err) 121 if (err)
123 return err; 122 return err;
124 idr_init(&ids->ipcs_idr); 123 idr_init(&ids->ipcs_idr);
125 ids->tables_initialized = true; 124 ids->tables_initialized = true;
125 ids->max_id = -1;
126#ifdef CONFIG_CHECKPOINT_RESTORE
127 ids->next_id = -1;
128#endif
126 return 0; 129 return 0;
127} 130}
128 131
@@ -186,41 +189,51 @@ static struct kern_ipc_perm *ipc_findkey(struct ipc_ids *ids, key_t key)
186 return NULL; 189 return NULL;
187} 190}
188 191
189/** 192#ifdef CONFIG_CHECKPOINT_RESTORE
190 * ipc_get_maxid - get the last assigned id 193/*
191 * @ids: ipc identifier set 194 * Specify desired id for next allocated IPC object.
192 *
193 * Called with ipc_ids.rwsem held.
194 */ 195 */
195int ipc_get_maxid(struct ipc_ids *ids) 196#define ipc_idr_alloc(ids, new) \
197 idr_alloc(&(ids)->ipcs_idr, (new), \
198 (ids)->next_id < 0 ? 0 : ipcid_to_idx((ids)->next_id),\
199 0, GFP_NOWAIT)
200
201static inline int ipc_buildid(int id, struct ipc_ids *ids,
202 struct kern_ipc_perm *new)
196{ 203{
197 struct kern_ipc_perm *ipc; 204 if (ids->next_id < 0) { /* default, behave as !CHECKPOINT_RESTORE */
198 int max_id = -1; 205 new->seq = ids->seq++;
199 int total, id; 206 if (ids->seq > IPCID_SEQ_MAX)
207 ids->seq = 0;
208 } else {
209 new->seq = ipcid_to_seqx(ids->next_id);
210 ids->next_id = -1;
211 }
200 212
201 if (ids->in_use == 0) 213 return SEQ_MULTIPLIER * new->seq + id;
202 return -1; 214}
203 215
204 if (ids->in_use == IPCMNI) 216#else
205 return IPCMNI - 1; 217#define ipc_idr_alloc(ids, new) \
218 idr_alloc(&(ids)->ipcs_idr, (new), 0, 0, GFP_NOWAIT)
206 219
207 /* Look for the last assigned id */ 220static inline int ipc_buildid(int id, struct ipc_ids *ids,
208 total = 0; 221 struct kern_ipc_perm *new)
209 for (id = 0; id < IPCMNI && total < ids->in_use; id++) { 222{
210 ipc = idr_find(&ids->ipcs_idr, id); 223 new->seq = ids->seq++;
211 if (ipc != NULL) { 224 if (ids->seq > IPCID_SEQ_MAX)
212 max_id = id; 225 ids->seq = 0;
213 total++; 226
214 } 227 return SEQ_MULTIPLIER * new->seq + id;
215 }
216 return max_id;
217} 228}
218 229
230#endif /* CONFIG_CHECKPOINT_RESTORE */
231
219/** 232/**
220 * ipc_addid - add an ipc identifier 233 * ipc_addid - add an ipc identifier
221 * @ids: ipc identifier set 234 * @ids: ipc identifier set
222 * @new: new ipc permission set 235 * @new: new ipc permission set
223 * @size: limit for the number of used ids 236 * @limit: limit for the number of used ids
224 * 237 *
225 * Add an entry 'new' to the ipc ids idr. The permissions object is 238 * Add an entry 'new' to the ipc ids idr. The permissions object is
226 * initialised and the first free entry is set up and the id assigned 239 * initialised and the first free entry is set up and the id assigned
@@ -229,17 +242,16 @@ int ipc_get_maxid(struct ipc_ids *ids)
229 * 242 *
230 * Called with writer ipc_ids.rwsem held. 243 * Called with writer ipc_ids.rwsem held.
231 */ 244 */
232int ipc_addid(struct ipc_ids *ids, struct kern_ipc_perm *new, int size) 245int ipc_addid(struct ipc_ids *ids, struct kern_ipc_perm *new, int limit)
233{ 246{
234 kuid_t euid; 247 kuid_t euid;
235 kgid_t egid; 248 kgid_t egid;
236 int id, err; 249 int id, err;
237 int next_id = ids->next_id;
238 250
239 if (size > IPCMNI) 251 if (limit > IPCMNI)
240 size = IPCMNI; 252 limit = IPCMNI;
241 253
242 if (!ids->tables_initialized || ids->in_use >= size) 254 if (!ids->tables_initialized || ids->in_use >= limit)
243 return -ENOSPC; 255 return -ENOSPC;
244 256
245 idr_preload(GFP_KERNEL); 257 idr_preload(GFP_KERNEL);
@@ -254,9 +266,7 @@ int ipc_addid(struct ipc_ids *ids, struct kern_ipc_perm *new, int size)
254 new->cuid = new->uid = euid; 266 new->cuid = new->uid = euid;
255 new->gid = new->cgid = egid; 267 new->gid = new->cgid = egid;
256 268
257 id = idr_alloc(&ids->ipcs_idr, new, 269 id = ipc_idr_alloc(ids, new);
258 (next_id < 0) ? 0 : ipcid_to_idx(next_id), 0,
259 GFP_NOWAIT);
260 idr_preload_end(); 270 idr_preload_end();
261 271
262 if (id >= 0 && new->key != IPC_PRIVATE) { 272 if (id >= 0 && new->key != IPC_PRIVATE) {
@@ -274,17 +284,11 @@ int ipc_addid(struct ipc_ids *ids, struct kern_ipc_perm *new, int size)
274 } 284 }
275 285
276 ids->in_use++; 286 ids->in_use++;
287 if (id > ids->max_id)
288 ids->max_id = id;
277 289
278 if (next_id < 0) { 290 new->id = ipc_buildid(id, ids, new);
279 new->seq = ids->seq++;
280 if (ids->seq > IPCID_SEQ_MAX)
281 ids->seq = 0;
282 } else {
283 new->seq = ipcid_to_seqx(next_id);
284 ids->next_id = -1;
285 }
286 291
287 new->id = ipc_buildid(id, new->seq);
288 return id; 292 return id;
289} 293}
290 294
@@ -429,6 +433,15 @@ void ipc_rmid(struct ipc_ids *ids, struct kern_ipc_perm *ipcp)
429 ipc_kht_remove(ids, ipcp); 433 ipc_kht_remove(ids, ipcp);
430 ids->in_use--; 434 ids->in_use--;
431 ipcp->deleted = true; 435 ipcp->deleted = true;
436
437 if (unlikely(lid == ids->max_id)) {
438 do {
439 lid--;
440 if (lid == -1)
441 break;
442 } while (!idr_find(&ids->ipcs_idr, lid));
443 ids->max_id = lid;
444 }
432} 445}
433 446
434/** 447/**
diff --git a/ipc/util.h b/ipc/util.h
index 579112d90016..89b8ec176fc4 100644
--- a/ipc/util.h
+++ b/ipc/util.h
@@ -13,6 +13,7 @@
13 13
14#include <linux/unistd.h> 14#include <linux/unistd.h>
15#include <linux/err.h> 15#include <linux/err.h>
16#include <linux/ipc_namespace.h>
16 17
17#define SEQ_MULTIPLIER (IPCMNI) 18#define SEQ_MULTIPLIER (IPCMNI)
18 19
@@ -99,9 +100,6 @@ void __init ipc_init_proc_interface(const char *path, const char *header,
99/* must be called with ids->rwsem acquired for writing */ 100/* must be called with ids->rwsem acquired for writing */
100int ipc_addid(struct ipc_ids *, struct kern_ipc_perm *, int); 101int ipc_addid(struct ipc_ids *, struct kern_ipc_perm *, int);
101 102
102/* must be called with ids->rwsem acquired for reading */
103int ipc_get_maxid(struct ipc_ids *);
104
105/* must be called with both locks acquired. */ 103/* must be called with both locks acquired. */
106void ipc_rmid(struct ipc_ids *, struct kern_ipc_perm *); 104void ipc_rmid(struct ipc_ids *, struct kern_ipc_perm *);
107 105
@@ -111,6 +109,23 @@ void ipc_set_key_private(struct ipc_ids *, struct kern_ipc_perm *);
111/* must be called with ipcp locked */ 109/* must be called with ipcp locked */
112int ipcperms(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp, short flg); 110int ipcperms(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp, short flg);
113 111
112/**
113 * ipc_get_maxid - get the last assigned id
114 * @ids: ipc identifier set
115 *
116 * Called with ipc_ids.rwsem held for reading.
117 */
118static inline int ipc_get_maxid(struct ipc_ids *ids)
119{
120 if (ids->in_use == 0)
121 return -1;
122
123 if (ids->in_use == IPCMNI)
124 return IPCMNI - 1;
125
126 return ids->max_id;
127}
128
114/* 129/*
115 * For allocation that need to be freed by RCU. 130 * For allocation that need to be freed by RCU.
116 * Objects are reference counted, they start with reference count 1. 131 * Objects are reference counted, they start with reference count 1.
@@ -146,11 +161,6 @@ extern struct msg_msg *load_msg(const void __user *src, size_t len);
146extern struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst); 161extern struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst);
147extern int store_msg(void __user *dest, struct msg_msg *msg, size_t len); 162extern int store_msg(void __user *dest, struct msg_msg *msg, size_t len);
148 163
149static inline int ipc_buildid(int id, int seq)
150{
151 return SEQ_MULTIPLIER * seq + id;
152}
153
154static inline int ipc_checkid(struct kern_ipc_perm *ipcp, int uid) 164static inline int ipc_checkid(struct kern_ipc_perm *ipcp, int uid)
155{ 165{
156 return uid / SEQ_MULTIPLIER != ipcp->seq; 166 return uid / SEQ_MULTIPLIER != ipcp->seq;