Commit a9fdc0b9 authored by Nicholas Bellinger's avatar Nicholas Bellinger Committed by Luis Henriques

target: Avoid dropping AllRegistrants reservation during unregister

commit 6c3c9baa upstream.

This patch fixes an issue with AllRegistrants reservations where
an unregister operation by the I_T nexus reservation holder would
incorrectly drop the reservation, instead of waiting until the
last active I_T nexus is unregistered as per SPC-4.

This includes updating __core_scsi3_complete_pro_release() to reset
dev->dev_pr_res_holder with another pr_reg for this special case,
as well as a new 'unreg' parameter to determine when the release
is occuring from an implicit unregister, vs. explicit RELEASE.

It also adds special handling in core_scsi3_free_pr_reg_from_nacl()
to release the left-over pr_res_holder, now that pr_reg is deleted
from pr_reg_list within __core_scsi3_complete_pro_release().
Reported-by: default avatarIlias Tsitsimpis <i.tsitsimpis@gmail.com>
Cc: James Bottomley <James.Bottomley@HansenPartnership.com>
Signed-off-by: default avatarNicholas Bellinger <nab@linux-iscsi.org>
Signed-off-by: default avatarLuis Henriques <luis.henriques@canonical.com>
parent 0dfc00b6
...@@ -76,7 +76,7 @@ enum preempt_type { ...@@ -76,7 +76,7 @@ enum preempt_type {
}; };
static void __core_scsi3_complete_pro_release(struct se_device *, struct se_node_acl *, static void __core_scsi3_complete_pro_release(struct se_device *, struct se_node_acl *,
struct t10_pr_registration *, int); struct t10_pr_registration *, int, int);
static sense_reason_t static sense_reason_t
target_scsi2_reservation_check(struct se_cmd *cmd) target_scsi2_reservation_check(struct se_cmd *cmd)
...@@ -1186,7 +1186,7 @@ static int core_scsi3_check_implicit_release( ...@@ -1186,7 +1186,7 @@ static int core_scsi3_check_implicit_release(
* service action with the SERVICE ACTION RESERVATION KEY * service action with the SERVICE ACTION RESERVATION KEY
* field set to zero (see 5.7.11.3). * field set to zero (see 5.7.11.3).
*/ */
__core_scsi3_complete_pro_release(dev, nacl, pr_reg, 0); __core_scsi3_complete_pro_release(dev, nacl, pr_reg, 0, 1);
ret = 1; ret = 1;
/* /*
* For 'All Registrants' reservation types, all existing * For 'All Registrants' reservation types, all existing
...@@ -1228,7 +1228,8 @@ static void __core_scsi3_free_registration( ...@@ -1228,7 +1228,8 @@ static void __core_scsi3_free_registration(
pr_reg->pr_reg_deve->def_pr_registered = 0; pr_reg->pr_reg_deve->def_pr_registered = 0;
pr_reg->pr_reg_deve->pr_res_key = 0; pr_reg->pr_reg_deve->pr_res_key = 0;
list_del(&pr_reg->pr_reg_list); if (!list_empty(&pr_reg->pr_reg_list))
list_del(&pr_reg->pr_reg_list);
/* /*
* Caller accessing *pr_reg using core_scsi3_locate_pr_reg(), * Caller accessing *pr_reg using core_scsi3_locate_pr_reg(),
* so call core_scsi3_put_pr_reg() to decrement our reference. * so call core_scsi3_put_pr_reg() to decrement our reference.
...@@ -1280,6 +1281,7 @@ void core_scsi3_free_pr_reg_from_nacl( ...@@ -1280,6 +1281,7 @@ void core_scsi3_free_pr_reg_from_nacl(
{ {
struct t10_reservation *pr_tmpl = &dev->t10_pr; struct t10_reservation *pr_tmpl = &dev->t10_pr;
struct t10_pr_registration *pr_reg, *pr_reg_tmp, *pr_res_holder; struct t10_pr_registration *pr_reg, *pr_reg_tmp, *pr_res_holder;
bool free_reg = false;
/* /*
* If the passed se_node_acl matches the reservation holder, * If the passed se_node_acl matches the reservation holder,
* release the reservation. * release the reservation.
...@@ -1287,13 +1289,18 @@ void core_scsi3_free_pr_reg_from_nacl( ...@@ -1287,13 +1289,18 @@ void core_scsi3_free_pr_reg_from_nacl(
spin_lock(&dev->dev_reservation_lock); spin_lock(&dev->dev_reservation_lock);
pr_res_holder = dev->dev_pr_res_holder; pr_res_holder = dev->dev_pr_res_holder;
if ((pr_res_holder != NULL) && if ((pr_res_holder != NULL) &&
(pr_res_holder->pr_reg_nacl == nacl)) (pr_res_holder->pr_reg_nacl == nacl)) {
__core_scsi3_complete_pro_release(dev, nacl, pr_res_holder, 0); __core_scsi3_complete_pro_release(dev, nacl, pr_res_holder, 0, 1);
free_reg = true;
}
spin_unlock(&dev->dev_reservation_lock); spin_unlock(&dev->dev_reservation_lock);
/* /*
* Release any registration associated with the struct se_node_acl. * Release any registration associated with the struct se_node_acl.
*/ */
spin_lock(&pr_tmpl->registration_lock); spin_lock(&pr_tmpl->registration_lock);
if (pr_res_holder && free_reg)
__core_scsi3_free_registration(dev, pr_res_holder, NULL, 0);
list_for_each_entry_safe(pr_reg, pr_reg_tmp, list_for_each_entry_safe(pr_reg, pr_reg_tmp,
&pr_tmpl->registration_list, pr_reg_list) { &pr_tmpl->registration_list, pr_reg_list) {
...@@ -1316,7 +1323,7 @@ void core_scsi3_free_all_registrations( ...@@ -1316,7 +1323,7 @@ void core_scsi3_free_all_registrations(
if (pr_res_holder != NULL) { if (pr_res_holder != NULL) {
struct se_node_acl *pr_res_nacl = pr_res_holder->pr_reg_nacl; struct se_node_acl *pr_res_nacl = pr_res_holder->pr_reg_nacl;
__core_scsi3_complete_pro_release(dev, pr_res_nacl, __core_scsi3_complete_pro_release(dev, pr_res_nacl,
pr_res_holder, 0); pr_res_holder, 0, 0);
} }
spin_unlock(&dev->dev_reservation_lock); spin_unlock(&dev->dev_reservation_lock);
...@@ -2126,13 +2133,13 @@ core_scsi3_emulate_pro_register(struct se_cmd *cmd, u64 res_key, u64 sa_res_key, ...@@ -2126,13 +2133,13 @@ core_scsi3_emulate_pro_register(struct se_cmd *cmd, u64 res_key, u64 sa_res_key,
/* /*
* sa_res_key=0 Unregister Reservation Key for registered I_T Nexus. * sa_res_key=0 Unregister Reservation Key for registered I_T Nexus.
*/ */
pr_holder = core_scsi3_check_implicit_release( type = pr_reg->pr_res_type;
cmd->se_dev, pr_reg); pr_holder = core_scsi3_check_implicit_release(cmd->se_dev,
pr_reg);
if (pr_holder < 0) { if (pr_holder < 0) {
ret = TCM_RESERVATION_CONFLICT; ret = TCM_RESERVATION_CONFLICT;
goto out; goto out;
} }
type = pr_reg->pr_res_type;
spin_lock(&pr_tmpl->registration_lock); spin_lock(&pr_tmpl->registration_lock);
/* /*
...@@ -2406,23 +2413,59 @@ static void __core_scsi3_complete_pro_release( ...@@ -2406,23 +2413,59 @@ static void __core_scsi3_complete_pro_release(
struct se_device *dev, struct se_device *dev,
struct se_node_acl *se_nacl, struct se_node_acl *se_nacl,
struct t10_pr_registration *pr_reg, struct t10_pr_registration *pr_reg,
int explicit) int explicit,
int unreg)
{ {
struct target_core_fabric_ops *tfo = se_nacl->se_tpg->se_tpg_tfo; struct target_core_fabric_ops *tfo = se_nacl->se_tpg->se_tpg_tfo;
char i_buf[PR_REG_ISID_ID_LEN]; char i_buf[PR_REG_ISID_ID_LEN];
int pr_res_type = 0, pr_res_scope = 0;
memset(i_buf, 0, PR_REG_ISID_ID_LEN); memset(i_buf, 0, PR_REG_ISID_ID_LEN);
core_pr_dump_initiator_port(pr_reg, i_buf, PR_REG_ISID_ID_LEN); core_pr_dump_initiator_port(pr_reg, i_buf, PR_REG_ISID_ID_LEN);
/* /*
* Go ahead and release the current PR reservation holder. * Go ahead and release the current PR reservation holder.
* If an All Registrants reservation is currently active and
* a unregister operation is requested, replace the current
* dev_pr_res_holder with another active registration.
*/ */
dev->dev_pr_res_holder = NULL; if (dev->dev_pr_res_holder) {
pr_res_type = dev->dev_pr_res_holder->pr_res_type;
pr_res_scope = dev->dev_pr_res_holder->pr_res_scope;
dev->dev_pr_res_holder->pr_res_type = 0;
dev->dev_pr_res_holder->pr_res_scope = 0;
dev->dev_pr_res_holder->pr_res_holder = 0;
dev->dev_pr_res_holder = NULL;
}
if (!unreg)
goto out;
pr_debug("SPC-3 PR [%s] Service Action: %s RELEASE cleared" spin_lock(&dev->t10_pr.registration_lock);
" reservation holder TYPE: %s ALL_TG_PT: %d\n", list_del_init(&pr_reg->pr_reg_list);
tfo->get_fabric_name(), (explicit) ? "explicit" : "implicit", /*
core_scsi3_pr_dump_type(pr_reg->pr_res_type), * If the I_T nexus is a reservation holder, the persistent reservation
(pr_reg->pr_reg_all_tg_pt) ? 1 : 0); * is of an all registrants type, and the I_T nexus is the last remaining
* registered I_T nexus, then the device server shall also release the
* persistent reservation.
*/
if (!list_empty(&dev->t10_pr.registration_list) &&
((pr_res_type == PR_TYPE_WRITE_EXCLUSIVE_ALLREG) ||
(pr_res_type == PR_TYPE_EXCLUSIVE_ACCESS_ALLREG))) {
dev->dev_pr_res_holder =
list_entry(dev->t10_pr.registration_list.next,
struct t10_pr_registration, pr_reg_list);
dev->dev_pr_res_holder->pr_res_type = pr_res_type;
dev->dev_pr_res_holder->pr_res_scope = pr_res_scope;
dev->dev_pr_res_holder->pr_res_holder = 1;
}
spin_unlock(&dev->t10_pr.registration_lock);
out:
if (!dev->dev_pr_res_holder) {
pr_debug("SPC-3 PR [%s] Service Action: %s RELEASE cleared"
" reservation holder TYPE: %s ALL_TG_PT: %d\n",
tfo->get_fabric_name(), (explicit) ? "explicit" :
"implicit", core_scsi3_pr_dump_type(pr_res_type),
(pr_reg->pr_reg_all_tg_pt) ? 1 : 0);
}
pr_debug("SPC-3 PR [%s] RELEASE Node: %s%s\n", pr_debug("SPC-3 PR [%s] RELEASE Node: %s%s\n",
tfo->get_fabric_name(), se_nacl->initiatorname, tfo->get_fabric_name(), se_nacl->initiatorname,
i_buf); i_buf);
...@@ -2553,7 +2596,7 @@ core_scsi3_emulate_pro_release(struct se_cmd *cmd, int type, int scope, ...@@ -2553,7 +2596,7 @@ core_scsi3_emulate_pro_release(struct se_cmd *cmd, int type, int scope,
* server shall not establish a unit attention condition. * server shall not establish a unit attention condition.
*/ */
__core_scsi3_complete_pro_release(dev, se_sess->se_node_acl, __core_scsi3_complete_pro_release(dev, se_sess->se_node_acl,
pr_reg, 1); pr_reg, 1, 0);
spin_unlock(&dev->dev_reservation_lock); spin_unlock(&dev->dev_reservation_lock);
...@@ -2641,7 +2684,7 @@ core_scsi3_emulate_pro_clear(struct se_cmd *cmd, u64 res_key) ...@@ -2641,7 +2684,7 @@ core_scsi3_emulate_pro_clear(struct se_cmd *cmd, u64 res_key)
if (pr_res_holder) { if (pr_res_holder) {
struct se_node_acl *pr_res_nacl = pr_res_holder->pr_reg_nacl; struct se_node_acl *pr_res_nacl = pr_res_holder->pr_reg_nacl;
__core_scsi3_complete_pro_release(dev, pr_res_nacl, __core_scsi3_complete_pro_release(dev, pr_res_nacl,
pr_res_holder, 0); pr_res_holder, 0, 0);
} }
spin_unlock(&dev->dev_reservation_lock); spin_unlock(&dev->dev_reservation_lock);
/* /*
...@@ -2700,7 +2743,7 @@ static void __core_scsi3_complete_pro_preempt( ...@@ -2700,7 +2743,7 @@ static void __core_scsi3_complete_pro_preempt(
*/ */
if (dev->dev_pr_res_holder) if (dev->dev_pr_res_holder)
__core_scsi3_complete_pro_release(dev, nacl, __core_scsi3_complete_pro_release(dev, nacl,
dev->dev_pr_res_holder, 0); dev->dev_pr_res_holder, 0, 0);
dev->dev_pr_res_holder = pr_reg; dev->dev_pr_res_holder = pr_reg;
pr_reg->pr_res_holder = 1; pr_reg->pr_res_holder = 1;
...@@ -2944,8 +2987,8 @@ core_scsi3_pro_preempt(struct se_cmd *cmd, int type, int scope, u64 res_key, ...@@ -2944,8 +2987,8 @@ core_scsi3_pro_preempt(struct se_cmd *cmd, int type, int scope, u64 res_key,
*/ */
if (pr_reg_n != pr_res_holder) if (pr_reg_n != pr_res_holder)
__core_scsi3_complete_pro_release(dev, __core_scsi3_complete_pro_release(dev,
pr_res_holder->pr_reg_nacl, pr_res_holder->pr_reg_nacl,
dev->dev_pr_res_holder, 0); dev->dev_pr_res_holder, 0, 0);
/* /*
* b) Remove the registrations for all I_T nexuses identified * b) Remove the registrations for all I_T nexuses identified
* by the SERVICE ACTION RESERVATION KEY field, except the * by the SERVICE ACTION RESERVATION KEY field, except the
...@@ -3415,7 +3458,7 @@ core_scsi3_emulate_pro_register_and_move(struct se_cmd *cmd, u64 res_key, ...@@ -3415,7 +3458,7 @@ core_scsi3_emulate_pro_register_and_move(struct se_cmd *cmd, u64 res_key,
* holder (i.e., the I_T nexus on which the * holder (i.e., the I_T nexus on which the
*/ */
__core_scsi3_complete_pro_release(dev, pr_res_nacl, __core_scsi3_complete_pro_release(dev, pr_res_nacl,
dev->dev_pr_res_holder, 0); dev->dev_pr_res_holder, 0, 0);
/* /*
* g) Move the persistent reservation to the specified I_T nexus using * g) Move the persistent reservation to the specified I_T nexus using
* the same scope and type as the persistent reservation released in * the same scope and type as the persistent reservation released in
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment