Message ID | 20190801021752.4986-4-david@fromorbit.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | mm, xfs: non-blocking inode reclaim | expand |
On 1.08.19 г. 5:17 ч., Dave Chinner wrote: > From: Dave Chinner <dchinner@redhat.com> > > Start to clean up the shrinker code by factoring out the calculation > that determines how much work to do. This separates the calculation > from clamping and other adjustments that are done before the > shrinker work is run. > > Also convert the calculation for the amount of work to be done to > use 64 bit logic so we don't have to keep jumping through hoops to > keep calculations within 32 bits on 32 bit systems. > > Signed-off-by: Dave Chinner <dchinner@redhat.com> > --- > mm/vmscan.c | 74 ++++++++++++++++++++++++++++++++++------------------- > 1 file changed, 47 insertions(+), 27 deletions(-) > > diff --git a/mm/vmscan.c b/mm/vmscan.c > index ae3035fe94bc..b7472953b0e6 100644 > --- a/mm/vmscan.c > +++ b/mm/vmscan.c > @@ -464,13 +464,45 @@ EXPORT_SYMBOL(unregister_shrinker); > > #define SHRINK_BATCH 128 > > +/* > + * Calculate the number of new objects to scan this time around. Return > + * the work to be done. If there are freeable objects, return that number in > + * @freeable_objects. > + */ > +static int64_t shrink_scan_count(struct shrink_control *shrinkctl, > + struct shrinker *shrinker, int priority, > + int64_t *freeable_objects) nit: make the return parm definition also uin64_t, also we have u64 types. > +{ > + uint64_t delta; > + uint64_t freeable; > + > + freeable = shrinker->count_objects(shrinker, shrinkctl); > + if (freeable == 0 || freeable == SHRINK_EMPTY) > + return freeable; > + > + if (shrinker->seeks) { > + delta = freeable >> (priority - 2); > + do_div(delta, shrinker->seeks); a comment about the reasoning behind this calculation would be nice. > + } else { > + /* > + * These objects don't require any IO to create. Trim > + * them aggressively under memory pressure to keep > + * them from causing refetches in the IO caches. > + */ > + delta = freeable / 2; > + } > + > + *freeable_objects = freeable; > + return delta > 0 ? delta : 0; > +} > + > static unsigned long do_shrink_slab(struct shrink_control *shrinkctl, > struct shrinker *shrinker, int priority) > { > unsigned long freed = 0; > - unsigned long long delta; > long total_scan; > - long freeable; > + int64_t freeable_objects = 0; > + int64_t scan_count; why int and not uint64 ? We can never have negative object count, right? > long nr; > long new_nr; > int nid = shrinkctl->nid; > @@ -481,9 +513,10 @@ static unsigned long do_shrink_slab(struct shrink_control *shrinkctl, > if (!(shrinker->flags & SHRINKER_NUMA_AWARE)) > nid = 0; > > - freeable = shrinker->count_objects(shrinker, shrinkctl); > - if (freeable == 0 || freeable == SHRINK_EMPTY) > - return freeable; > + scan_count = shrink_scan_count(shrinkctl, shrinker, priority, > + &freeable_objects); > + if (scan_count == 0 || scan_count == SHRINK_EMPTY) > + return scan_count; > > /* > * copy the current shrinker scan count into a local variable > @@ -492,25 +525,11 @@ static unsigned long do_shrink_slab(struct shrink_control *shrinkctl, > */ > nr = atomic_long_xchg(&shrinker->nr_deferred[nid], 0); > > - total_scan = nr; > - if (shrinker->seeks) { > - delta = freeable >> priority; > - delta *= 4; > - do_div(delta, shrinker->seeks); > - } else { > - /* > - * These objects don't require any IO to create. Trim > - * them aggressively under memory pressure to keep > - * them from causing refetches in the IO caches. > - */ > - delta = freeable / 2; > - } > - > - total_scan += delta; > + total_scan = nr + scan_count; > if (total_scan < 0) { > pr_err("shrink_slab: %pS negative objects to delete nr=%ld\n", > shrinker->scan_objects, total_scan); > - total_scan = freeable; > + total_scan = scan_count; > next_deferred = nr; > } else > next_deferred = total_scan; > @@ -527,19 +546,20 @@ static unsigned long do_shrink_slab(struct shrink_control *shrinkctl, > * Hence only allow the shrinker to scan the entire cache when > * a large delta change is calculated directly. > */ > - if (delta < freeable / 4) > - total_scan = min(total_scan, freeable / 2); > + if (scan_count < freeable_objects / 4) > + total_scan = min_t(long, total_scan, freeable_objects / 2); > > /* > * Avoid risking looping forever due to too large nr value: > * never try to free more than twice the estimate number of > * freeable entries. > */ > - if (total_scan > freeable * 2) > - total_scan = freeable * 2; > + if (total_scan > freeable_objects * 2) > + total_scan = freeable_objects * 2; > > trace_mm_shrink_slab_start(shrinker, shrinkctl, nr, > - freeable, delta, total_scan, priority); > + freeable_objects, scan_count, > + total_scan, priority); > > /* > * If the shrinker can't run (e.g. due to gfp_mask constraints), then > @@ -564,7 +584,7 @@ static unsigned long do_shrink_slab(struct shrink_control *shrinkctl, > * possible. > */ > while (total_scan >= batch_size || > - total_scan >= freeable) { > + total_scan >= freeable_objects) { > unsigned long ret; > unsigned long nr_to_scan = min(batch_size, total_scan); > >
On Thu, Aug 01, 2019 at 12:17:31PM +1000, Dave Chinner wrote: > From: Dave Chinner <dchinner@redhat.com> > > Start to clean up the shrinker code by factoring out the calculation > that determines how much work to do. This separates the calculation > from clamping and other adjustments that are done before the > shrinker work is run. > > Also convert the calculation for the amount of work to be done to > use 64 bit logic so we don't have to keep jumping through hoops to > keep calculations within 32 bits on 32 bit systems. > > Signed-off-by: Dave Chinner <dchinner@redhat.com> > --- > mm/vmscan.c | 74 ++++++++++++++++++++++++++++++++++------------------- > 1 file changed, 47 insertions(+), 27 deletions(-) > > diff --git a/mm/vmscan.c b/mm/vmscan.c > index ae3035fe94bc..b7472953b0e6 100644 > --- a/mm/vmscan.c > +++ b/mm/vmscan.c > @@ -464,13 +464,45 @@ EXPORT_SYMBOL(unregister_shrinker); > > #define SHRINK_BATCH 128 > > +/* > + * Calculate the number of new objects to scan this time around. Return > + * the work to be done. If there are freeable objects, return that number in > + * @freeable_objects. > + */ > +static int64_t shrink_scan_count(struct shrink_control *shrinkctl, > + struct shrinker *shrinker, int priority, > + int64_t *freeable_objects) > +{ > + uint64_t delta; > + uint64_t freeable; > + > + freeable = shrinker->count_objects(shrinker, shrinkctl); > + if (freeable == 0 || freeable == SHRINK_EMPTY) > + return freeable; > + > + if (shrinker->seeks) { > + delta = freeable >> (priority - 2); > + do_div(delta, shrinker->seeks); > + } else { > + /* > + * These objects don't require any IO to create. Trim > + * them aggressively under memory pressure to keep > + * them from causing refetches in the IO caches. > + */ > + delta = freeable / 2; > + } > + > + *freeable_objects = freeable; > + return delta > 0 ? delta : 0; I see Nikolay had some similar comments but FWIW delta is unsigned so I'm not sure the point of the > 0 check. Brian > +} > + > static unsigned long do_shrink_slab(struct shrink_control *shrinkctl, > struct shrinker *shrinker, int priority) > { > unsigned long freed = 0; > - unsigned long long delta; > long total_scan; > - long freeable; > + int64_t freeable_objects = 0; > + int64_t scan_count; > long nr; > long new_nr; > int nid = shrinkctl->nid; > @@ -481,9 +513,10 @@ static unsigned long do_shrink_slab(struct shrink_control *shrinkctl, > if (!(shrinker->flags & SHRINKER_NUMA_AWARE)) > nid = 0; > > - freeable = shrinker->count_objects(shrinker, shrinkctl); > - if (freeable == 0 || freeable == SHRINK_EMPTY) > - return freeable; > + scan_count = shrink_scan_count(shrinkctl, shrinker, priority, > + &freeable_objects); > + if (scan_count == 0 || scan_count == SHRINK_EMPTY) > + return scan_count; > > /* > * copy the current shrinker scan count into a local variable > @@ -492,25 +525,11 @@ static unsigned long do_shrink_slab(struct shrink_control *shrinkctl, > */ > nr = atomic_long_xchg(&shrinker->nr_deferred[nid], 0); > > - total_scan = nr; > - if (shrinker->seeks) { > - delta = freeable >> priority; > - delta *= 4; > - do_div(delta, shrinker->seeks); > - } else { > - /* > - * These objects don't require any IO to create. Trim > - * them aggressively under memory pressure to keep > - * them from causing refetches in the IO caches. > - */ > - delta = freeable / 2; > - } > - > - total_scan += delta; > + total_scan = nr + scan_count; > if (total_scan < 0) { > pr_err("shrink_slab: %pS negative objects to delete nr=%ld\n", > shrinker->scan_objects, total_scan); > - total_scan = freeable; > + total_scan = scan_count; Why the change from the (now) freeable_objects value to scan_count? Brian > next_deferred = nr; > } else > next_deferred = total_scan; > @@ -527,19 +546,20 @@ static unsigned long do_shrink_slab(struct shrink_control *shrinkctl, > * Hence only allow the shrinker to scan the entire cache when > * a large delta change is calculated directly. > */ > - if (delta < freeable / 4) > - total_scan = min(total_scan, freeable / 2); > + if (scan_count < freeable_objects / 4) > + total_scan = min_t(long, total_scan, freeable_objects / 2); > > /* > * Avoid risking looping forever due to too large nr value: > * never try to free more than twice the estimate number of > * freeable entries. > */ > - if (total_scan > freeable * 2) > - total_scan = freeable * 2; > + if (total_scan > freeable_objects * 2) > + total_scan = freeable_objects * 2; > > trace_mm_shrink_slab_start(shrinker, shrinkctl, nr, > - freeable, delta, total_scan, priority); > + freeable_objects, scan_count, > + total_scan, priority); > > /* > * If the shrinker can't run (e.g. due to gfp_mask constraints), then > @@ -564,7 +584,7 @@ static unsigned long do_shrink_slab(struct shrink_control *shrinkctl, > * possible. > */ > while (total_scan >= batch_size || > - total_scan >= freeable) { > + total_scan >= freeable_objects) { > unsigned long ret; > unsigned long nr_to_scan = min(batch_size, total_scan); > > -- > 2.22.0 >
On Fri, Aug 02, 2019 at 06:08:37PM +0300, Nikolay Borisov wrote: > > > On 1.08.19 г. 5:17 ч., Dave Chinner wrote: > > From: Dave Chinner <dchinner@redhat.com> > > > > Start to clean up the shrinker code by factoring out the calculation > > that determines how much work to do. This separates the calculation > > from clamping and other adjustments that are done before the > > shrinker work is run. > > > > Also convert the calculation for the amount of work to be done to > > use 64 bit logic so we don't have to keep jumping through hoops to > > keep calculations within 32 bits on 32 bit systems. > > > > Signed-off-by: Dave Chinner <dchinner@redhat.com> > > --- > > mm/vmscan.c | 74 ++++++++++++++++++++++++++++++++++------------------- > > 1 file changed, 47 insertions(+), 27 deletions(-) > > > > diff --git a/mm/vmscan.c b/mm/vmscan.c > > index ae3035fe94bc..b7472953b0e6 100644 > > --- a/mm/vmscan.c > > +++ b/mm/vmscan.c > > @@ -464,13 +464,45 @@ EXPORT_SYMBOL(unregister_shrinker); > > > > #define SHRINK_BATCH 128 > > > > +/* > > + * Calculate the number of new objects to scan this time around. Return > > + * the work to be done. If there are freeable objects, return that number in > > + * @freeable_objects. > > + */ > > +static int64_t shrink_scan_count(struct shrink_control *shrinkctl, > > + struct shrinker *shrinker, int priority, > > + int64_t *freeable_objects) > > nit: make the return parm definition also uin64_t, also we have u64 types. SHRINK_EMPTY is actually a negative number (-2), and it gets whacked back into a signed long value in the caller. So returning a signed integer is actually correct. > > +{ > > + uint64_t delta; > > + uint64_t freeable; > > + > > + freeable = shrinker->count_objects(shrinker, shrinkctl); > > + if (freeable == 0 || freeable == SHRINK_EMPTY) > > + return freeable; > > + > > + if (shrinker->seeks) { > > + delta = freeable >> (priority - 2); > > + do_div(delta, shrinker->seeks); > > a comment about the reasoning behind this calculation would be nice. I'm just moving code here. The reason for this calculation requires an awfully long description that isn't actually appropriate here or in this patch set. If there should be any comment describing how shrinker work biasing should be configured, it needs to be in include/linux/shrinker.h around the definition of DEFAULT_SEEKS and shrinker->seeks as this code requires shrinker->seeks to be configured appropriately by the code that registers the shrinker. > > static unsigned long do_shrink_slab(struct shrink_control *shrinkctl, > > struct shrinker *shrinker, int priority) > > { > > unsigned long freed = 0; > > - unsigned long long delta; > > long total_scan; > > - long freeable; > > + int64_t freeable_objects = 0; > > + int64_t scan_count; > > why int and not uint64 ? We can never have negative object count, right? SHRINK_STOP, SHRINK_EMPTY are negative numbers, and the higher level interface uses longs, not unsigned longs. So we have to treat numbers greater than LONG_MAX as invalid for object/scan counts. Cheers, Dave.
diff --git a/mm/vmscan.c b/mm/vmscan.c index ae3035fe94bc..b7472953b0e6 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -464,13 +464,45 @@ EXPORT_SYMBOL(unregister_shrinker); #define SHRINK_BATCH 128 +/* + * Calculate the number of new objects to scan this time around. Return + * the work to be done. If there are freeable objects, return that number in + * @freeable_objects. + */ +static int64_t shrink_scan_count(struct shrink_control *shrinkctl, + struct shrinker *shrinker, int priority, + int64_t *freeable_objects) +{ + uint64_t delta; + uint64_t freeable; + + freeable = shrinker->count_objects(shrinker, shrinkctl); + if (freeable == 0 || freeable == SHRINK_EMPTY) + return freeable; + + if (shrinker->seeks) { + delta = freeable >> (priority - 2); + do_div(delta, shrinker->seeks); + } else { + /* + * These objects don't require any IO to create. Trim + * them aggressively under memory pressure to keep + * them from causing refetches in the IO caches. + */ + delta = freeable / 2; + } + + *freeable_objects = freeable; + return delta > 0 ? delta : 0; +} + static unsigned long do_shrink_slab(struct shrink_control *shrinkctl, struct shrinker *shrinker, int priority) { unsigned long freed = 0; - unsigned long long delta; long total_scan; - long freeable; + int64_t freeable_objects = 0; + int64_t scan_count; long nr; long new_nr; int nid = shrinkctl->nid; @@ -481,9 +513,10 @@ static unsigned long do_shrink_slab(struct shrink_control *shrinkctl, if (!(shrinker->flags & SHRINKER_NUMA_AWARE)) nid = 0; - freeable = shrinker->count_objects(shrinker, shrinkctl); - if (freeable == 0 || freeable == SHRINK_EMPTY) - return freeable; + scan_count = shrink_scan_count(shrinkctl, shrinker, priority, + &freeable_objects); + if (scan_count == 0 || scan_count == SHRINK_EMPTY) + return scan_count; /* * copy the current shrinker scan count into a local variable @@ -492,25 +525,11 @@ static unsigned long do_shrink_slab(struct shrink_control *shrinkctl, */ nr = atomic_long_xchg(&shrinker->nr_deferred[nid], 0); - total_scan = nr; - if (shrinker->seeks) { - delta = freeable >> priority; - delta *= 4; - do_div(delta, shrinker->seeks); - } else { - /* - * These objects don't require any IO to create. Trim - * them aggressively under memory pressure to keep - * them from causing refetches in the IO caches. - */ - delta = freeable / 2; - } - - total_scan += delta; + total_scan = nr + scan_count; if (total_scan < 0) { pr_err("shrink_slab: %pS negative objects to delete nr=%ld\n", shrinker->scan_objects, total_scan); - total_scan = freeable; + total_scan = scan_count; next_deferred = nr; } else next_deferred = total_scan; @@ -527,19 +546,20 @@ static unsigned long do_shrink_slab(struct shrink_control *shrinkctl, * Hence only allow the shrinker to scan the entire cache when * a large delta change is calculated directly. */ - if (delta < freeable / 4) - total_scan = min(total_scan, freeable / 2); + if (scan_count < freeable_objects / 4) + total_scan = min_t(long, total_scan, freeable_objects / 2); /* * Avoid risking looping forever due to too large nr value: * never try to free more than twice the estimate number of * freeable entries. */ - if (total_scan > freeable * 2) - total_scan = freeable * 2; + if (total_scan > freeable_objects * 2) + total_scan = freeable_objects * 2; trace_mm_shrink_slab_start(shrinker, shrinkctl, nr, - freeable, delta, total_scan, priority); + freeable_objects, scan_count, + total_scan, priority); /* * If the shrinker can't run (e.g. due to gfp_mask constraints), then @@ -564,7 +584,7 @@ static unsigned long do_shrink_slab(struct shrink_control *shrinkctl, * possible. */ while (total_scan >= batch_size || - total_scan >= freeable) { + total_scan >= freeable_objects) { unsigned long ret; unsigned long nr_to_scan = min(batch_size, total_scan);