summaryrefslogtreecommitdiffstats
path: root/ipc/util.c
diff options
context:
space:
mode:
Diffstat (limited to 'ipc/util.c')
-rw-r--r--ipc/util.c35
1 files changed, 30 insertions, 5 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