aboutsummaryrefslogtreecommitdiff
path: root/ipc/util.c
diff options
context:
space:
mode:
authorLinus Torvalds2019-05-14 20:08:51 -0700
committerLinus Torvalds2019-05-14 20:08:51 -0700
commit1064d857738187c764c0bd76040f424397f857c7 (patch)
tree13d16c0aed50b64c20b8fe235b15172f3c997f15 /ipc/util.c
parent35c99ffa20edd3c24be352d28a63cd3a23121282 (diff)
parentdef0fdae813dbbbbb588bfc5f52856be2e842b35 (diff)
Merge branch 'akpm' (patches from Andrew)
Merge more updates from Andrew Morton: - a couple of hotfixes - almost all of the rest of MM - lib/ updates - binfmt_elf updates - autofs updates - quite a lot of misc fixes and updates - reiserfs, fatfs - signals - exec - cpumask - rapidio - sysctl - pids - eventfd - gcov - panic - pps - gdb script updates - ipc updates * emailed patches from Andrew Morton <akpm@linux-foundation.org>: (126 commits) mm: memcontrol: fix NUMA round-robin reclaim at intermediate level mm: memcontrol: fix recursive statistics correctness & scalabilty mm: memcontrol: move stat/event counting functions out-of-line mm: memcontrol: make cgroup stats and events query API explicitly local drivers/virt/fsl_hypervisor.c: prevent integer overflow in ioctl drivers/virt/fsl_hypervisor.c: dereferencing error pointers in ioctl mm, memcg: rename ambiguously named memory.stat counters and functions arch: remove <asm/sizes.h> and <asm-generic/sizes.h> treewide: replace #include <asm/sizes.h> with #include <linux/sizes.h> fs/block_dev.c: Remove duplicate header fs/cachefiles/namei.c: remove duplicate header include/linux/sched/signal.h: replace `tsk' with `task' fs/coda/psdev.c: remove duplicate header ipc: do cyclic id allocation for the ipc object. ipc: conserve sequence numbers in ipcmni_extend mode ipc: allow boot time extension of IPCMNI from 32k to 16M ipc/mqueue: optimize msg_get() ipc/mqueue: remove redundant wq task assignment ipc: prevent lockup on alloc_msg and free_msg scripts/gdb: print cached rate in lx-clk-summary ...
Diffstat (limited to 'ipc/util.c')
-rw-r--r--ipc/util.c48
1 files changed, 39 insertions, 9 deletions
diff --git a/ipc/util.c b/ipc/util.c
index 095274a871f8..d126d156efc6 100644
--- a/ipc/util.c
+++ b/ipc/util.c
@@ -109,7 +109,7 @@ static const struct rhashtable_params ipc_kht_params = {
* @ids: ipc identifier set
*
* Set up the sequence range to use for the ipc identifier range (limited
- * below IPCMNI) then initialise the keys hashtable and ids idr.
+ * below ipc_mni) then initialise the keys hashtable and ids idr.
*/
void ipc_init_ids(struct ipc_ids *ids)
{
@@ -119,6 +119,7 @@ void ipc_init_ids(struct ipc_ids *ids)
rhashtable_init(&ids->key_ht, &ipc_kht_params);
idr_init(&ids->ipcs_idr);
ids->max_idx = -1;
+ ids->last_idx = -1;
#ifdef CONFIG_CHECKPOINT_RESTORE
ids->next_id = -1;
#endif
@@ -192,6 +193,10 @@ static struct kern_ipc_perm *ipc_findkey(struct ipc_ids *ids, key_t key)
*
* The caller must own kern_ipc_perm.lock.of the new object.
* On error, the function returns a (negative) error code.
+ *
+ * To conserve sequence number space, especially with extended ipc_mni,
+ * the sequence number is incremented only when the returned ID is less than
+ * the last one.
*/
static inline int ipc_idr_alloc(struct ipc_ids *ids, struct kern_ipc_perm *new)
{
@@ -215,17 +220,42 @@ static inline int ipc_idr_alloc(struct ipc_ids *ids, struct kern_ipc_perm *new)
*/
if (next_id < 0) { /* !CHECKPOINT_RESTORE or next_id is unset */
- new->seq = ids->seq++;
- if (ids->seq > IPCID_SEQ_MAX)
- ids->seq = 0;
- idx = idr_alloc(&ids->ipcs_idr, new, 0, 0, GFP_NOWAIT);
+ int max_idx;
+
+ max_idx = max(ids->in_use*3/2, ipc_min_cycle);
+ max_idx = min(max_idx, ipc_mni);
+
+ /* allocate the idx, with a NULL struct kern_ipc_perm */
+ idx = idr_alloc_cyclic(&ids->ipcs_idr, NULL, 0, max_idx,
+ GFP_NOWAIT);
+
+ if (idx >= 0) {
+ /*
+ * idx got allocated successfully.
+ * Now calculate the sequence number and set the
+ * pointer for real.
+ */
+ if (idx <= ids->last_idx) {
+ ids->seq++;
+ if (ids->seq >= ipcid_seq_max())
+ ids->seq = 0;
+ }
+ ids->last_idx = idx;
+
+ new->seq = ids->seq;
+ /* no need for smp_wmb(), this is done
+ * inside idr_replace, as part of
+ * rcu_assign_pointer
+ */
+ idr_replace(&ids->ipcs_idr, new, idx);
+ }
} else {
new->seq = ipcid_to_seqx(next_id);
idx = idr_alloc(&ids->ipcs_idr, new, ipcid_to_idx(next_id),
0, GFP_NOWAIT);
}
if (idx >= 0)
- new->id = SEQ_MULTIPLIER * new->seq + idx;
+ new->id = (new->seq << ipcmni_seq_shift()) + idx;
return idx;
}
@@ -253,8 +283,8 @@ int ipc_addid(struct ipc_ids *ids, struct kern_ipc_perm *new, int limit)
/* 1) Initialize the refcount so that ipc_rcu_putref works */
refcount_set(&new->refcount, 1);
- if (limit > IPCMNI)
- limit = IPCMNI;
+ if (limit > ipc_mni)
+ limit = ipc_mni;
if (ids->in_use >= limit)
return -ENOSPC;
@@ -737,7 +767,7 @@ static struct kern_ipc_perm *sysvipc_find_ipc(struct ipc_ids *ids, loff_t pos,
if (total >= ids->in_use)
return NULL;
- for (; pos < IPCMNI; pos++) {
+ for (; pos < ipc_mni; pos++) {
ipc = idr_find(&ids->ipcs_idr, pos);
if (ipc != NULL) {
*new_pos = pos + 1;