summaryrefslogtreecommitdiffstats
path: root/ipc
diff options
context:
space:
mode:
Diffstat (limited to 'ipc')
-rw-r--r--ipc/util.c35
-rw-r--r--ipc/util.h8
2 files changed, 34 insertions, 9 deletions
diff --git a/ipc/util.c b/ipc/util.c
index cf5d1087409e..71f3f3982fc8 100644
--- a/ipc/util.c
+++ b/ipc/util.c
@@ -119,6 +119,7 @@ void ipc_init_ids(struct ipc_ids *ids)
119 rhashtable_init(&ids->key_ht, &ipc_kht_params); 119 rhashtable_init(&ids->key_ht, &ipc_kht_params);
120 idr_init(&ids->ipcs_idr); 120 idr_init(&ids->ipcs_idr);
121 ids->max_idx = -1; 121 ids->max_idx = -1;
122 ids->last_idx = -1;
122#ifdef CONFIG_CHECKPOINT_RESTORE 123#ifdef CONFIG_CHECKPOINT_RESTORE
123 ids->next_id = -1; 124 ids->next_id = -1;
124#endif 125#endif
@@ -192,6 +193,10 @@ static struct kern_ipc_perm *ipc_findkey(struct ipc_ids *ids, key_t key)
192 * 193 *
193 * The caller must own kern_ipc_perm.lock.of the new object. 194 * The caller must own kern_ipc_perm.lock.of the new object.
194 * On error, the function returns a (negative) error code. 195 * On error, the function returns a (negative) error code.
196 *
197 * To conserve sequence number space, especially with extended ipc_mni,
198 * the sequence number is incremented only when the returned ID is less than
199 * the last one.
195 */ 200 */
196static inline int ipc_idr_alloc(struct ipc_ids *ids, struct kern_ipc_perm *new) 201static inline int ipc_idr_alloc(struct ipc_ids *ids, struct kern_ipc_perm *new)
197{ 202{
@@ -215,17 +220,37 @@ static inline int ipc_idr_alloc(struct ipc_ids *ids, struct kern_ipc_perm *new)
215 */ 220 */
216 221
217 if (next_id < 0) { /* !CHECKPOINT_RESTORE or next_id is unset */ 222 if (next_id < 0) { /* !CHECKPOINT_RESTORE or next_id is unset */
218 new->seq = ids->seq++; 223
219 if (ids->seq > IPCID_SEQ_MAX) 224 /* allocate the idx, with a NULL struct kern_ipc_perm */
220 ids->seq = 0; 225 idx = idr_alloc(&ids->ipcs_idr, NULL, 0, 0, GFP_NOWAIT);
221 idx = idr_alloc(&ids->ipcs_idr, new, 0, 0, GFP_NOWAIT); 226
227 if (idx >= 0) {
228 /*
229 * idx got allocated successfully.
230 * Now calculate the sequence number and set the
231 * pointer for real.
232 */
233 if (idx <= ids->last_idx) {
234 ids->seq++;
235 if (ids->seq >= ipcid_seq_max())
236 ids->seq = 0;
237 }
238 ids->last_idx = idx;
239
240 new->seq = ids->seq;
241 /* no need for smp_wmb(), this is done
242 * inside idr_replace, as part of
243 * rcu_assign_pointer
244 */
245 idr_replace(&ids->ipcs_idr, new, idx);
246 }
222 } else { 247 } else {
223 new->seq = ipcid_to_seqx(next_id); 248 new->seq = ipcid_to_seqx(next_id);
224 idx = idr_alloc(&ids->ipcs_idr, new, ipcid_to_idx(next_id), 249 idx = idr_alloc(&ids->ipcs_idr, new, ipcid_to_idx(next_id),
225 0, GFP_NOWAIT); 250 0, GFP_NOWAIT);
226 } 251 }
227 if (idx >= 0) 252 if (idx >= 0)
228 new->id = (new->seq << IPCMNI_SEQ_SHIFT) + idx; 253 new->id = (new->seq << ipcmni_seq_shift()) + idx;
229 return idx; 254 return idx;
230} 255}
231 256
diff --git a/ipc/util.h b/ipc/util.h
index 9746886757de..8c834ed39012 100644
--- a/ipc/util.h
+++ b/ipc/util.h
@@ -34,13 +34,13 @@
34extern int ipc_mni; 34extern int ipc_mni;
35extern int ipc_mni_shift; 35extern int ipc_mni_shift;
36 36
37#define IPCMNI_SEQ_SHIFT ipc_mni_shift 37#define ipcmni_seq_shift() ipc_mni_shift
38#define IPCMNI_IDX_MASK ((1 << ipc_mni_shift) - 1) 38#define IPCMNI_IDX_MASK ((1 << ipc_mni_shift) - 1)
39 39
40#else /* CONFIG_SYSVIPC_SYSCTL */ 40#else /* CONFIG_SYSVIPC_SYSCTL */
41 41
42#define ipc_mni IPCMNI 42#define ipc_mni IPCMNI
43#define IPCMNI_SEQ_SHIFT IPCMNI_SHIFT 43#define ipcmni_seq_shift() IPCMNI_SHIFT
44#define IPCMNI_IDX_MASK ((1 << IPCMNI_SHIFT) - 1) 44#define IPCMNI_IDX_MASK ((1 << IPCMNI_SHIFT) - 1)
45#endif /* CONFIG_SYSVIPC_SYSCTL */ 45#endif /* CONFIG_SYSVIPC_SYSCTL */
46 46
@@ -123,8 +123,8 @@ struct pid_namespace *ipc_seq_pid_ns(struct seq_file *);
123#define IPC_SHM_IDS 2 123#define IPC_SHM_IDS 2
124 124
125#define ipcid_to_idx(id) ((id) & IPCMNI_IDX_MASK) 125#define ipcid_to_idx(id) ((id) & IPCMNI_IDX_MASK)
126#define ipcid_to_seqx(id) ((id) >> IPCMNI_SEQ_SHIFT) 126#define ipcid_to_seqx(id) ((id) >> ipcmni_seq_shift())
127#define IPCID_SEQ_MAX (INT_MAX >> IPCMNI_SEQ_SHIFT) 127#define ipcid_seq_max() (INT_MAX >> ipcmni_seq_shift())
128 128
129/* must be called with ids->rwsem acquired for writing */ 129/* must be called with ids->rwsem acquired for writing */
130int ipc_addid(struct ipc_ids *, struct kern_ipc_perm *, int); 130int ipc_addid(struct ipc_ids *, struct kern_ipc_perm *, int);