diff options
Diffstat (limited to 'ipc/util.c')
-rw-r--r-- | ipc/util.c | 266 |
1 files changed, 118 insertions, 148 deletions
diff --git a/ipc/util.c b/ipc/util.c index 44e5135aee47..2a205875d277 100644 --- a/ipc/util.c +++ b/ipc/util.c | |||
@@ -129,23 +129,16 @@ __initcall(ipc_init); | |||
129 | /** | 129 | /** |
130 | * ipc_init_ids - initialise IPC identifiers | 130 | * ipc_init_ids - initialise IPC identifiers |
131 | * @ids: Identifier set | 131 | * @ids: Identifier set |
132 | * @size: Number of identifiers | ||
133 | * | 132 | * |
134 | * Given a size for the ipc identifier range (limited below IPCMNI) | 133 | * Set up the sequence range to use for the ipc identifier range (limited |
135 | * set up the sequence range to use then allocate and initialise the | 134 | * below IPCMNI) then initialise the ids idr. |
136 | * array itself. | ||
137 | */ | 135 | */ |
138 | 136 | ||
139 | void ipc_init_ids(struct ipc_ids* ids, int size) | 137 | void ipc_init_ids(struct ipc_ids *ids) |
140 | { | 138 | { |
141 | int i; | ||
142 | |||
143 | mutex_init(&ids->mutex); | 139 | mutex_init(&ids->mutex); |
144 | 140 | ||
145 | if(size > IPCMNI) | ||
146 | size = IPCMNI; | ||
147 | ids->in_use = 0; | 141 | ids->in_use = 0; |
148 | ids->max_id = -1; | ||
149 | ids->seq = 0; | 142 | ids->seq = 0; |
150 | { | 143 | { |
151 | int seq_limit = INT_MAX/SEQ_MULTIPLIER; | 144 | int seq_limit = INT_MAX/SEQ_MULTIPLIER; |
@@ -155,17 +148,7 @@ void ipc_init_ids(struct ipc_ids* ids, int size) | |||
155 | ids->seq_max = seq_limit; | 148 | ids->seq_max = seq_limit; |
156 | } | 149 | } |
157 | 150 | ||
158 | ids->entries = ipc_rcu_alloc(sizeof(struct kern_ipc_perm *)*size + | 151 | idr_init(&ids->ipcs_idr); |
159 | sizeof(struct ipc_id_ary)); | ||
160 | |||
161 | if(ids->entries == NULL) { | ||
162 | printk(KERN_ERR "ipc_init_ids() failed, ipc service disabled.\n"); | ||
163 | size = 0; | ||
164 | ids->entries = &ids->nullentry; | ||
165 | } | ||
166 | ids->entries->size = size; | ||
167 | for(i=0;i<size;i++) | ||
168 | ids->entries->p[i] = NULL; | ||
169 | } | 152 | } |
170 | 153 | ||
171 | #ifdef CONFIG_PROC_FS | 154 | #ifdef CONFIG_PROC_FS |
@@ -209,72 +192,73 @@ void __init ipc_init_proc_interface(const char *path, const char *header, | |||
209 | * @key: The key to find | 192 | * @key: The key to find |
210 | * | 193 | * |
211 | * Requires ipc_ids.mutex locked. | 194 | * Requires ipc_ids.mutex locked. |
212 | * Returns the identifier if found or -1 if not. | 195 | * Returns the LOCKED pointer to the ipc structure if found or NULL |
196 | * if not. | ||
197 | * If key is found ipc contains its ipc structure | ||
213 | */ | 198 | */ |
214 | 199 | ||
215 | int ipc_findkey(struct ipc_ids* ids, key_t key) | 200 | struct kern_ipc_perm *ipc_findkey(struct ipc_ids *ids, key_t key) |
216 | { | 201 | { |
217 | int id; | 202 | struct kern_ipc_perm *ipc; |
218 | struct kern_ipc_perm* p; | 203 | int next_id; |
219 | int max_id = ids->max_id; | 204 | int total; |
220 | 205 | ||
221 | /* | 206 | for (total = 0, next_id = 0; total < ids->in_use; next_id++) { |
222 | * rcu_dereference() is not needed here | 207 | ipc = idr_find(&ids->ipcs_idr, next_id); |
223 | * since ipc_ids.mutex is held | 208 | |
224 | */ | 209 | if (ipc == NULL) |
225 | for (id = 0; id <= max_id; id++) { | ||
226 | p = ids->entries->p[id]; | ||
227 | if(p==NULL) | ||
228 | continue; | 210 | continue; |
229 | if (key == p->key) | 211 | |
230 | return id; | 212 | if (ipc->key != key) { |
213 | total++; | ||
214 | continue; | ||
215 | } | ||
216 | |||
217 | ipc_lock_by_ptr(ipc); | ||
218 | return ipc; | ||
231 | } | 219 | } |
232 | return -1; | 220 | |
221 | return NULL; | ||
233 | } | 222 | } |
234 | 223 | ||
235 | /* | 224 | /** |
236 | * Requires ipc_ids.mutex locked | 225 | * ipc_get_maxid - get the last assigned id |
226 | * @ids: IPC identifier set | ||
227 | * | ||
228 | * Called with ipc_ids.mutex held. | ||
237 | */ | 229 | */ |
238 | static int grow_ary(struct ipc_ids* ids, int newsize) | ||
239 | { | ||
240 | struct ipc_id_ary* new; | ||
241 | struct ipc_id_ary* old; | ||
242 | int i; | ||
243 | int size = ids->entries->size; | ||
244 | |||
245 | if(newsize > IPCMNI) | ||
246 | newsize = IPCMNI; | ||
247 | if(newsize <= size) | ||
248 | return newsize; | ||
249 | |||
250 | new = ipc_rcu_alloc(sizeof(struct kern_ipc_perm *)*newsize + | ||
251 | sizeof(struct ipc_id_ary)); | ||
252 | if(new == NULL) | ||
253 | return size; | ||
254 | new->size = newsize; | ||
255 | memcpy(new->p, ids->entries->p, sizeof(struct kern_ipc_perm *)*size); | ||
256 | for(i=size;i<newsize;i++) { | ||
257 | new->p[i] = NULL; | ||
258 | } | ||
259 | old = ids->entries; | ||
260 | 230 | ||
261 | /* | 231 | int ipc_get_maxid(struct ipc_ids *ids) |
262 | * Use rcu_assign_pointer() to make sure the memcpyed contents | 232 | { |
263 | * of the new array are visible before the new array becomes visible. | 233 | struct kern_ipc_perm *ipc; |
264 | */ | 234 | int max_id = -1; |
265 | rcu_assign_pointer(ids->entries, new); | 235 | int total, id; |
236 | |||
237 | if (ids->in_use == 0) | ||
238 | return -1; | ||
266 | 239 | ||
267 | __ipc_fini_ids(ids, old); | 240 | if (ids->in_use == IPCMNI) |
268 | return newsize; | 241 | return IPCMNI - 1; |
242 | |||
243 | /* Look for the last assigned id */ | ||
244 | total = 0; | ||
245 | for (id = 0; id < IPCMNI && total < ids->in_use; id++) { | ||
246 | ipc = idr_find(&ids->ipcs_idr, id); | ||
247 | if (ipc != NULL) { | ||
248 | max_id = id; | ||
249 | total++; | ||
250 | } | ||
251 | } | ||
252 | return max_id; | ||
269 | } | 253 | } |
270 | 254 | ||
271 | /** | 255 | /** |
272 | * ipc_addid - add an IPC identifier | 256 | * ipc_addid - add an IPC identifier |
273 | * @ids: IPC identifier set | 257 | * @ids: IPC identifier set |
274 | * @new: new IPC permission set | 258 | * @new: new IPC permission set |
275 | * @size: new size limit for the id array | 259 | * @size: limit for the number of used ids |
276 | * | 260 | * |
277 | * Add an entry 'new' to the IPC arrays. The permissions object is | 261 | * Add an entry 'new' to the IPC idr. The permissions object is |
278 | * initialised and the first free entry is set up and the id assigned | 262 | * initialised and the first free entry is set up and the id assigned |
279 | * is returned. The list is returned in a locked state on success. | 263 | * is returned. The list is returned in a locked state on success. |
280 | * On failure the list is not locked and -1 is returned. | 264 | * On failure the list is not locked and -1 is returned. |
@@ -284,23 +268,23 @@ static int grow_ary(struct ipc_ids* ids, int newsize) | |||
284 | 268 | ||
285 | int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size) | 269 | int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size) |
286 | { | 270 | { |
287 | int id; | 271 | int id, err; |
288 | |||
289 | size = grow_ary(ids,size); | ||
290 | 272 | ||
291 | /* | 273 | /* |
292 | * rcu_dereference()() is not needed here since | 274 | * rcu_dereference()() is not needed here since |
293 | * ipc_ids.mutex is held | 275 | * ipc_ids.mutex is held |
294 | */ | 276 | */ |
295 | for (id = 0; id < size; id++) { | 277 | if (size > IPCMNI) |
296 | if(ids->entries->p[id] == NULL) | 278 | size = IPCMNI; |
297 | goto found; | 279 | |
298 | } | 280 | if (ids->in_use >= size) |
299 | return -1; | 281 | return -1; |
300 | found: | 282 | |
283 | err = idr_get_new(&ids->ipcs_idr, new, &id); | ||
284 | if (err) | ||
285 | return -1; | ||
286 | |||
301 | ids->in_use++; | 287 | ids->in_use++; |
302 | if (id > ids->max_id) | ||
303 | ids->max_id = id; | ||
304 | 288 | ||
305 | new->cuid = new->uid = current->euid; | 289 | new->cuid = new->uid = current->euid; |
306 | new->gid = new->cgid = current->egid; | 290 | new->gid = new->cgid = current->egid; |
@@ -313,48 +297,32 @@ found: | |||
313 | new->deleted = 0; | 297 | new->deleted = 0; |
314 | rcu_read_lock(); | 298 | rcu_read_lock(); |
315 | spin_lock(&new->lock); | 299 | spin_lock(&new->lock); |
316 | ids->entries->p[id] = new; | ||
317 | return id; | 300 | return id; |
318 | } | 301 | } |
319 | 302 | ||
320 | /** | 303 | /** |
321 | * ipc_rmid - remove an IPC identifier | 304 | * ipc_rmid - remove an IPC identifier |
322 | * @ids: identifier set | 305 | * @ids: identifier set |
323 | * @id: Identifier to remove | 306 | * @id: ipc perm structure containing the identifier to remove |
324 | * | 307 | * |
325 | * The identifier must be valid, and in use. The kernel will panic if | 308 | * The identifier must be valid, and in use. The kernel will panic if |
326 | * fed an invalid identifier. The entry is removed and internal | 309 | * fed an invalid identifier. The entry is removed and internal |
327 | * variables recomputed. The object associated with the identifier | 310 | * variables recomputed. |
328 | * is returned. | 311 | * ipc_ids.mutex and the spinlock for this ID are held before this |
329 | * ipc_ids.mutex and the spinlock for this ID is hold before this function | 312 | * function is called, and remain locked on the exit. |
330 | * is called, and remain locked on the exit. | ||
331 | */ | 313 | */ |
332 | 314 | ||
333 | struct kern_ipc_perm* ipc_rmid(struct ipc_ids* ids, int id) | 315 | void ipc_rmid(struct ipc_ids *ids, struct kern_ipc_perm *ipcp) |
334 | { | 316 | { |
335 | struct kern_ipc_perm* p; | 317 | int lid = ipcp->id % SEQ_MULTIPLIER; |
336 | int lid = id % SEQ_MULTIPLIER; | 318 | |
337 | BUG_ON(lid >= ids->entries->size); | 319 | idr_remove(&ids->ipcs_idr, lid); |
338 | 320 | ||
339 | /* | ||
340 | * do not need a rcu_dereference()() here to force ordering | ||
341 | * on Alpha, since the ipc_ids.mutex is held. | ||
342 | */ | ||
343 | p = ids->entries->p[lid]; | ||
344 | ids->entries->p[lid] = NULL; | ||
345 | BUG_ON(p==NULL); | ||
346 | ids->in_use--; | 321 | ids->in_use--; |
347 | 322 | ||
348 | if (lid == ids->max_id) { | 323 | ipcp->deleted = 1; |
349 | do { | 324 | |
350 | lid--; | 325 | return; |
351 | if(lid == -1) | ||
352 | break; | ||
353 | } while (ids->entries->p[lid] == NULL); | ||
354 | ids->max_id = lid; | ||
355 | } | ||
356 | p->deleted = 1; | ||
357 | return p; | ||
358 | } | 326 | } |
359 | 327 | ||
360 | /** | 328 | /** |
@@ -613,33 +581,26 @@ void ipc64_perm_to_ipc_perm (struct ipc64_perm *in, struct ipc_perm *out) | |||
613 | * if in the future ipc_get() is used by other places without ipc_ids.mutex | 581 | * if in the future ipc_get() is used by other places without ipc_ids.mutex |
614 | * down, then ipc_get() needs read memery barriers as ipc_lock() does. | 582 | * down, then ipc_get() needs read memery barriers as ipc_lock() does. |
615 | */ | 583 | */ |
616 | struct kern_ipc_perm* ipc_get(struct ipc_ids* ids, int id) | 584 | struct kern_ipc_perm *ipc_get(struct ipc_ids *ids, int id) |
617 | { | 585 | { |
618 | struct kern_ipc_perm* out; | 586 | struct kern_ipc_perm *out; |
619 | int lid = id % SEQ_MULTIPLIER; | 587 | int lid = id % SEQ_MULTIPLIER; |
620 | if(lid >= ids->entries->size) | 588 | out = idr_find(&ids->ipcs_idr, lid); |
621 | return NULL; | ||
622 | out = ids->entries->p[lid]; | ||
623 | return out; | 589 | return out; |
624 | } | 590 | } |
625 | 591 | ||
626 | struct kern_ipc_perm* ipc_lock(struct ipc_ids* ids, int id) | 592 | struct kern_ipc_perm *ipc_lock(struct ipc_ids *ids, int id) |
627 | { | 593 | { |
628 | struct kern_ipc_perm* out; | 594 | struct kern_ipc_perm *out; |
629 | int lid = id % SEQ_MULTIPLIER; | 595 | int lid = id % SEQ_MULTIPLIER; |
630 | struct ipc_id_ary* entries; | ||
631 | 596 | ||
632 | rcu_read_lock(); | 597 | rcu_read_lock(); |
633 | entries = rcu_dereference(ids->entries); | 598 | out = idr_find(&ids->ipcs_idr, lid); |
634 | if(lid >= entries->size) { | 599 | if (out == NULL) { |
635 | rcu_read_unlock(); | ||
636 | return NULL; | ||
637 | } | ||
638 | out = entries->p[lid]; | ||
639 | if(out == NULL) { | ||
640 | rcu_read_unlock(); | 600 | rcu_read_unlock(); |
641 | return NULL; | 601 | return NULL; |
642 | } | 602 | } |
603 | |||
643 | spin_lock(&out->lock); | 604 | spin_lock(&out->lock); |
644 | 605 | ||
645 | /* ipc_rmid() may have already freed the ID while ipc_lock | 606 | /* ipc_rmid() may have already freed the ID while ipc_lock |
@@ -650,6 +611,7 @@ struct kern_ipc_perm* ipc_lock(struct ipc_ids* ids, int id) | |||
650 | rcu_read_unlock(); | 611 | rcu_read_unlock(); |
651 | return NULL; | 612 | return NULL; |
652 | } | 613 | } |
614 | |||
653 | return out; | 615 | return out; |
654 | } | 616 | } |
655 | 617 | ||
@@ -707,27 +669,30 @@ struct ipc_proc_iter { | |||
707 | struct ipc_proc_iface *iface; | 669 | struct ipc_proc_iface *iface; |
708 | }; | 670 | }; |
709 | 671 | ||
710 | static void *sysvipc_proc_next(struct seq_file *s, void *it, loff_t *pos) | 672 | /* |
673 | * This routine locks the ipc structure found at least at position pos. | ||
674 | */ | ||
675 | struct kern_ipc_perm *sysvipc_find_ipc(struct ipc_ids *ids, loff_t pos, | ||
676 | loff_t *new_pos) | ||
711 | { | 677 | { |
712 | struct ipc_proc_iter *iter = s->private; | 678 | struct kern_ipc_perm *ipc; |
713 | struct ipc_proc_iface *iface = iter->iface; | 679 | int total, id; |
714 | struct kern_ipc_perm *ipc = it; | ||
715 | loff_t p; | ||
716 | struct ipc_ids *ids; | ||
717 | 680 | ||
718 | ids = iter->ns->ids[iface->ids]; | 681 | total = 0; |
682 | for (id = 0; id < pos && total < ids->in_use; id++) { | ||
683 | ipc = idr_find(&ids->ipcs_idr, id); | ||
684 | if (ipc != NULL) | ||
685 | total++; | ||
686 | } | ||
719 | 687 | ||
720 | /* If we had an ipc id locked before, unlock it */ | 688 | if (total >= ids->in_use) |
721 | if (ipc && ipc != SEQ_START_TOKEN) | 689 | return NULL; |
722 | ipc_unlock(ipc); | ||
723 | 690 | ||
724 | /* | 691 | for ( ; pos < IPCMNI; pos++) { |
725 | * p = *pos - 1 (because id 0 starts at position 1) | 692 | ipc = idr_find(&ids->ipcs_idr, pos); |
726 | * + 1 (because we increment the position by one) | 693 | if (ipc != NULL) { |
727 | */ | 694 | *new_pos = pos + 1; |
728 | for (p = *pos; p <= ids->max_id; p++) { | 695 | ipc_lock_by_ptr(ipc); |
729 | if ((ipc = ipc_lock(ids, p)) != NULL) { | ||
730 | *pos = p + 1; | ||
731 | return ipc; | 696 | return ipc; |
732 | } | 697 | } |
733 | } | 698 | } |
@@ -736,6 +701,19 @@ static void *sysvipc_proc_next(struct seq_file *s, void *it, loff_t *pos) | |||
736 | return NULL; | 701 | return NULL; |
737 | } | 702 | } |
738 | 703 | ||
704 | static void *sysvipc_proc_next(struct seq_file *s, void *it, loff_t *pos) | ||
705 | { | ||
706 | struct ipc_proc_iter *iter = s->private; | ||
707 | struct ipc_proc_iface *iface = iter->iface; | ||
708 | struct kern_ipc_perm *ipc = it; | ||
709 | |||
710 | /* If we had an ipc id locked before, unlock it */ | ||
711 | if (ipc && ipc != SEQ_START_TOKEN) | ||
712 | ipc_unlock(ipc); | ||
713 | |||
714 | return sysvipc_find_ipc(iter->ns->ids[iface->ids], *pos, pos); | ||
715 | } | ||
716 | |||
739 | /* | 717 | /* |
740 | * File positions: pos 0 -> header, pos n -> ipc id + 1. | 718 | * File positions: pos 0 -> header, pos n -> ipc id + 1. |
741 | * SeqFile iterator: iterator value locked shp or SEQ_TOKEN_START. | 719 | * SeqFile iterator: iterator value locked shp or SEQ_TOKEN_START. |
@@ -744,8 +722,6 @@ static void *sysvipc_proc_start(struct seq_file *s, loff_t *pos) | |||
744 | { | 722 | { |
745 | struct ipc_proc_iter *iter = s->private; | 723 | struct ipc_proc_iter *iter = s->private; |
746 | struct ipc_proc_iface *iface = iter->iface; | 724 | struct ipc_proc_iface *iface = iter->iface; |
747 | struct kern_ipc_perm *ipc; | ||
748 | loff_t p; | ||
749 | struct ipc_ids *ids; | 725 | struct ipc_ids *ids; |
750 | 726 | ||
751 | ids = iter->ns->ids[iface->ids]; | 727 | ids = iter->ns->ids[iface->ids]; |
@@ -765,13 +741,7 @@ static void *sysvipc_proc_start(struct seq_file *s, loff_t *pos) | |||
765 | return SEQ_START_TOKEN; | 741 | return SEQ_START_TOKEN; |
766 | 742 | ||
767 | /* Find the (pos-1)th ipc */ | 743 | /* Find the (pos-1)th ipc */ |
768 | for (p = *pos - 1; p <= ids->max_id; p++) { | 744 | return sysvipc_find_ipc(ids, *pos - 1, pos); |
769 | if ((ipc = ipc_lock(ids, p)) != NULL) { | ||
770 | *pos = p + 1; | ||
771 | return ipc; | ||
772 | } | ||
773 | } | ||
774 | return NULL; | ||
775 | } | 745 | } |
776 | 746 | ||
777 | static void sysvipc_proc_stop(struct seq_file *s, void *it) | 747 | static void sysvipc_proc_stop(struct seq_file *s, void *it) |