dm zoned: Fix random zone reclaim selection
authorDamien Le Moal <damien.lemoal@wdc.com>
Fri, 19 Jun 2020 07:49:55 +0000 (16:49 +0900)
committerMike Snitzer <snitzer@redhat.com>
Fri, 19 Jun 2020 16:28:23 +0000 (12:28 -0400)
Commit 2094045fe5b5 ("dm zoned: prefer full zones for reclaim")
modified dmz_get_rnd_zone_for_reclaim() to add a search for the buffer
zone with the heaviest weight as an optimal candidate for reclaim. This
modification uses the zone pointer variabl "last" which is set only once
and never modified as zones are scanned, resulting in the search being
inefective. Furthermore, if the selected buffer zone at the end of the
search loop is active or already locked for reclaim,
dmz_get_rnd_zone_for_reclaim() returns NULL even if other random zones
with a lesser weight can be reclaimed.

To fix the search and to guarantee that reclaim can make forward
progress, fix dmz_get_rnd_zone_for_reclaim() loop to correctly find
the buffer zone with the heaviest weight using the variable maxw_z.
Also make sure to fallback to finding the first random zone that can
be reclaimed if this best candidate zone cannot be reclaimed.

While at it, also fix the device index check to consider only random
zones, ignoring cache zones belonging to the cache device if one is
used as that device does not have a reclaim process.

Fixes: 2094045fe5b5 ("dm zoned: prefer full zones for reclaim")
Signed-off-by: Damien Le Moal <damien.lemoal@wdc.com>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
drivers/md/dm-zoned-metadata.c

index fc1329c..b40e643 100644 (file)
@@ -1950,7 +1950,7 @@ static struct dm_zone *dmz_get_rnd_zone_for_reclaim(struct dmz_metadata *zmd,
                                                    unsigned int idx, bool idle)
 {
        struct dm_zone *dzone = NULL;
-       struct dm_zone *zone, *last = NULL;
+       struct dm_zone *zone, *maxw_z = NULL;
        struct list_head *zone_list;
 
        /* If we have cache zones select from the cache zone list */
@@ -1962,18 +1962,37 @@ static struct dm_zone *dmz_get_rnd_zone_for_reclaim(struct dmz_metadata *zmd,
        } else
                zone_list = &zmd->dev[idx].map_rnd_list;
 
+       /*
+        * Find the buffer zone with the heaviest weight or the first (oldest)
+        * data zone that can be reclaimed.
+        */
        list_for_each_entry(zone, zone_list, link) {
                if (dmz_is_buf(zone)) {
                        dzone = zone->bzone;
-                       if (dzone->dev->dev_idx != idx)
+                       if (dmz_is_rnd(dzone) && dzone->dev->dev_idx != idx)
                                continue;
-                       if (!last) {
-                               last = dzone;
-                               continue;
-                       }
-                       if (last->weight < dzone->weight)
+                       if (!maxw_z || maxw_z->weight < dzone->weight)
+                               maxw_z = dzone;
+               } else {
+                       dzone = zone;
+                       if (dmz_lock_zone_reclaim(dzone))
+                               return dzone;
+               }
+       }
+
+       if (maxw_z && dmz_lock_zone_reclaim(maxw_z))
+               return maxw_z;
+
+       /*
+        * If we come here, none of the zones inspected could be locked for
+        * reclaim. Try again, being more aggressive, that is, find the
+        * first zone that can be reclaimed regardless of its weitght.
+        */
+       list_for_each_entry(zone, zone_list, link) {
+               if (dmz_is_buf(zone)) {
+                       dzone = zone->bzone;
+                       if (dmz_is_rnd(dzone) && dzone->dev->dev_idx != idx)
                                continue;
-                       dzone = last;
                } else
                        dzone = zone;
                if (dmz_lock_zone_reclaim(dzone))