dmaengine: make clients responsible for managing channels
[linux-2.6.git] / ipc / util.c
index 10e836d..7536a72 100644 (file)
@@ -7,25 +7,31 @@
  *            Occurs in several places in the IPC code.
  *            Chris Evans, <chris@ferret.lmh.ox.ac.uk>
  * Nov 1999 - ipc helper functions, unified SMP locking
- *           Manfred Spraul <manfreds@colorfullife.com>
+ *           Manfred Spraul <manfred@colorfullife.com>
  * Oct 2002 - One lock per IPC id. RCU ipc_free for lock-free grow_ary().
  *            Mingming Cao <cmm@us.ibm.com>
+ * Mar 2006 - support for audit of ipc object properties
+ *            Dustin Kirkland <dustin.kirkland@us.ibm.com>
+ * Jun 2006 - namespaces ssupport
+ *            OpenVZ, SWsoft Inc.
+ *            Pavel Emelianov <xemul@openvz.org>
  */
 
-#include <linux/config.h>
 #include <linux/mm.h>
 #include <linux/shm.h>
 #include <linux/init.h>
 #include <linux/msg.h>
-#include <linux/smp_lock.h>
 #include <linux/vmalloc.h>
 #include <linux/slab.h>
+#include <linux/capability.h>
 #include <linux/highuid.h>
 #include <linux/security.h>
 #include <linux/rcupdate.h>
 #include <linux/workqueue.h>
 #include <linux/seq_file.h>
 #include <linux/proc_fs.h>
+#include <linux/audit.h>
+#include <linux/nsproxy.h>
 
 #include <asm/unistd.h>
 
 struct ipc_proc_iface {
        const char *path;
        const char *header;
-       struct ipc_ids *ids;
+       int ids;
        int (*show)(struct seq_file *, void *);
 };
 
+struct ipc_namespace init_ipc_ns = {
+       .kref = {
+               .refcount       = ATOMIC_INIT(2),
+       },
+};
+
+#ifdef CONFIG_IPC_NS
+static struct ipc_namespace *clone_ipc_ns(struct ipc_namespace *old_ns)
+{
+       int err;
+       struct ipc_namespace *ns;
+
+       err = -ENOMEM;
+       ns = kmalloc(sizeof(struct ipc_namespace), GFP_KERNEL);
+       if (ns == NULL)
+               goto err_mem;
+
+       err = sem_init_ns(ns);
+       if (err)
+               goto err_sem;
+       err = msg_init_ns(ns);
+       if (err)
+               goto err_msg;
+       err = shm_init_ns(ns);
+       if (err)
+               goto err_shm;
+
+       kref_init(&ns->kref);
+       return ns;
+
+err_shm:
+       msg_exit_ns(ns);
+err_msg:
+       sem_exit_ns(ns);
+err_sem:
+       kfree(ns);
+err_mem:
+       return ERR_PTR(err);
+}
+
+struct ipc_namespace *copy_ipcs(unsigned long flags, struct ipc_namespace *ns)
+{
+       struct ipc_namespace *new_ns;
+
+       BUG_ON(!ns);
+       get_ipc_ns(ns);
+
+       if (!(flags & CLONE_NEWIPC))
+               return ns;
+
+       new_ns = clone_ipc_ns(ns);
+
+       put_ipc_ns(ns);
+       return new_ns;
+}
+
+void free_ipc_ns(struct kref *kref)
+{
+       struct ipc_namespace *ns;
+
+       ns = container_of(kref, struct ipc_namespace, kref);
+       sem_exit_ns(ns);
+       msg_exit_ns(ns);
+       shm_exit_ns(ns);
+       kfree(ns);
+}
+#else
+struct ipc_namespace *copy_ipcs(unsigned long flags, struct ipc_namespace *ns)
+{
+       if (flags & CLONE_NEWIPC)
+               return ERR_PTR(-EINVAL);
+       return ns;
+}
+#endif
+
 /**
  *     ipc_init        -       initialise IPC subsystem
  *
  *     The various system5 IPC resources (semaphores, messages and shared
- *     memory are initialised
+ *     memory) are initialised
  */
  
 static int __init ipc_init(void)
@@ -64,10 +145,11 @@ __initcall(ipc_init);
  *     array itself. 
  */
  
-void __init ipc_init_ids(struct ipc_ids* ids, int size)
+void __ipc_init ipc_init_ids(struct ipc_ids* ids, int size)
 {
        int i;
-       sema_init(&ids->sem,1);
+
+       mutex_init(&ids->mutex);
 
        if(size > IPCMNI)
                size = IPCMNI;
@@ -96,18 +178,16 @@ void __init ipc_init_ids(struct ipc_ids* ids, int size)
 }
 
 #ifdef CONFIG_PROC_FS
-static struct file_operations sysvipc_proc_fops;
+static const struct file_operations sysvipc_proc_fops;
 /**
- *     ipc_init_proc_interface -  Create a proc interface for sysipc types
- *                                using a seq_file interface.
+ *     ipc_init_proc_interface -  Create a proc interface for sysipc types using a seq_file interface.
  *     @path: Path in procfs
  *     @header: Banner to be printed at the beginning of the file.
  *     @ids: ipc id table to iterate.
  *     @show: show routine.
  */
 void __init ipc_init_proc_interface(const char *path, const char *header,
-                                   struct ipc_ids *ids,
-                                   int (*show)(struct seq_file *, void *))
+               int ids, int (*show)(struct seq_file *, void *))
 {
        struct proc_dir_entry *pde;
        struct ipc_proc_iface *iface;
@@ -137,7 +217,7 @@ void __init ipc_init_proc_interface(const char *path, const char *header,
  *     @ids: Identifier set
  *     @key: The key to find
  *     
- *     Requires ipc_ids.sem locked.
+ *     Requires ipc_ids.mutex locked.
  *     Returns the identifier if found or -1 if not.
  */
  
@@ -149,7 +229,7 @@ int ipc_findkey(struct ipc_ids* ids, key_t key)
 
        /*
         * rcu_dereference() is not needed here
-        * since ipc_ids.sem is held
+        * since ipc_ids.mutex is held
         */
        for (id = 0; id <= max_id; id++) {
                p = ids->entries->p[id];
@@ -162,7 +242,7 @@ int ipc_findkey(struct ipc_ids* ids, key_t key)
 }
 
 /*
- * Requires ipc_ids.sem locked
+ * Requires ipc_ids.mutex locked
  */
 static int grow_ary(struct ipc_ids* ids, int newsize)
 {
@@ -181,8 +261,7 @@ static int grow_ary(struct ipc_ids* ids, int newsize)
        if(new == NULL)
                return size;
        new->size = newsize;
-       memcpy(new->p, ids->entries->p, sizeof(struct kern_ipc_perm *)*size +
-                                       sizeof(struct ipc_id_ary));
+       memcpy(new->p, ids->entries->p, sizeof(struct kern_ipc_perm *)*size);
        for(i=size;i<newsize;i++) {
                new->p[i] = NULL;
        }
@@ -194,7 +273,7 @@ static int grow_ary(struct ipc_ids* ids, int newsize)
         */
        rcu_assign_pointer(ids->entries, new);
 
-       ipc_rcu_putref(old);
+       __ipc_fini_ids(ids, old);
        return newsize;
 }
 
@@ -209,7 +288,7 @@ static int grow_ary(struct ipc_ids* ids, int newsize)
  *     is returned. The list is returned in a locked state on success.
  *     On failure the list is not locked and -1 is returned.
  *
- *     Called with ipc_ids.sem held.
+ *     Called with ipc_ids.mutex held.
  */
  
 int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size)
@@ -220,7 +299,7 @@ int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size)
 
        /*
         * rcu_dereference()() is not needed here since
-        * ipc_ids.sem is held
+        * ipc_ids.mutex is held
         */
        for (id = 0; id < size; id++) {
                if(ids->entries->p[id] == NULL)
@@ -256,7 +335,7 @@ found:
  *     fed an invalid identifier. The entry is removed and internal
  *     variables recomputed. The object associated with the identifier
  *     is returned.
- *     ipc_ids.sem and the spinlock for this ID is hold before this function
+ *     ipc_ids.mutex and the spinlock for this ID is hold before this function
  *     is called, and remain locked on the exit.
  */
  
@@ -264,17 +343,15 @@ struct kern_ipc_perm* ipc_rmid(struct ipc_ids* ids, int id)
 {
        struct kern_ipc_perm* p;
        int lid = id % SEQ_MULTIPLIER;
-       if(lid >= ids->entries->size)
-               BUG();
+       BUG_ON(lid >= ids->entries->size);
 
        /* 
         * do not need a rcu_dereference()() here to force ordering
-        * on Alpha, since the ipc_ids.sem is held.
+        * on Alpha, since the ipc_ids.mutex is held.
         */     
        p = ids->entries->p[lid];
        ids->entries->p[lid] = NULL;
-       if(p==NULL)
-               BUG();
+       BUG_ON(p==NULL);
        ids->in_use--;
 
        if (lid == ids->max_id) {
@@ -312,7 +389,7 @@ void* ipc_alloc(int size)
  *     @ptr: pointer returned by ipc_alloc
  *     @size: size of block
  *
- *     Free a block created with ipc_alloc. The caller must know the size
+ *     Free a block created with ipc_alloc(). The caller must know the size
  *     used in the allocation call.
  */
 
@@ -409,11 +486,17 @@ void ipc_rcu_getref(void *ptr)
        container_of(ptr, struct ipc_rcu_hdr, data)->refcount++;
 }
 
+static void ipc_do_vfree(struct work_struct *work)
+{
+       vfree(container_of(work, struct ipc_rcu_sched, work));
+}
+
 /**
- *     ipc_schedule_free       - free ipc + rcu space
+ * ipc_schedule_free - free ipc + rcu space
+ * @head: RCU callback structure for queued work
  * 
  * Since RCU callback function is called in bh,
- * we need to defer the vfree to schedule_work
+ * we need to defer the vfree to schedule_work().
  */
 static void ipc_schedule_free(struct rcu_head *head)
 {
@@ -422,15 +505,15 @@ static void ipc_schedule_free(struct rcu_head *head)
        struct ipc_rcu_sched *sched =
                        container_of(&(grace->data[0]), struct ipc_rcu_sched, data[0]);
 
-       INIT_WORK(&sched->work, vfree, sched);
+       INIT_WORK(&sched->work, ipc_do_vfree);
        schedule_work(&sched->work);
 }
 
 /**
- *     ipc_immediate_free      - free ipc + rcu space
- *
- *     Free from the RCU callback context
+ * ipc_immediate_free - free ipc + rcu space
+ * @head: RCU callback structure that contains pointer to be freed
  *
+ * Free from the RCU callback context.
  */
 static void ipc_immediate_free(struct rcu_head *head)
 {
@@ -464,8 +547,10 @@ void ipc_rcu_putref(void *ptr)
  
 int ipcperms (struct kern_ipc_perm *ipcp, short flag)
 {      /* flag will most probably be 0 or S_...UGO from <linux/stat.h> */
-       int requested_mode, granted_mode;
+       int requested_mode, granted_mode, err;
 
+       if (unlikely((err = audit_ipc_obj(ipcp))))
+               return err;
        requested_mode = (flag >> 6) | (flag >> 3) | flag;
        granted_mode = ipcp->mode;
        if (current->euid == ipcp->cuid || current->euid == ipcp->uid)
@@ -490,8 +575,8 @@ int ipcperms (struct kern_ipc_perm *ipcp, short flag)
  *     @in: kernel permissions
  *     @out: new style IPC permissions
  *
- *     Turn the kernel object 'in' into a set of permissions descriptions
- *     for returning to userspace (out).
+ *     Turn the kernel object @in into a set of permissions descriptions
+ *     for returning to userspace (@out).
  */
  
 
@@ -511,8 +596,8 @@ void kernel_to_ipc64_perm (struct kern_ipc_perm *in, struct ipc64_perm *out)
  *     @in: new style IPC permissions
  *     @out: old style IPC permissions
  *
- *     Turn the new style permissions object in into a compatibility
- *     object and store it into the 'out' pointer.
+ *     Turn the new style permissions object @in into a compatibility
+ *     object and store it into the @out pointer.
  */
  
 void ipc64_perm_to_ipc_perm (struct ipc64_perm *in, struct ipc_perm *out)
@@ -528,13 +613,13 @@ void ipc64_perm_to_ipc_perm (struct ipc64_perm *in, struct ipc_perm *out)
 
 /*
  * So far only shm_get_stat() calls ipc_get() via shm_get(), so ipc_get()
- * is called with shm_ids.sem locked.  Since grow_ary() is also called with
- * shm_ids.sem down(for Shared Memory), there is no need to add read 
+ * is called with shm_ids.mutex locked.  Since grow_ary() is also called with
+ * shm_ids.mutex down(for Shared Memory), there is no need to add read
  * barriers here to gurantee the writes in grow_ary() are seen in order 
  * here (for Alpha).
  *
- * However ipc_get() itself does not necessary require ipc_ids.sem down. So
- * if in the future ipc_get() is used by other places without ipc_ids.sem
+ * However ipc_get() itself does not necessary require ipc_ids.mutex down. So
+ * if in the future ipc_get() is used by other places without ipc_ids.mutex
  * down, then ipc_get() needs read memery barriers as ipc_lock() does.
  */
 struct kern_ipc_perm* ipc_get(struct ipc_ids* ids, int id)
@@ -609,7 +694,7 @@ int ipc_checkid(struct ipc_ids* ids, struct kern_ipc_perm* ipcp, int uid)
  *     @cmd: pointer to command
  *
  *     Return IPC_64 for new style IPC and IPC_OLD for old style IPC. 
- *     The cmd value is turned from an encoding command and version into
+ *     The @cmd value is turned from an encoding command and version into
  *     just the command code.
  */
  
@@ -626,11 +711,20 @@ int ipc_parse_version (int *cmd)
 #endif /* __ARCH_WANT_IPC_PARSE_VERSION */
 
 #ifdef CONFIG_PROC_FS
+struct ipc_proc_iter {
+       struct ipc_namespace *ns;
+       struct ipc_proc_iface *iface;
+};
+
 static void *sysvipc_proc_next(struct seq_file *s, void *it, loff_t *pos)
 {
-       struct ipc_proc_iface *iface = s->private;
+       struct ipc_proc_iter *iter = s->private;
+       struct ipc_proc_iface *iface = iter->iface;
        struct kern_ipc_perm *ipc = it;
        loff_t p;
+       struct ipc_ids *ids;
+
+       ids = iter->ns->ids[iface->ids];
 
        /* If we had an ipc id locked before, unlock it */
        if (ipc && ipc != SEQ_START_TOKEN)
@@ -640,8 +734,8 @@ static void *sysvipc_proc_next(struct seq_file *s, void *it, loff_t *pos)
         * p = *pos - 1 (because id 0 starts at position 1)
         *          + 1 (because we increment the position by one)
         */
-       for (p = *pos; p <= iface->ids->max_id; p++) {
-               if ((ipc = ipc_lock(iface->ids, p)) != NULL) {
+       for (p = *pos; p <= ids->max_id; p++) {
+               if ((ipc = ipc_lock(ids, p)) != NULL) {
                        *pos = p + 1;
                        return ipc;
                }
@@ -657,15 +751,19 @@ static void *sysvipc_proc_next(struct seq_file *s, void *it, loff_t *pos)
  */
 static void *sysvipc_proc_start(struct seq_file *s, loff_t *pos)
 {
-       struct ipc_proc_iface *iface = s->private;
+       struct ipc_proc_iter *iter = s->private;
+       struct ipc_proc_iface *iface = iter->iface;
        struct kern_ipc_perm *ipc;
        loff_t p;
+       struct ipc_ids *ids;
+
+       ids = iter->ns->ids[iface->ids];
 
        /*
         * Take the lock - this will be released by the corresponding
         * call to stop().
         */
-       down(&iface->ids->sem);
+       mutex_lock(&ids->mutex);
 
        /* pos < 0 is invalid */
        if (*pos < 0)
@@ -676,8 +774,8 @@ static void *sysvipc_proc_start(struct seq_file *s, loff_t *pos)
                return SEQ_START_TOKEN;
 
        /* Find the (pos-1)th ipc */
-       for (p = *pos - 1; p <= iface->ids->max_id; p++) {
-               if ((ipc = ipc_lock(iface->ids, p)) != NULL) {
+       for (p = *pos - 1; p <= ids->max_id; p++) {
+               if ((ipc = ipc_lock(ids, p)) != NULL) {
                        *pos = p + 1;
                        return ipc;
                }
@@ -688,19 +786,23 @@ static void *sysvipc_proc_start(struct seq_file *s, loff_t *pos)
 static void sysvipc_proc_stop(struct seq_file *s, void *it)
 {
        struct kern_ipc_perm *ipc = it;
-       struct ipc_proc_iface *iface = s->private;
+       struct ipc_proc_iter *iter = s->private;
+       struct ipc_proc_iface *iface = iter->iface;
+       struct ipc_ids *ids;
 
        /* If we had a locked segment, release it */
        if (ipc && ipc != SEQ_START_TOKEN)
                ipc_unlock(ipc);
 
+       ids = iter->ns->ids[iface->ids];
        /* Release the lock we took in start() */
-       up(&iface->ids->sem);
+       mutex_unlock(&ids->mutex);
 }
 
 static int sysvipc_proc_show(struct seq_file *s, void *it)
 {
-       struct ipc_proc_iface *iface = s->private;
+       struct ipc_proc_iter *iter = s->private;
+       struct ipc_proc_iface *iface = iter->iface;
 
        if (it == SEQ_START_TOKEN)
                return seq_puts(s, iface->header);
@@ -715,22 +817,45 @@ static struct seq_operations sysvipc_proc_seqops = {
        .show  = sysvipc_proc_show,
 };
 
-static int sysvipc_proc_open(struct inode *inode, struct file *file) {
+static int sysvipc_proc_open(struct inode *inode, struct file *file)
+{
        int ret;
        struct seq_file *seq;
+       struct ipc_proc_iter *iter;
+
+       ret = -ENOMEM;
+       iter = kmalloc(sizeof(*iter), GFP_KERNEL);
+       if (!iter)
+               goto out;
 
        ret = seq_open(file, &sysvipc_proc_seqops);
-       if (!ret) {
-               seq = file->private_data;
-               seq->private = PDE(inode)->data;
-       }
+       if (ret)
+               goto out_kfree;
+
+       seq = file->private_data;
+       seq->private = iter;
+
+       iter->iface = PDE(inode)->data;
+       iter->ns    = get_ipc_ns(current->nsproxy->ipc_ns);
+out:
        return ret;
+out_kfree:
+       kfree(iter);
+       goto out;
+}
+
+static int sysvipc_proc_release(struct inode *inode, struct file *file)
+{
+       struct seq_file *seq = file->private_data;
+       struct ipc_proc_iter *iter = seq->private;
+       put_ipc_ns(iter->ns);
+       return seq_release_private(inode, file);
 }
 
-static struct file_operations sysvipc_proc_fops = {
+static const struct file_operations sysvipc_proc_fops = {
        .open    = sysvipc_proc_open,
        .read    = seq_read,
        .llseek  = seq_lseek,
-       .release = seq_release,
+       .release = sysvipc_proc_release,
 };
 #endif /* CONFIG_PROC_FS */