| From c14a57264399efd39514a2329c591a4b954246d8 Mon Sep 17 00:00:00 2001 |
| From: Bart Van Assche <bvanassche@acm.org> |
| Date: Mon, 25 Mar 2019 10:01:46 -0700 |
| Subject: scsi: sd: Fix a race between closing an sd device and sd I/O |
| |
| From: Bart Van Assche <bvanassche@acm.org> |
| |
| commit c14a57264399efd39514a2329c591a4b954246d8 upstream. |
| |
| The scsi_end_request() function calls scsi_cmd_to_driver() indirectly and |
| hence needs the disk->private_data pointer. Avoid that that pointer is |
| cleared before all affected I/O requests have finished. This patch avoids |
| that the following crash occurs: |
| |
| Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000 |
| Call trace: |
| scsi_mq_uninit_cmd+0x1c/0x30 |
| scsi_end_request+0x7c/0x1b8 |
| scsi_io_completion+0x464/0x668 |
| scsi_finish_command+0xbc/0x160 |
| scsi_eh_flush_done_q+0x10c/0x170 |
| sas_scsi_recover_host+0x84c/0xa98 [libsas] |
| scsi_error_handler+0x140/0x5b0 |
| kthread+0x100/0x12c |
| ret_from_fork+0x10/0x18 |
| |
| Cc: Christoph Hellwig <hch@lst.de> |
| Cc: Ming Lei <ming.lei@redhat.com> |
| Cc: Hannes Reinecke <hare@suse.com> |
| Cc: Johannes Thumshirn <jthumshirn@suse.de> |
| Cc: Jason Yan <yanaijie@huawei.com> |
| Cc: <stable@vger.kernel.org> |
| Signed-off-by: Bart Van Assche <bvanassche@acm.org> |
| Reported-by: Jason Yan <yanaijie@huawei.com> |
| Reviewed-by: Christoph Hellwig <hch@lst.de> |
| Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/scsi/sd.c | 19 +++++++++++++------ |
| 1 file changed, 13 insertions(+), 6 deletions(-) |
| |
| --- a/drivers/scsi/sd.c |
| +++ b/drivers/scsi/sd.c |
| @@ -1420,11 +1420,6 @@ static void sd_release(struct gendisk *d |
| scsi_set_medium_removal(sdev, SCSI_REMOVAL_ALLOW); |
| } |
| |
| - /* |
| - * XXX and what if there are packets in flight and this close() |
| - * XXX is followed by a "rmmod sd_mod"? |
| - */ |
| - |
| scsi_disk_put(sdkp); |
| } |
| |
| @@ -3521,11 +3516,23 @@ static void scsi_disk_release(struct dev |
| { |
| struct scsi_disk *sdkp = to_scsi_disk(dev); |
| struct gendisk *disk = sdkp->disk; |
| - |
| + struct request_queue *q = disk->queue; |
| + |
| spin_lock(&sd_index_lock); |
| ida_remove(&sd_index_ida, sdkp->index); |
| spin_unlock(&sd_index_lock); |
| |
| + /* |
| + * Wait until all requests that are in progress have completed. |
| + * This is necessary to avoid that e.g. scsi_end_request() crashes |
| + * due to clearing the disk->private_data pointer. Wait from inside |
| + * scsi_disk_release() instead of from sd_release() to avoid that |
| + * freezing and unfreezing the request queue affects user space I/O |
| + * in case multiple processes open a /dev/sd... node concurrently. |
| + */ |
| + blk_mq_freeze_queue(q); |
| + blk_mq_unfreeze_queue(q); |
| + |
| disk->private_data = NULL; |
| put_disk(disk); |
| put_device(&sdkp->device->sdev_gendev); |