Skip to content

Commit

Permalink
mm/vmscan: throttle reclaim and compaction when too may pages are iso…
Browse files Browse the repository at this point in the history
…lated

Page reclaim throttles on congestion if too many parallel reclaim
instances have isolated too many pages.  This makes no sense, excessive
parallelisation has nothing to do with writeback or congestion.

This patch creates an additional workqueue to sleep on when too many
pages are isolated.  The throttled tasks are woken when the number of
isolated pages is reduced or a timeout occurs.  There may be some false
positive wakeups for GFP_NOIO/GFP_NOFS callers but the tasks will
throttle again if necessary.

[[email protected]: Wake up from compaction context]
[[email protected]: Account number of throttled tasks only for writeback]

Link: https://lkml.kernel.org/r/[email protected]
Signed-off-by: Mel Gorman <[email protected]>
Acked-by: Vlastimil Babka <[email protected]>
Cc: Andreas Dilger <[email protected]>
Cc: "Darrick J . Wong" <[email protected]>
Cc: Dave Chinner <[email protected]>
Cc: Johannes Weiner <[email protected]>
Cc: Jonathan Corbet <[email protected]>
Cc: Matthew Wilcox <[email protected]>
Cc: Michal Hocko <[email protected]>
Cc: NeilBrown <[email protected]>
Cc: Rik van Riel <[email protected]>
Cc: "Theodore Ts'o" <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
gormanm authored and torvalds committed Nov 6, 2021
1 parent 8cd7c58 commit d818fca
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 9 deletions.
1 change: 1 addition & 0 deletions include/linux/mmzone.h
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ enum lru_list {

enum vmscan_throttle_state {
VMSCAN_THROTTLE_WRITEBACK,
VMSCAN_THROTTLE_ISOLATED,
NR_VMSCAN_THROTTLE,
};

Expand Down
4 changes: 3 additions & 1 deletion include/trace/events/vmscan.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@
) : "RECLAIM_WB_NONE"

#define _VMSCAN_THROTTLE_WRITEBACK (1 << VMSCAN_THROTTLE_WRITEBACK)
#define _VMSCAN_THROTTLE_ISOLATED (1 << VMSCAN_THROTTLE_ISOLATED)

#define show_throttle_flags(flags) \
(flags) ? __print_flags(flags, "|", \
{_VMSCAN_THROTTLE_WRITEBACK, "VMSCAN_THROTTLE_WRITEBACK"} \
{_VMSCAN_THROTTLE_WRITEBACK, "VMSCAN_THROTTLE_WRITEBACK"}, \
{_VMSCAN_THROTTLE_ISOLATED, "VMSCAN_THROTTLE_ISOLATED"} \
) : "VMSCAN_THROTTLE_NONE"


Expand Down
10 changes: 8 additions & 2 deletions mm/compaction.c
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,8 @@ isolate_freepages_range(struct compact_control *cc,
/* Similar to reclaim, but different enough that they don't share logic */
static bool too_many_isolated(pg_data_t *pgdat)
{
bool too_many;

unsigned long active, inactive, isolated;

inactive = node_page_state(pgdat, NR_INACTIVE_FILE) +
Expand All @@ -770,7 +772,11 @@ static bool too_many_isolated(pg_data_t *pgdat)
isolated = node_page_state(pgdat, NR_ISOLATED_FILE) +
node_page_state(pgdat, NR_ISOLATED_ANON);

return isolated > (inactive + active) / 2;
too_many = isolated > (inactive + active) / 2;
if (!too_many)
wake_throttle_isolated(pgdat);

return too_many;
}

/**
Expand Down Expand Up @@ -822,7 +828,7 @@ isolate_migratepages_block(struct compact_control *cc, unsigned long low_pfn,
if (cc->mode == MIGRATE_ASYNC)
return -EAGAIN;

congestion_wait(BLK_RW_ASYNC, HZ/10);
reclaim_throttle(pgdat, VMSCAN_THROTTLE_ISOLATED, HZ/10);

if (fatal_signal_pending(current))
return -EINTR;
Expand Down
11 changes: 11 additions & 0 deletions mm/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ static inline void acct_reclaim_writeback(struct page *page)
__acct_reclaim_writeback(pgdat, page, nr_throttled);
}

static inline void wake_throttle_isolated(pg_data_t *pgdat)
{
wait_queue_head_t *wqh;

wqh = &pgdat->reclaim_wait[VMSCAN_THROTTLE_ISOLATED];
if (waitqueue_active(wqh))
wake_up(wqh);
}

vm_fault_t do_swap_page(struct vm_fault *vmf);

void free_pgtables(struct mmu_gather *tlb, struct vm_area_struct *start_vma,
Expand Down Expand Up @@ -121,6 +130,8 @@ extern unsigned long highest_memmap_pfn;
*/
extern int isolate_lru_page(struct page *page);
extern void putback_lru_page(struct page *page);
extern void reclaim_throttle(pg_data_t *pgdat, enum vmscan_throttle_state reason,
long timeout);

/*
* in mm/rmap.c:
Expand Down
22 changes: 16 additions & 6 deletions mm/vmscan.c
Original file line number Diff line number Diff line change
Expand Up @@ -1006,12 +1006,12 @@ static void handle_write_error(struct address_space *mapping,
unlock_page(page);
}

static void
reclaim_throttle(pg_data_t *pgdat, enum vmscan_throttle_state reason,
void reclaim_throttle(pg_data_t *pgdat, enum vmscan_throttle_state reason,
long timeout)
{
wait_queue_head_t *wqh = &pgdat->reclaim_wait[reason];
long ret;
bool acct_writeback = (reason == VMSCAN_THROTTLE_WRITEBACK);
DEFINE_WAIT(wait);

/*
Expand All @@ -1023,15 +1023,18 @@ reclaim_throttle(pg_data_t *pgdat, enum vmscan_throttle_state reason,
current->flags & (PF_IO_WORKER|PF_KTHREAD))
return;

if (atomic_inc_return(&pgdat->nr_writeback_throttled) == 1) {
if (acct_writeback &&
atomic_inc_return(&pgdat->nr_writeback_throttled) == 1) {
WRITE_ONCE(pgdat->nr_reclaim_start,
node_page_state(pgdat, NR_THROTTLED_WRITTEN));
}

prepare_to_wait(wqh, &wait, TASK_UNINTERRUPTIBLE);
ret = schedule_timeout(timeout);
finish_wait(wqh, &wait);
atomic_dec(&pgdat->nr_writeback_throttled);

if (acct_writeback)
atomic_dec(&pgdat->nr_writeback_throttled);

trace_mm_vmscan_throttled(pgdat->node_id, jiffies_to_usecs(timeout),
jiffies_to_usecs(timeout - ret),
Expand Down Expand Up @@ -2175,6 +2178,7 @@ static int too_many_isolated(struct pglist_data *pgdat, int file,
struct scan_control *sc)
{
unsigned long inactive, isolated;
bool too_many;

if (current_is_kswapd())
return 0;
Expand All @@ -2198,7 +2202,13 @@ static int too_many_isolated(struct pglist_data *pgdat, int file,
if ((sc->gfp_mask & (__GFP_IO | __GFP_FS)) == (__GFP_IO | __GFP_FS))
inactive >>= 3;

return isolated > inactive;
too_many = isolated > inactive;

/* Wake up tasks throttled due to too_many_isolated. */
if (!too_many)
wake_throttle_isolated(pgdat);

return too_many;
}

/*
Expand Down Expand Up @@ -2307,8 +2317,8 @@ shrink_inactive_list(unsigned long nr_to_scan, struct lruvec *lruvec,
return 0;

/* wait a bit for the reclaimer. */
msleep(100);
stalled = true;
reclaim_throttle(pgdat, VMSCAN_THROTTLE_ISOLATED, HZ/10);

/* We are about to die and free our memory. Return now. */
if (fatal_signal_pending(current))
Expand Down

0 comments on commit d818fca

Please sign in to comment.