Commit ecc1a8993751de4e82eb18640d631dae1f626bd6
1 parent
54f5de7099
Exists in
master
and in
20 other branches
do_mremap() untangling, part 2
Take the MREMAP_FIXED into a separate helper, simplify the living hell out of conditions in both cases. Acked-by: Russell King <rmk+kernel@arm.linux.org.uk> Acked-by: Hugh Dickins <hugh.dickins@tiscali.co.uk> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Showing 1 changed file with 72 additions and 48 deletions Side-by-side Diff
mm/mremap.c
... | ... | @@ -313,6 +313,59 @@ |
313 | 313 | return ERR_PTR(-EAGAIN); |
314 | 314 | } |
315 | 315 | |
316 | +static unsigned long mremap_to(unsigned long addr, | |
317 | + unsigned long old_len, unsigned long new_addr, | |
318 | + unsigned long new_len) | |
319 | +{ | |
320 | + struct mm_struct *mm = current->mm; | |
321 | + struct vm_area_struct *vma; | |
322 | + unsigned long ret = -EINVAL; | |
323 | + unsigned long charged = 0; | |
324 | + | |
325 | + if (new_addr & ~PAGE_MASK) | |
326 | + goto out; | |
327 | + | |
328 | + if (new_len > TASK_SIZE || new_addr > TASK_SIZE - new_len) | |
329 | + goto out; | |
330 | + | |
331 | + /* Check if the location we're moving into overlaps the | |
332 | + * old location at all, and fail if it does. | |
333 | + */ | |
334 | + if ((new_addr <= addr) && (new_addr+new_len) > addr) | |
335 | + goto out; | |
336 | + | |
337 | + if ((addr <= new_addr) && (addr+old_len) > new_addr) | |
338 | + goto out; | |
339 | + | |
340 | + ret = security_file_mmap(NULL, 0, 0, 0, new_addr, 1); | |
341 | + if (ret) | |
342 | + goto out; | |
343 | + | |
344 | + ret = do_munmap(mm, new_addr, new_len); | |
345 | + if (ret) | |
346 | + goto out; | |
347 | + | |
348 | + if (old_len >= new_len) { | |
349 | + ret = do_munmap(mm, addr+new_len, old_len - new_len); | |
350 | + if (ret && old_len != new_len) | |
351 | + goto out; | |
352 | + old_len = new_len; | |
353 | + } | |
354 | + | |
355 | + vma = vma_to_resize(addr, old_len, new_len, &charged); | |
356 | + if (IS_ERR(vma)) { | |
357 | + ret = PTR_ERR(vma); | |
358 | + goto out; | |
359 | + } | |
360 | + | |
361 | + ret = move_vma(vma, addr, old_len, new_len, new_addr); | |
362 | + if (ret & ~PAGE_MASK) | |
363 | + vm_unacct_memory(charged); | |
364 | + | |
365 | +out: | |
366 | + return ret; | |
367 | +} | |
368 | + | |
316 | 369 | /* |
317 | 370 | * Expand (or shrink) an existing mapping, potentially moving it at the |
318 | 371 | * same time (controlled by the MREMAP_MAYMOVE flag and available VM space) |
319 | 372 | |
... | ... | @@ -346,32 +399,10 @@ |
346 | 399 | if (!new_len) |
347 | 400 | goto out; |
348 | 401 | |
349 | - /* new_addr is only valid if MREMAP_FIXED is specified */ | |
350 | 402 | if (flags & MREMAP_FIXED) { |
351 | - if (new_addr & ~PAGE_MASK) | |
352 | - goto out; | |
353 | - if (!(flags & MREMAP_MAYMOVE)) | |
354 | - goto out; | |
355 | - | |
356 | - if (new_len > TASK_SIZE || new_addr > TASK_SIZE - new_len) | |
357 | - goto out; | |
358 | - | |
359 | - /* Check if the location we're moving into overlaps the | |
360 | - * old location at all, and fail if it does. | |
361 | - */ | |
362 | - if ((new_addr <= addr) && (new_addr+new_len) > addr) | |
363 | - goto out; | |
364 | - | |
365 | - if ((addr <= new_addr) && (addr+old_len) > new_addr) | |
366 | - goto out; | |
367 | - | |
368 | - ret = security_file_mmap(NULL, 0, 0, 0, new_addr, 1); | |
369 | - if (ret) | |
370 | - goto out; | |
371 | - | |
372 | - ret = do_munmap(mm, new_addr, new_len); | |
373 | - if (ret) | |
374 | - goto out; | |
403 | + if (flags & MREMAP_MAYMOVE) | |
404 | + ret = mremap_to(addr, old_len, new_addr, new_len); | |
405 | + goto out; | |
375 | 406 | } |
376 | 407 | |
377 | 408 | /* |
378 | 409 | |
... | ... | @@ -384,13 +415,11 @@ |
384 | 415 | if (ret && old_len != new_len) |
385 | 416 | goto out; |
386 | 417 | ret = addr; |
387 | - if (!(flags & MREMAP_FIXED) || (new_addr == addr)) | |
388 | - goto out; | |
389 | - old_len = new_len; | |
418 | + goto out; | |
390 | 419 | } |
391 | 420 | |
392 | 421 | /* |
393 | - * Ok, we need to grow.. or relocate. | |
422 | + * Ok, we need to grow.. | |
394 | 423 | */ |
395 | 424 | vma = vma_to_resize(addr, old_len, new_len, &charged); |
396 | 425 | if (IS_ERR(vma)) { |
397 | 426 | |
... | ... | @@ -399,11 +428,8 @@ |
399 | 428 | } |
400 | 429 | |
401 | 430 | /* old_len exactly to the end of the area.. |
402 | - * And we're not relocating the area. | |
403 | 431 | */ |
404 | - if (old_len == vma->vm_end - addr && | |
405 | - !((flags & MREMAP_FIXED) && (addr != new_addr)) && | |
406 | - (old_len != new_len || !(flags & MREMAP_MAYMOVE))) { | |
432 | + if (old_len == vma->vm_end - addr) { | |
407 | 433 | unsigned long max_addr = TASK_SIZE; |
408 | 434 | if (vma->vm_next) |
409 | 435 | max_addr = vma->vm_next->vm_start; |
410 | 436 | |
411 | 437 | |
... | ... | @@ -432,22 +458,20 @@ |
432 | 458 | */ |
433 | 459 | ret = -ENOMEM; |
434 | 460 | if (flags & MREMAP_MAYMOVE) { |
435 | - if (!(flags & MREMAP_FIXED)) { | |
436 | - unsigned long map_flags = 0; | |
437 | - if (vma->vm_flags & VM_MAYSHARE) | |
438 | - map_flags |= MAP_SHARED; | |
461 | + unsigned long map_flags = 0; | |
462 | + if (vma->vm_flags & VM_MAYSHARE) | |
463 | + map_flags |= MAP_SHARED; | |
439 | 464 | |
440 | - new_addr = get_unmapped_area(vma->vm_file, 0, new_len, | |
441 | - vma->vm_pgoff, map_flags); | |
442 | - if (new_addr & ~PAGE_MASK) { | |
443 | - ret = new_addr; | |
444 | - goto out; | |
445 | - } | |
446 | - | |
447 | - ret = security_file_mmap(NULL, 0, 0, 0, new_addr, 1); | |
448 | - if (ret) | |
449 | - goto out; | |
465 | + new_addr = get_unmapped_area(vma->vm_file, 0, new_len, | |
466 | + vma->vm_pgoff, map_flags); | |
467 | + if (new_addr & ~PAGE_MASK) { | |
468 | + ret = new_addr; | |
469 | + goto out; | |
450 | 470 | } |
471 | + | |
472 | + ret = security_file_mmap(NULL, 0, 0, 0, new_addr, 1); | |
473 | + if (ret) | |
474 | + goto out; | |
451 | 475 | ret = move_vma(vma, addr, old_len, new_len, new_addr); |
452 | 476 | } |
453 | 477 | out: |