diff options
| -rw-r--r-- | mm/rmap.c | 39 |
1 files changed, 36 insertions, 3 deletions
| @@ -200,6 +200,32 @@ int anon_vma_prepare(struct vm_area_struct *vma) | |||
| 200 | return -ENOMEM; | 200 | return -ENOMEM; |
| 201 | } | 201 | } |
| 202 | 202 | ||
| 203 | /* | ||
| 204 | * This is a useful helper function for locking the anon_vma root as | ||
| 205 | * we traverse the vma->anon_vma_chain, looping over anon_vma's that | ||
| 206 | * have the same vma. | ||
| 207 | * | ||
| 208 | * Such anon_vma's should have the same root, so you'd expect to see | ||
| 209 | * just a single mutex_lock for the whole traversal. | ||
| 210 | */ | ||
| 211 | static inline struct anon_vma *lock_anon_vma_root(struct anon_vma *root, struct anon_vma *anon_vma) | ||
| 212 | { | ||
| 213 | struct anon_vma *new_root = anon_vma->root; | ||
| 214 | if (new_root != root) { | ||
| 215 | if (WARN_ON_ONCE(root)) | ||
| 216 | mutex_unlock(&root->mutex); | ||
| 217 | root = new_root; | ||
| 218 | mutex_lock(&root->mutex); | ||
| 219 | } | ||
| 220 | return root; | ||
| 221 | } | ||
| 222 | |||
| 223 | static inline void unlock_anon_vma_root(struct anon_vma *root) | ||
| 224 | { | ||
| 225 | if (root) | ||
| 226 | mutex_unlock(&root->mutex); | ||
| 227 | } | ||
| 228 | |||
| 203 | static void anon_vma_chain_link(struct vm_area_struct *vma, | 229 | static void anon_vma_chain_link(struct vm_area_struct *vma, |
| 204 | struct anon_vma_chain *avc, | 230 | struct anon_vma_chain *avc, |
| 205 | struct anon_vma *anon_vma) | 231 | struct anon_vma *anon_vma) |
| @@ -208,13 +234,11 @@ static void anon_vma_chain_link(struct vm_area_struct *vma, | |||
| 208 | avc->anon_vma = anon_vma; | 234 | avc->anon_vma = anon_vma; |
| 209 | list_add(&avc->same_vma, &vma->anon_vma_chain); | 235 | list_add(&avc->same_vma, &vma->anon_vma_chain); |
| 210 | 236 | ||
| 211 | anon_vma_lock(anon_vma); | ||
| 212 | /* | 237 | /* |
| 213 | * It's critical to add new vmas to the tail of the anon_vma, | 238 | * It's critical to add new vmas to the tail of the anon_vma, |
| 214 | * see comment in huge_memory.c:__split_huge_page(). | 239 | * see comment in huge_memory.c:__split_huge_page(). |
| 215 | */ | 240 | */ |
| 216 | list_add_tail(&avc->same_anon_vma, &anon_vma->head); | 241 | list_add_tail(&avc->same_anon_vma, &anon_vma->head); |
| 217 | anon_vma_unlock(anon_vma); | ||
| 218 | } | 242 | } |
| 219 | 243 | ||
| 220 | /* | 244 | /* |
| @@ -224,16 +248,23 @@ static void anon_vma_chain_link(struct vm_area_struct *vma, | |||
| 224 | int anon_vma_clone(struct vm_area_struct *dst, struct vm_area_struct *src) | 248 | int anon_vma_clone(struct vm_area_struct *dst, struct vm_area_struct *src) |
| 225 | { | 249 | { |
| 226 | struct anon_vma_chain *avc, *pavc; | 250 | struct anon_vma_chain *avc, *pavc; |
| 251 | struct anon_vma *root = NULL; | ||
| 227 | 252 | ||
| 228 | list_for_each_entry_reverse(pavc, &src->anon_vma_chain, same_vma) { | 253 | list_for_each_entry_reverse(pavc, &src->anon_vma_chain, same_vma) { |
| 254 | struct anon_vma *anon_vma; | ||
| 255 | |||
| 229 | avc = anon_vma_chain_alloc(); | 256 | avc = anon_vma_chain_alloc(); |
| 230 | if (!avc) | 257 | if (!avc) |
| 231 | goto enomem_failure; | 258 | goto enomem_failure; |
| 232 | anon_vma_chain_link(dst, avc, pavc->anon_vma); | 259 | anon_vma = pavc->anon_vma; |
| 260 | root = lock_anon_vma_root(root, anon_vma); | ||
| 261 | anon_vma_chain_link(dst, avc, anon_vma); | ||
| 233 | } | 262 | } |
| 263 | unlock_anon_vma_root(root); | ||
| 234 | return 0; | 264 | return 0; |
| 235 | 265 | ||
| 236 | enomem_failure: | 266 | enomem_failure: |
| 267 | unlock_anon_vma_root(root); | ||
| 237 | unlink_anon_vmas(dst); | 268 | unlink_anon_vmas(dst); |
| 238 | return -ENOMEM; | 269 | return -ENOMEM; |
| 239 | } | 270 | } |
| @@ -280,7 +311,9 @@ int anon_vma_fork(struct vm_area_struct *vma, struct vm_area_struct *pvma) | |||
| 280 | get_anon_vma(anon_vma->root); | 311 | get_anon_vma(anon_vma->root); |
| 281 | /* Mark this anon_vma as the one where our new (COWed) pages go. */ | 312 | /* Mark this anon_vma as the one where our new (COWed) pages go. */ |
| 282 | vma->anon_vma = anon_vma; | 313 | vma->anon_vma = anon_vma; |
| 314 | anon_vma_lock(anon_vma); | ||
| 283 | anon_vma_chain_link(vma, avc, anon_vma); | 315 | anon_vma_chain_link(vma, avc, anon_vma); |
| 316 | anon_vma_unlock(anon_vma); | ||
| 284 | 317 | ||
| 285 | return 0; | 318 | return 0; |
| 286 | 319 | ||
