drivers: power: report battery voltage in AOSP compatible format
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / ipc / util.c
index 809ec5ec812295f7ef652b365ff0b35c4a6060d1..735342570a875fea7eb77029c1db2fcbc797d422 100644 (file)
  * Jun 2006 - namespaces ssupport
  *            OpenVZ, SWsoft Inc.
  *            Pavel Emelianov <xemul@openvz.org>
+ *
+ * General sysv ipc locking scheme:
+ *     rcu_read_lock()
+ *          obtain the ipc object (kern_ipc_perm) by looking up the id in an idr
+ *         tree.
+ *         - perform initial checks (capabilities, auditing and permission,
+ *           etc).
+ *         - perform read-only operations, such as STAT, INFO commands.
+ *           acquire the ipc lock (kern_ipc_perm.lock) through
+ *           ipc_lock_object()
+ *             - perform data updates, such as SET, RMID commands and
+ *               mechanism-specific operations (semop/semtimedop,
+ *               msgsnd/msgrcv, shmat/shmdt).
+ *         drop the ipc lock, through ipc_unlock_object().
+ *     rcu_read_unlock()
+ *
+ *  The ids->rwsem must be taken when:
+ *     - creating, removing and iterating the existing entries in ipc
+ *       identifier sets.
+ *     - iterating through files under /proc/sysvipc/
+ *
+ *  Note that sems have a special fast path that avoids kern_ipc_perm.lock -
+ *  see sem_lock().
  */
 
 #include <linux/mm.h>
@@ -119,7 +142,7 @@ __initcall(ipc_init);
  
 void ipc_init_ids(struct ipc_ids *ids)
 {
-       init_rwsem(&ids->rw_mutex);
+       init_rwsem(&ids->rwsem);
 
        ids->in_use = 0;
        ids->seq = 0;
@@ -174,7 +197,7 @@ void __init ipc_init_proc_interface(const char *path, const char *header,
  *     @ids: Identifier set
  *     @key: The key to find
  *     
- *     Requires ipc_ids.rw_mutex locked.
+ *     Requires ipc_ids.rwsem locked.
  *     Returns the LOCKED pointer to the ipc structure if found or NULL
  *     if not.
  *     If key is found ipc points to the owning ipc structure
@@ -197,7 +220,8 @@ static struct kern_ipc_perm *ipc_findkey(struct ipc_ids *ids, key_t key)
                        continue;
                }
 
-               ipc_lock_by_ptr(ipc);
+               rcu_read_lock();
+               ipc_lock_object(ipc);
                return ipc;
        }
 
@@ -208,7 +232,7 @@ static struct kern_ipc_perm *ipc_findkey(struct ipc_ids *ids, key_t key)
  *     ipc_get_maxid   -       get the last assigned id
  *     @ids: IPC identifier set
  *
- *     Called with ipc_ids.rw_mutex held.
+ *     Called with ipc_ids.rwsem held.
  */
 
 int ipc_get_maxid(struct ipc_ids *ids)
@@ -246,9 +270,8 @@ int ipc_get_maxid(struct ipc_ids *ids)
  *     is returned. The 'new' entry is returned in a locked state on success.
  *     On failure the entry is not locked and a negative err-code is returned.
  *
- *     Called with ipc_ids.rw_mutex held as a writer.
+ *     Called with writer ipc_ids.rwsem held.
  */
 int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size)
 {
        kuid_t euid;
@@ -269,6 +292,10 @@ int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size)
        rcu_read_lock();
        spin_lock(&new->lock);
 
+       current_euid_egid(&euid, &egid);
+       new->cuid = new->uid = euid;
+       new->gid = new->cgid = egid;
+
        id = idr_alloc(&ids->ipcs_idr, new,
                       (next_id < 0) ? 0 : ipcid_to_idx(next_id), 0,
                       GFP_NOWAIT);
@@ -281,10 +308,6 @@ int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size)
 
        ids->in_use++;
 
-       current_euid_egid(&euid, &egid);
-       new->cuid = new->uid = euid;
-       new->gid = new->cgid = egid;
-
        if (next_id < 0) {
                new->seq = ids->seq++;
                if (ids->seq > ids->seq_max)
@@ -313,9 +336,9 @@ static int ipcget_new(struct ipc_namespace *ns, struct ipc_ids *ids,
 {
        int err;
 
-       down_write(&ids->rw_mutex);
+       down_write(&ids->rwsem);
        err = ops->getnew(ns, params);
-       up_write(&ids->rw_mutex);
+       up_write(&ids->rwsem);
        return err;
 }
 
@@ -332,7 +355,7 @@ static int ipcget_new(struct ipc_namespace *ns, struct ipc_ids *ids,
  *
  *     On success, the IPC id is returned.
  *
- *     It is called with ipc_ids.rw_mutex and ipcp->lock held.
+ *     It is called with ipc_ids.rwsem and ipcp->lock held.
  */
 static int ipc_check_perms(struct ipc_namespace *ns,
                           struct kern_ipc_perm *ipcp,
@@ -377,7 +400,7 @@ static int ipcget_public(struct ipc_namespace *ns, struct ipc_ids *ids,
         * Take the lock as a writer since we are potentially going to add
         * a new entry + read locks are not "upgradable"
         */
-       down_write(&ids->rw_mutex);
+       down_write(&ids->rwsem);
        ipcp = ipc_findkey(ids, params->key);
        if (ipcp == NULL) {
                /* key not used */
@@ -403,7 +426,7 @@ static int ipcget_public(struct ipc_namespace *ns, struct ipc_ids *ids,
                }
                ipc_unlock(ipcp);
        }
-       up_write(&ids->rw_mutex);
+       up_write(&ids->rwsem);
 
        return err;
 }
@@ -414,7 +437,7 @@ static int ipcget_public(struct ipc_namespace *ns, struct ipc_ids *ids,
  *     @ids: IPC identifier set
  *     @ipcp: ipc perm structure containing the identifier to remove
  *
- *     ipc_ids.rw_mutex (as a writer) and the spinlock for this ID are held
+ *     ipc_ids.rwsem (as a writer) and the spinlock for this ID are held
  *     before this function is called, and remain locked on the exit.
  */
  
@@ -466,13 +489,6 @@ void ipc_free(void* ptr, int size)
                kfree(ptr);
 }
 
-struct ipc_rcu {
-       struct rcu_head rcu;
-       atomic_t refcount;
-       /* "void *" makes sure alignment of following data is sane. */
-       void *data[0];
-};
-
 /**
  *     ipc_rcu_alloc   -       allocate ipc and rcu space 
  *     @size: size desired
@@ -489,35 +505,34 @@ void *ipc_rcu_alloc(int size)
        if (unlikely(!out))
                return NULL;
        atomic_set(&out->refcount, 1);
-       return out->data;
+       return out + 1;
 }
 
 int ipc_rcu_getref(void *ptr)
 {
-       return atomic_inc_not_zero(&container_of(ptr, struct ipc_rcu, data)->refcount);
-}
+       struct ipc_rcu *p = ((struct ipc_rcu *)ptr) - 1;
 
-/**
- * ipc_schedule_free - free ipc + rcu space
- * @head: RCU callback structure for queued work
- */
-static void ipc_schedule_free(struct rcu_head *head)
-{
-       vfree(container_of(head, struct ipc_rcu, rcu));
+       return atomic_inc_not_zero(&p->refcount);
 }
 
-void ipc_rcu_putref(void *ptr)
+void ipc_rcu_putref(void *ptr, void (*func)(struct rcu_head *head))
 {
-       struct ipc_rcu *p = container_of(ptr, struct ipc_rcu, data);
+       struct ipc_rcu *p = ((struct ipc_rcu *)ptr) - 1;
 
        if (!atomic_dec_and_test(&p->refcount))
                return;
 
-       if (is_vmalloc_addr(ptr)) {
-               call_rcu(&p->rcu, ipc_schedule_free);
-       } else {
-               kfree_rcu(p, rcu);
-       }
+       call_rcu(&p->rcu, func);
+}
+
+void ipc_rcu_free(struct rcu_head *head)
+{
+       struct ipc_rcu *p = container_of(head, struct ipc_rcu, rcu);
+
+       if (is_vmalloc_addr(p))
+               vfree(p);
+       else
+               kfree(p);
 }
 
 /**
@@ -622,7 +637,7 @@ struct kern_ipc_perm *ipc_obtain_object(struct ipc_ids *ids, int id)
 }
 
 /**
- * ipc_lock - Lock an ipc structure without rw_mutex held
+ * ipc_lock - Lock an ipc structure without rwsem held
  * @ids: IPC identifier set
  * @id: ipc id to look for
  *
@@ -678,22 +693,6 @@ out:
        return out;
 }
 
-struct kern_ipc_perm *ipc_lock_check(struct ipc_ids *ids, int id)
-{
-       struct kern_ipc_perm *out;
-
-       out = ipc_lock(ids, id);
-       if (IS_ERR(out))
-               return out;
-
-       if (ipc_checkid(out, id)) {
-               ipc_unlock(out);
-               return ERR_PTR(-EIDRM);
-       }
-
-       return out;
-}
-
 /**
  * ipcget - Common sys_*get() code
  * @ns : namsepace
@@ -734,7 +733,7 @@ int ipc_update_perm(struct ipc64_perm *in, struct kern_ipc_perm *out)
 }
 
 /**
- * ipcctl_pre_down - retrieve an ipc and check permissions for some IPC_XXX cmd
+ * ipcctl_pre_down_nolock - retrieve an ipc and check permissions for some IPC_XXX cmd
  * @ns:  the ipc namespace
  * @ids:  the table of ids where to look for the ipc
  * @id:   the id of the ipc to retrieve
@@ -747,39 +746,22 @@ int ipc_update_perm(struct ipc64_perm *in, struct kern_ipc_perm *out)
  * It must be called without any lock held and
  *  - retrieves the ipc with the given id in the given table.
  *  - performs some audit and permission check, depending on the given cmd
- *  - returns the ipc with both ipc and rw_mutex locks held in case of success
- *    or an err-code without any lock held otherwise.
+ *  - returns a pointer to the ipc object or otherwise, the corresponding error.
+ *
+ * Call holding the both the rwsem and the rcu read lock.
  */
-struct kern_ipc_perm *ipcctl_pre_down(struct ipc_namespace *ns,
-                                     struct ipc_ids *ids, int id, int cmd,
-                                     struct ipc64_perm *perm, int extra_perm)
-{
-       struct kern_ipc_perm *ipcp;
-
-       ipcp = ipcctl_pre_down_nolock(ns, ids, id, cmd, perm, extra_perm);
-       if (IS_ERR(ipcp))
-               goto out;
-
-       spin_lock(&ipcp->lock);
-out:
-       return ipcp;
-}
-
 struct kern_ipc_perm *ipcctl_pre_down_nolock(struct ipc_namespace *ns,
-                                            struct ipc_ids *ids, int id, int cmd,
-                                            struct ipc64_perm *perm, int extra_perm)
+                                       struct ipc_ids *ids, int id, int cmd,
+                                       struct ipc64_perm *perm, int extra_perm)
 {
        kuid_t euid;
        int err = -EPERM;
        struct kern_ipc_perm *ipcp;
 
-       down_write(&ids->rw_mutex);
-       rcu_read_lock();
-
        ipcp = ipc_obtain_object_check(ids, id);
        if (IS_ERR(ipcp)) {
                err = PTR_ERR(ipcp);
-               goto out_up;
+               goto err;
        }
 
        audit_ipc_obj(ipcp);
@@ -790,16 +772,8 @@ struct kern_ipc_perm *ipcctl_pre_down_nolock(struct ipc_namespace *ns,
        euid = current_euid();
        if (uid_eq(euid, ipcp->cuid) || uid_eq(euid, ipcp->uid)  ||
            ns_capable(ns->user_ns, CAP_SYS_ADMIN))
-               return ipcp;
-
-out_up:
-       /*
-        * Unsuccessful lookup, unlock and return
-        * the corresponding error.
-        */
-       rcu_read_unlock();
-       up_write(&ids->rw_mutex);
-
+               return ipcp; /* successful lookup */
+err:
        return ERR_PTR(err);
 }
 
@@ -856,7 +830,8 @@ static struct kern_ipc_perm *sysvipc_find_ipc(struct ipc_ids *ids, loff_t pos,
                ipc = idr_find(&ids->ipcs_idr, pos);
                if (ipc != NULL) {
                        *new_pos = pos + 1;
-                       ipc_lock_by_ptr(ipc);
+                       rcu_read_lock();
+                       ipc_lock_object(ipc);
                        return ipc;
                }
        }
@@ -894,7 +869,7 @@ static void *sysvipc_proc_start(struct seq_file *s, loff_t *pos)
         * Take the lock - this will be released by the corresponding
         * call to stop().
         */
-       down_read(&ids->rw_mutex);
+       down_read(&ids->rwsem);
 
        /* pos < 0 is invalid */
        if (*pos < 0)
@@ -921,7 +896,7 @@ static void sysvipc_proc_stop(struct seq_file *s, void *it)
 
        ids = &iter->ns->ids[iface->ids];
        /* Release the lock we took in start() */
-       up_read(&ids->rw_mutex);
+       up_read(&ids->rwsem);
 }
 
 static int sysvipc_proc_show(struct seq_file *s, void *it)