diff options
| -rw-r--r-- | include/linux/ipc_namespace.h | 1 | ||||
| -rw-r--r-- | ipc/util.c | 35 | ||||
| -rw-r--r-- | ipc/util.h | 8 |
3 files changed, 35 insertions, 9 deletions
diff --git a/include/linux/ipc_namespace.h b/include/linux/ipc_namespace.h index 6ab8c1bada3f..c309f43bde45 100644 --- a/include/linux/ipc_namespace.h +++ b/include/linux/ipc_namespace.h | |||
| @@ -19,6 +19,7 @@ struct ipc_ids { | |||
| 19 | struct rw_semaphore rwsem; | 19 | struct rw_semaphore rwsem; |
| 20 | struct idr ipcs_idr; | 20 | struct idr ipcs_idr; |
| 21 | int max_idx; | 21 | int max_idx; |
| 22 | int last_idx; /* For wrap around detection */ | ||
| 22 | #ifdef CONFIG_CHECKPOINT_RESTORE | 23 | #ifdef CONFIG_CHECKPOINT_RESTORE |
| 23 | int next_id; | 24 | int next_id; |
| 24 | #endif | 25 | #endif |
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 | ||
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 @@ | |||
| 34 | extern int ipc_mni; | 34 | extern int ipc_mni; |
| 35 | extern int ipc_mni_shift; | 35 | extern 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 */ |
| 130 | int ipc_addid(struct ipc_ids *, struct kern_ipc_perm *, int); | 130 | int ipc_addid(struct ipc_ids *, struct kern_ipc_perm *, int); |
