aboutsummaryrefslogtreecommitdiffstats
path: root/ipc/util.c
diff options
context:
space:
mode:
Diffstat (limited to 'ipc/util.c')
-rw-r--r--ipc/util.c266
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
139void ipc_init_ids(struct ipc_ids* ids, int size) 137void 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
215int ipc_findkey(struct ipc_ids* ids, key_t key) 200struct 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 */
238static 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 /* 231int 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
285int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size) 269int 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;
300found: 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
333struct kern_ipc_perm* ipc_rmid(struct ipc_ids* ids, int id) 315void 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 */
616struct kern_ipc_perm* ipc_get(struct ipc_ids* ids, int id) 584struct 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
626struct kern_ipc_perm* ipc_lock(struct ipc_ids* ids, int id) 592struct 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
710static 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 */
675struct 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
704static 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
777static void sysvipc_proc_stop(struct seq_file *s, void *it) 747static void sysvipc_proc_stop(struct seq_file *s, void *it)