Merge branch 'akpm' (patches from Andrew)
[sfrench/cifs-2.6.git] / ipc / util.c
index cfa0045e748d55a5e0b1fbd608fe477b8cf4c96f..0027e47626b7bbc582c964af5e9bd02887218fb3 100644 (file)
@@ -64,6 +64,7 @@
 #include <linux/memory.h>
 #include <linux/ipc_namespace.h>
 #include <linux/rhashtable.h>
+#include <linux/log2.h>
 
 #include <asm/unistd.h>
 
@@ -450,6 +451,41 @@ static void ipc_kht_remove(struct ipc_ids *ids, struct kern_ipc_perm *ipcp)
                                       ipc_kht_params);
 }
 
+/**
+ * ipc_search_maxidx - search for the highest assigned index
+ * @ids: ipc identifier set
+ * @limit: known upper limit for highest assigned index
+ *
+ * The function determines the highest assigned index in @ids. It is intended
+ * to be called when ids->max_idx needs to be updated.
+ * Updating ids->max_idx is necessary when the current highest index ipc
+ * object is deleted.
+ * If no ipc object is allocated, then -1 is returned.
+ *
+ * ipc_ids.rwsem needs to be held by the caller.
+ */
+static int ipc_search_maxidx(struct ipc_ids *ids, int limit)
+{
+       int tmpidx;
+       int i;
+       int retval;
+
+       i = ilog2(limit+1);
+
+       retval = 0;
+       for (; i >= 0; i--) {
+               tmpidx = retval | (1<<i);
+               /*
+                * "0" is a possible index value, thus search using
+                * e.g. 15,7,3,1,0 instead of 16,8,4,2,1.
+                */
+               tmpidx = tmpidx-1;
+               if (idr_get_next(&ids->ipcs_idr, &tmpidx))
+                       retval |= (1<<i);
+       }
+       return retval - 1;
+}
+
 /**
  * ipc_rmid - remove an ipc identifier
  * @ids: ipc identifier set
@@ -468,11 +504,9 @@ void ipc_rmid(struct ipc_ids *ids, struct kern_ipc_perm *ipcp)
        ipcp->deleted = true;
 
        if (unlikely(idx == ids->max_idx)) {
-               do {
-                       idx--;
-                       if (idx == -1)
-                               break;
-               } while (!idr_find(&ids->ipcs_idr, idx));
+               idx = ids->max_idx-1;
+               if (idx >= 0)
+                       idx = ipc_search_maxidx(ids, idx);
                ids->max_idx = idx;
        }
 }