Linux userland API discussions
 help / color / mirror / Atom feed
* Re: [PATCH 01/11] fs: add O_BENEATH_ONLY flag to openat(2)
From: Christoph Hellwig @ 2014-07-08 13:12 UTC (permalink / raw)
  To: Meredydd Luff
  Cc: Christoph Hellwig, Andy Lutomirski, David Drysdale, Al Viro,
	LSM List, Greg Kroah-Hartman, James Morris, Kees Cook, Linux API,
	linux-kernel@vger.kernel.org, linux-man
In-Reply-To: <CAD=T17FcSKCkEwGMDtcss0Ye-EwuW4Gd2bmXiVj0YV2JG3xeWQ@mail.gmail.com>

On Tue, Jul 08, 2014 at 02:04:45PM +0100, Meredydd Luff wrote:
> On 8 July 2014 13:51, Christoph Hellwig <hch@infradead.org> wrote:
> > Yeah, it won't work for an explicit directory - I was thinking of
> > working relative to $CWD.
> 
> I think that would sacrifice far too much flexibility. Even without
> Capsicum, it would be worthwhile to be able to wire up a static
> seccomp-bpf filter to enforce constraints such as "you can open files
> under fd#5 for reading, but you can only write to files under fd#6,
> and you can't do any global lookups."

Yeah, I didn't intend to advocate this further after your reply.


^ permalink raw reply

* Re: [PATCH 01/11] fs: add O_BENEATH_ONLY flag to openat(2)
From: Meredydd Luff @ 2014-07-08 13:04 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Andy Lutomirski, David Drysdale, Al Viro, LSM List,
	Greg Kroah-Hartman, James Morris, Kees Cook, Linux API,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, linux-man
In-Reply-To: <20140708125138.GA4749-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org>

On 8 July 2014 13:51, Christoph Hellwig <hch-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org> wrote:
> Yeah, it won't work for an explicit directory - I was thinking of
> working relative to $CWD.

I think that would sacrifice far too much flexibility. Even without
Capsicum, it would be worthwhile to be able to wire up a static
seccomp-bpf filter to enforce constraints such as "you can open files
under fd#5 for reading, but you can only write to files under fd#6,
and you can't do any global lookups."

Meredydd
--
To unsubscribe from this list: send the line "unsubscribe linux-man" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH 01/11] fs: add O_BENEATH_ONLY flag to openat(2)
From: Christoph Hellwig @ 2014-07-08 12:51 UTC (permalink / raw)
  To: Meredydd Luff
  Cc: Christoph Hellwig, Andy Lutomirski, David Drysdale, Al Viro,
	LSM List, Greg Kroah-Hartman, James Morris, Kees Cook, Linux API,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-man-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <CAD=T17FQEZV+iy91wQAvAdd0PW2tsfjpU7atp-xeatm5sEGz5Q-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>

On Tue, Jul 08, 2014 at 01:48:27PM +0100, Meredydd Luff wrote:
> How would that work? The directory beneath which openat is looking is
> conveyed in the dfd argument itself. If I'm understanding this right,
> you'd have to pass a different value for "open relative to fd#5" and
> "open relative to fd#5, but beneath it only", which doesn't sound
> hugely elegant to me.

Yeah, it won't work for an explicit directory - I was thinking of
working relative to $CWD.

^ permalink raw reply

* Re: [PATCH 01/11] fs: add O_BENEATH_ONLY flag to openat(2)
From: Meredydd Luff @ 2014-07-08 12:48 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Andy Lutomirski, David Drysdale, Al Viro, LSM List,
	Greg Kroah-Hartman, James Morris, Kees Cook, Linux API,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-man-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20140708120702.GB30459-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org>

On 8 July 2014 13:07, Christoph Hellwig <hch-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org> wrote:
> There's two different AT_* namespaces.  The flags that most *at syscalls
> has, and the the one for the dfd argument, which currently only contains
> AT_FDCWD, although a new constant has recently been proposed to it.
>
> Having an AT_BENEATH magic value for the dfd argument certainly feels
> elegant to me

How would that work? The directory beneath which openat is looking is
conveyed in the dfd argument itself. If I'm understanding this right,
you'd have to pass a different value for "open relative to fd#5" and
"open relative to fd#5, but beneath it only", which doesn't sound
hugely elegant to me.

Meredydd

^ permalink raw reply

* Re: [PATCH v3 0/3] mm: introduce fincore() v3
From: Christoph Hellwig @ 2014-07-08 12:16 UTC (permalink / raw)
  To: Naoya Horiguchi
  Cc: Andrew Morton, Konstantin Khlebnikov, Wu Fengguang,
	Arnaldo Carvalho de Melo, Borislav Petkov, Kirill A. Shutemov,
	Johannes Weiner, Rusty Russell, David Miller, Andres Freund,
	linux-kernel, linux-mm, Dave Hansen, Christoph Hellwig,
	Dave Chinner, Michael Kerrisk, Linux API, Naoya Horiguchi
In-Reply-To: <1404756006-23794-1-git-send-email-n-horiguchi@ah.jp.nec.com>

Still a hard NAK for exposing page flags in a syscall ABI.  These are
way to volatile to go into an application interface.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply

* Re: [PATCH 1/1] include/uapi: Define AT_FDNODIR constant as future guarantee
From: Christoph Hellwig @ 2014-07-08 12:12 UTC (permalink / raw)
  To: Steven Stewart-Gallus
  Cc: linux-kernel, Jeff Layton, J. Bruce Fields, linux-fsdevel,
	linux-api, Andy Lutomirski, linux-man
In-Reply-To: <fb469d6277ef.53b85737@langara.bc.ca>

On Sat, Jul 05, 2014 at 07:51:19PM +0000, Steven Stewart-Gallus wrote:
> From: Steven Stewart-Gallus <sstewartgallus00@mylangara.bc.ca
> 
> This constant means that in the far future it might be possible to
> define other AT_FD* constants.
> 
> Signed-off-by: Steven Stewart-Gallus <sstewartgallus00@mylangara.bc.ca>
> ---
> 
> This is my first kernel patch but this is really trivial so I hope I'm doing
> this right.

Almost.  For one you really should explain what the flag does,
especially if it's not actually use.

It took me a while that you're trying to document the way Linux
currently behaves for the cases where the dfd argument to the various
*at is neither a valid file descriptor nor the magic AT_FDCWD constant.

Posix says that openat with an absolute path is equivalent to open, so
the only strictly allowed other flags would those that allow some form
of relative path, but treat it differently than the currently possible
options.  I can't really think of a good way to do that, although we
recently had a case where a different AT_* value would be useful, even
if not fully conforming to that Posix wording.

Your explanation of what this constant does should also go into the man
page, so in addition to the kernel patch you should also propose a patch
to the man page documenting it.

Also I don't think AT_FDNODIR is a good choice of name, AT_ABSOLUTE
would be a lot more descriptive.

^ permalink raw reply

* Re: [PATCH 01/11] fs: add O_BENEATH_ONLY flag to openat(2)
From: Christoph Hellwig @ 2014-07-08 12:07 UTC (permalink / raw)
  To: Andy Lutomirski
  Cc: David Drysdale, Al Viro, LSM List, Greg Kroah-Hartman,
	James Morris, Kees Cook, Linux API, Meredydd Luff,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-man-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <CALCETrWJ-rqDo8OvSZWPUt1806gObNtwVHvC4M6kfQgvd3Eg9w-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>

On Mon, Jun 30, 2014 at 08:53:01AM -0700, Andy Lutomirski wrote:
> > Wouldn't it need to be both O_BENEATH_ONLY (for openat()) and
> > AT_BENEATH_ONLY (for other *at() functions), like O_NOFOLLOW and
> > AT_SYMLINK_NOFOLLOW?  (I.e. aren't the AT_* flags in a different
> > numbering space than O_* flags?)
> >
> > Or am I misunderstanding?
> >
> 
> Ugh, you're probably right.  I wish openat had separate flags and
> atflags arguments.  Oh well.

There's two different AT_* namespaces.  The flags that most *at syscalls
has, and the the one for the dfd argument, which currently only contains
AT_FDCWD, although a new constant has recently been proposed to it.

Having an AT_BENEATH magic value for the dfd argument certainly feels
elegant to me, but seems to be against the language for openat in Posix:

"The openat() function shall be equivalent to the open() function except
in the case where path specifies a relative path. In this case the file
to be opened is determined relative to the directory associated with the
file descriptor fd instead of the current working directory. If the file
descriptor was opened without O_SEARCH, the function shall check whether
directory searches are permitted using the current permissions of the
directory underlying the file descriptor. If the file descriptor was
opened with O_SEARCH, the function shall not perform the check.

The oflag parameter and the optional fourth parameter correspond exactly
to the parameters of open().

If openat() is passed the special value AT_FDCWD in the fd parameter,
the current working directory shall be used and the behavior shall be
identical to a call to open()."

^ permalink raw reply

* Re: [PATCH 01/11] fs: add O_BENEATH_ONLY flag to openat(2)
From: Christoph Hellwig @ 2014-07-08 12:03 UTC (permalink / raw)
  To: David Drysdale
  Cc: linux-security-module-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Greg Kroah-Hartman,
	Alexander Viro, Meredydd Luff, Kees Cook, James Morris,
	linux-api-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1404124096-21445-2-git-send-email-drysdale-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>

On Mon, Jun 30, 2014 at 11:28:01AM +0100, David Drysdale wrote:
> Add a new O_BENEATH_ONLY flag for openat(2) which restricts the
> provided path, rejecting (with -EACCES) paths that are not beneath
> the provided dfd.  In particular, reject:
>  - paths that contain .. components
>  - paths that begin with /
>  - symlinks that have paths as above.


How is this implemented in FreeBSD?  I can't find any references to
O_BENEATH_ONLY except for your patchset.

^ permalink raw reply

* Re: [PATCH v11 6/7] ARM: add pmd_[dirty|mkclean] for THP
From: Steve Capper @ 2014-07-08 11:37 UTC (permalink / raw)
  To: Minchan Kim
  Cc: Andrew Morton, linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-mm-Bw31MaZKKs3YtjvyW6yDsg, Michael Kerrisk, Linux API,
	Hugh Dickins, Johannes Weiner, Rik van Riel, KOSAKI Motohiro,
	Mel Gorman, Jason Evans, Zhang Yanfei, Kirill A. Shutemov,
	Catalin Marinas, Will Deacon, Russell King,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <1404799424-1120-7-git-send-email-minchan-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>

On Tue, Jul 08, 2014 at 03:03:43PM +0900, Minchan Kim wrote:
> MADV_FREE needs pmd_dirty and pmd_mkclean for detecting recent
> overwrite of the contents since MADV_FREE syscall is called for
> THP page.
> 
> This patch adds pmd_dirty and pmd_mkclean for THP page MADV_FREE
> support.
> 
> Cc: Catalin Marinas <catalin.marinas-5wv7dgnIgG8@public.gmane.org>
> Cc: Will Deacon <will.deacon-5wv7dgnIgG8@public.gmane.org>
> Cc: Steve Capper <steve.capper-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> Cc: Russell King <linux-lFZ/pmaqli7XmaaqVzeoHQ@public.gmane.org>
> Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
> Signed-off-by: Minchan Kim <minchan-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> ---
>  arch/arm/include/asm/pgtable-3level.h | 3 +++
>  arch/arm64/include/asm/pgtable.h      | 2 ++
>  2 files changed, 5 insertions(+)

Hi Minchan,
arch/arm and arch/arm64 are handled separately.
Could you please split this patch into arm and arm64 versions?


> 
> diff --git a/arch/arm/include/asm/pgtable-3level.h b/arch/arm/include/asm/pgtable-3level.h
> index 85c60adc8b60..3a7bb8dc7d05 100644
> --- a/arch/arm/include/asm/pgtable-3level.h
> +++ b/arch/arm/include/asm/pgtable-3level.h
> @@ -220,6 +220,8 @@ static inline pmd_t *pmd_offset(pud_t *pud, unsigned long addr)
>  #define pmd_trans_splitting(pmd) (pmd_val(pmd) & PMD_SECT_SPLITTING)
>  #endif
>  
> +#define pmd_dirty	(pmd_val(pmd) & PMD_SECT_DIRTY)

This macro is missing the parameter definition, and will generate a
compile errror when CONFIG_ARM_LPAE=y

For 32-bit ARM with CONFIG_ARM_LPAE=n, we don't have THP support and I noticed
some compiler errors in mm/madvise.c:

  CC      mm/madvise.o
mm/madvise.c: In function ‘madvise_free_pte_range’:
mm/madvise.c:279:3: error: implicit declaration of function ‘pmdp_get_and_clear’ [-Werror=implicit-function-declaration]
   orig_pmd = pmdp_get_and_clear(mm, addr, pmd);
   ^
mm/madvise.c:285:3: error: implicit declaration of function ‘pmd_mkold’ [-Werror=implicit-function-declaration]
   orig_pmd = pmd_mkold(orig_pmd);
   ^
mm/madvise.c:286:3: error: implicit declaration of function ‘pmd_mkclean’ [-Werror=implicit-function-declaration]
   orig_pmd = pmd_mkclean(orig_pmd);
   ^
mm/madvise.c:288:3: error: implicit declaration of function ‘set_pmd_at’ [-Werror=implicit-function-declaration]
   set_pmd_at(mm, addr, pmd, orig_pmd);
   ^
cc1: some warnings being treated as errors
make[1]: *** [mm/madvise.o] Error 1


Cheers,
-- 
Steve 

^ permalink raw reply

* Re: [PATCH v10 7/7] mm: Don't split THP page when syscall is called
From: Kirill A. Shutemov @ 2014-07-08  9:48 UTC (permalink / raw)
  To: Minchan Kim
  Cc: Andrew Morton, linux-kernel, linux-mm, Michael Kerrisk, Linux API,
	Hugh Dickins, Johannes Weiner, Rik van Riel, KOSAKI Motohiro,
	Mel Gorman, Jason Evans, Zhang Yanfei
In-Reply-To: <20140708013038.GD6076@bbox>

On Tue, Jul 08, 2014 at 10:30:38AM +0900, Minchan Kim wrote:
> Actually, I did but found no problem except CONFIG_DEBUG_VM but rollback
> after peeking [1].
> When I read the description in detail by your review, I think we can remove
> BUG_ON(PageTransHuge(page)) in try_to_unmap and go with no split for lazyfree
> page because they are not in swapcache any more so the assumption of [1] is
> not valid. Will do it in next revision.

No. try_to_unmap() knows nothing about PMDs, so page_check_address() will
always return NULL for THP and you will not unmap anything.

-- 
 Kirill A. Shutemov

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply

* Re: [PATCH v11 1/7] mm: support madvise(MADV_FREE)
From: Kirill A. Shutemov @ 2014-07-08  9:41 UTC (permalink / raw)
  To: Minchan Kim
  Cc: Andrew Morton, linux-kernel, linux-mm, Michael Kerrisk, Linux API,
	Hugh Dickins, Johannes Weiner, Rik van Riel, KOSAKI Motohiro,
	Mel Gorman, Jason Evans, Zhang Yanfei
In-Reply-To: <1404799424-1120-2-git-send-email-minchan@kernel.org>

On Tue, Jul 08, 2014 at 03:03:38PM +0900, Minchan Kim wrote:
> Linux doesn't have an ability to free pages lazy while other OS
> already have been supported that named by madvise(MADV_FREE).
> 
> The gain is clear that kernel can discard freed pages rather than
> swapping out or OOM if memory pressure happens.
> 
> Without memory pressure, freed pages would be reused by userspace
> without another additional overhead(ex, page fault + allocation
> + zeroing).
> 
> How to work is following as.
> 
> When madvise syscall is called, VM clears dirty bit of ptes of
> the range. If memory pressure happens, VM checks dirty bit of
> page table and if it found still "clean", it means it's a
> "lazyfree pages" so VM could discard the page instead of swapping out.
> Once there was store operation for the page before VM peek a page
> to reclaim, dirty bit is set so VM can swap out the page instead of
> discarding.
> 
> Firstly, heavy users would be general allocators(ex, jemalloc,
> tcmalloc and hope glibc supports it) and jemalloc/tcmalloc already
> have supported the feature for other OS(ex, FreeBSD)
> 
> barrios@blaptop:~/benchmark/ebizzy$ lscpu
> Architecture:          x86_64
> CPU op-mode(s):        32-bit, 64-bit
> Byte Order:            Little Endian
> CPU(s):                4
> On-line CPU(s) list:   0-3
> Thread(s) per core:    2
> Core(s) per socket:    2
> Socket(s):             1
> NUMA node(s):          1
> Vendor ID:             GenuineIntel
> CPU family:            6
> Model:                 42
> Stepping:              7
> CPU MHz:               2801.000
> BogoMIPS:              5581.64
> Virtualization:        VT-x
> L1d cache:             32K
> L1i cache:             32K
> L2 cache:              256K
> L3 cache:              4096K
> NUMA node0 CPU(s):     0-3
> 
> ebizzy benchmark(./ebizzy -S 10 -n 512)
> 
>  vanilla-jemalloc		MADV_free-jemalloc
> 
> 1 thread
> records:  10              records:  10
> avg:      7682.10         avg:      15306.10
> std:      62.35(0.81%)    std:      347.99(2.27%)
> max:      7770.00         max:      15622.00
> min:      7598.00         min:      14772.00
> 
> 2 thread
> records:  10              records:  10
> avg:      12747.50        avg:      24171.00
> std:      792.06(6.21%)   std:      895.18(3.70%)
> max:      13337.00        max:      26023.00
> min:      10535.00        min:      23152.00
> 
> 4 thread
> records:  10              records:  10
> avg:      16474.60        avg:      33717.90
> std:      1496.45(9.08%)  std:      2008.97(5.96%)
> max:      17877.00        max:      35958.00
> min:      12224.00        min:      29565.00
> 
> 8 thread
> records:  10              records:  10
> avg:      16778.50        avg:      33308.10
> std:      825.53(4.92%)   std:      1668.30(5.01%)
> max:      17543.00        max:      36010.00
> min:      14576.00        min:      29577.00
> 
> 16 thread
> records:  10              records:  10
> avg:      20614.40        avg:      35516.30
> std:      602.95(2.92%)   std:      1283.65(3.61%)
> max:      21753.00        max:      37178.00
> min:      19605.00        min:      33217.00
> 
> 32 thread
> records:  10              records:  10
> avg:      22771.70        avg:      36018.50
> std:      598.94(2.63%)   std:      1046.76(2.91%)
> max:      24035.00        max:      37266.00
> min:      22108.00        min:      34149.00
> 
> In summary, MADV_FREE is about 2 time faster than MADV_DONTNEED.
> 
> Cc: Michael Kerrisk <mtk.manpages@gmail.com>
> Cc: Linux API <linux-api@vger.kernel.org>
> Cc: Hugh Dickins <hughd@google.com>
> Cc: Johannes Weiner <hannes@cmpxchg.org>
> Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
> Cc: Mel Gorman <mgorman@suse.de>
> Cc: Jason Evans <je@fb.com>
> Cc: Zhang Yanfei <zhangyanfei@cn.fujitsu.com>
> Acked-by: Rik van Riel <riel@redhat.com>
> Signed-off-by: Minchan Kim <minchan@kernel.org>
> ---
>  include/linux/rmap.h                   |   9 ++-
>  include/linux/vm_event_item.h          |   1 +
>  include/uapi/asm-generic/mman-common.h |   1 +
>  mm/madvise.c                           | 135 +++++++++++++++++++++++++++++++++
>  mm/rmap.c                              |  42 +++++++++-
>  mm/vmscan.c                            |  40 ++++++++--
>  mm/vmstat.c                            |   1 +
>  7 files changed, 217 insertions(+), 12 deletions(-)
> 

...

> @@ -251,6 +260,124 @@ static long madvise_willneed(struct vm_area_struct *vma,
>  	return 0;
>  }
>  
> +static int madvise_free_pte_range(pmd_t *pmd, unsigned long addr,
> +				unsigned long end, struct mm_walk *walk)
> +
> +{
> +	struct madvise_free_private *fp = walk->private;
> +	struct mmu_gather *tlb = fp->tlb;
> +	struct mm_struct *mm = tlb->mm;
> +	struct vm_area_struct *vma = fp->vma;
> +	spinlock_t *ptl;
> +	pte_t *pte, ptent;
> +	struct page *page;
> +
> +	split_huge_page_pmd(vma, addr, pmd);
> +	if (pmd_trans_unstable(pmd))
> +		return 0;
> +
> +	pte = pte_offset_map_lock(mm, pmd, addr, &ptl);
> +	arch_enter_lazy_mmu_mode();
> +	for (; addr != end; pte++, addr += PAGE_SIZE) {
> +		ptent = *pte;
> +
> +		if (pte_none(ptent))
> +			continue;

The check is redundant: all pte_none() entries are also !pte_present().

> +
> +		if (!pte_present(ptent))
> +			continue;
> +
> +		page = vm_normal_page(vma, addr, ptent);
> +		if (page && PageSwapCache(page)) {
> +			if (trylock_page(page)) {
> +				if (try_to_free_swap(page))
> +					ClearPageDirty(page);
> +				unlock_page(page);
> +			} else
> +				continue;
> +		}

Is it safe to touch non-vm_normal entries? I would suggest to put
  if (!page)
	  continue;
instead.

> +		/*
> +		 * Some of architecture(ex, PPC) don't update TLB
> +		 * with set_pte_at and tlb_remove_tlb_entry so for
> +		 * the portability, remap the pte with old|clean
> +		 * after pte clearing.
> +		 */
> +		ptent = ptep_get_and_clear_full(mm, addr, pte,
> +						tlb->fullmm);
> +		ptent = pte_mkold(ptent);
> +		ptent = pte_mkclean(ptent);
> +		set_pte_at(mm, addr, pte, ptent);
> +		tlb_remove_tlb_entry(tlb, pte, addr);
> +	}
> +	arch_leave_lazy_mmu_mode();
> +	pte_unmap_unlock(pte - 1, ptl);
> +	cond_resched();
> +	return 0;
> +}
> +
> +static void madvise_free_page_range(struct mmu_gather *tlb,
> +			     struct vm_area_struct *vma,
> +			     unsigned long addr, unsigned long end)
> +{
> +	struct madvise_free_private fp = {
> +		.vma = vma,
> +		.tlb = tlb,
> +	};
> +
> +	struct mm_walk free_walk = {
> +		.pmd_entry = madvise_free_pte_range,
> +		.mm = vma->vm_mm,
> +		.private = &fp,
> +	};
> +
> +	BUG_ON(addr >= end);
> +	tlb_start_vma(tlb, vma);
> +	walk_page_range(addr, end, &free_walk);
> +	tlb_end_vma(tlb, vma);
> +}
> +
> +static int madvise_free_single_vma(struct vm_area_struct *vma,
> +			unsigned long start_addr, unsigned long end_addr)
> +{
> +	unsigned long start, end;
> +	struct mm_struct *mm = vma->vm_mm;
> +	struct mmu_gather tlb;
> +
> +	if (vma->vm_flags & (VM_LOCKED|VM_HUGETLB|VM_PFNMAP))
> +		return -EINVAL;

VM_MIXEDMAP? VM_IO? Should it be whitelist instead?

> +
> +	/* MADV_FREE works for only anon vma at the moment */
> +	if (vma->vm_file)
> +		return -EINVAL;
> +
> +	start = max(vma->vm_start, start_addr);
> +	if (start >= vma->vm_end)
> +		return -EINVAL;
> +	end = min(vma->vm_end, end_addr);
> +	if (end <= vma->vm_start)
> +		return -EINVAL;
> +
> +	lru_add_drain();
> +	tlb_gather_mmu(&tlb, mm, start, end);
> +	update_hiwater_rss(mm);
> +
> +	mmu_notifier_invalidate_range_start(mm, start, end);
> +	madvise_free_page_range(&tlb, vma, start, end);
> +	mmu_notifier_invalidate_range_end(mm, start, end);
> +	tlb_finish_mmu(&tlb, start, end);
> +
> +	return 0;
> +}
> +
> +static long madvise_free(struct vm_area_struct *vma,
> +			     struct vm_area_struct **prev,
> +			     unsigned long start, unsigned long end)
> +{
> +	*prev = vma;
> +	return madvise_free_single_vma(vma, start, end);
> +}
> +
>  /*
>   * Application no longer needs these pages.  If the pages are dirty,
>   * it's OK to just throw them away.  The app will be more careful about
> @@ -381,6 +508,13 @@ madvise_vma(struct vm_area_struct *vma, struct vm_area_struct **prev,
>  		return madvise_remove(vma, prev, start, end);
>  	case MADV_WILLNEED:
>  		return madvise_willneed(vma, prev, start, end);
> +	case MADV_FREE:
> +		/*
> +		 * XXX: In this implementation, MADV_FREE works like
> +		 * MADV_DONTNEED on swapless system or full swap.
> +		 */
> +		if (get_nr_swap_pages() > 0)
> +			return madvise_free(vma, prev, start, end);

/* passthough */

>  	case MADV_DONTNEED:
>  		return madvise_dontneed(vma, prev, start, end);
>  	default:

...

> @@ -1186,6 +1210,19 @@ static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma,
>  		swp_entry_t entry = { .val = page_private(page) };
>  		pte_t swp_pte;
>  
> +		if (flags & TTU_FREE) {
> +			VM_BUG_ON_PAGE(PageSwapCache(page), page);
> +			if (dirty || PageDirty(page)) {
> +				set_pte_at(mm, address, pte, pteval);
> +				ret = SWAP_FAIL;
> +				goto out_unmap;

Hm. Again: do we really want stop here if caller asks for
TTU_FREE|TTU_UNMAP or should proceed?

> +			} else {
> +				/* It's a freeable page by MADV_FREE */
> +				dec_mm_counter(mm, MM_ANONPAGES);
> +				goto discard;
> +			}
> +		}
> +
>  		if (PageSwapCache(page)) {
>  			/*
>  			 * Store the swap location in the pte.
-- 
 Kirill A. Shutemov

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply

* Re: [net-next PATCH 3/3] drivers: net: cpsw: Add support for multicast/boradcast rate limit
From: Mugunthan V N @ 2014-07-08  9:18 UTC (permalink / raw)
  To: netdev-u79uwXL29TY76Z2rM5mHXA
  Cc: davem-fT/PcQaiUtIeIZ0/mPfg9Q, linux-api-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1404809861-7040-4-git-send-email-mugunthanvnm-l0cyMroinI0@public.gmane.org>

On Tuesday 08 July 2014 02:27 PM, Mugunthan V N wrote:
> Add support for multicast/boradcast rate limit feature via ethtool coalesce.
>
> Signed-off-by: Mugunthan V N <mugunthanvnm-l0cyMroinI0@public.gmane.org>

s/boradcast/broadcast

Will fix this in next patch set.

Regards
Mugunthan V N

^ permalink raw reply

* [net-next PATCH 3/3] drivers: net: cpsw: Add support for multicast/boradcast rate limit
From: Mugunthan V N @ 2014-07-08  8:57 UTC (permalink / raw)
  To: netdev; +Cc: davem, linux-api, linux-kernel, Mugunthan V N
In-Reply-To: <1404809861-7040-1-git-send-email-mugunthanvnm@ti.com>

Add support for multicast/boradcast rate limit feature via ethtool coalesce.

Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
---
 drivers/net/ethernet/ti/cpsw.c | 80 ++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 78 insertions(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
index a6117e6..2a984e6 100644
--- a/drivers/net/ethernet/ti/cpsw.c
+++ b/drivers/net/ethernet/ti/cpsw.c
@@ -403,6 +403,8 @@ struct cpsw_priv {
 	bool irq_enabled;
 	struct cpts *cpts;
 	u32 emac_port;
+	u32 rx_max_mcast;
+	u32 rx_max_bcast;
 };
 
 struct cpsw_stats {
@@ -871,11 +873,13 @@ static int cpsw_get_coalesce(struct net_device *ndev,
 	struct cpsw_priv *priv = netdev_priv(ndev);
 
 	coal->rx_coalesce_usecs = priv->coal_intvl;
+	coal->rx_max_mcast = priv->rx_max_mcast;
+	coal->rx_max_bcast = priv->rx_max_bcast;
 	return 0;
 }
 
-static int cpsw_set_coalesce(struct net_device *ndev,
-				struct ethtool_coalesce *coal)
+static int cpsw_set_coalesce_usecs(struct net_device *ndev,
+				   struct ethtool_coalesce *coal)
 {
 	struct cpsw_priv *priv = netdev_priv(ndev);
 	u32 int_ctrl;
@@ -933,6 +937,74 @@ static int cpsw_set_coalesce(struct net_device *ndev,
 	return 0;
 }
 
+static int cpsw_set_coalesce_mcast(struct net_device *ndev,
+				   struct ethtool_coalesce *coal)
+{
+	struct cpsw_priv *priv = netdev_priv(ndev);
+	int port;
+
+	priv->rx_max_mcast = coal->rx_max_mcast;
+
+	if (priv->data.dual_emac)
+		port = priv->emac_port;
+	else
+		port = priv->data.active_slave;
+
+	cpsw_ale_control_set(priv->ale, port, ALE_PORT_MCAST_LIMIT,
+			     coal->rx_max_mcast);
+
+	dev_dbg(priv->dev, "rx_max_mcast set to %d\n", priv->rx_max_mcast);
+	return 0;
+}
+
+static int cpsw_set_coalesce_bcast(struct net_device *ndev,
+				   struct ethtool_coalesce *coal)
+{
+	struct cpsw_priv *priv = netdev_priv(ndev);
+	int port;
+
+	priv->rx_max_bcast = coal->rx_max_bcast;
+
+	if (priv->data.dual_emac)
+		port = priv->emac_port + 1;
+	else
+		port = priv->data.active_slave + 1;
+
+	cpsw_ale_control_set(priv->ale, port, ALE_PORT_BCAST_LIMIT,
+			     coal->rx_max_bcast);
+
+	dev_dbg(priv->dev, "rx_max_mcast set to %d\n", priv->rx_max_bcast);
+	return 0;
+}
+
+static int cpsw_set_coalesce(struct net_device *ndev,
+			     struct ethtool_coalesce *coal)
+{
+	int ret = -EINVAL;
+
+	if (coal->rx_coalesce_usecs) {
+		ret = cpsw_set_coalesce_usecs(ndev, coal);
+		if (ret) {
+			dev_err(&ndev->dev, "set rx-usecs failed\n");
+			return ret;
+		}
+	}
+
+	ret = cpsw_set_coalesce_mcast(ndev, coal);
+	if (ret) {
+		dev_err(&ndev->dev, "set coalesce rx-max-mcast failed\n");
+		return ret;
+	}
+
+	ret = cpsw_set_coalesce_bcast(ndev, coal);
+	if (ret) {
+		dev_err(&ndev->dev, "set coalesce rx-max-bcast failed\n");
+		return ret;
+	}
+
+	return ret;
+}
+
 static int cpsw_get_sset_count(struct net_device *ndev, int sset)
 {
 	switch (sset) {
@@ -1227,6 +1299,10 @@ static int cpsw_ndo_open(struct net_device *ndev)
 		/* enable statistics collection only on all ports */
 		__raw_writel(0x7, &priv->regs->stat_port_en);
 
+		/* Enable rate limit feature in the switch for rx only */
+		cpsw_ale_control_set(priv->ale, 0, ALE_RATE_LIMIT, 1);
+		cpsw_ale_control_set(priv->ale, 0, ALE_RATE_LIMIT_TX, 0);
+
 		if (WARN_ON(!priv->data.rx_descs))
 			priv->data.rx_descs = 128;
 
-- 
2.0.0.390.gcb682f8

^ permalink raw reply related

* [net-next PATCH 2/3] drivers: net: cpsw: remove redundancy check
From: Mugunthan V N @ 2014-07-08  8:57 UTC (permalink / raw)
  To: netdev; +Cc: davem, linux-api, linux-kernel, Mugunthan V N
In-Reply-To: <1404809861-7040-1-git-send-email-mugunthanvnm@ti.com>

In cpsw_set_coalesce, rx_coalesce_usecs is already checked before
calling this function, so removing redundancy check.

Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
---
 drivers/net/ethernet/ti/cpsw.c | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
index b988d16..a6117e6 100644
--- a/drivers/net/ethernet/ti/cpsw.c
+++ b/drivers/net/ethernet/ti/cpsw.c
@@ -884,9 +884,6 @@ static int cpsw_set_coalesce(struct net_device *ndev,
 	u32 addnl_dvdr = 1;
 	u32 coal_intvl = 0;
 
-	if (!coal->rx_coalesce_usecs)
-		return -EINVAL;
-
 	coal_intvl = coal->rx_coalesce_usecs;
 
 	int_ctrl =  readl(&priv->wr_regs->int_control);
-- 
2.0.0.390.gcb682f8

^ permalink raw reply related

* [net-next PATCH 1/3] net: ethtool: Add Multicast and broadcast rate limit coalescing feature
From: Mugunthan V N @ 2014-07-08  8:57 UTC (permalink / raw)
  To: netdev; +Cc: davem, linux-api, linux-kernel, Mugunthan V N
In-Reply-To: <1404809861-7040-1-git-send-email-mugunthanvnm@ti.com>

Add ability to limit the broadcast or multicast packets to prevent
the loading of the CPU by a hacker with a broadcast or multicast
packet flood.

Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
---
 include/uapi/linux/ethtool.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h
index e3c7a71..96ade34 100644
--- a/include/uapi/linux/ethtool.h
+++ b/include/uapi/linux/ethtool.h
@@ -350,6 +350,8 @@ struct ethtool_modinfo {
  *	a TX interrupt, when the packet rate is above @pkt_rate_high.
  * @rate_sample_interval: How often to do adaptive coalescing packet rate
  *	sampling, measured in seconds.  Must not be zero.
+ * @rx_max_mcast: Threshold for high multicast packet rate (packets per second)
+ * @rx_max_bcast: Threshold for high broadcast packet rate (packets per second)
  *
  * Each pair of (usecs, max_frames) fields specifies that interrupts
  * should be coalesced until
@@ -400,6 +402,8 @@ struct ethtool_coalesce {
 	__u32	tx_coalesce_usecs_high;
 	__u32	tx_max_coalesced_frames_high;
 	__u32	rate_sample_interval;
+	__u32	rx_max_mcast;
+	__u32	rx_max_bcast;
 };
 
 /**
-- 
2.0.0.390.gcb682f8

^ permalink raw reply related

* [net-next PATCH 0/3] Broadcast/Multicast rate limit via Ethtool Coalesce
From: Mugunthan V N @ 2014-07-08  8:57 UTC (permalink / raw)
  To: netdev-u79uwXL29TY76Z2rM5mHXA
  Cc: davem-fT/PcQaiUtIeIZ0/mPfg9Q, linux-api-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Mugunthan V N

A system/cpu can be loaded by a hacker with flooding of broadcast or
multicast packets, to prevent this some Ethernet controllers like CPSW
provide a mechanism to limit the broadcast/multicast packet rate via
hardware limiters. This patch series enables this feature via
Ethtool Coalesce.

Mugunthan V N (3):
  net: ethtool: Add Multicast and broadcast rate limit coalescing
    feature
  drivers: net: cpsw: remove redundancy check
  drivers: net: cpsw: Add support for multicast/boradcast rate limit

 drivers/net/ethernet/ti/cpsw.c | 83 +++++++++++++++++++++++++++++++++++++++---
 include/uapi/linux/ethtool.h   |  4 ++
 2 files changed, 82 insertions(+), 5 deletions(-)

-- 
2.0.0.390.gcb682f8

^ permalink raw reply

* Re: [PATCH v11 2/7] x86: add pmd_[dirty|mkclean] for THP
From: Zhang Yanfei @ 2014-07-08  6:25 UTC (permalink / raw)
  To: Minchan Kim
  Cc: Andrew Morton, linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-mm-Bw31MaZKKs3YtjvyW6yDsg, Michael Kerrisk, Linux API,
	Hugh Dickins, Johannes Weiner, Rik van Riel, KOSAKI Motohiro,
	Mel Gorman, Jason Evans, Kirill A. Shutemov, Thomas Gleixner,
	Ingo Molnar, H. Peter Anvin, x86-DgEjT+Ai2ygdnm+yROfE0A
In-Reply-To: <1404799424-1120-3-git-send-email-minchan-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>

On 07/08/2014 02:03 PM, Minchan Kim wrote:
> MADV_FREE needs pmd_dirty and pmd_mkclean for detecting recent
> overwrite of the contents since MADV_FREE syscall is called for
> THP page.
> 
> This patch adds pmd_dirty and pmd_mkclean for THP page MADV_FREE
> support.
> 
> Cc: Thomas Gleixner <tglx-hfZtesqFncYOwBW4kG4KsQ@public.gmane.org>
> Cc: Ingo Molnar <mingo-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
> Cc: "H. Peter Anvin" <hpa-YMNOUZJC4hwAvxtiuMwx3w@public.gmane.org>
> Cc: x86-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org
> Acked-by: Kirill A. Shutemov <kirill.shutemov-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
> Signed-off-by: Minchan Kim <minchan-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>

Acked-by: Zhang Yanfei <zhangyanfei-BthXqXjhjHXQFUHtdCDX3A@public.gmane.org>

> ---
>  arch/x86/include/asm/pgtable.h | 10 ++++++++++
>  1 file changed, 10 insertions(+)
> 
> diff --git a/arch/x86/include/asm/pgtable.h b/arch/x86/include/asm/pgtable.h
> index 0ec056012618..329865799653 100644
> --- a/arch/x86/include/asm/pgtable.h
> +++ b/arch/x86/include/asm/pgtable.h
> @@ -104,6 +104,11 @@ static inline int pmd_young(pmd_t pmd)
>  	return pmd_flags(pmd) & _PAGE_ACCESSED;
>  }
>  
> +static inline int pmd_dirty(pmd_t pmd)
> +{
> +	return pmd_flags(pmd) & _PAGE_DIRTY;
> +}
> +
>  static inline int pte_write(pte_t pte)
>  {
>  	return pte_flags(pte) & _PAGE_RW;
> @@ -267,6 +272,11 @@ static inline pmd_t pmd_mkold(pmd_t pmd)
>  	return pmd_clear_flags(pmd, _PAGE_ACCESSED);
>  }
>  
> +static inline pmd_t pmd_mkclean(pmd_t pmd)
> +{
> +	return pmd_clear_flags(pmd, _PAGE_DIRTY);
> +}
> +
>  static inline pmd_t pmd_wrprotect(pmd_t pmd)
>  {
>  	return pmd_clear_flags(pmd, _PAGE_RW);
> 


-- 
Thanks.
Zhang Yanfei

^ permalink raw reply

* Re: [PATCH v11 1/7] mm: support madvise(MADV_FREE)
From: Zhang Yanfei @ 2014-07-08  6:22 UTC (permalink / raw)
  To: Minchan Kim
  Cc: Andrew Morton, linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-mm-Bw31MaZKKs3YtjvyW6yDsg, Michael Kerrisk, Linux API,
	Hugh Dickins, Johannes Weiner, Rik van Riel, KOSAKI Motohiro,
	Mel Gorman, Jason Evans, Kirill A. Shutemov
In-Reply-To: <1404799424-1120-2-git-send-email-minchan-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>

On 07/08/2014 02:03 PM, Minchan Kim wrote:
> Linux doesn't have an ability to free pages lazy while other OS
> already have been supported that named by madvise(MADV_FREE).
> 
> The gain is clear that kernel can discard freed pages rather than
> swapping out or OOM if memory pressure happens.
> 
> Without memory pressure, freed pages would be reused by userspace
> without another additional overhead(ex, page fault + allocation
> + zeroing).
> 
> How to work is following as.
> 
> When madvise syscall is called, VM clears dirty bit of ptes of
> the range. If memory pressure happens, VM checks dirty bit of
> page table and if it found still "clean", it means it's a
> "lazyfree pages" so VM could discard the page instead of swapping out.
> Once there was store operation for the page before VM peek a page
> to reclaim, dirty bit is set so VM can swap out the page instead of
> discarding.
> 
> Firstly, heavy users would be general allocators(ex, jemalloc,
> tcmalloc and hope glibc supports it) and jemalloc/tcmalloc already
> have supported the feature for other OS(ex, FreeBSD)
> 
> barrios@blaptop:~/benchmark/ebizzy$ lscpu
> Architecture:          x86_64
> CPU op-mode(s):        32-bit, 64-bit
> Byte Order:            Little Endian
> CPU(s):                4
> On-line CPU(s) list:   0-3
> Thread(s) per core:    2
> Core(s) per socket:    2
> Socket(s):             1
> NUMA node(s):          1
> Vendor ID:             GenuineIntel
> CPU family:            6
> Model:                 42
> Stepping:              7
> CPU MHz:               2801.000
> BogoMIPS:              5581.64
> Virtualization:        VT-x
> L1d cache:             32K
> L1i cache:             32K
> L2 cache:              256K
> L3 cache:              4096K
> NUMA node0 CPU(s):     0-3
> 
> ebizzy benchmark(./ebizzy -S 10 -n 512)
> 
>  vanilla-jemalloc		MADV_free-jemalloc
> 
> 1 thread
> records:  10              records:  10
> avg:      7682.10         avg:      15306.10
> std:      62.35(0.81%)    std:      347.99(2.27%)
> max:      7770.00         max:      15622.00
> min:      7598.00         min:      14772.00
> 
> 2 thread
> records:  10              records:  10
> avg:      12747.50        avg:      24171.00
> std:      792.06(6.21%)   std:      895.18(3.70%)
> max:      13337.00        max:      26023.00
> min:      10535.00        min:      23152.00
> 
> 4 thread
> records:  10              records:  10
> avg:      16474.60        avg:      33717.90
> std:      1496.45(9.08%)  std:      2008.97(5.96%)
> max:      17877.00        max:      35958.00
> min:      12224.00        min:      29565.00
> 
> 8 thread
> records:  10              records:  10
> avg:      16778.50        avg:      33308.10
> std:      825.53(4.92%)   std:      1668.30(5.01%)
> max:      17543.00        max:      36010.00
> min:      14576.00        min:      29577.00
> 
> 16 thread
> records:  10              records:  10
> avg:      20614.40        avg:      35516.30
> std:      602.95(2.92%)   std:      1283.65(3.61%)
> max:      21753.00        max:      37178.00
> min:      19605.00        min:      33217.00
> 
> 32 thread
> records:  10              records:  10
> avg:      22771.70        avg:      36018.50
> std:      598.94(2.63%)   std:      1046.76(2.91%)
> max:      24035.00        max:      37266.00
> min:      22108.00        min:      34149.00
> 
> In summary, MADV_FREE is about 2 time faster than MADV_DONTNEED.
> 
> Cc: Michael Kerrisk <mtk.manpages-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> Cc: Linux API <linux-api-u79uwXL29TY76Z2rM5mHXA@public.gmane.org>
> Cc: Hugh Dickins <hughd-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
> Cc: Johannes Weiner <hannes-druUgvl0LCNAfugRpC6u6w@public.gmane.org>
> Cc: KOSAKI Motohiro <kosaki.motohiro-+CUm20s59erQFUHtdCDX3A@public.gmane.org>
> Cc: Mel Gorman <mgorman-l3A5Bk7waGM@public.gmane.org>
> Cc: Jason Evans <je-b10kYP2dOMg@public.gmane.org>
> Cc: Zhang Yanfei <zhangyanfei-BthXqXjhjHXQFUHtdCDX3A@public.gmane.org>
> Acked-by: Rik van Riel <riel-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
> Signed-off-by: Minchan Kim <minchan-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>

A quick respin, looks good to me now for this !THP part. And
looks neat with the Pagewalker.

Acked-by: Zhang Yanfei <zhangyanfei-BthXqXjhjHXQFUHtdCDX3A@public.gmane.org>

> ---
>  include/linux/rmap.h                   |   9 ++-
>  include/linux/vm_event_item.h          |   1 +
>  include/uapi/asm-generic/mman-common.h |   1 +
>  mm/madvise.c                           | 135 +++++++++++++++++++++++++++++++++
>  mm/rmap.c                              |  42 +++++++++-
>  mm/vmscan.c                            |  40 ++++++++--
>  mm/vmstat.c                            |   1 +
>  7 files changed, 217 insertions(+), 12 deletions(-)
> 
> diff --git a/include/linux/rmap.h b/include/linux/rmap.h
> index be574506e6a9..0ba377b97a38 100644
> --- a/include/linux/rmap.h
> +++ b/include/linux/rmap.h
> @@ -75,6 +75,7 @@ enum ttu_flags {
>  	TTU_UNMAP = 1,			/* unmap mode */
>  	TTU_MIGRATION = 2,		/* migration mode */
>  	TTU_MUNLOCK = 4,		/* munlock mode */
> +	TTU_FREE = 8,			/* free mode */
>  
>  	TTU_IGNORE_MLOCK = (1 << 8),	/* ignore mlock */
>  	TTU_IGNORE_ACCESS = (1 << 9),	/* don't age */
> @@ -181,7 +182,8 @@ static inline void page_dup_rmap(struct page *page)
>   * Called from mm/vmscan.c to handle paging out
>   */
>  int page_referenced(struct page *, int is_locked,
> -			struct mem_cgroup *memcg, unsigned long *vm_flags);
> +			struct mem_cgroup *memcg, unsigned long *vm_flags,
> +			int *is_dirty);
>  
>  #define TTU_ACTION(x) ((x) & TTU_ACTION_MASK)
>  
> @@ -260,9 +262,12 @@ int rmap_walk(struct page *page, struct rmap_walk_control *rwc);
>  
>  static inline int page_referenced(struct page *page, int is_locked,
>  				  struct mem_cgroup *memcg,
> -				  unsigned long *vm_flags)
> +				  unsigned long *vm_flags,
> +				  int *is_pte_dirty)
>  {
>  	*vm_flags = 0;
> +	if (is_pte_dirty)
> +		*is_pte_dirty = 0;
>  	return 0;
>  }
>  
> diff --git a/include/linux/vm_event_item.h b/include/linux/vm_event_item.h
> index ced92345c963..e2d3fb1e9814 100644
> --- a/include/linux/vm_event_item.h
> +++ b/include/linux/vm_event_item.h
> @@ -25,6 +25,7 @@ enum vm_event_item { PGPGIN, PGPGOUT, PSWPIN, PSWPOUT,
>  		FOR_ALL_ZONES(PGALLOC),
>  		PGFREE, PGACTIVATE, PGDEACTIVATE,
>  		PGFAULT, PGMAJFAULT,
> +		PGLAZYFREED,
>  		FOR_ALL_ZONES(PGREFILL),
>  		FOR_ALL_ZONES(PGSTEAL_KSWAPD),
>  		FOR_ALL_ZONES(PGSTEAL_DIRECT),
> diff --git a/include/uapi/asm-generic/mman-common.h b/include/uapi/asm-generic/mman-common.h
> index ddc3b36f1046..7a94102b7a02 100644
> --- a/include/uapi/asm-generic/mman-common.h
> +++ b/include/uapi/asm-generic/mman-common.h
> @@ -34,6 +34,7 @@
>  #define MADV_SEQUENTIAL	2		/* expect sequential page references */
>  #define MADV_WILLNEED	3		/* will need these pages */
>  #define MADV_DONTNEED	4		/* don't need these pages */
> +#define MADV_FREE	5		/* free pages only if memory pressure */
>  
>  /* common parameters: try to keep these consistent across architectures */
>  #define MADV_REMOVE	9		/* remove these pages & resources */
> diff --git a/mm/madvise.c b/mm/madvise.c
> index 0938b30da4ab..a6aa7d4c4e02 100644
> --- a/mm/madvise.c
> +++ b/mm/madvise.c
> @@ -19,6 +19,14 @@
>  #include <linux/blkdev.h>
>  #include <linux/swap.h>
>  #include <linux/swapops.h>
> +#include <linux/mmu_notifier.h>
> +
> +#include <asm/tlb.h>
> +
> +struct madvise_free_private {
> +	struct vm_area_struct *vma;
> +	struct mmu_gather *tlb;
> +};
>  
>  /*
>   * Any behaviour which results in changes to the vma->vm_flags needs to
> @@ -31,6 +39,7 @@ static int madvise_need_mmap_write(int behavior)
>  	case MADV_REMOVE:
>  	case MADV_WILLNEED:
>  	case MADV_DONTNEED:
> +	case MADV_FREE:
>  		return 0;
>  	default:
>  		/* be safe, default to 1. list exceptions explicitly */
> @@ -251,6 +260,124 @@ static long madvise_willneed(struct vm_area_struct *vma,
>  	return 0;
>  }
>  
> +static int madvise_free_pte_range(pmd_t *pmd, unsigned long addr,
> +				unsigned long end, struct mm_walk *walk)
> +
> +{
> +	struct madvise_free_private *fp = walk->private;
> +	struct mmu_gather *tlb = fp->tlb;
> +	struct mm_struct *mm = tlb->mm;
> +	struct vm_area_struct *vma = fp->vma;
> +	spinlock_t *ptl;
> +	pte_t *pte, ptent;
> +	struct page *page;
> +
> +	split_huge_page_pmd(vma, addr, pmd);
> +	if (pmd_trans_unstable(pmd))
> +		return 0;
> +
> +	pte = pte_offset_map_lock(mm, pmd, addr, &ptl);
> +	arch_enter_lazy_mmu_mode();
> +	for (; addr != end; pte++, addr += PAGE_SIZE) {
> +		ptent = *pte;
> +
> +		if (pte_none(ptent))
> +			continue;
> +
> +		if (!pte_present(ptent))
> +			continue;
> +
> +		page = vm_normal_page(vma, addr, ptent);
> +		if (page && PageSwapCache(page)) {
> +			if (trylock_page(page)) {
> +				if (try_to_free_swap(page))
> +					ClearPageDirty(page);
> +				unlock_page(page);
> +			} else
> +				continue;
> +		}
> +
> +		/*
> +		 * Some of architecture(ex, PPC) don't update TLB
> +		 * with set_pte_at and tlb_remove_tlb_entry so for
> +		 * the portability, remap the pte with old|clean
> +		 * after pte clearing.
> +		 */
> +		ptent = ptep_get_and_clear_full(mm, addr, pte,
> +						tlb->fullmm);
> +		ptent = pte_mkold(ptent);
> +		ptent = pte_mkclean(ptent);
> +		set_pte_at(mm, addr, pte, ptent);
> +		tlb_remove_tlb_entry(tlb, pte, addr);
> +	}
> +	arch_leave_lazy_mmu_mode();
> +	pte_unmap_unlock(pte - 1, ptl);
> +	cond_resched();
> +	return 0;
> +}
> +
> +static void madvise_free_page_range(struct mmu_gather *tlb,
> +			     struct vm_area_struct *vma,
> +			     unsigned long addr, unsigned long end)
> +{
> +	struct madvise_free_private fp = {
> +		.vma = vma,
> +		.tlb = tlb,
> +	};
> +
> +	struct mm_walk free_walk = {
> +		.pmd_entry = madvise_free_pte_range,
> +		.mm = vma->vm_mm,
> +		.private = &fp,
> +	};
> +
> +	BUG_ON(addr >= end);
> +	tlb_start_vma(tlb, vma);
> +	walk_page_range(addr, end, &free_walk);
> +	tlb_end_vma(tlb, vma);
> +}
> +
> +static int madvise_free_single_vma(struct vm_area_struct *vma,
> +			unsigned long start_addr, unsigned long end_addr)
> +{
> +	unsigned long start, end;
> +	struct mm_struct *mm = vma->vm_mm;
> +	struct mmu_gather tlb;
> +
> +	if (vma->vm_flags & (VM_LOCKED|VM_HUGETLB|VM_PFNMAP))
> +		return -EINVAL;
> +
> +	/* MADV_FREE works for only anon vma at the moment */
> +	if (vma->vm_file)
> +		return -EINVAL;
> +
> +	start = max(vma->vm_start, start_addr);
> +	if (start >= vma->vm_end)
> +		return -EINVAL;
> +	end = min(vma->vm_end, end_addr);
> +	if (end <= vma->vm_start)
> +		return -EINVAL;
> +
> +	lru_add_drain();
> +	tlb_gather_mmu(&tlb, mm, start, end);
> +	update_hiwater_rss(mm);
> +
> +	mmu_notifier_invalidate_range_start(mm, start, end);
> +	madvise_free_page_range(&tlb, vma, start, end);
> +	mmu_notifier_invalidate_range_end(mm, start, end);
> +	tlb_finish_mmu(&tlb, start, end);
> +
> +	return 0;
> +}
> +
> +static long madvise_free(struct vm_area_struct *vma,
> +			     struct vm_area_struct **prev,
> +			     unsigned long start, unsigned long end)
> +{
> +	*prev = vma;
> +	return madvise_free_single_vma(vma, start, end);
> +}
> +
>  /*
>   * Application no longer needs these pages.  If the pages are dirty,
>   * it's OK to just throw them away.  The app will be more careful about
> @@ -381,6 +508,13 @@ madvise_vma(struct vm_area_struct *vma, struct vm_area_struct **prev,
>  		return madvise_remove(vma, prev, start, end);
>  	case MADV_WILLNEED:
>  		return madvise_willneed(vma, prev, start, end);
> +	case MADV_FREE:
> +		/*
> +		 * XXX: In this implementation, MADV_FREE works like
> +		 * MADV_DONTNEED on swapless system or full swap.
> +		 */
> +		if (get_nr_swap_pages() > 0)
> +			return madvise_free(vma, prev, start, end);
>  	case MADV_DONTNEED:
>  		return madvise_dontneed(vma, prev, start, end);
>  	default:
> @@ -400,6 +534,7 @@ madvise_behavior_valid(int behavior)
>  	case MADV_REMOVE:
>  	case MADV_WILLNEED:
>  	case MADV_DONTNEED:
> +	case MADV_FREE:
>  #ifdef CONFIG_KSM
>  	case MADV_MERGEABLE:
>  	case MADV_UNMERGEABLE:
> diff --git a/mm/rmap.c b/mm/rmap.c
> index 7928ddd91b6e..a8e34596dc97 100644
> --- a/mm/rmap.c
> +++ b/mm/rmap.c
> @@ -663,6 +663,7 @@ int page_mapped_in_vma(struct page *page, struct vm_area_struct *vma)
>  }
>  
>  struct page_referenced_arg {
> +	int dirtied;
>  	int mapcount;
>  	int referenced;
>  	unsigned long vm_flags;
> @@ -677,6 +678,7 @@ static int page_referenced_one(struct page *page, struct vm_area_struct *vma,
>  	struct mm_struct *mm = vma->vm_mm;
>  	spinlock_t *ptl;
>  	int referenced = 0;
> +	int dirty = 0;
>  	struct page_referenced_arg *pra = arg;
>  
>  	if (unlikely(PageTransHuge(page))) {
> @@ -700,6 +702,11 @@ static int page_referenced_one(struct page *page, struct vm_area_struct *vma,
>  		/* go ahead even if the pmd is pmd_trans_splitting() */
>  		if (pmdp_clear_flush_young_notify(vma, address, pmd))
>  			referenced++;
> +
> +		/*
> +		 * In this implmentation, MADV_FREE doesn't support THP free
> +		 */
> +		dirty++;
>  		spin_unlock(ptl);
>  	} else {
>  		pte_t *pte;
> @@ -729,6 +736,10 @@ static int page_referenced_one(struct page *page, struct vm_area_struct *vma,
>  			if (likely(!(vma->vm_flags & VM_SEQ_READ)))
>  				referenced++;
>  		}
> +
> +		if (pte_dirty(*pte))
> +			dirty++;
> +
>  		pte_unmap_unlock(pte, ptl);
>  	}
>  
> @@ -737,6 +748,9 @@ static int page_referenced_one(struct page *page, struct vm_area_struct *vma,
>  		pra->vm_flags |= vma->vm_flags;
>  	}
>  
> +	if (dirty)
> +		pra->dirtied++;
> +
>  	pra->mapcount--;
>  	if (!pra->mapcount)
>  		return SWAP_SUCCESS; /* To break the loop */
> @@ -761,6 +775,7 @@ static bool invalid_page_referenced_vma(struct vm_area_struct *vma, void *arg)
>   * @is_locked: caller holds lock on the page
>   * @memcg: target memory cgroup
>   * @vm_flags: collect encountered vma->vm_flags who actually referenced the page
> + * @is_pte_dirty: ptes which have marked dirty bit - used for lazyfree page
>   *
>   * Quick test_and_clear_referenced for all mappings to a page,
>   * returns the number of ptes which referenced the page.
> @@ -768,7 +783,8 @@ static bool invalid_page_referenced_vma(struct vm_area_struct *vma, void *arg)
>  int page_referenced(struct page *page,
>  		    int is_locked,
>  		    struct mem_cgroup *memcg,
> -		    unsigned long *vm_flags)
> +		    unsigned long *vm_flags,
> +		    int *is_pte_dirty)
>  {
>  	int ret;
>  	int we_locked = 0;
> @@ -783,6 +799,9 @@ int page_referenced(struct page *page,
>  	};
>  
>  	*vm_flags = 0;
> +	if (is_pte_dirty)
> +		*is_pte_dirty = 0;
> +
>  	if (!page_mapped(page))
>  		return 0;
>  
> @@ -810,6 +829,9 @@ int page_referenced(struct page *page,
>  	if (we_locked)
>  		unlock_page(page);
>  
> +	if (is_pte_dirty)
> +		*is_pte_dirty = pra.dirtied;
> +
>  	return pra.referenced;
>  }
>  
> @@ -1128,6 +1150,7 @@ static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma,
>  	spinlock_t *ptl;
>  	int ret = SWAP_AGAIN;
>  	enum ttu_flags flags = (enum ttu_flags)arg;
> +	int dirty = 0;
>  
>  	pte = page_check_address(page, mm, address, &ptl, 0);
>  	if (!pte)
> @@ -1157,7 +1180,8 @@ static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma,
>  	pteval = ptep_clear_flush(vma, address, pte);
>  
>  	/* Move the dirty bit to the physical page now the pte is gone. */
> -	if (pte_dirty(pteval))
> +	dirty = pte_dirty(pteval);
> +	if (dirty)
>  		set_page_dirty(page);
>  
>  	/* Update high watermark before we lower rss */
> @@ -1186,6 +1210,19 @@ static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma,
>  		swp_entry_t entry = { .val = page_private(page) };
>  		pte_t swp_pte;
>  
> +		if (flags & TTU_FREE) {
> +			VM_BUG_ON_PAGE(PageSwapCache(page), page);
> +			if (dirty || PageDirty(page)) {
> +				set_pte_at(mm, address, pte, pteval);
> +				ret = SWAP_FAIL;
> +				goto out_unmap;
> +			} else {
> +				/* It's a freeable page by MADV_FREE */
> +				dec_mm_counter(mm, MM_ANONPAGES);
> +				goto discard;
> +			}
> +		}
> +
>  		if (PageSwapCache(page)) {
>  			/*
>  			 * Store the swap location in the pte.
> @@ -1227,6 +1264,7 @@ static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma,
>  	} else
>  		dec_mm_counter(mm, MM_FILEPAGES);
>  
> +discard:
>  	page_remove_rmap(page);
>  	page_cache_release(page);
>  
> diff --git a/mm/vmscan.c b/mm/vmscan.c
> index 6d24fd63b209..d88413ccadcc 100644
> --- a/mm/vmscan.c
> +++ b/mm/vmscan.c
> @@ -707,13 +707,17 @@ enum page_references {
>  };
>  
>  static enum page_references page_check_references(struct page *page,
> -						  struct scan_control *sc)
> +						  struct scan_control *sc,
> +						  bool *freeable)
>  {
>  	int referenced_ptes, referenced_page;
>  	unsigned long vm_flags;
> +	int pte_dirty;
> +
> +	VM_BUG_ON_PAGE(!PageLocked(page), page);
>  
>  	referenced_ptes = page_referenced(page, 1, sc->target_mem_cgroup,
> -					  &vm_flags);
> +					  &vm_flags, &pte_dirty);
>  	referenced_page = TestClearPageReferenced(page);
>  
>  	/*
> @@ -754,6 +758,10 @@ static enum page_references page_check_references(struct page *page,
>  		return PAGEREF_KEEP;
>  	}
>  
> +	if (PageAnon(page) && !pte_dirty && !PageSwapCache(page) &&
> +			!PageDirty(page))
> +		*freeable = true;
> +
>  	/* Reclaim if clean, defer dirty pages to writeback */
>  	if (referenced_page && !PageSwapBacked(page))
>  		return PAGEREF_RECLAIM_CLEAN;
> @@ -823,6 +831,7 @@ static unsigned long shrink_page_list(struct list_head *page_list,
>  		int may_enter_fs;
>  		enum page_references references = PAGEREF_RECLAIM_CLEAN;
>  		bool dirty, writeback;
> +		bool freeable = false;
>  
>  		cond_resched();
>  
> @@ -945,7 +954,8 @@ static unsigned long shrink_page_list(struct list_head *page_list,
>  		}
>  
>  		if (!force_reclaim)
> -			references = page_check_references(page, sc);
> +			references = page_check_references(page, sc,
> +							&freeable);
>  
>  		switch (references) {
>  		case PAGEREF_ACTIVATE:
> @@ -961,7 +971,7 @@ static unsigned long shrink_page_list(struct list_head *page_list,
>  		 * Anonymous process memory has backing store?
>  		 * Try to allocate it some swap space here.
>  		 */
> -		if (PageAnon(page) && !PageSwapCache(page)) {
> +		if (PageAnon(page) && !PageSwapCache(page) && !freeable) {
>  			if (!(sc->gfp_mask & __GFP_IO))
>  				goto keep_locked;
>  			if (!add_to_swap(page, page_list))
> @@ -976,8 +986,9 @@ static unsigned long shrink_page_list(struct list_head *page_list,
>  		 * The page is mapped into the page tables of one or more
>  		 * processes. Try to unmap it here.
>  		 */
> -		if (page_mapped(page) && mapping) {
> -			switch (try_to_unmap(page, ttu_flags)) {
> +		if (page_mapped(page) && (mapping || freeable)) {
> +			switch (try_to_unmap(page, ttu_flags |
> +					(freeable ? TTU_FREE : 0))) {
>  			case SWAP_FAIL:
>  				goto activate_locked;
>  			case SWAP_AGAIN:
> @@ -985,7 +996,20 @@ static unsigned long shrink_page_list(struct list_head *page_list,
>  			case SWAP_MLOCK:
>  				goto cull_mlocked;
>  			case SWAP_SUCCESS:
> -				; /* try to free the page below */
> +				/* try to free the page below */
> +				if (!freeable)
> +					break;
> +				/*
> +				 * Freeable anon page doesn't have mapping
> +				 * due to skipping of swapcache so we free
> +				 * page in here rather than __remove_mapping.
> +				 */
> +				VM_BUG_ON_PAGE(PageSwapCache(page), page);
> +				if (!page_freeze_refs(page, 1))
> +					goto keep_locked;
> +				__clear_page_locked(page);
> +				count_vm_event(PGLAZYFREED);
> +				goto free_it;
>  			}
>  		}
>  
> @@ -1727,7 +1751,7 @@ static void shrink_active_list(unsigned long nr_to_scan,
>  		}
>  
>  		if (page_referenced(page, 0, sc->target_mem_cgroup,
> -				    &vm_flags)) {
> +				    &vm_flags, NULL)) {
>  			nr_rotated += hpage_nr_pages(page);
>  			/*
>  			 * Identify referenced, file-backed active pages and
> diff --git a/mm/vmstat.c b/mm/vmstat.c
> index eef6321c8470..da18337c6c66 100644
> --- a/mm/vmstat.c
> +++ b/mm/vmstat.c
> @@ -794,6 +794,7 @@ const char * const vmstat_text[] = {
>  
>  	"pgfault",
>  	"pgmajfault",
> +	"pglazyfreed",
>  
>  	TEXTS_FOR_ZONES("pgrefill")
>  	TEXTS_FOR_ZONES("pgsteal_kswapd")
> 


-- 
Thanks.
Zhang Yanfei

^ permalink raw reply

* Re: [PATCH v10 7/7] mm: Don't split THP page when syscall is called
From: Minchan Kim @ 2014-07-08  6:10 UTC (permalink / raw)
  To: Kirill A. Shutemov
  Cc: Andrew Morton, linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-mm-Bw31MaZKKs3YtjvyW6yDsg, Michael Kerrisk, Linux API,
	Hugh Dickins, Johannes Weiner, Rik van Riel, KOSAKI Motohiro,
	Mel Gorman, Jason Evans, Zhang Yanfei
In-Reply-To: <20140708013038.GD6076@bbox>

On Tue, Jul 08, 2014 at 10:30:38AM +0900, Minchan Kim wrote:
> On Mon, Jul 07, 2014 at 02:13:03PM +0300, Kirill A. Shutemov wrote:
> > On Mon, Jul 07, 2014 at 09:53:58AM +0900, Minchan Kim wrote:
> > > We don't need to split THP page when MADV_FREE syscall is
> > > called. It could be done when VM decide really frees it so
> > > we could reduce the number of THP split.
> > > 
> > > Signed-off-by: Minchan Kim <minchan-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> > > ---
> > >  include/linux/huge_mm.h |  3 +++
> > >  mm/huge_memory.c        | 25 +++++++++++++++++++++++++
> > >  mm/madvise.c            | 19 +++++++++++++++++--
> > >  mm/rmap.c               |  4 ++++
> > >  mm/vmscan.c             | 24 ++++++++++++++++--------
> > >  5 files changed, 65 insertions(+), 10 deletions(-)
> > > 
> > > diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h
> > > index 63579cb8d3dc..f0d37238cf8f 100644
> > > --- a/include/linux/huge_mm.h
> > > +++ b/include/linux/huge_mm.h
> > > @@ -19,6 +19,9 @@ extern struct page *follow_trans_huge_pmd(struct vm_area_struct *vma,
> > >  					  unsigned long addr,
> > >  					  pmd_t *pmd,
> > >  					  unsigned int flags);
> > > +extern int madvise_free_pmd(struct mmu_gather *tlb,
> > > +			struct vm_area_struct *vma,
> > > +			pmd_t *pmd, unsigned long addr);
> > >  extern int zap_huge_pmd(struct mmu_gather *tlb,
> > >  			struct vm_area_struct *vma,
> > >  			pmd_t *pmd, unsigned long addr);
> > > diff --git a/mm/huge_memory.c b/mm/huge_memory.c
> > > index 5d562a9fe931..2a70069dcfc0 100644
> > > --- a/mm/huge_memory.c
> > > +++ b/mm/huge_memory.c
> > > @@ -1384,6 +1384,31 @@ out:
> > >  	return 0;
> > >  }
> > >  
> > > +int madvise_free_pmd(struct mmu_gather *tlb, struct vm_area_struct *vma,
> > > +		 pmd_t *pmd, unsigned long addr)
> > > +{
> > > +	spinlock_t *ptl;
> > > +	int ret = 0;
> > > +
> > > +	if (__pmd_trans_huge_lock(pmd, vma, &ptl) == 1) {
> > > +		pmd_t orig_pmd;
> > > +		struct mm_struct *mm = vma->vm_mm;
> > > +
> > > +		/* No hugepage in swapcache */
> > > +		VM_BUG_ON(PageSwapCache(pmd_page(orig_pmd)));
> > 
> > VM_BUG_ON_PAGE() ?
> 
> NP.
> 
> > 
> > > +
> > > +		orig_pmd = pmdp_get_and_clear(tlb->mm, addr, pmd);
> > > +		orig_pmd = pmd_mkold(orig_pmd);
> > > +		orig_pmd = pmd_mkclean(orig_pmd);
> > > +
> > > +		set_pmd_at(mm, addr, pmd, orig_pmd);
> > > +		tlb_remove_pmd_tlb_entry(tlb, pmd, addr);
> > > +		spin_unlock(ptl);
> > > +		ret = 1;
> > > +	}
> > > +	return ret;
> > > +}
> > > +
> > >  int zap_huge_pmd(struct mmu_gather *tlb, struct vm_area_struct *vma,
> > >  		 pmd_t *pmd, unsigned long addr)
> > >  {
> > > diff --git a/mm/madvise.c b/mm/madvise.c
> > > index 372a25a8ea82..3c99919ee094 100644
> > > --- a/mm/madvise.c
> > > +++ b/mm/madvise.c
> > > @@ -320,8 +320,23 @@ static inline unsigned long madvise_free_pmd_range(struct mmu_gather *tlb,
> > >  		 * if the range covers.
> > >  		 */
> > >  		next = pmd_addr_end(addr, end);
> > > -		if (pmd_trans_huge(*pmd))
> > > -			split_huge_page_pmd(vma, addr, pmd);
> > > +		if (pmd_trans_huge(*pmd)) {
> > > +			if (next - addr != HPAGE_PMD_SIZE) {
> > > +#ifdef CONFIG_DEBUG_VM
> > > +				if (!rwsem_is_locked(&tlb->mm->mmap_sem)) {
> > > +					pr_err("%s: mmap_sem is unlocked! addr=0x%lx end=0x%lx vma->vm_start=0x%lx vma->vm_end=0x%lx\n",
> > > +						__func__, addr, end,
> > > +						vma->vm_start,
> > > +						vma->vm_end);
> > > +					BUG();
> > > +				}
> > > +#endif
> > > +				split_huge_page_pmd(vma, addr, pmd);
> > > +			} else if (madvise_free_pmd(tlb, vma, pmd, addr))
> > > +				goto next;
> > > +			/* fall through */
> > > +		}
> > > +
> > >  		/*
> > >  		 * Here there can be other concurrent MADV_DONTNEED or
> > >  		 * trans huge page faults running, and if the pmd is
> > > diff --git a/mm/rmap.c b/mm/rmap.c
> > > index ee495d84c8b3..3c415eb8b6f0 100644
> > > --- a/mm/rmap.c
> > > +++ b/mm/rmap.c
> > > @@ -702,6 +702,10 @@ static int page_referenced_one(struct page *page, struct vm_area_struct *vma,
> > >  		/* go ahead even if the pmd is pmd_trans_splitting() */
> > >  		if (pmdp_clear_flush_young_notify(vma, address, pmd))
> > >  			referenced++;
> > > +
> > > +		if (pmd_dirty(*pmd))
> > > +			dirty++;
> > > +
> > >  		spin_unlock(ptl);
> > >  	} else {
> > >  		pte_t *pte;
> > > diff --git a/mm/vmscan.c b/mm/vmscan.c
> > > index f7a45600846f..4e15babf4414 100644
> > > --- a/mm/vmscan.c
> > > +++ b/mm/vmscan.c
> > > @@ -971,15 +971,23 @@ static unsigned long shrink_page_list(struct list_head *page_list,
> > >  		 * Anonymous process memory has backing store?
> > >  		 * Try to allocate it some swap space here.
> > >  		 */
> > > -		if (PageAnon(page) && !PageSwapCache(page) && !freeable) {
> > > -			if (!(sc->gfp_mask & __GFP_IO))
> > > -				goto keep_locked;
> > > -			if (!add_to_swap(page, page_list))
> > > -				goto activate_locked;
> > > -			may_enter_fs = 1;
> > > +		if (PageAnon(page) && !PageSwapCache(page)) {
> > > +			if (!freeable) {
> > > +				if (!(sc->gfp_mask & __GFP_IO))
> > > +					goto keep_locked;
> > > +				if (!add_to_swap(page, page_list))
> > > +					goto activate_locked;
> > > +				may_enter_fs = 1;
> > >  
> > > -			/* Adding to swap updated mapping */
> > > -			mapping = page_mapping(page);
> > > +				/* Adding to swap updated mapping */
> > > +				mapping = page_mapping(page);
> > > +			} else {
> > > +				if (unlikely(PageTransHuge(page))) {
> > > +					if (unlikely(split_huge_page_to_list(
> > > +						page, page_list)))
> > > +						goto keep_locked;
> > 
> > Hm. It would be better to free the huge page without splitting. 
> > It shouldn't be a big deal: walk over rmap and zap all pmds.
> > Or I miss something?
> 
> Actually, I did but found no problem except CONFIG_DEBUG_VM but rollback
> after peeking [1].
> When I read the description in detail by your review, I think we can remove
> BUG_ON(PageTransHuge(page)) in try_to_unmap and go with no split for lazyfree
> page because they are not in swapcache any more so the assumption of [1] is
> not valid. Will do it in next revision.
> 
> Thanks for the review, Kirill!
> 
> [1] thp: split_huge_page paging, 3f04f62f9

I just respin without this part because it needs more review but
no enough time to me at the moment.
Nontheless, I want to get review other parts from community.

It's like one of optimization so I think it shouldn't a party pooper.
But finally I will have a time to see that.(ie, I just added some
comment to make try_to_unmap be aware of THP freeing)

Thanks for the review!

-- 
Kind regards,
Minchan Kim

^ permalink raw reply

* [PATCH v11 7/7] mm: Don't split THP page when syscall is called
From: Minchan Kim @ 2014-07-08  6:03 UTC (permalink / raw)
  To: Andrew Morton
  Cc: linux-kernel, linux-mm, Michael Kerrisk, Linux API, Hugh Dickins,
	Johannes Weiner, Rik van Riel, KOSAKI Motohiro, Mel Gorman,
	Jason Evans, Zhang Yanfei, Kirill A. Shutemov, Minchan Kim
In-Reply-To: <1404799424-1120-1-git-send-email-minchan@kernel.org>

We don't need to split THP page when MADV_FREE syscall is
called. It could be done when VM decide really frees it so
we could avoid unnecessary THP split.

Signed-off-by: Minchan Kim <minchan@kernel.org>
---
 mm/madvise.c | 20 +++++++++++++++++++-
 mm/rmap.c    |  7 +++----
 mm/vmscan.c  | 28 ++++++++++++++++++----------
 3 files changed, 40 insertions(+), 15 deletions(-)

diff --git a/mm/madvise.c b/mm/madvise.c
index a6aa7d4c4e02..77f13a99584c 100644
--- a/mm/madvise.c
+++ b/mm/madvise.c
@@ -272,7 +272,25 @@ static int madvise_free_pte_range(pmd_t *pmd, unsigned long addr,
 	pte_t *pte, ptent;
 	struct page *page;
 
-	split_huge_page_pmd(vma, addr, pmd);
+	if (pmd_trans_huge_lock(pmd, vma, &ptl) == 1) {
+		struct page *page;
+		pmd_t orig_pmd;
+
+		orig_pmd = pmdp_get_and_clear(mm, addr, pmd);
+
+		/* No hugepage in swapcache */
+		page = pmd_page(orig_pmd);
+		VM_BUG_ON_PAGE(PageSwapCache(page), page);
+
+		orig_pmd = pmd_mkold(orig_pmd);
+		orig_pmd = pmd_mkclean(orig_pmd);
+
+		set_pmd_at(mm, addr, pmd, orig_pmd);
+		tlb_remove_pmd_tlb_entry(tlb, pmd, addr);
+		spin_unlock(ptl);
+		return 0;
+	}
+
 	if (pmd_trans_unstable(pmd))
 		return 0;
 
diff --git a/mm/rmap.c b/mm/rmap.c
index a8e34596dc97..67e1c1859c1d 100644
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -703,10 +703,9 @@ static int page_referenced_one(struct page *page, struct vm_area_struct *vma,
 		if (pmdp_clear_flush_young_notify(vma, address, pmd))
 			referenced++;
 
-		/*
-		 * In this implmentation, MADV_FREE doesn't support THP free
-		 */
-		dirty++;
+		if (pmd_dirty(*pmd))
+			dirty++;
+
 		spin_unlock(ptl);
 	} else {
 		pte_t *pte;
diff --git a/mm/vmscan.c b/mm/vmscan.c
index d88413ccadcc..6557f0b36321 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -971,17 +971,25 @@ static unsigned long shrink_page_list(struct list_head *page_list,
 		 * Anonymous process memory has backing store?
 		 * Try to allocate it some swap space here.
 		 */
-		if (PageAnon(page) && !PageSwapCache(page) && !freeable) {
-			if (!(sc->gfp_mask & __GFP_IO))
-				goto keep_locked;
-			if (!add_to_swap(page, page_list))
-				goto activate_locked;
-			may_enter_fs = 1;
-
-			/* Adding to swap updated mapping */
-			mapping = page_mapping(page);
+		if (PageAnon(page) && !PageSwapCache(page)) {
+			if (!freeable) {
+				if (!(sc->gfp_mask & __GFP_IO))
+					goto keep_locked;
+				if (!add_to_swap(page, page_list))
+					goto activate_locked;
+				may_enter_fs = 1;
+				/* Adding to swap updated mapping */
+				mapping = page_mapping(page);
+			} else {
+				if (likely(!PageTransHuge(page)))
+					goto unmap;
+				/* try_to_unmap isn't aware of THP page */
+				if (unlikely(split_huge_page_to_list(page,
+								page_list)))
+					goto keep_locked;
+			}
 		}
-
+unmap:
 		/*
 		 * The page is mapped into the page tables of one or more
 		 * processes. Try to unmap it here.
-- 
2.0.0

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply related

* [PATCH v11 6/7] ARM: add pmd_[dirty|mkclean] for THP
From: Minchan Kim @ 2014-07-08  6:03 UTC (permalink / raw)
  To: Andrew Morton
  Cc: linux-kernel, linux-mm, Michael Kerrisk, Linux API, Hugh Dickins,
	Johannes Weiner, Rik van Riel, KOSAKI Motohiro, Mel Gorman,
	Jason Evans, Zhang Yanfei, Kirill A. Shutemov, Minchan Kim,
	Catalin Marinas, Will Deacon, Steve Capper, Russell King,
	linux-arm-kernel
In-Reply-To: <1404799424-1120-1-git-send-email-minchan@kernel.org>

MADV_FREE needs pmd_dirty and pmd_mkclean for detecting recent
overwrite of the contents since MADV_FREE syscall is called for
THP page.

This patch adds pmd_dirty and pmd_mkclean for THP page MADV_FREE
support.

Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Steve Capper <steve.capper@linaro.org>
Cc: Russell King <linux@arm.linux.org.uk>
Cc: linux-arm-kernel@lists.infradead.org
Signed-off-by: Minchan Kim <minchan@kernel.org>
---
 arch/arm/include/asm/pgtable-3level.h | 3 +++
 arch/arm64/include/asm/pgtable.h      | 2 ++
 2 files changed, 5 insertions(+)

diff --git a/arch/arm/include/asm/pgtable-3level.h b/arch/arm/include/asm/pgtable-3level.h
index 85c60adc8b60..3a7bb8dc7d05 100644
--- a/arch/arm/include/asm/pgtable-3level.h
+++ b/arch/arm/include/asm/pgtable-3level.h
@@ -220,6 +220,8 @@ static inline pmd_t *pmd_offset(pud_t *pud, unsigned long addr)
 #define pmd_trans_splitting(pmd) (pmd_val(pmd) & PMD_SECT_SPLITTING)
 #endif
 
+#define pmd_dirty	(pmd_val(pmd) & PMD_SECT_DIRTY)
+
 #define PMD_BIT_FUNC(fn,op) \
 static inline pmd_t pmd_##fn(pmd_t pmd) { pmd_val(pmd) op; return pmd; }
 
@@ -228,6 +230,7 @@ PMD_BIT_FUNC(mkold,	&= ~PMD_SECT_AF);
 PMD_BIT_FUNC(mksplitting, |= PMD_SECT_SPLITTING);
 PMD_BIT_FUNC(mkwrite,   &= ~PMD_SECT_RDONLY);
 PMD_BIT_FUNC(mkdirty,   |= PMD_SECT_DIRTY);
+PMD_BIT_FUNC(mkclean,   &= ~PMD_SECT_DIRTY);
 PMD_BIT_FUNC(mkyoung,   |= PMD_SECT_AF);
 
 #define pmd_mkhuge(pmd)		(__pmd(pmd_val(pmd) & ~PMD_TABLE_BIT))
diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h
index 579702086488..f3ec01cef04f 100644
--- a/arch/arm64/include/asm/pgtable.h
+++ b/arch/arm64/include/asm/pgtable.h
@@ -240,10 +240,12 @@ static inline pmd_t pte_pmd(pte_t pte)
 #endif
 
 #define pmd_young(pmd)		pte_young(pmd_pte(pmd))
+#define pmd_dirty(pmd)		pte_dirty(pmd_pte(pmd))
 #define pmd_wrprotect(pmd)	pte_pmd(pte_wrprotect(pmd_pte(pmd)))
 #define pmd_mksplitting(pmd)	pte_pmd(pte_mkspecial(pmd_pte(pmd)))
 #define pmd_mkold(pmd)		pte_pmd(pte_mkold(pmd_pte(pmd)))
 #define pmd_mkwrite(pmd)	pte_pmd(pte_mkwrite(pmd_pte(pmd)))
+#define pmd_mkclean(pmd)	pte_pmd(pte_mkclean(pmd_pte(pmd)))
 #define pmd_mkdirty(pmd)	pte_pmd(pte_mkdirty(pmd_pte(pmd)))
 #define pmd_mkyoung(pmd)	pte_pmd(pte_mkyoung(pmd_pte(pmd)))
 #define pmd_mknotpresent(pmd)	(__pmd(pmd_val(pmd) & ~PMD_TYPE_MASK))
-- 
2.0.0

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply related

* [PATCH v11 5/7] s390: add pmd_[dirty|mkclean] for THP
From: Minchan Kim @ 2014-07-08  6:03 UTC (permalink / raw)
  To: Andrew Morton
  Cc: linux-kernel, linux-mm, Michael Kerrisk, Linux API, Hugh Dickins,
	Johannes Weiner, Rik van Riel, KOSAKI Motohiro, Mel Gorman,
	Jason Evans, Zhang Yanfei, Kirill A. Shutemov, Minchan Kim,
	Martin Schwidefsky, Heiko Carstens, Dominik Dingel,
	Christian Borntraeger, linux-s390
In-Reply-To: <1404799424-1120-1-git-send-email-minchan@kernel.org>

MADV_FREE needs pmd_dirty and pmd_mkclean for detecting recent
overwrite of the contents since MADV_FREE syscall is called for
THP page but for s390 pmds only referenced bit is available
because there is no free bit left in the pmd entry for the
software dirty bit so this patch adds dumb pmd_dirty which
returns always true by suggesting by Martin.

They finally find a solution in future.
http://marc.info/?l=linux-api&m=140440328820808&w=2

Cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
Cc: Heiko Carstens <heiko.carstens@de.ibm.com>
Cc: Dominik Dingel <dingel@linux.vnet.ibm.com>
Cc: Christian Borntraeger <borntraeger@de.ibm.com>
Cc: linux-s390@vger.kernel.org
Acked-by: Gerald Schaefer <gerald.schaefer@de.ibm.com>
Signed-off-by: Minchan Kim <minchan@kernel.org>
---
 arch/s390/include/asm/pgtable.h | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h
index fcba5e03839f..9862fcb0592b 100644
--- a/arch/s390/include/asm/pgtable.h
+++ b/arch/s390/include/asm/pgtable.h
@@ -1586,6 +1586,18 @@ static inline pmd_t pmd_mkdirty(pmd_t pmd)
 	return pmd;
 }
 
+static inline int pmd_dirty(pmd_t pmd)
+{
+	/* No dirty bit in the segment table entry */
+	return 1;
+}
+
+static inline pmd_t pmd_mkclean(pmd_t pmd)
+{
+	/* No dirty bit in the segment table entry */
+	return pmd;
+}
+
 #define __HAVE_ARCH_PMDP_TEST_AND_CLEAR_YOUNG
 static inline int pmdp_test_and_clear_young(struct vm_area_struct *vma,
 					    unsigned long address, pmd_t *pmdp)
-- 
2.0.0

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply related

* [PATCH v11 4/7] powerpc: add pmd_[dirty|mkclean] for THP
From: Minchan Kim @ 2014-07-08  6:03 UTC (permalink / raw)
  To: Andrew Morton
  Cc: linux-kernel, linux-mm, Michael Kerrisk, Linux API, Hugh Dickins,
	Johannes Weiner, Rik van Riel, KOSAKI Motohiro, Mel Gorman,
	Jason Evans, Zhang Yanfei, Kirill A. Shutemov, Minchan Kim,
	Benjamin Herrenschmidt, Paul Mackerras, Aneesh Kumar K.V,
	linuxppc-dev
In-Reply-To: <1404799424-1120-1-git-send-email-minchan@kernel.org>

MADV_FREE needs pmd_dirty and pmd_mkclean for detecting recent
overwrite of the contents since MADV_FREE syscall is called for
THP page.

This patch adds pmd_dirty and pmd_mkclean for THP page MADV_FREE
support.

Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: "Aneesh Kumar K.V" <aneesh.kumar@linux.vnet.ibm.com>
Cc: linuxppc-dev@lists.ozlabs.org
Signed-off-by: Minchan Kim <minchan@kernel.org>
---
 arch/powerpc/include/asm/pgtable-ppc64.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/powerpc/include/asm/pgtable-ppc64.h b/arch/powerpc/include/asm/pgtable-ppc64.h
index eb9261024f51..c9a4bbe8e179 100644
--- a/arch/powerpc/include/asm/pgtable-ppc64.h
+++ b/arch/powerpc/include/asm/pgtable-ppc64.h
@@ -468,9 +468,11 @@ static inline pte_t *pmdp_ptep(pmd_t *pmd)
 
 #define pmd_pfn(pmd)		pte_pfn(pmd_pte(pmd))
 #define pmd_young(pmd)		pte_young(pmd_pte(pmd))
+#define pmd_dirty(pmd)		pte_dirty(pmd_pte(pmd))
 #define pmd_mkold(pmd)		pte_pmd(pte_mkold(pmd_pte(pmd)))
 #define pmd_wrprotect(pmd)	pte_pmd(pte_wrprotect(pmd_pte(pmd)))
 #define pmd_mkdirty(pmd)	pte_pmd(pte_mkdirty(pmd_pte(pmd)))
+#define pmd_mkclean(pmd)	pte_pmd(pte_mkclean(pmd_pte(pmd)))
 #define pmd_mkyoung(pmd)	pte_pmd(pte_mkyoung(pmd_pte(pmd)))
 #define pmd_mkwrite(pmd)	pte_pmd(pte_mkwrite(pmd_pte(pmd)))
 
-- 
2.0.0

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply related

* [PATCH v11 3/7] sparc: add pmd_[dirty|mkclean] for THP
From: Minchan Kim @ 2014-07-08  6:03 UTC (permalink / raw)
  To: Andrew Morton
  Cc: linux-kernel, linux-mm, Michael Kerrisk, Linux API, Hugh Dickins,
	Johannes Weiner, Rik van Riel, KOSAKI Motohiro, Mel Gorman,
	Jason Evans, Zhang Yanfei, Kirill A. Shutemov, Minchan Kim,
	David S. Miller, sparclinux
In-Reply-To: <1404799424-1120-1-git-send-email-minchan@kernel.org>

MADV_FREE needs pmd_dirty and pmd_mkclean for detecting recent
overwrite of the contents since MADV_FREE syscall is called for
THP page.

This patch adds pmd_dirty and pmd_mkclean for THP page MADV_FREE
support.

Cc: "David S. Miller" <davem@davemloft.net>
Cc: sparclinux@vger.kernel.org
Signed-off-by: Minchan Kim <minchan@kernel.org>
---
 arch/sparc/include/asm/pgtable_64.h | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/arch/sparc/include/asm/pgtable_64.h b/arch/sparc/include/asm/pgtable_64.h
index 3770bf5c6e1b..0a3e5fdfead2 100644
--- a/arch/sparc/include/asm/pgtable_64.h
+++ b/arch/sparc/include/asm/pgtable_64.h
@@ -666,6 +666,13 @@ static inline unsigned long pmd_young(pmd_t pmd)
 	return pte_young(pte);
 }
 
+static inline int pmd_dirty(pmd_t pmd)
+{
+	pte_t pte = __pte(pmd_val(pmd));
+
+	return pte_dirty(pte);
+}
+
 static inline unsigned long pmd_write(pmd_t pmd)
 {
 	pte_t pte = __pte(pmd_val(pmd));
@@ -723,6 +730,15 @@ static inline pmd_t pmd_mkdirty(pmd_t pmd)
 	return __pmd(pte_val(pte));
 }
 
+static inline pmd_mkclean(pmd_t pmd)
+{
+	pte_t pte = __pte(pmd_val(pmd));
+
+	pte = pte_mkclean(pte);
+
+	return __pmd(pte_val(pte));
+}
+
 static inline pmd_t pmd_mkyoung(pmd_t pmd)
 {
 	pte_t pte = __pte(pmd_val(pmd));
-- 
2.0.0

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply related

* [PATCH v11 2/7] x86: add pmd_[dirty|mkclean] for THP
From: Minchan Kim @ 2014-07-08  6:03 UTC (permalink / raw)
  To: Andrew Morton
  Cc: linux-kernel, linux-mm, Michael Kerrisk, Linux API, Hugh Dickins,
	Johannes Weiner, Rik van Riel, KOSAKI Motohiro, Mel Gorman,
	Jason Evans, Zhang Yanfei, Kirill A. Shutemov, Minchan Kim,
	Thomas Gleixner, Ingo Molnar, H. Peter Anvin, x86
In-Reply-To: <1404799424-1120-1-git-send-email-minchan@kernel.org>

MADV_FREE needs pmd_dirty and pmd_mkclean for detecting recent
overwrite of the contents since MADV_FREE syscall is called for
THP page.

This patch adds pmd_dirty and pmd_mkclean for THP page MADV_FREE
support.

Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: x86@kernel.org
Acked-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Signed-off-by: Minchan Kim <minchan@kernel.org>
---
 arch/x86/include/asm/pgtable.h | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/arch/x86/include/asm/pgtable.h b/arch/x86/include/asm/pgtable.h
index 0ec056012618..329865799653 100644
--- a/arch/x86/include/asm/pgtable.h
+++ b/arch/x86/include/asm/pgtable.h
@@ -104,6 +104,11 @@ static inline int pmd_young(pmd_t pmd)
 	return pmd_flags(pmd) & _PAGE_ACCESSED;
 }
 
+static inline int pmd_dirty(pmd_t pmd)
+{
+	return pmd_flags(pmd) & _PAGE_DIRTY;
+}
+
 static inline int pte_write(pte_t pte)
 {
 	return pte_flags(pte) & _PAGE_RW;
@@ -267,6 +272,11 @@ static inline pmd_t pmd_mkold(pmd_t pmd)
 	return pmd_clear_flags(pmd, _PAGE_ACCESSED);
 }
 
+static inline pmd_t pmd_mkclean(pmd_t pmd)
+{
+	return pmd_clear_flags(pmd, _PAGE_DIRTY);
+}
+
 static inline pmd_t pmd_wrprotect(pmd_t pmd)
 {
 	return pmd_clear_flags(pmd, _PAGE_RW);
-- 
2.0.0

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox