diff options
Diffstat (limited to 'mm')
| -rw-r--r-- | mm/util.c | 75 |
1 files changed, 75 insertions, 0 deletions
| @@ -7,6 +7,7 @@ | |||
| 7 | #include <linux/err.h> | 7 | #include <linux/err.h> |
| 8 | #include <linux/sched.h> | 8 | #include <linux/sched.h> |
| 9 | #include <linux/sched/mm.h> | 9 | #include <linux/sched/mm.h> |
| 10 | #include <linux/sched/signal.h> | ||
| 10 | #include <linux/sched/task_stack.h> | 11 | #include <linux/sched/task_stack.h> |
| 11 | #include <linux/security.h> | 12 | #include <linux/security.h> |
| 12 | #include <linux/swap.h> | 13 | #include <linux/swap.h> |
| @@ -300,6 +301,80 @@ void arch_pick_mmap_layout(struct mm_struct *mm, struct rlimit *rlim_stack) | |||
| 300 | } | 301 | } |
| 301 | #endif | 302 | #endif |
| 302 | 303 | ||
| 304 | /** | ||
| 305 | * __account_locked_vm - account locked pages to an mm's locked_vm | ||
| 306 | * @mm: mm to account against | ||
| 307 | * @pages: number of pages to account | ||
| 308 | * @inc: %true if @pages should be considered positive, %false if not | ||
| 309 | * @task: task used to check RLIMIT_MEMLOCK | ||
| 310 | * @bypass_rlim: %true if checking RLIMIT_MEMLOCK should be skipped | ||
| 311 | * | ||
| 312 | * Assumes @task and @mm are valid (i.e. at least one reference on each), and | ||
| 313 | * that mmap_sem is held as writer. | ||
| 314 | * | ||
| 315 | * Return: | ||
| 316 | * * 0 on success | ||
| 317 | * * -ENOMEM if RLIMIT_MEMLOCK would be exceeded. | ||
| 318 | */ | ||
| 319 | int __account_locked_vm(struct mm_struct *mm, unsigned long pages, bool inc, | ||
| 320 | struct task_struct *task, bool bypass_rlim) | ||
| 321 | { | ||
| 322 | unsigned long locked_vm, limit; | ||
| 323 | int ret = 0; | ||
| 324 | |||
| 325 | lockdep_assert_held_write(&mm->mmap_sem); | ||
| 326 | |||
| 327 | locked_vm = mm->locked_vm; | ||
| 328 | if (inc) { | ||
| 329 | if (!bypass_rlim) { | ||
| 330 | limit = task_rlimit(task, RLIMIT_MEMLOCK) >> PAGE_SHIFT; | ||
| 331 | if (locked_vm + pages > limit) | ||
| 332 | ret = -ENOMEM; | ||
| 333 | } | ||
| 334 | if (!ret) | ||
| 335 | mm->locked_vm = locked_vm + pages; | ||
| 336 | } else { | ||
| 337 | WARN_ON_ONCE(pages > locked_vm); | ||
| 338 | mm->locked_vm = locked_vm - pages; | ||
| 339 | } | ||
| 340 | |||
| 341 | pr_debug("%s: [%d] caller %ps %c%lu %lu/%lu%s\n", __func__, task->pid, | ||
| 342 | (void *)_RET_IP_, (inc) ? '+' : '-', pages << PAGE_SHIFT, | ||
| 343 | locked_vm << PAGE_SHIFT, task_rlimit(task, RLIMIT_MEMLOCK), | ||
| 344 | ret ? " - exceeded" : ""); | ||
| 345 | |||
| 346 | return ret; | ||
| 347 | } | ||
| 348 | EXPORT_SYMBOL_GPL(__account_locked_vm); | ||
| 349 | |||
| 350 | /** | ||
| 351 | * account_locked_vm - account locked pages to an mm's locked_vm | ||
| 352 | * @mm: mm to account against, may be NULL | ||
| 353 | * @pages: number of pages to account | ||
| 354 | * @inc: %true if @pages should be considered positive, %false if not | ||
| 355 | * | ||
| 356 | * Assumes a non-NULL @mm is valid (i.e. at least one reference on it). | ||
| 357 | * | ||
| 358 | * Return: | ||
| 359 | * * 0 on success, or if mm is NULL | ||
| 360 | * * -ENOMEM if RLIMIT_MEMLOCK would be exceeded. | ||
| 361 | */ | ||
| 362 | int account_locked_vm(struct mm_struct *mm, unsigned long pages, bool inc) | ||
| 363 | { | ||
| 364 | int ret; | ||
| 365 | |||
| 366 | if (pages == 0 || !mm) | ||
| 367 | return 0; | ||
| 368 | |||
| 369 | down_write(&mm->mmap_sem); | ||
| 370 | ret = __account_locked_vm(mm, pages, inc, current, | ||
| 371 | capable(CAP_IPC_LOCK)); | ||
| 372 | up_write(&mm->mmap_sem); | ||
| 373 | |||
| 374 | return ret; | ||
| 375 | } | ||
| 376 | EXPORT_SYMBOL_GPL(account_locked_vm); | ||
| 377 | |||
| 303 | unsigned long vm_mmap_pgoff(struct file *file, unsigned long addr, | 378 | unsigned long vm_mmap_pgoff(struct file *file, unsigned long addr, |
| 304 | unsigned long len, unsigned long prot, | 379 | unsigned long len, unsigned long prot, |
| 305 | unsigned long flag, unsigned long pgoff) | 380 | unsigned long flag, unsigned long pgoff) |
