scsi: sd_zbc: Write unlock zone from sd_uninit_cmnd()
authorDamien Le Moal <damien.lemoal@wdc.com>
Wed, 9 Aug 2017 03:00:22 +0000 (12:00 +0900)
committerMartin K. Petersen <martin.petersen@oracle.com>
Thu, 17 Aug 2017 00:01:31 +0000 (20:01 -0400)
Releasing a zone write lock only when the write commnand that acquired
the lock completes can cause deadlocks due to potential command
reordering if the lock owning request is requeued and not executed. This
problem exists only with the scsi-mq path as, unlike the legacy path,
requests are moved out of the dispatch queue before being prepared and
so before locking a zone for a write command.

Since sd_uninit_cmnd() is now always called when a request is requeued,
call sd_zbc_write_unlock_zone() from that function for write requests
that acquired a zone lock instead of from sd_done(). Acquisition of a zone
lock by a write command is indicated using the new command
flag SCMD_ZONE_WRITE_LOCK.

Signed-off-by: Damien Le Moal <damien.lemoal@wdc.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Bart Van Assche <Bart.VanAssche@wdc.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/sd.c
drivers/scsi/sd_zbc.c
include/scsi/scsi_cmnd.h

index bea36adeee178ab57bced3591b0969bc2d973264..e2647f2d44304b0ded65ebb0f55d629c7289d0b7 100644 (file)
@@ -1277,6 +1277,9 @@ static void sd_uninit_command(struct scsi_cmnd *SCpnt)
 {
        struct request *rq = SCpnt->request;
 
+       if (SCpnt->flags & SCMD_ZONE_WRITE_LOCK)
+               sd_zbc_write_unlock_zone(SCpnt);
+
        if (rq->rq_flags & RQF_SPECIAL_PAYLOAD)
                __free_page(rq->special_vec.bv_page);
 
index 96855df9f49ddf3756b8fc6ee98b127727d9bbeb..8aa54779aac1b4e6112d86dc536dd14b1522d6eb 100644 (file)
@@ -294,6 +294,9 @@ int sd_zbc_write_lock_zone(struct scsi_cmnd *cmd)
            test_and_set_bit(zno, sdkp->zones_wlock))
                return BLKPREP_DEFER;
 
+       WARN_ON_ONCE(cmd->flags & SCMD_ZONE_WRITE_LOCK);
+       cmd->flags |= SCMD_ZONE_WRITE_LOCK;
+
        return BLKPREP_OK;
 }
 
@@ -302,9 +305,10 @@ void sd_zbc_write_unlock_zone(struct scsi_cmnd *cmd)
        struct request *rq = cmd->request;
        struct scsi_disk *sdkp = scsi_disk(rq->rq_disk);
 
-       if (sdkp->zones_wlock) {
+       if (sdkp->zones_wlock && cmd->flags & SCMD_ZONE_WRITE_LOCK) {
                unsigned int zno = sd_zbc_zone_no(sdkp, blk_rq_pos(rq));
                WARN_ON_ONCE(!test_bit(zno, sdkp->zones_wlock));
+               cmd->flags &= ~SCMD_ZONE_WRITE_LOCK;
                clear_bit_unlock(zno, sdkp->zones_wlock);
                smp_mb__after_atomic();
        }
@@ -335,9 +339,6 @@ void sd_zbc_complete(struct scsi_cmnd *cmd,
        case REQ_OP_WRITE_ZEROES:
        case REQ_OP_WRITE_SAME:
 
-               /* Unlock the zone */
-               sd_zbc_write_unlock_zone(cmd);
-
                if (result &&
                    sshdr->sense_key == ILLEGAL_REQUEST &&
                    sshdr->asc == 0x21)
index a1266d318c855dec12dbb41a6bb1ab9e4dc27f08..6af198d8120b098f31d674f15584ec7114832743 100644 (file)
@@ -57,6 +57,7 @@ struct scsi_pointer {
 /* for scmd->flags */
 #define SCMD_TAGGED            (1 << 0)
 #define SCMD_UNCHECKED_ISA_DMA (1 << 1)
+#define SCMD_ZONE_WRITE_LOCK   (1 << 2)
 
 struct scsi_cmnd {
        struct scsi_request req;