zbd: use zone_lock to lock a zone
authorNaohiro Aota <naohiro.aota@wdc.com>
Fri, 28 Feb 2020 07:12:45 +0000 (16:12 +0900)
committerJens Axboe <axboe@kernel.dk>
Wed, 18 Mar 2020 02:05:54 +0000 (20:05 -0600)
commit 6f0c608564c3 ("zbd: Avoid async I/O multi-job workload deadlock")
introduced io_u_quiesce() when it failed to lock a zone to avoid deadlock.
This situation can happen on the other locking place like
zbd_convert_to_open_zone(). Thus, introduce common helper "zone_lock" to
lock a zone.

Reviewed-by: Damien Le Moal <damien.lemoal@wdc.com>
Tested-by: Damien Le Moal <damien.lemoal@wdc.com>
Signed-off-by: Naohiro Aota <naohiro.aota@wdc.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
zbd.c

diff --git a/zbd.c b/zbd.c
index 18a55ea46ef9f973c2602e135d2f81534a24de39..b2d9424972da3d85e47c6fa80120987a7519a85b 100644 (file)
--- a/zbd.c
+++ b/zbd.c
@@ -927,6 +927,24 @@ static void zbd_close_zone(struct thread_data *td, const struct fio_file *f,
        f->zbd_info->zone_info[zone_idx].open = 0;
 }
 
        f->zbd_info->zone_info[zone_idx].open = 0;
 }
 
+static void zone_lock(struct thread_data *td, struct fio_zone_info *z)
+{
+       /*
+        * Lock the io_u target zone. The zone will be unlocked if io_u offset
+        * is changed or when io_u completes and zbd_put_io() executed.
+        * To avoid multiple jobs doing asynchronous I/Os from deadlocking each
+        * other waiting for zone locks when building an io_u batch, first
+        * only trylock the zone. If the zone is already locked by another job,
+        * process the currently queued I/Os so that I/O progress is made and
+        * zones unlocked.
+        */
+       if (pthread_mutex_trylock(&z->mutex) != 0) {
+               if (!td_ioengine_flagged(td, FIO_SYNCIO))
+                       io_u_quiesce(td);
+               pthread_mutex_lock(&z->mutex);
+       }
+}
+
 /*
  * Modify the offset of an I/O unit that does not refer to an open zone such
  * that it refers to an open zone. Close an open zone and open a new zone if
 /*
  * Modify the offset of an I/O unit that does not refer to an open zone such
  * that it refers to an open zone. Close an open zone and open a new zone if
@@ -969,7 +987,7 @@ static struct fio_zone_info *zbd_convert_to_open_zone(struct thread_data *td,
        for (;;) {
                z = &f->zbd_info->zone_info[zone_idx];
 
        for (;;) {
                z = &f->zbd_info->zone_info[zone_idx];
 
-               pthread_mutex_lock(&z->mutex);
+               zone_lock(td, z);
                pthread_mutex_lock(&f->zbd_info->mutex);
                if (td->o.max_open_zones == 0)
                        goto examine_zone;
                pthread_mutex_lock(&f->zbd_info->mutex);
                if (td->o.max_open_zones == 0)
                        goto examine_zone;
@@ -1017,7 +1035,7 @@ examine_zone:
                        z = &f->zbd_info->zone_info[zone_idx];
                }
                assert(is_valid_offset(f, z->start));
                        z = &f->zbd_info->zone_info[zone_idx];
                }
                assert(is_valid_offset(f, z->start));
-               pthread_mutex_lock(&z->mutex);
+               zone_lock(td, z);
                if (z->open)
                        continue;
                if (zbd_open_zone(td, io_u, zone_idx))
                if (z->open)
                        continue;
                if (zbd_open_zone(td, io_u, zone_idx))
@@ -1035,7 +1053,7 @@ examine_zone:
 
                z = &f->zbd_info->zone_info[zone_idx];
 
 
                z = &f->zbd_info->zone_info[zone_idx];
 
-               pthread_mutex_lock(&z->mutex);
+               zone_lock(td, z);
                if (z->wp + min_bs <= (z+1)->start)
                        goto out;
                pthread_mutex_lock(&f->zbd_info->mutex);
                if (z->wp + min_bs <= (z+1)->start)
                        goto out;
                pthread_mutex_lock(&f->zbd_info->mutex);
@@ -1321,20 +1339,7 @@ enum io_u_action zbd_adjust_block(struct thread_data *td, struct io_u *io_u)
 
        zbd_check_swd(f);
 
 
        zbd_check_swd(f);
 
-       /*
-        * Lock the io_u target zone. The zone will be unlocked if io_u offset
-        * is changed or when io_u completes and zbd_put_io() executed.
-        * To avoid multiple jobs doing asynchronous I/Os from deadlocking each
-        * other waiting for zone locks when building an io_u batch, first
-        * only trylock the zone. If the zone is already locked by another job,
-        * process the currently queued I/Os so that I/O progress is made and
-        * zones unlocked.
-        */
-       if (pthread_mutex_trylock(&zb->mutex) != 0) {
-               if (!td_ioengine_flagged(td, FIO_SYNCIO))
-                       io_u_quiesce(td);
-               pthread_mutex_lock(&zb->mutex);
-       }
+       zone_lock(td, zb);
 
        switch (io_u->ddir) {
        case DDIR_READ:
 
        switch (io_u->ddir) {
        case DDIR_READ: