diff --git a/scst/include/scst.h b/scst/include/scst.h index efe2bf5ec..23444523c 100644 --- a/scst/include/scst.h +++ b/scst/include/scst.h @@ -568,9 +568,6 @@ enum scst_exec_context { /* Set if tgt_dev has Unit Attention sense */ #define SCST_TGT_DEV_UA_PENDING 0 -/* Set if tgt_dev is RESERVED by another session */ -#define SCST_TGT_DEV_RESERVED 1 - /************************************************************* ** I/O grouping types. Changing them don't forget to change ** the corresponding *_STR values in scst_const.h! @@ -2355,14 +2352,14 @@ struct scst_dev_registrant { struct scst_device { unsigned int type; /* SCSI type of the device */ + /* Set if reserved via the SPC-2 SCSI RESERVE command. */ + struct scst_session *reserved_by; + /************************************************************* ** Dev's flags. Updates serialized by dev_lock or suspended ** activity *************************************************************/ - /* Set if dev is RESERVED */ - unsigned int dev_reserved:1; - /* Set if double reset UA is possible */ unsigned int dev_double_ua_possible:1; diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c index a8f3efdde..d6bb3b093 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -4197,8 +4197,6 @@ static int scst_alloc_add_tgt_dev(struct scst_session *sess, spin_lock_bh(&dev->dev_lock); list_add_tail(&tgt_dev->dev_tgt_dev_list_entry, &dev->dev_tgt_dev_list); - if (dev->dev_reserved) - __set_bit(SCST_TGT_DEV_RESERVED, &tgt_dev->tgt_dev_flags); spin_unlock_bh(&dev->dev_lock); head = &sess->sess_tgt_dev_list[SESS_TGT_DEV_LIST_HASH_FN(tgt_dev->lun)]; @@ -5057,16 +5055,9 @@ static void scst_clear_reservation(struct scst_tgt_dev *tgt_dev) TRACE_ENTRY(); spin_lock_bh(&dev->dev_lock); - if (dev->dev_reserved && - !test_bit(SCST_TGT_DEV_RESERVED, &tgt_dev->tgt_dev_flags)) { + if (scst_is_reservation_holder(dev, tgt_dev->sess)) { /* This is one who holds the reservation */ - struct scst_tgt_dev *tgt_dev_tmp; - list_for_each_entry(tgt_dev_tmp, &dev->dev_tgt_dev_list, - dev_tgt_dev_list_entry) { - clear_bit(SCST_TGT_DEV_RESERVED, - &tgt_dev_tmp->tgt_dev_flags); - } - dev->dev_reserved = 0; + scst_clear_dev_reservation(dev); release = 1; } spin_unlock_bh(&dev->dev_lock); @@ -7668,22 +7659,12 @@ void scst_process_reset(struct scst_device *dev, TRACE_ENTRY(); /* Clear RESERVE'ation, if necessary */ - if (dev->dev_reserved) { - list_for_each_entry(tgt_dev, &dev->dev_tgt_dev_list, - dev_tgt_dev_list_entry) { - TRACE_MGMT_DBG("Clearing RESERVE'ation for " - "tgt_dev LUN %lld", - (long long unsigned int)tgt_dev->lun); - clear_bit(SCST_TGT_DEV_RESERVED, - &tgt_dev->tgt_dev_flags); - } - dev->dev_reserved = 0; - /* - * There is no need to send RELEASE, since the device is going - * to be reset. Actually, since we can be in RESET TM - * function, it might be dangerous. - */ - } + scst_clear_dev_reservation(dev); + /* + * There is no need to send RELEASE, since the device is going + * to be reset. Actually, since we can be in RESET TM + * function, it might be dangerous. + */ dev->dev_double_ua_possible = 1; @@ -8657,10 +8638,8 @@ void scst_reassign_retained_sess_states(struct scst_session *new_sess, /** Reassign regular reservations **/ - if (dev->dev_reserved && - !test_bit(SCST_TGT_DEV_RESERVED, &old_tgt_dev->tgt_dev_flags)) { - clear_bit(SCST_TGT_DEV_RESERVED, &new_tgt_dev->tgt_dev_flags); - set_bit(SCST_TGT_DEV_RESERVED, &old_tgt_dev->tgt_dev_flags); + if (scst_is_reservation_holder(dev, old_sess)) { + scst_reserve_dev(dev, new_sess); TRACE_DBG("Reservation reassigned from old_tgt_dev %p " "to new_tgt_dev %p", old_tgt_dev, new_tgt_dev); } diff --git a/scst/src/scst_priv.h b/scst/src/scst_priv.h index 325d92cd8..4b03e6725 100644 --- a/scst/src/scst_priv.h +++ b/scst/src/scst_priv.h @@ -564,6 +564,54 @@ void scst_acn_sysfs_del(struct scst_acn *acn); #endif /* CONFIG_SCST_PROC */ +/* + * Check SPC-2 reservation state. + * Must not be called from atomic context. + */ +static inline bool scst_dev_reserved(struct scst_device *dev) +{ + return dev->reserved_by; +} + +/* + * Whether @sess holds a reservation on @dev. + * The caller may but does not have to hold dev->dev_lock. + */ +static inline bool scst_is_reservation_holder(struct scst_device *dev, + struct scst_session *sess) +{ + EXTRACHECKS_BUG_ON(sess == NULL); + return dev->reserved_by == sess; +} + +/* + * Whether another session than @sess holds a reservation on @dev. + * The caller may but does not have to hold dev->dev_lock. + */ +static inline bool scst_is_not_reservation_holder(struct scst_device *dev, + struct scst_session *sess) +{ + struct scst_session *reserved_by = dev->reserved_by; + + EXTRACHECKS_BUG_ON(sess == NULL); + return reserved_by != NULL && reserved_by != sess; +} + +static inline void scst_reserve_dev(struct scst_device *dev, + struct scst_session *sess) +{ + EXTRACHECKS_BUG_ON(sess == NULL); + dev->reserved_by = sess; +} + +static inline void scst_clear_dev_reservation(struct scst_device *dev) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) + lockdep_assert_held(&dev->dev_lock); +#endif + dev->reserved_by = NULL; +} + void scst_tgt_dev_del_free_UA(struct scst_tgt_dev *tgt_dev, struct scst_tgt_dev_UA *ua); void scst_dev_check_set_UA(struct scst_device *dev, diff --git a/scst/src/scst_targ.c b/scst/src/scst_targ.c index 8c447e256..046d0f039 100644 --- a/scst/src/scst_targ.c +++ b/scst/src/scst_targ.c @@ -2037,7 +2037,6 @@ static int scst_reserve_local(struct scst_cmd *cmd) { int res = SCST_EXEC_NOT_COMPLETED; struct scst_device *dev; - struct scst_tgt_dev *tgt_dev_tmp; TRACE_ENTRY(); @@ -2079,21 +2078,12 @@ static int scst_reserve_local(struct scst_cmd *cmd) } spin_lock_bh(&dev->dev_lock); - - if (test_bit(SCST_TGT_DEV_RESERVED, &cmd->tgt_dev->tgt_dev_flags)) { + if (scst_is_not_reservation_holder(dev, cmd->sess)) { spin_unlock_bh(&dev->dev_lock); scst_set_cmd_error_status(cmd, SAM_STAT_RESERVATION_CONFLICT); goto out_done; } - - list_for_each_entry(tgt_dev_tmp, &dev->dev_tgt_dev_list, - dev_tgt_dev_list_entry) { - if (cmd->tgt_dev != tgt_dev_tmp) - set_bit(SCST_TGT_DEV_RESERVED, - &tgt_dev_tmp->tgt_dev_flags); - } - dev->dev_reserved = 1; - + scst_reserve_dev(dev, cmd->sess); spin_unlock_bh(&dev->dev_lock); out: @@ -2113,7 +2103,6 @@ out_done: static int scst_release_local(struct scst_cmd *cmd) { int res = SCST_EXEC_NOT_COMPLETED; - struct scst_tgt_dev *tgt_dev_tmp; struct scst_device *dev; TRACE_ENTRY(); @@ -2142,7 +2131,12 @@ static int scst_release_local(struct scst_cmd *cmd) * is closed (see scst_free_tgt_dev()), but this actually doesn't * matter, so use lock and no retest for DEV_RESERVED bits again */ - if (test_bit(SCST_TGT_DEV_RESERVED, &cmd->tgt_dev->tgt_dev_flags)) { + if (scst_is_not_reservation_holder(dev, cmd->sess)) { + /* + * SPC-2 requires to report SCSI status GOOD if a RELEASE + * command fails because a reservation is held by another + * session. + */ res = SCST_EXEC_COMPLETED; cmd->status = 0; cmd->msg_status = 0; @@ -2150,13 +2144,7 @@ static int scst_release_local(struct scst_cmd *cmd) cmd->driver_status = 0; cmd->completed = 1; } else { - list_for_each_entry(tgt_dev_tmp, - &dev->dev_tgt_dev_list, - dev_tgt_dev_list_entry) { - clear_bit(SCST_TGT_DEV_RESERVED, - &tgt_dev_tmp->tgt_dev_flags); - } - dev->dev_reserved = 0; + scst_clear_dev_reservation(dev); } spin_unlock_bh(&dev->dev_lock); @@ -2205,7 +2193,7 @@ static int scst_persistent_reserve_in_local(struct scst_cmd *cmd) goto out_done; } - if (dev->dev_reserved) { + if (scst_dev_reserved(dev)) { TRACE_PR("PR command rejected, because device %s holds regular " "reservation", dev->virt_name); scst_set_cmd_error_status(cmd, SAM_STAT_RESERVATION_CONFLICT); @@ -2309,7 +2297,7 @@ static int scst_persistent_reserve_out_local(struct scst_cmd *cmd) TRACE(TRACE_SCSI, "PR action %x for '%s' (LUN %llx) from '%s'", action, dev->virt_name, tgt_dev->lun, session->initiator_name); - if (dev->dev_reserved) { + if (scst_dev_reserved(dev)) { TRACE_PR("PR command rejected, because device %s holds regular " "reservation", dev->virt_name); scst_set_cmd_error_status(cmd, SAM_STAT_RESERVATION_CONFLICT); @@ -2464,8 +2452,7 @@ int __scst_check_local_events(struct scst_cmd *cmd, bool preempt_tests_only) cmd->double_ua_possible = 1; /* Reserve check before Unit Attention */ - if (unlikely(test_bit(SCST_TGT_DEV_RESERVED, - &tgt_dev->tgt_dev_flags))) { + if (unlikely(scst_is_not_reservation_holder(dev, tgt_dev->sess))) { if ((cmd->op_flags & SCST_REG_RESERVE_ALLOWED) == 0) { scst_set_cmd_error_status(cmd, SAM_STAT_RESERVATION_CONFLICT); @@ -3308,11 +3295,9 @@ static int scst_pre_dev_done(struct scst_cmd *cmd) cmd, cmd->status); if ((cmd->cdb[0] == RESERVE) || (cmd->cdb[0] == RESERVE_10)) { - if (!test_bit(SCST_TGT_DEV_RESERVED, - &cmd->tgt_dev->tgt_dev_flags)) { - struct scst_tgt_dev *tgt_dev_tmp; - struct scst_device *dev = cmd->dev; + struct scst_device *dev = cmd->dev; + if (scst_is_reservation_holder(dev, cmd->sess)) { TRACE(TRACE_SCSI, "RESERVE failed lun=%lld, " "status=%x", (long long unsigned int)cmd->lun, @@ -3320,15 +3305,8 @@ static int scst_pre_dev_done(struct scst_cmd *cmd) PRINT_BUFF_FLAG(TRACE_SCSI, "Sense", cmd->sense, cmd->sense_valid_len); - /* Clearing the reservation */ spin_lock_bh(&dev->dev_lock); - list_for_each_entry(tgt_dev_tmp, - &dev->dev_tgt_dev_list, - dev_tgt_dev_list_entry) { - clear_bit(SCST_TGT_DEV_RESERVED, - &tgt_dev_tmp->tgt_dev_flags); - } - dev->dev_reserved = 0; + scst_clear_dev_reservation(dev); spin_unlock_bh(&dev->dev_lock); } }