diff mbox

[1/3] vmalloc: Add __vmalloc_node_try_addr function

Message ID 1529532570-21765-2-git-send-email-rick.p.edgecombe@intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Rick Edgecombe June 20, 2018, 10:09 p.m. UTC
Create __vmalloc_node_try_addr function that tries to allocate at a specific
address.  The implementation relies on __vmalloc_node_range for the bulk of the
work.  To keep this function from spamming the logs when an allocation failure
is fails, __vmalloc_node_range is changed to only warn when __GFP_NOWARN is not
set.  This behavior is consistent with this flags interpretation in
alloc_vmap_area.

Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
---
 include/linux/vmalloc.h |  3 +++
 mm/vmalloc.c            | 41 +++++++++++++++++++++++++++++++++++++++--
 2 files changed, 42 insertions(+), 2 deletions(-)

Comments

Randy Dunlap June 20, 2018, 10:16 p.m. UTC | #1
On 06/20/2018 03:09 PM, Rick Edgecombe wrote:
> Create __vmalloc_node_try_addr function that tries to allocate at a specific
> address.  The implementation relies on __vmalloc_node_range for the bulk of the
> work.  To keep this function from spamming the logs when an allocation failure
> is fails, __vmalloc_node_range is changed to only warn when __GFP_NOWARN is not
> set.  This behavior is consistent with this flags interpretation in
> alloc_vmap_area.
> 
> Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
> ---
>  include/linux/vmalloc.h |  3 +++
>  mm/vmalloc.c            | 41 +++++++++++++++++++++++++++++++++++++++--
>  2 files changed, 42 insertions(+), 2 deletions(-)
> 
> diff --git a/include/linux/vmalloc.h b/include/linux/vmalloc.h
> index 398e9c9..6eaa896 100644
> --- a/include/linux/vmalloc.h
> +++ b/include/linux/vmalloc.h
> @@ -82,6 +82,9 @@ extern void *__vmalloc_node_range(unsigned long size, unsigned long align,
>  			unsigned long start, unsigned long end, gfp_t gfp_mask,
>  			pgprot_t prot, unsigned long vm_flags, int node,
>  			const void *caller);
> +extern void *__vmalloc_node_try_addr(unsigned long addr, unsigned long size,
> +			gfp_t gfp_mask,	pgprot_t prot, unsigned long vm_flags,
> +			int node, const void *caller);
>  #ifndef CONFIG_MMU
>  extern void *__vmalloc_node_flags(unsigned long size, int node, gfp_t flags);
>  static inline void *__vmalloc_node_flags_caller(unsigned long size, int node,

> diff --git a/mm/vmalloc.c b/mm/vmalloc.c
> index cfea25b..9e0820c9 100644
> --- a/mm/vmalloc.c
> +++ b/mm/vmalloc.c
> @@ -1710,6 +1710,42 @@ static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,
>  }
>  
>  /**
> + *	__vmalloc_try_addr  -  try to alloc at a specific address

    *   __vmalloc_node_try_addr - try to allocate at a specific address

> + *	@addr:		address to try
> + *	@size:		size to try
> + *	@gfp_mask:	flags for the page level allocator
> + *	@prot:		protection mask for the allocated pages
> + *	@vm_flags:	additional vm area flags (e.g. %VM_NO_GUARD)
> + *	@node:		node to use for allocation or NUMA_NO_NODE
> + *	@caller:	caller's return address
> + *
> + *	Try to allocate at the specific address. If it succeeds the address is
> + *	returned. If it fails NULL is returned.  It may trigger TLB flushes.
> + */
> +void *__vmalloc_node_try_addr(unsigned long addr, unsigned long size,
> +			gfp_t gfp_mask,	pgprot_t prot, unsigned long vm_flags,
> +			int node, const void *caller)
> +{

so this isn't optional, eh?  You are going to force it on people because?

thanks,
Matthew Wilcox June 20, 2018, 10:26 p.m. UTC | #2
On Wed, Jun 20, 2018 at 03:09:28PM -0700, Rick Edgecombe wrote:
>  
>  /**
> + *	__vmalloc_try_addr  -  try to alloc at a specific address
> + *	@addr:		address to try
> + *	@size:		size to try
> + *	@gfp_mask:	flags for the page level allocator
> + *	@prot:		protection mask for the allocated pages
> + *	@vm_flags:	additional vm area flags (e.g. %VM_NO_GUARD)
> + *	@node:		node to use for allocation or NUMA_NO_NODE
> + *	@caller:	caller's return address
> + *
> + *	Try to allocate at the specific address. If it succeeds the address is
> + *	returned. If it fails NULL is returned.  It may trigger TLB flushes.

 * Try to allocate memory at a specific address.  May trigger TLB flushes.
 *
 * Context: Process context.
 * Return: The allocated address if it succeeds.  NULL if it fails.

> @@ -1759,8 +1795,9 @@ void *__vmalloc_node_range(unsigned long size, unsigned long align,
>  	return addr;
>  
>  fail:
> -	warn_alloc(gfp_mask, NULL,
> -			  "vmalloc: allocation failure: %lu bytes", real_size);
> +	if (!(gfp_mask & __GFP_NOWARN))
> +		warn_alloc(gfp_mask, NULL,
> +			"vmalloc: allocation failure: %lu bytes", real_size);
>  	return NULL;

Not needed:

void warn_alloc(gfp_t gfp_mask, nodemask_t *nodemask, const char *fmt, ...)
{
...
        if ((gfp_mask & __GFP_NOWARN) || !__ratelimit(&nopage_rs))
                return;
Kees Cook June 20, 2018, 10:35 p.m. UTC | #3
On Wed, Jun 20, 2018 at 3:16 PM, Randy Dunlap <rdunlap@infradead.org> wrote:
> On 06/20/2018 03:09 PM, Rick Edgecombe wrote:
>> +void *__vmalloc_node_try_addr(unsigned long addr, unsigned long size,
>> +                     gfp_t gfp_mask, pgprot_t prot, unsigned long vm_flags,
>> +                     int node, const void *caller)
>> +{
>
> so this isn't optional, eh?  You are going to force it on people because?

RANDOMIZE_BASE isn't optional either. :) This improves the module
address entropy with (what seems to be) no down-side, so yeah, I think
it should be non-optional. :)

-Kees
Randy Dunlap June 20, 2018, 10:44 p.m. UTC | #4
On 06/20/2018 03:35 PM, Kees Cook wrote:
> On Wed, Jun 20, 2018 at 3:16 PM, Randy Dunlap <rdunlap@infradead.org> wrote:
>> On 06/20/2018 03:09 PM, Rick Edgecombe wrote:
>>> +void *__vmalloc_node_try_addr(unsigned long addr, unsigned long size,
>>> +                     gfp_t gfp_mask, pgprot_t prot, unsigned long vm_flags,
>>> +                     int node, const void *caller)
>>> +{
>>
>> so this isn't optional, eh?  You are going to force it on people because?
> 
> RANDOMIZE_BASE isn't optional either. :) This improves the module
> address entropy with (what seems to be) no down-side, so yeah, I think
> it should be non-optional. :)

In what kernel tree is RANDOMIZE_BASE not optional?

x86:
config RANDOMIZE_BASE
	bool "Randomize the address of the kernel image (KASLR)"
	depends on RELOCATABLE
	default y

mips:
config RANDOMIZE_BASE
	bool "Randomize the address of the kernel image"
	depends on RELOCATABLE

arm64:
config RANDOMIZE_BASE
	bool "Randomize the address of the kernel image"
	select ARM64_MODULE_PLTS if MODULES
	select RELOCATABLE


thanks,
Kees Cook June 20, 2018, 11:05 p.m. UTC | #5
On Wed, Jun 20, 2018 at 3:44 PM, Randy Dunlap <rdunlap@infradead.org> wrote:
> On 06/20/2018 03:35 PM, Kees Cook wrote:
>> On Wed, Jun 20, 2018 at 3:16 PM, Randy Dunlap <rdunlap@infradead.org> wrote:
>>> On 06/20/2018 03:09 PM, Rick Edgecombe wrote:
>>>> +void *__vmalloc_node_try_addr(unsigned long addr, unsigned long size,
>>>> +                     gfp_t gfp_mask, pgprot_t prot, unsigned long vm_flags,
>>>> +                     int node, const void *caller)
>>>> +{
>>>
>>> so this isn't optional, eh?  You are going to force it on people because?
>>
>> RANDOMIZE_BASE isn't optional either. :) This improves the module
>> address entropy with (what seems to be) no down-side, so yeah, I think
>> it should be non-optional. :)
>
> In what kernel tree is RANDOMIZE_BASE not optional?

Oh, sorry, I misspoke: on by default. It _is_ possible to turn it off.

But patch #2 does check for RANDOMIZE_BASE, so it should work as expected, yes?

Or did you want even this helper function to be compiled out without it?

-Kees
Randy Dunlap June 20, 2018, 11:16 p.m. UTC | #6
On 06/20/2018 04:05 PM, Kees Cook wrote:
> On Wed, Jun 20, 2018 at 3:44 PM, Randy Dunlap <rdunlap@infradead.org> wrote:
>> On 06/20/2018 03:35 PM, Kees Cook wrote:
>>> On Wed, Jun 20, 2018 at 3:16 PM, Randy Dunlap <rdunlap@infradead.org> wrote:
>>>> On 06/20/2018 03:09 PM, Rick Edgecombe wrote:
>>>>> +void *__vmalloc_node_try_addr(unsigned long addr, unsigned long size,
>>>>> +                     gfp_t gfp_mask, pgprot_t prot, unsigned long vm_flags,
>>>>> +                     int node, const void *caller)
>>>>> +{
>>>>
>>>> so this isn't optional, eh?  You are going to force it on people because?
>>>
>>> RANDOMIZE_BASE isn't optional either. :) This improves the module
>>> address entropy with (what seems to be) no down-side, so yeah, I think
>>> it should be non-optional. :)
>>
>> In what kernel tree is RANDOMIZE_BASE not optional?
> 
> Oh, sorry, I misspoke: on by default. It _is_ possible to turn it off.
> 
> But patch #2 does check for RANDOMIZE_BASE, so it should work as expected, yes?
> 
> Or did you want even this helper function to be compiled out without it?

Thanks, I missed it.  :(

Looks fine.
Rick Edgecombe June 21, 2018, 10:02 p.m. UTC | #7
On Wed, 2018-06-20 at 15:26 -0700, Matthew Wilcox wrote:
> Not needed:

> 

> void warn_alloc(gfp_t gfp_mask, nodemask_t *nodemask, const char

> *fmt, ...)

> {

> ...

>         if ((gfp_mask & __GFP_NOWARN) || !__ratelimit(&nopage_rs))

>                 return;

> 

Yes, thanks!
diff mbox

Patch

diff --git a/include/linux/vmalloc.h b/include/linux/vmalloc.h
index 398e9c9..6eaa896 100644
--- a/include/linux/vmalloc.h
+++ b/include/linux/vmalloc.h
@@ -82,6 +82,9 @@  extern void *__vmalloc_node_range(unsigned long size, unsigned long align,
 			unsigned long start, unsigned long end, gfp_t gfp_mask,
 			pgprot_t prot, unsigned long vm_flags, int node,
 			const void *caller);
+extern void *__vmalloc_node_try_addr(unsigned long addr, unsigned long size,
+			gfp_t gfp_mask,	pgprot_t prot, unsigned long vm_flags,
+			int node, const void *caller);
 #ifndef CONFIG_MMU
 extern void *__vmalloc_node_flags(unsigned long size, int node, gfp_t flags);
 static inline void *__vmalloc_node_flags_caller(unsigned long size, int node,
diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index cfea25b..9e0820c9 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -1710,6 +1710,42 @@  static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,
 }
 
 /**
+ *	__vmalloc_try_addr  -  try to alloc at a specific address
+ *	@addr:		address to try
+ *	@size:		size to try
+ *	@gfp_mask:	flags for the page level allocator
+ *	@prot:		protection mask for the allocated pages
+ *	@vm_flags:	additional vm area flags (e.g. %VM_NO_GUARD)
+ *	@node:		node to use for allocation or NUMA_NO_NODE
+ *	@caller:	caller's return address
+ *
+ *	Try to allocate at the specific address. If it succeeds the address is
+ *	returned. If it fails NULL is returned.  It may trigger TLB flushes.
+ */
+void *__vmalloc_node_try_addr(unsigned long addr, unsigned long size,
+			gfp_t gfp_mask,	pgprot_t prot, unsigned long vm_flags,
+			int node, const void *caller)
+{
+	unsigned long addr_end;
+	unsigned long vsize = PAGE_ALIGN(size);
+
+	if (!vsize || (vsize >> PAGE_SHIFT) > totalram_pages)
+		return NULL;
+
+	if (!(vm_flags & VM_NO_GUARD))
+		vsize += PAGE_SIZE;
+
+	addr_end = addr + vsize;
+
+	if (addr > addr_end)
+		return NULL;
+
+	return __vmalloc_node_range(size, 1, addr, addr_end,
+				gfp_mask | __GFP_NOWARN, prot, vm_flags, node,
+				caller);
+}
+
+/**
  *	__vmalloc_node_range  -  allocate virtually contiguous memory
  *	@size:		allocation size
  *	@align:		desired alignment
@@ -1759,8 +1795,9 @@  void *__vmalloc_node_range(unsigned long size, unsigned long align,
 	return addr;
 
 fail:
-	warn_alloc(gfp_mask, NULL,
-			  "vmalloc: allocation failure: %lu bytes", real_size);
+	if (!(gfp_mask & __GFP_NOWARN))
+		warn_alloc(gfp_mask, NULL,
+			"vmalloc: allocation failure: %lu bytes", real_size);
 	return NULL;
 }