diff options
| author | Davidlohr Bueso <dave@stgolabs.net> | 2017-11-17 18:31:18 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-11-17 19:10:04 -0500 |
| commit | 15df03c87983660a4d1eedb4541778592bd97684 (patch) | |
| tree | fb8e7ae25cee35177eb45e11bb3435f79ccc009f /ipc | |
| parent | ebf66799acfb5f52ada4ff96ecc9579867941ea9 (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.c | 43 | ||||
| -rw-r--r-- | ipc/util.h | 21 |
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 | */ | ||
| 197 | int 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 */ |
| 100 | int ipc_addid(struct ipc_ids *, struct kern_ipc_perm *, int); | 101 | int ipc_addid(struct ipc_ids *, struct kern_ipc_perm *, int); |
| 101 | 102 | ||
| 102 | /* must be called with ids->rwsem acquired for reading */ | ||
| 103 | int ipc_get_maxid(struct ipc_ids *); | ||
| 104 | |||
| 105 | /* must be called with both locks acquired. */ | 103 | /* must be called with both locks acquired. */ |
| 106 | void ipc_rmid(struct ipc_ids *, struct kern_ipc_perm *); | 104 | void 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 */ |
| 112 | int ipcperms(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp, short flg); | 110 | int 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 | */ | ||
| 118 | static 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. |
