mm/madvise: introduce process_madvise() syscall: an external memory hinting API
authorMinchan Kim <minchan@kernel.org>
Sat, 17 Oct 2020 23:14:59 +0000 (16:14 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sun, 18 Oct 2020 16:27:10 +0000 (09:27 -0700)
There is usecase that System Management Software(SMS) want to give a
memory hint like MADV_[COLD|PAGEEOUT] to other processes and in the
case of Android, it is the ActivityManagerService.

The information required to make the reclaim decision is not known to the
app.  Instead, it is known to the centralized userspace
daemon(ActivityManagerService), and that daemon must be able to initiate
reclaim on its own without any app involvement.

To solve the issue, this patch introduces a new syscall
process_madvise(2).  It uses pidfd of an external process to give the
hint.  It also supports vector address range because Android app has
thousands of vmas due to zygote so it's totally waste of CPU and power if
we should call the syscall one by one for each vma.(With testing 2000-vma
syscall vs 1-vector syscall, it showed 15% performance improvement.  I
think it would be bigger in real practice because the testing ran very
cache friendly environment).

Another potential use case for the vector range is to amortize the cost
ofTLB shootdowns for multiple ranges when using MADV_DONTNEED; this could
benefit users like TCP receive zerocopy and malloc implementations.  In
future, we could find more usecases for other advises so let's make it
happens as API since we introduce a new syscall at this moment.  With
that, existing madvise(2) user could replace it with process_madvise(2)
with their own pid if they want to have batch address ranges support
feature.

ince it could affect other process's address range, only privileged
process(PTRACE_MODE_ATTACH_FSCREDS) or something else(e.g., being the same
UID) gives it the right to ptrace the process could use it successfully.
The flag argument is reserved for future use if we need to extend the API.

I think supporting all hints madvise has/will supported/support to
process_madvise is rather risky.  Because we are not sure all hints make
sense from external process and implementation for the hint may rely on
the caller being in the current context so it could be error-prone.  Thus,
I just limited hints as MADV_[COLD|PAGEOUT] in this patch.

If someone want to add other hints, we could hear the usecase and review
it for each hint.  It's safer for maintenance rather than introducing a
buggy syscall but hard to fix it later.

So finally, the API is as follows,

      ssize_t process_madvise(int pidfd, const struct iovec *iovec,
                unsigned long vlen, int advice, unsigned int flags);

    DESCRIPTION
      The process_madvise() system call is used to give advice or directions
      to the kernel about the address ranges from external process as well as
      local process. It provides the advice to address ranges of process
      described by iovec and vlen. The goal of such advice is to improve
      system or application performance.

      The pidfd selects the process referred to by the PID file descriptor
      specified in pidfd. (See pidofd_open(2) for further information)

      The pointer iovec points to an array of iovec structures, defined in
      <sys/uio.h> as:

        struct iovec {
            void *iov_base;         /* starting address */
            size_t iov_len;         /* number of bytes to be advised */
        };

      The iovec describes address ranges beginning at address(iov_base)
      and with size length of bytes(iov_len).

      The vlen represents the number of elements in iovec.

      The advice is indicated in the advice argument, which is one of the
      following at this moment if the target process specified by pidfd is
      external.

        MADV_COLD
        MADV_PAGEOUT

      Permission to provide a hint to external process is governed by a
      ptrace access mode PTRACE_MODE_ATTACH_FSCREDS check; see ptrace(2).

      The process_madvise supports every advice madvise(2) has if target
      process is in same thread group with calling process so user could
      use process_madvise(2) to extend existing madvise(2) to support
      vector address ranges.

    RETURN VALUE
      On success, process_madvise() returns the number of bytes advised.
      This return value may be less than the total number of requested
      bytes, if an error occurred. The caller should check return value
      to determine whether a partial advice occurred.

FAQ:

Q.1 - Why does any external entity have better knowledge?

Quote from Sandeep

"For Android, every application (including the special SystemServer)
are forked from Zygote.  The reason of course is to share as many
libraries and classes between the two as possible to benefit from the
preloading during boot.

After applications start, (almost) all of the APIs end up calling into
this SystemServer process over IPC (binder) and back to the
application.

In a fully running system, the SystemServer monitors every single
process periodically to calculate their PSS / RSS and also decides
which process is "important" to the user for interactivity.

So, because of how these processes start _and_ the fact that the
SystemServer is looping to monitor each process, it does tend to *know*
which address range of the application is not used / useful.

Besides, we can never rely on applications to clean things up
themselves.  We've had the "hey app1, the system is low on memory,
please trim your memory usage down" notifications for a long time[1].
They rely on applications honoring the broadcasts and very few do.

So, if we want to avoid the inevitable killing of the application and
restarting it, some way to be able to tell the OS about unimportant
memory in these applications will be useful.

- ssp

Q.2 - How to guarantee the race(i.e., object validation) between when
giving a hint from an external process and get the hint from the target
process?

process_madvise operates on the target process's address space as it
exists at the instant that process_madvise is called.  If the space
target process can run between the time the process_madvise process
inspects the target process address space and the time that
process_madvise is actually called, process_madvise may operate on
memory regions that the calling process does not expect.  It's the
responsibility of the process calling process_madvise to close this
race condition.  For example, the calling process can suspend the
target process with ptrace, SIGSTOP, or the freezer cgroup so that it
doesn't have an opportunity to change its own address space before
process_madvise is called.  Another option is to operate on memory
regions that the caller knows a priori will be unchanged in the target
process.  Yet another option is to accept the race for certain
process_madvise calls after reasoning that mistargeting will do no
harm.  The suggested API itself does not provide synchronization.  It
also apply other APIs like move_pages, process_vm_write.

The race isn't really a problem though.  Why is it so wrong to require
that callers do their own synchronization in some manner?  Nobody
objects to write(2) merely because it's possible for two processes to
open the same file and clobber each other's writes --- instead, we tell
people to use flock or something.  Think about mmap.  It never
guarantees newly allocated address space is still valid when the user
tries to access it because other threads could unmap the memory right
before.  That's where we need synchronization by using other API or
design from userside.  It shouldn't be part of API itself.  If someone
needs more fine-grained synchronization rather than process level,
there were two ideas suggested - cookie[2] and anon-fd[3].  Both are
applicable via using last reserved argument of the API but I don't
think it's necessary right now since we have already ways to prevent
the race so don't want to add additional complexity with more
fine-grained optimization model.

To make the API extend, it reserved an unsigned long as last argument
so we could support it in future if someone really needs it.

Q.3 - Why doesn't ptrace work?

Injecting an madvise in the target process using ptrace would not work
for us because such injected madvise would have to be executed by the
target process, which means that process would have to be runnable and
that creates the risk of the abovementioned race and hinting a wrong
VMA.  Furthermore, we want to act the hint in caller's context, not the
callee's, because the callee is usually limited in cpuset/cgroups or
even freezed state so they can't act by themselves quick enough, which
causes more thrashing/kill.  It doesn't work if the target process are
ptraced(e.g., strace, debugger, minidump) because a process can have at
most one ptracer.

[1] https://developer.android.com/topic/performance/memory"

[2] process_getinfo for getting the cookie which is updated whenever
    vma of process address layout are changed - Daniel Colascione -
    https://lore.kernel.org/lkml/20190520035254.57579-1-minchan@kernel.org/T/#m7694416fd179b2066a2c62b5b139b14e3894e224

[3] anonymous fd which is used for the object(i.e., address range)
    validation - Michal Hocko -
    https://lore.kernel.org/lkml/20200120112722.GY18451@dhcp22.suse.cz/

[minchan@kernel.org: fix process_madvise build break for arm64]
Link: http://lkml.kernel.org/r/20200303145756.GA219683@google.com
[minchan@kernel.org: fix build error for mips of process_madvise]
Link: http://lkml.kernel.org/r/20200508052517.GA197378@google.com
[akpm@linux-foundation.org: fix patch ordering issue]
[akpm@linux-foundation.org: fix arm64 whoops]
[minchan@kernel.org: make process_madvise() vlen arg have type size_t, per Florian]
[akpm@linux-foundation.org: fix i386 build]
[sfr@canb.auug.org.au: fix syscall numbering]
Link: https://lkml.kernel.org/r/20200905142639.49fc3f1a@canb.auug.org.au
[sfr@canb.auug.org.au: madvise.c needs compat.h]
Link: https://lkml.kernel.org/r/20200908204547.285646b4@canb.auug.org.au
[minchan@kernel.org: fix mips build]
Link: https://lkml.kernel.org/r/20200909173655.GC2435453@google.com
[yuehaibing@huawei.com: remove duplicate header which is included twice]
Link: https://lkml.kernel.org/r/20200915121550.30584-1-yuehaibing@huawei.com
[minchan@kernel.org: do not use helper functions for process_madvise]
Link: https://lkml.kernel.org/r/20200921175539.GB387368@google.com
[akpm@linux-foundation.org: pidfd_get_pid() gained an argument]
[sfr@canb.auug.org.au: fix up for "iov_iter: transparently handle compat iovecs in import_iovec"]
Link: https://lkml.kernel.org/r/20200928212542.468e1fef@canb.auug.org.au
Signed-off-by: Minchan Kim <minchan@kernel.org>
Signed-off-by: YueHaibing <yuehaibing@huawei.com>
Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Reviewed-by: Suren Baghdasaryan <surenb@google.com>
Reviewed-by: Vlastimil Babka <vbabka@suse.cz>
Acked-by: David Rientjes <rientjes@google.com>
Cc: Alexander Duyck <alexander.h.duyck@linux.intel.com>
Cc: Brian Geffon <bgeffon@google.com>
Cc: Christian Brauner <christian@brauner.io>
Cc: Daniel Colascione <dancol@google.com>
Cc: Jann Horn <jannh@google.com>
Cc: Jens Axboe <axboe@kernel.dk>
Cc: Joel Fernandes <joel@joelfernandes.org>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: John Dias <joaodias@google.com>
Cc: Kirill Tkhai <ktkhai@virtuozzo.com>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Oleksandr Natalenko <oleksandr@redhat.com>
Cc: Sandeep Patil <sspatil@google.com>
Cc: SeongJae Park <sj38.park@gmail.com>
Cc: SeongJae Park <sjpark@amazon.de>
Cc: Shakeel Butt <shakeelb@google.com>
Cc: Sonny Rao <sonnyrao@google.com>
Cc: Tim Murray <timmurray@google.com>
Cc: Christian Brauner <christian.brauner@ubuntu.com>
Cc: Florian Weimer <fw@deneb.enyo.de>
Cc: <linux-man@vger.kernel.org>
Link: http://lkml.kernel.org/r/20200302193630.68771-3-minchan@kernel.org
Link: http://lkml.kernel.org/r/20200508183320.GA125527@google.com
Link: http://lkml.kernel.org/r/20200622192900.22757-4-minchan@kernel.org
Link: https://lkml.kernel.org/r/20200901000633.1920247-4-minchan@kernel.org
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
22 files changed:
arch/alpha/kernel/syscalls/syscall.tbl
arch/arm/tools/syscall.tbl
arch/arm64/include/asm/unistd.h
arch/arm64/include/asm/unistd32.h
arch/ia64/kernel/syscalls/syscall.tbl
arch/m68k/kernel/syscalls/syscall.tbl
arch/microblaze/kernel/syscalls/syscall.tbl
arch/mips/kernel/syscalls/syscall_n32.tbl
arch/mips/kernel/syscalls/syscall_n64.tbl
arch/mips/kernel/syscalls/syscall_o32.tbl
arch/parisc/kernel/syscalls/syscall.tbl
arch/powerpc/kernel/syscalls/syscall.tbl
arch/s390/kernel/syscalls/syscall.tbl
arch/sh/kernel/syscalls/syscall.tbl
arch/sparc/kernel/syscalls/syscall.tbl
arch/x86/entry/syscalls/syscall_32.tbl
arch/x86/entry/syscalls/syscall_64.tbl
arch/xtensa/kernel/syscalls/syscall.tbl
include/linux/syscalls.h
include/uapi/asm-generic/unistd.h
kernel/sys_ni.c
mm/madvise.c

index ec8bed9e7b75b6a9b22fd75a36d614c2712982cc..ee7b01bb7346c9c57843e68a3fff34ad703601fa 100644 (file)
 547    common  openat2                         sys_openat2
 548    common  pidfd_getfd                     sys_pidfd_getfd
 549    common  faccessat2                      sys_faccessat2
+550    common  process_madvise                 sys_process_madvise
index 171077cbf419b419ab7d906a23c7ed120a129ccb..d056a548358ea024fc64f3a107849951a26838d7 100644 (file)
 437    common  openat2                         sys_openat2
 438    common  pidfd_getfd                     sys_pidfd_getfd
 439    common  faccessat2                      sys_faccessat2
+440    common  process_madvise                 sys_process_madvise
index 3b859596840de5238645308ecd1bf389401822c5..b3b2019f8d16bbeef51948c249dcfb6dbd97a0fb 100644 (file)
@@ -38,7 +38,7 @@
 #define __ARM_NR_compat_set_tls                (__ARM_NR_COMPAT_BASE + 5)
 #define __ARM_NR_COMPAT_END            (__ARM_NR_COMPAT_BASE + 0x800)
 
-#define __NR_compat_syscalls           440
+#define __NR_compat_syscalls           441
 #endif
 
 #define __ARCH_WANT_SYS_CLONE
index 2a3ad9b9accd55d28ba9268b6a6415a8d65faff2..107f08e03b9fdc4d54b41c7fca707727e1524c92 100644 (file)
@@ -887,6 +887,8 @@ __SYSCALL(__NR_openat2, sys_openat2)
 __SYSCALL(__NR_pidfd_getfd, sys_pidfd_getfd)
 #define __NR_faccessat2 439
 __SYSCALL(__NR_faccessat2, sys_faccessat2)
+#define __NR_process_madvise 440
+__SYSCALL(__NR_process_madvise, sys_process_madvise)
 
 /*
  * Please add new compat syscalls above this comment and update
index 4799c96c325f14f232b2168f52869f06ac574c9e..b96ed8b8a508999112a1d8065db035d90efaec0c 100644 (file)
 437    common  openat2                         sys_openat2
 438    common  pidfd_getfd                     sys_pidfd_getfd
 439    common  faccessat2                      sys_faccessat2
+440    common  process_madvise                 sys_process_madvise
index 81fc799d839234f00c0b13e56959f5f165827a97..625fb6d3284240e0d8bc96556c0b3ff92b350540 100644 (file)
 437    common  openat2                         sys_openat2
 438    common  pidfd_getfd                     sys_pidfd_getfd
 439    common  faccessat2                      sys_faccessat2
+440    common  process_madvise                 sys_process_madvise
index b4e263916f4132a9a710f4f6fc4f604f675d9e3a..aae729c95cf99d23e056c510651c6f3126d4ceff 100644 (file)
 437    common  openat2                         sys_openat2
 438    common  pidfd_getfd                     sys_pidfd_getfd
 439    common  faccessat2                      sys_faccessat2
+440    common  process_madvise                 sys_process_madvise
index cf72a0206a87c2d318c2b9433120a832db911684..32817c954435d7c7dba4a126b35054f18ae0d9ee 100644 (file)
 437    n32     openat2                         sys_openat2
 438    n32     pidfd_getfd                     sys_pidfd_getfd
 439    n32     faccessat2                      sys_faccessat2
+440    n32     process_madvise                 sys_process_madvise
index 557f9954a2b9a8fd66179f572d0fcffb63638bcd..9e4ea3c31b1ce026fdad266866b60be6426d882b 100644 (file)
 437    n64     openat2                         sys_openat2
 438    n64     pidfd_getfd                     sys_pidfd_getfd
 439    n64     faccessat2                      sys_faccessat2
+440    n64     process_madvise                 sys_process_madvise
index a17aab5abeb2d618b54081b1adc8b69939135d24..29f5f28cf5cea83d6119637ea57d40ad6abe4d40 100644 (file)
 437    o32     openat2                         sys_openat2
 438    o32     pidfd_getfd                     sys_pidfd_getfd
 439    o32     faccessat2                      sys_faccessat2
+440    o32     process_madvise                 sys_process_madvise
index ae3dab371f6f85990331dd0767a41ed9cf22ff53..38c63e5404bcd0dbebbe493e824549be565d9163 100644 (file)
 437    common  openat2                         sys_openat2
 438    common  pidfd_getfd                     sys_pidfd_getfd
 439    common  faccessat2                      sys_faccessat2
+440    common  process_madvise                 sys_process_madvise
index 9d7fb4ced290406cef9417176cdacf557d4a0b9b..1275daec7fec365024fd50d2b43d42616d829b20 100644 (file)
 437    common  openat2                         sys_openat2
 438    common  pidfd_getfd                     sys_pidfd_getfd
 439    common  faccessat2                      sys_faccessat2
+440    common  process_madvise                 sys_process_madvise
index 1c3b48165e86fe79a67fa6db544c457163e32c66..28c16800048344821461943d58f99ff1f50ab4c6 100644 (file)
 437  common    openat2                 sys_openat2                     sys_openat2
 438  common    pidfd_getfd             sys_pidfd_getfd                 sys_pidfd_getfd
 439  common    faccessat2              sys_faccessat2                  sys_faccessat2
+440  common    process_madvise         sys_process_madvise             sys_process_madvise
index ae0a00beea5f2e8f2b8335c634a5f7341323c345..783738448ff555f66baa8d9b559dce0d3e08b325 100644 (file)
 437    common  openat2                         sys_openat2
 438    common  pidfd_getfd                     sys_pidfd_getfd
 439    common  faccessat2                      sys_faccessat2
+440    common  process_madvise                 sys_process_madvise
index 37ec52b34c73c5d163911030e9f1b2d5cc6c6ee9..78160260991be726ff888371314cfd10b82d5b6a 100644 (file)
 437    common  openat2                 sys_openat2
 438    common  pidfd_getfd                     sys_pidfd_getfd
 439    common  faccessat2                      sys_faccessat2
+440    common  process_madvise                 sys_process_madvise
index 9b6931f8d5551ec7f2837a8f2a416aea58649796..0d0667a9fbd70aeb9e297342b9f855a457d45ea4 100644 (file)
 437    i386    openat2                 sys_openat2
 438    i386    pidfd_getfd             sys_pidfd_getfd
 439    i386    faccessat2              sys_faccessat2
+440    i386    process_madvise         sys_process_madvise
index 347809649ba28fa15be86511d563ff9d427ecaea..1f47e24fb65cf8a7525be8a53934eb4fa63da7d1 100644 (file)
 437    common  openat2                 sys_openat2
 438    common  pidfd_getfd             sys_pidfd_getfd
 439    common  faccessat2              sys_faccessat2
+440    common  process_madvise         sys_process_madvise
 
 #
 # x32-specific system call numbers start at 512 to avoid cache impact
index 6276e3c2d3fc0d82ac59907e43c3895f42f54a74..b070f272995d6447895287704a0c76237619c2cc 100644 (file)
 437    common  openat2                         sys_openat2
 438    common  pidfd_getfd                     sys_pidfd_getfd
 439    common  faccessat2                      sys_faccessat2
+440    common  process_madvise                 sys_process_madvise
index 06db09875aa4a73aec07c53d2cf0465e1090ad10..2eda7678fe1d974b7a29a3e53ea92105e95fd0a4 100644 (file)
@@ -879,6 +879,8 @@ asmlinkage long sys_munlockall(void);
 asmlinkage long sys_mincore(unsigned long start, size_t len,
                                unsigned char __user * vec);
 asmlinkage long sys_madvise(unsigned long start, size_t len, int behavior);
+asmlinkage long sys_process_madvise(int pidfd, const struct iovec __user *vec,
+                       size_t vlen, int behavior, unsigned int flags);
 asmlinkage long sys_remap_file_pages(unsigned long start, unsigned long size,
                        unsigned long prot, unsigned long pgoff,
                        unsigned long flags);
index f2b5d72a46c23b9f9abd6b439b29e1192bf59dff..2056318988f774931c4e0a3104144bf4a75ff52f 100644 (file)
@@ -857,9 +857,11 @@ __SYSCALL(__NR_openat2, sys_openat2)
 __SYSCALL(__NR_pidfd_getfd, sys_pidfd_getfd)
 #define __NR_faccessat2 439
 __SYSCALL(__NR_faccessat2, sys_faccessat2)
+#define __NR_process_madvise 440
+__SYSCALL(__NR_process_madvise, sys_process_madvise)
 
 #undef __NR_syscalls
-#define __NR_syscalls 440
+#define __NR_syscalls 441
 
 /*
  * 32 bit systems traditionally used different
index c925d1e1777efcf828e2796ce135d0e4c546dc31..f27ac94d5fa7270ba9f272be1bcba6a903149206 100644 (file)
@@ -280,6 +280,7 @@ COND_SYSCALL(mlockall);
 COND_SYSCALL(munlockall);
 COND_SYSCALL(mincore);
 COND_SYSCALL(madvise);
+COND_SYSCALL(process_madvise);
 COND_SYSCALL(remap_file_pages);
 COND_SYSCALL(mbind);
 COND_SYSCALL_COMPAT(mbind);
index d550ef045288e8c6b2fe13ffa6968ca83d72a0ae..416a56b8e757bf3465ab13cea51e0751ade2c745 100644 (file)
@@ -17,6 +17,8 @@
 #include <linux/falloc.h>
 #include <linux/fadvise.h>
 #include <linux/sched.h>
+#include <linux/sched/mm.h>
+#include <linux/uio.h>
 #include <linux/ksm.h>
 #include <linux/fs.h>
 #include <linux/file.h>
@@ -27,7 +29,6 @@
 #include <linux/swapops.h>
 #include <linux/shmem_fs.h>
 #include <linux/mmu_notifier.h>
-#include <linux/sched/mm.h>
 
 #include <asm/tlb.h>
 
@@ -988,6 +989,18 @@ madvise_behavior_valid(int behavior)
        }
 }
 
+static bool
+process_madvise_behavior_valid(int behavior)
+{
+       switch (behavior) {
+       case MADV_COLD:
+       case MADV_PAGEOUT:
+               return true;
+       default:
+               return false;
+       }
+}
+
 /*
  * The madvise(2) system call.
  *
@@ -1035,6 +1048,11 @@ madvise_behavior_valid(int behavior)
  *  MADV_DONTDUMP - the application wants to prevent pages in the given range
  *             from being included in its core dump.
  *  MADV_DODUMP - cancel MADV_DONTDUMP: no longer exclude from core dump.
+ *  MADV_COLD - the application is not expected to use this memory soon,
+ *             deactivate pages in this range so that they can be reclaimed
+ *             easily if memory pressure hanppens.
+ *  MADV_PAGEOUT - the application is not expected to use this memory soon,
+ *             page out the pages in this range immediately.
  *
  * return values:
  *  zero    - success
@@ -1151,3 +1169,76 @@ SYSCALL_DEFINE3(madvise, unsigned long, start, size_t, len_in, int, behavior)
 {
        return do_madvise(current->mm, start, len_in, behavior);
 }
+
+SYSCALL_DEFINE5(process_madvise, int, pidfd, const struct iovec __user *, vec,
+               size_t, vlen, int, behavior, unsigned int, flags)
+{
+       ssize_t ret;
+       struct iovec iovstack[UIO_FASTIOV], iovec;
+       struct iovec *iov = iovstack;
+       struct iov_iter iter;
+       struct pid *pid;
+       struct task_struct *task;
+       struct mm_struct *mm;
+       size_t total_len;
+       unsigned int f_flags;
+
+       if (flags != 0) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       ret = import_iovec(READ, vec, vlen, ARRAY_SIZE(iovstack), &iov, &iter);
+       if (ret < 0)
+               goto out;
+
+       pid = pidfd_get_pid(pidfd, &f_flags);
+       if (IS_ERR(pid)) {
+               ret = PTR_ERR(pid);
+               goto free_iov;
+       }
+
+       task = get_pid_task(pid, PIDTYPE_PID);
+       if (!task) {
+               ret = -ESRCH;
+               goto put_pid;
+       }
+
+       if (task->mm != current->mm &&
+                       !process_madvise_behavior_valid(behavior)) {
+               ret = -EINVAL;
+               goto release_task;
+       }
+
+       mm = mm_access(task, PTRACE_MODE_ATTACH_FSCREDS);
+       if (IS_ERR_OR_NULL(mm)) {
+               ret = IS_ERR(mm) ? PTR_ERR(mm) : -ESRCH;
+               goto release_task;
+       }
+
+       total_len = iov_iter_count(&iter);
+
+       while (iov_iter_count(&iter)) {
+               iovec = iov_iter_iovec(&iter);
+               ret = do_madvise(mm, (unsigned long)iovec.iov_base,
+                                       iovec.iov_len, behavior);
+               if (ret < 0)
+                       break;
+               iov_iter_advance(&iter, iovec.iov_len);
+       }
+
+       if (ret == 0)
+               ret = total_len - iov_iter_count(&iter);
+
+       mmput(mm);
+       return ret;
+
+release_task:
+       put_task_struct(task);
+put_pid:
+       put_pid(pid);
+free_iov:
+       kfree(iov);
+out:
+       return ret;
+}