scst: Rework RESERVE / RELEASE handling

Reduce the number of reservation state variables from (one per
device + one per session) to one per device. Introduce helper
functions for manipulating the SPC-2 reservation state.


git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@5218 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
Bart Van Assche
2014-01-21 08:09:01 +00:00
parent e2fb6f4080
commit d602302863
4 changed files with 76 additions and 74 deletions

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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,

View File

@@ -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);
}
}