diff options
Diffstat (limited to 'ipc/util.c')
| -rw-r--r-- | ipc/util.c | 59 |
1 files changed, 33 insertions, 26 deletions
diff --git a/ipc/util.c b/ipc/util.c index e829da9ed01f..7684f41bce76 100644 --- a/ipc/util.c +++ b/ipc/util.c | |||
| @@ -17,12 +17,27 @@ | |||
| 17 | * Pavel Emelianov <xemul@openvz.org> | 17 | * Pavel Emelianov <xemul@openvz.org> |
| 18 | * | 18 | * |
| 19 | * General sysv ipc locking scheme: | 19 | * General sysv ipc locking scheme: |
| 20 | * when doing ipc id lookups, take the ids->rwsem | 20 | * rcu_read_lock() |
| 21 | * rcu_read_lock() | 21 | * obtain the ipc object (kern_ipc_perm) by looking up the id in an idr |
| 22 | * obtain the ipc object (kern_ipc_perm) | 22 | * tree. |
| 23 | * perform security, capabilities, auditing and permission checks, etc. | 23 | * - perform initial checks (capabilities, auditing and permission, |
| 24 | * acquire the ipc lock (kern_ipc_perm.lock) throught ipc_lock_object() | 24 | * etc). |
| 25 | * perform data updates (ie: SET, RMID, LOCK/UNLOCK commands) | 25 | * - perform read-only operations, such as STAT, INFO commands. |
| 26 | * acquire the ipc lock (kern_ipc_perm.lock) through | ||
| 27 | * ipc_lock_object() | ||
| 28 | * - perform data updates, such as SET, RMID commands and | ||
| 29 | * mechanism-specific operations (semop/semtimedop, | ||
| 30 | * msgsnd/msgrcv, shmat/shmdt). | ||
| 31 | * drop the ipc lock, through ipc_unlock_object(). | ||
| 32 | * rcu_read_unlock() | ||
| 33 | * | ||
| 34 | * The ids->rwsem must be taken when: | ||
| 35 | * - creating, removing and iterating the existing entries in ipc | ||
| 36 | * identifier sets. | ||
| 37 | * - iterating through files under /proc/sysvipc/ | ||
| 38 | * | ||
| 39 | * Note that sems have a special fast path that avoids kern_ipc_perm.lock - | ||
| 40 | * see sem_lock(). | ||
| 26 | */ | 41 | */ |
| 27 | 42 | ||
| 28 | #include <linux/mm.h> | 43 | #include <linux/mm.h> |
| @@ -474,11 +489,6 @@ void ipc_free(void* ptr, int size) | |||
| 474 | kfree(ptr); | 489 | kfree(ptr); |
| 475 | } | 490 | } |
| 476 | 491 | ||
| 477 | struct ipc_rcu { | ||
| 478 | struct rcu_head rcu; | ||
| 479 | atomic_t refcount; | ||
| 480 | } ____cacheline_aligned_in_smp; | ||
| 481 | |||
| 482 | /** | 492 | /** |
| 483 | * ipc_rcu_alloc - allocate ipc and rcu space | 493 | * ipc_rcu_alloc - allocate ipc and rcu space |
| 484 | * @size: size desired | 494 | * @size: size desired |
| @@ -505,27 +515,24 @@ int ipc_rcu_getref(void *ptr) | |||
| 505 | return atomic_inc_not_zero(&p->refcount); | 515 | return atomic_inc_not_zero(&p->refcount); |
| 506 | } | 516 | } |
| 507 | 517 | ||
| 508 | /** | 518 | void ipc_rcu_putref(void *ptr, void (*func)(struct rcu_head *head)) |
| 509 | * ipc_schedule_free - free ipc + rcu space | ||
| 510 | * @head: RCU callback structure for queued work | ||
| 511 | */ | ||
| 512 | static void ipc_schedule_free(struct rcu_head *head) | ||
| 513 | { | ||
| 514 | vfree(container_of(head, struct ipc_rcu, rcu)); | ||
| 515 | } | ||
| 516 | |||
| 517 | void ipc_rcu_putref(void *ptr) | ||
| 518 | { | 519 | { |
| 519 | struct ipc_rcu *p = ((struct ipc_rcu *)ptr) - 1; | 520 | struct ipc_rcu *p = ((struct ipc_rcu *)ptr) - 1; |
| 520 | 521 | ||
| 521 | if (!atomic_dec_and_test(&p->refcount)) | 522 | if (!atomic_dec_and_test(&p->refcount)) |
| 522 | return; | 523 | return; |
| 523 | 524 | ||
| 524 | if (is_vmalloc_addr(ptr)) { | 525 | call_rcu(&p->rcu, func); |
| 525 | call_rcu(&p->rcu, ipc_schedule_free); | 526 | } |
| 526 | } else { | 527 | |
| 527 | kfree_rcu(p, rcu); | 528 | void ipc_rcu_free(struct rcu_head *head) |
| 528 | } | 529 | { |
| 530 | struct ipc_rcu *p = container_of(head, struct ipc_rcu, rcu); | ||
| 531 | |||
| 532 | if (is_vmalloc_addr(p)) | ||
| 533 | vfree(p); | ||
| 534 | else | ||
| 535 | kfree(p); | ||
| 529 | } | 536 | } |
| 530 | 537 | ||
| 531 | /** | 538 | /** |
