diff options
Diffstat (limited to 'ipc/util.c')
-rw-r--r-- | ipc/util.c | 35 |
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 | */ |
196 | static inline int ipc_idr_alloc(struct ipc_ids *ids, struct kern_ipc_perm *new) | 201 | static 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 | ||