On Thu, 2017-03-16 at 23:19 +0000, Bart Van Assche wrote: > On Thu, 2017-03-16 at 15:53 -0700, James Bottomley wrote: > > On Thu, 2017-03-16 at 13:56 -0700, Bart Van Assche wrote: > > > scsi_target_unblock() must unblock SCSI devices even if this > > > function > > > is called after unloading of the LLD that created these devices > > > has > > > started. This is necessary to prevent that __scsi_remove_device() > > > hangs on the SYNCHRONIZE CACHE command issued by the sd driver > > > during > > > shutdown. > > > > Your special get function misses the try_module_get(). But this is > > all > > really a bit ugly. Since the only problem is the SYNC CACHE > > triggered by device_del, isn't a better solution a new state: > > SDEV_CANCEL_BLOCK. This will make the device visible to > > scsi_get_device() and we can take it back from CANCEL_BLOCKED > > ->CANCEL when the queue is unblocked. I suspect we could also > > simply throw away the sync cache command when the device is blocked > > (the cache should destage naturally in the time it takes for the > > device to be unblocked). > > Hello James, > > The purpose of this patch series is to make sure that unblock also > occurs after module unloading has started. My understanding of > try_module_get() is that it fails once module unloading has started. > In other words, it is on purpose that try_module_get() is not > called. From the kernel module > code: > > bool try_module_get(struct module *module) > { > bool ret = true; > > if (module) { > preempt_disable(); > /* Note: here, we can fail to get a reference */ > if (likely(module_is_live(module) && > atomic_inc_not_zero(&module->refcnt) != 0)) > trace_module_get(module, _RET_IP_); > else > ret = false; > preempt_enable(); > } > return ret; > } > > static inline int module_is_live(struct module *mod) > { > return mod->state != MODULE_STATE_GOING; > } So it's better to use the module without a reference in place and take the risk that it may exit and release its code area while we're calling it? > Regarding introducing a new device state: this is something I would > like to avoid. Any code that manipulates the SCSI device state is > unnecessarily hard to modify because multiple types of state > information have been mixed up in a single state variable (blocked / > not blocked; created / running / cancel / offline). Additionally, the > SCSI device state is visible to user space. > Adding a new SCSI device state could break existing user space > applications. I'm not sure that's a real concern for a new cancel state, but it's addressable by not exposing the state to user space (it can just show up as blocked) > There is another problem with the introduction of the > SDEV_CANCEL_BLOCKED state: we do not want open() calls to succeed for > devices that are in the SDEV_DEL, SDEV_CANCEL nor for devices that > are in the SDEV_CANCEL_BLOCKED state. scsi_disk_get() in the sd > driver relies on scsi_device_get() to check the SCSI device state. If > scsi_device_get() would succeed for devices in the > SDEV_CANCEL_BLOCKED state then an explicit check for that state would > have to be added in several users of scsi_device_get(). That really doesn't matter: getting a reference via open is a race. Currently if you do it just before SDEV_CANCEL you end up in the same position: a properly refcounted open device that can't send any commands, so this doesn't add any new problems. > In other words, I think adding the SDEV_CANCEL_BLOCKED state would > result in a much more complex and also harder to test patch. Fine, here's what I thing it would look like; it seems a lot shorter and simpler to me, but if you want to pursue your approach fixing the race with module exit is a requirement. James --- diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index ba22866..b952a6a 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1248,6 +1248,13 @@ scsi_prep_state_check(struct scsi_device *sdev, struct request *req) "rejecting I/O to dead device\n"); ret = BLKPREP_KILL; break; + case SDEV_CANCEL_BLOCK: + /* + * silently kill the I/O: the only allowed thing + * is a ULD remove one (like SYNC CACHE) + */ + ret = BLKPREP_KILL; + break; case SDEV_BLOCK: case SDEV_CREATED_BLOCK: ret = BLKPREP_DEFER; @@ -2604,6 +2611,15 @@ scsi_device_set_state(struct scsi_device *sdev, enum scsi_device_state state) } break; + case SDEV_CANCEL_BLOCK: + switch (oldstate) { + case SDEV_CANCEL: + break; + default: + goto illegal; + } + break; + case SDEV_CANCEL: switch (oldstate) { case SDEV_CREATED: @@ -2612,6 +2628,7 @@ scsi_device_set_state(struct scsi_device *sdev, enum scsi_device_state state) case SDEV_OFFLINE: case SDEV_TRANSPORT_OFFLINE: case SDEV_BLOCK: + case SDEV_CANCEL_BLOCK: break; default: goto illegal; @@ -3017,6 +3034,8 @@ scsi_internal_device_unblock(struct scsi_device *sdev, sdev->sdev_state = new_state; else sdev->sdev_state = SDEV_CREATED; + } else if (sdev->sdev_state == SDEV_CANCEL_BLOCK) { + sdev->sdev_state = SDEV_CANCEL; } else if (sdev->sdev_state != SDEV_CANCEL && sdev->sdev_state != SDEV_OFFLINE) return -EINVAL; diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 82dfe07..fd1ba1d 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -39,6 +39,7 @@ static const struct { { SDEV_TRANSPORT_OFFLINE, "transport-offline" }, { SDEV_BLOCK, "blocked" }, { SDEV_CREATED_BLOCK, "created-blocked" }, + { SDEV_CANCEL_BLOCK, "blocked" }, }; const char *scsi_device_state_name(enum scsi_device_state state) @@ -972,7 +973,8 @@ sdev_store_dh_state(struct device *dev, struct device_attribute *attr, int err = -EINVAL; if (sdev->sdev_state == SDEV_CANCEL || - sdev->sdev_state == SDEV_DEL) + sdev->sdev_state == SDEV_DEL || + sdev->sdev_state == SDEV_CANCEL_BLOCK) return -ENODEV; if (!sdev->handler) { @@ -1282,7 +1284,8 @@ void __scsi_remove_device(struct scsi_device *sdev) return; if (sdev->is_visible) { - if (scsi_device_set_state(sdev, SDEV_CANCEL) != 0) + if (scsi_device_set_state(sdev, SDEV_CANCEL) != 0 + && scsi_device_set_state(sdev, SDEV_CANCEL_BLOCK) != 0) return; bsg_unregister_queue(sdev->request_queue); diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 6f22b39..a78a17a 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -48,6 +48,7 @@ enum scsi_device_state { * should be issued to the scsi * lld. */ SDEV_CREATED_BLOCK, /* same as above but for created devices */ + SDEV_CANCEL_BLOCK, /* same as above but for cancelling devices */ }; enum scsi_scan_mode {