[PATCH 6/7] ipc: share ids rwsem when possible in ipcget_public

From: Davidlohr Bueso
Date: Fri Jan 10 2014 - 13:49:08 EST


... and rewrite the function. For scenarios where the key is found and we end
up just doing different routinary checks, we can downgrade the ids->rwsem and
share it among concurrent readers. These checks include the following, which
are all safe to share the lock:

ops->more_checks() >> sem_more_checks(), shm_more_checks()
ipc_check_perms() >> ipcperms(),ops->associate() >> [lsm]_[ipctype]_associate()

Signed-off-by: Davidlohr Bueso <davidlohr@xxxxxx>
---
ipc/util.c | 60 +++++++++++++++++++++++++++++++++---------------------------
1 file changed, 33 insertions(+), 27 deletions(-)

diff --git a/ipc/util.c b/ipc/util.c
index e1b4c6d..fc5c655 100644
--- a/ipc/util.c
+++ b/ipc/util.c
@@ -183,7 +183,7 @@ void __init ipc_init_proc_interface(const char *path, const char *header,
* ipc_findkey - find a key in an ipc identifier set
* @ids: ipc identifier set
* @key: key to find
- *
+ *
* Returns the locked pointer to the ipc structure if found or NULL
* otherwise. If key is found ipc points to the owning ipc structure
*
@@ -375,48 +375,54 @@ static int ipc_check_perms(struct ipc_namespace *ns,
* On success, the ipc id is returned.
*/
static int ipcget_public(struct ipc_namespace *ns, struct ipc_ids *ids,
- struct ipc_ops *ops, struct ipc_params *params)
+ struct ipc_ops *ops, struct ipc_params *params)
{
struct kern_ipc_perm *ipcp;
int flg = params->flg;
- int err;
+ int err = 0;

- /*
- * Take the lock as a writer since we are potentially going to add
- * a new entry + read locks are not "upgradable"
- */
down_write(&ids->rwsem);
ipcp = ipc_findkey(ids, params->key);
- if (ipcp == NULL) {
+
+ if (!ipcp) {
/* key not used */
if (!(flg & IPC_CREAT))
err = -ENOENT;
- else
+ else /* create new ipc object */
err = ops->getnew(ns, params);
- } else {
- /* ipc object has been locked by ipc_findkey() */
-
- if (flg & IPC_CREAT && flg & IPC_EXCL)
- err = -EEXIST;
- else {
- err = 0;
- if (ops->more_checks)
- err = ops->more_checks(ipcp, params);
- if (!err)
- /*
- * ipc_check_perms returns the IPC id on
- * success
- */
- err = ipc_check_perms(ns, ipcp, ops, params);
- }
+
+ goto done_write;
+ }
+
+ if ((flg & IPC_CREAT) && (flg & IPC_EXCL)) {
+ /* ipc object was locked by successful ipc_findkey() lookup */
ipc_unlock(ipcp);
+ err = -ENOENT;
+
+ goto done_write;
}
- up_write(&ids->rwsem);

+ /*
+ * The key was found, so we will just perform routinary checks on
+ * ipc the object. Share the lock among other readers.
+ */
+ downgrade_write(&ids->rwsem);
+
+ if (ops->more_checks)
+ err = ops->more_checks(ipcp, params);
+ if (!err)
+ /* returns the IPC id on success */
+ err = ipc_check_perms(ns, ipcp, ops, params);
+
+ ipc_unlock(ipcp);
+
+ up_read(&ids->rwsem);
+ return err;
+done_write:
+ up_write(&ids->rwsem);
return err;
}

-
/**
* ipc_rmid - remove an ipc identifier
* @ids: ipc identifier set
--
1.8.1.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/