aboutsummaryrefslogtreecommitdiffstats
path: root/ipc
diff options
context:
space:
mode:
authorDavidlohr Bueso <dave@stgolabs.net>2017-11-17 18:31:18 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2017-11-17 19:10:04 -0500
commit15df03c87983660a4d1eedb4541778592bd97684 (patch)
treefb8e7ae25cee35177eb45e11bb3435f79ccc009f /ipc
parentebf66799acfb5f52ada4ff96ecc9579867941ea9 (diff)
sysvipc: make get_maxid O(1) again
For a custom microbenchmark on a 3.30GHz Xeon SandyBridge, which calls IPC_STAT over and over, it was calculated that, on avg the cost of ipc_get_maxid() for increasing amounts of keys was: 10 keys: ~900 cycles 100 keys: ~15000 cycles 1000 keys: ~150000 cycles 10000 keys: ~2100000 cycles This is unsurprising as maxid is currently O(n). By having the max_id available in O(1) we save all those cycles for each semctl(_STAT) command, the idr_find can be expensive -- which some real (customer) workloads actually poll on. Note that this used to be the case, until commit 7ca7e564e04 ("ipc: store ipcs into IDRs"). The cost is the extra idr_find when doing RMIDs, but we simply go backwards, and should not take too many iterations to find the new value. [akpm@linux-foundation.org: coding-style fixes] Link: http://lkml.kernel.org/r/20170831172049.14576-5-dave@stgolabs.net Signed-off-by: Davidlohr Bueso <dbueso@suse.de> Cc: Manfred Spraul <manfred@colorfullife.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'ipc')
-rw-r--r--ipc/util.c43
-rw-r--r--ipc/util.h21
2 files changed, 31 insertions, 33 deletions
diff --git a/ipc/util.c b/ipc/util.c
index e09bf76610ef..ff045fec8d83 100644
--- a/ipc/util.c
+++ b/ipc/util.c
@@ -122,6 +122,7 @@ int ipc_init_ids(struct ipc_ids *ids)
122 return err; 122 return err;
123 idr_init(&ids->ipcs_idr); 123 idr_init(&ids->ipcs_idr);
124 ids->tables_initialized = true; 124 ids->tables_initialized = true;
125 ids->max_id = -1;
125#ifdef CONFIG_CHECKPOINT_RESTORE 126#ifdef CONFIG_CHECKPOINT_RESTORE
126 ids->next_id = -1; 127 ids->next_id = -1;
127#endif 128#endif
@@ -188,36 +189,6 @@ static struct kern_ipc_perm *ipc_findkey(struct ipc_ids *ids, key_t key)
188 return NULL; 189 return NULL;
189} 190}
190 191
191/**
192 * ipc_get_maxid - get the last assigned id
193 * @ids: ipc identifier set
194 *
195 * Called with ipc_ids.rwsem held.
196 */
197int ipc_get_maxid(struct ipc_ids *ids)
198{
199 struct kern_ipc_perm *ipc;
200 int max_id = -1;
201 int total, id;
202
203 if (ids->in_use == 0)
204 return -1;
205
206 if (ids->in_use == IPCMNI)
207 return IPCMNI - 1;
208
209 /* Look for the last assigned id */
210 total = 0;
211 for (id = 0; id < IPCMNI && total < ids->in_use; id++) {
212 ipc = idr_find(&ids->ipcs_idr, id);
213 if (ipc != NULL) {
214 max_id = id;
215 total++;
216 }
217 }
218 return max_id;
219}
220
221#ifdef CONFIG_CHECKPOINT_RESTORE 192#ifdef CONFIG_CHECKPOINT_RESTORE
222/* 193/*
223 * Specify desired id for next allocated IPC object. 194 * Specify desired id for next allocated IPC object.
@@ -313,6 +284,9 @@ int ipc_addid(struct ipc_ids *ids, struct kern_ipc_perm *new, int limit)
313 } 284 }
314 285
315 ids->in_use++; 286 ids->in_use++;
287 if (id > ids->max_id)
288 ids->max_id = id;
289
316 new->id = ipc_buildid(id, ids, new); 290 new->id = ipc_buildid(id, ids, new);
317 291
318 return id; 292 return id;
@@ -459,6 +433,15 @@ void ipc_rmid(struct ipc_ids *ids, struct kern_ipc_perm *ipcp)
459 ipc_kht_remove(ids, ipcp); 433 ipc_kht_remove(ids, ipcp);
460 ids->in_use--; 434 ids->in_use--;
461 ipcp->deleted = true; 435 ipcp->deleted = true;
436
437 if (unlikely(lid == ids->max_id)) {
438 do {
439 lid--;
440 if (lid == -1)
441 break;
442 } while (!idr_find(&ids->ipcs_idr, lid));
443 ids->max_id = lid;
444 }
462} 445}
463 446
464/** 447/**
diff --git a/ipc/util.h b/ipc/util.h
index 0cd6201fe63a..89b8ec176fc4 100644
--- a/ipc/util.h
+++ b/ipc/util.h
@@ -13,6 +13,7 @@
13 13
14#include <linux/unistd.h> 14#include <linux/unistd.h>
15#include <linux/err.h> 15#include <linux/err.h>
16#include <linux/ipc_namespace.h>
16 17
17#define SEQ_MULTIPLIER (IPCMNI) 18#define SEQ_MULTIPLIER (IPCMNI)
18 19
@@ -99,9 +100,6 @@ void __init ipc_init_proc_interface(const char *path, const char *header,
99/* must be called with ids->rwsem acquired for writing */ 100/* must be called with ids->rwsem acquired for writing */
100int ipc_addid(struct ipc_ids *, struct kern_ipc_perm *, int); 101int ipc_addid(struct ipc_ids *, struct kern_ipc_perm *, int);
101 102
102/* must be called with ids->rwsem acquired for reading */
103int ipc_get_maxid(struct ipc_ids *);
104
105/* must be called with both locks acquired. */ 103/* must be called with both locks acquired. */
106void ipc_rmid(struct ipc_ids *, struct kern_ipc_perm *); 104void ipc_rmid(struct ipc_ids *, struct kern_ipc_perm *);
107 105
@@ -111,6 +109,23 @@ void ipc_set_key_private(struct ipc_ids *, struct kern_ipc_perm *);
111/* must be called with ipcp locked */ 109/* must be called with ipcp locked */
112int ipcperms(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp, short flg); 110int ipcperms(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp, short flg);
113 111
112/**
113 * ipc_get_maxid - get the last assigned id
114 * @ids: ipc identifier set
115 *
116 * Called with ipc_ids.rwsem held for reading.
117 */
118static inline int ipc_get_maxid(struct ipc_ids *ids)
119{
120 if (ids->in_use == 0)
121 return -1;
122
123 if (ids->in_use == IPCMNI)
124 return IPCMNI - 1;
125
126 return ids->max_id;
127}
128
114/* 129/*
115 * For allocation that need to be freed by RCU. 130 * For allocation that need to be freed by RCU.
116 * Objects are reference counted, they start with reference count 1. 131 * Objects are reference counted, they start with reference count 1.