Commit 66f71da9 authored by Aaron Lu's avatar Aaron Lu Committed by Linus Torvalds

mm/swap: use nr_node_ids for avail_lists in swap_info_struct

Since a2468cc9 ("swap: choose swap device according to numa node"),
avail_lists field of swap_info_struct is changed to an array with
MAX_NUMNODES elements.  This made swap_info_struct size increased to 40KiB
and needs an order-4 page to hold it.

This is not optimal in that:
1 Most systems have way less than MAX_NUMNODES(1024) nodes so it
  is a waste of memory;
2 It could cause swapon failure if the swap device is swapped on
  after system has been running for a while, due to no order-4
  page is available as pointed out by Vasily Averin.

Solve the above two issues by using nr_node_ids(which is the actual
possible node number the running system has) for avail_lists instead of
MAX_NUMNODES.

nr_node_ids is unknown at compile time so can't be directly used when
declaring this array.  What I did here is to declare avail_lists as zero
element array and allocate space for it when allocating space for
swap_info_struct.  The reason why keep using array but not pointer is
plist_for_each_entry needs the field to be part of the struct, so pointer
will not work.

This patch is on top of Vasily Averin's fix commit.  I think the use of
kvzalloc for swap_info_struct is still needed in case nr_node_ids is
really big on some systems.

Link: http://lkml.kernel.org/r/20181115083847.GA11129@intel.comSigned-off-by: default avatarAaron Lu <aaron.lu@intel.com>
Reviewed-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Acked-by: default avatarMichal Hocko <mhocko@suse.com>
Cc: Vasily Averin <vvs@virtuozzo.com>
Cc: Huang Ying <ying.huang@intel.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 8b09549c
...@@ -235,7 +235,6 @@ struct swap_info_struct { ...@@ -235,7 +235,6 @@ struct swap_info_struct {
unsigned long flags; /* SWP_USED etc: see above */ unsigned long flags; /* SWP_USED etc: see above */
signed short prio; /* swap priority of this type */ signed short prio; /* swap priority of this type */
struct plist_node list; /* entry in swap_active_head */ struct plist_node list; /* entry in swap_active_head */
struct plist_node avail_lists[MAX_NUMNODES];/* entry in swap_avail_heads */
signed char type; /* strange name for an index */ signed char type; /* strange name for an index */
unsigned int max; /* extent of the swap_map */ unsigned int max; /* extent of the swap_map */
unsigned char *swap_map; /* vmalloc'ed array of usage counts */ unsigned char *swap_map; /* vmalloc'ed array of usage counts */
...@@ -276,6 +275,16 @@ struct swap_info_struct { ...@@ -276,6 +275,16 @@ struct swap_info_struct {
*/ */
struct work_struct discard_work; /* discard worker */ struct work_struct discard_work; /* discard worker */
struct swap_cluster_list discard_clusters; /* discard clusters list */ struct swap_cluster_list discard_clusters; /* discard clusters list */
struct plist_node avail_lists[0]; /*
* entries in swap_avail_heads, one
* entry per node.
* Must be last as the number of the
* array is nr_node_ids, which is not
* a fixed value so have to allocate
* dynamically.
* And it has to be an array so that
* plist_for_each_* can work.
*/
}; };
#ifdef CONFIG_64BIT #ifdef CONFIG_64BIT
......
...@@ -2812,8 +2812,9 @@ static struct swap_info_struct *alloc_swap_info(void) ...@@ -2812,8 +2812,9 @@ static struct swap_info_struct *alloc_swap_info(void)
struct swap_info_struct *p; struct swap_info_struct *p;
unsigned int type; unsigned int type;
int i; int i;
int size = sizeof(*p) + nr_node_ids * sizeof(struct plist_node);
p = kvzalloc(sizeof(*p), GFP_KERNEL); p = kvzalloc(size, GFP_KERNEL);
if (!p) if (!p)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment