@@ -7,6 +7,7 @@
#include <target/target_core_base.h>
#include <target/target_core_fabric.h>
#include <target/target_core_backend.h>
+#include "target_core_ua.h"
#include "target_core_pr.h"
#include "dlm_ckv.h"
@@ -78,7 +79,8 @@ struct pr_lvb {
u8 pr_type;
u8 pr_scope;
u8 pr_aptpl;
- u8 reserved[3];
+ u8 pro_sa;
+ u8 reserved[2];
u32 reserved_by_nodeid;
};
@@ -314,7 +316,7 @@ static int pr_reg_realloc(struct target_cluster_data *cluster_data,
return res;
}
-static int target_pr_sync_dlm(struct se_device *dev)
+static int target_pr_sync_dlm(struct se_device *dev, u8 pro_sa)
{
struct target_cluster_data *cluster_data = dev->cluster_data;
struct t10_pr_registration *pr_reg;
@@ -328,6 +330,7 @@ static int target_pr_sync_dlm(struct se_device *dev)
pr_data.version = 1;
pr_data.pr_generation = atomic_read(&dev->t10_pr.pr_generation);
+ pr_data.pro_sa = pro_sa;
pr_data.nr_registrants = target_get_nr_registrants(dev);
pr_data.pr_is_set = !!dev->dev_pr_res_holder;
pr_data.pr_aptpl = dev->t10_pr.pr_aptpl_active;
@@ -546,6 +549,19 @@ target_create_pr_reg(struct se_device *dev,
return NULL;
}
+static void target_allocate_pr_ua(struct se_device *dev, u8 asc)
+{
+ struct t10_pr_registration *pr_reg;
+
+ list_for_each_entry(pr_reg, &dev->t10_pr.registration_list, pr_reg_list) {
+ target_ua_allocate_lun(
+ pr_reg->pr_reg_nacl,
+ pr_reg->pr_res_mapped_lun,
+ 0x2A,
+ asc);
+ }
+}
+
static void target_pr_sync_cb(void *arg)
{
struct se_device *dev = arg;
@@ -558,6 +574,9 @@ static void target_pr_sync_cb(void *arg)
struct async_group grp;
struct pr_lvb pr_data;
bool res_to_delete = false;
+ struct se_node_acl *pr_reg_nacl;
+ u64 pr_res_mapped_lun;
+ bool reg_deleted = false;
bool was_held;
u8 was_type;
u8 was_scope;
@@ -656,10 +675,41 @@ static void target_pr_sync_cb(void *arg)
/* deregister obsolete entries */
list_for_each_entry_safe(pr_reg, pr_reg_tmp, &to_be_deleted_list,
pr_reg_list) {
+ reg_deleted = true;
+ pr_reg_nacl = pr_reg->pr_reg_nacl;
+ pr_res_mapped_lun = pr_reg->pr_res_mapped_lun;
+
if (dev->dev_pr_res_holder != pr_reg)
__core_scsi3_free_registration(dev, pr_reg, NULL, 0);
else
res_to_delete = true;
+
+ switch (pr_data.pro_sa) {
+ case PRO_CLEAR:
+ /*
+ * establish a unit attention condition for the initiator
+ * port associated with every registered I_T nexus other
+ * than the I_T nexus on which the PERSISTENT RESERVE OUT
+ * command with CLEAR service action was received, with
+ * the additional sense code set to RESERVATIONS PREEMPTED
+ */
+ target_ua_allocate_lun(pr_reg_nacl,
+ pr_res_mapped_lun, 0x2A,
+ ASCQ_2AH_RESERVATIONS_PREEMPTED);
+ break;
+ case PRO_PREEMPT_AND_ABORT:
+ case PRO_REGISTER_AND_IGNORE_EXISTING_KEY:
+ /*
+ * establish a unit attention condition for the initiator
+ * port associated with every I_T nexus that lost its
+ * persistent reservation and/or registration, with the
+ * additional sense code set to REGISTRATIONS PREEMPTED;
+ */
+ target_ua_allocate_lun(pr_reg_nacl,
+ pr_res_mapped_lun, 0x2A,
+ ASCQ_2AH_REGISTRATIONS_PREEMPTED);
+ break;
+ }
}
spin_unlock(&dev->t10_pr.registration_lock);
@@ -677,6 +727,65 @@ static void target_pr_sync_cb(void *arg)
pr_reg_res_holder = pr_reg;
}
+ switch (pr_data.pro_sa) {
+ case PRO_REGISTER_AND_IGNORE_EXISTING_KEY:
+ case PRO_REGISTER:
+ /*
+ * If the I_T nexus is the reservation holder and the persistent
+ * reservation is of a type other than all registrants, then
+ * the device server shall also release the persistent reservation
+ *
+ * If the persistent reservation is a registrants only
+ * type, the device server shall establish a unit
+ * attention condition for the initiator port associated
+ * with every registered I_T nexus except for the I_T
+ * nexus on which the PERSISTENT RESERVE OUT command was
+ * received, with the additional sense code set to
+ * RESERVATIONS RELEASED.
+ */
+ if (reg_deleted && was_held &&
+ (was_type == PR_TYPE_WRITE_EXCLUSIVE_REGONLY ||
+ was_type == PR_TYPE_EXCLUSIVE_ACCESS_REGONLY)) {
+ target_allocate_pr_ua(dev,
+ ASCQ_2AH_RESERVATIONS_RELEASED);
+ }
+ break;
+ case PRO_RELEASE:
+ /*
+ * if the released persistent reservation is either a registrants
+ * only type or an all registrants type persistent reservation,
+ * then the device server shall establish a unit attention
+ * condition for the initiator port associated with every
+ * registered I_T nexus other than I_T nexus on which the
+ * PERSISTENT RESERVE OUT command with RELEASE service action was
+ * received, with the additional sense code set to RESERVATIONS RELEASED
+ */
+ if ((was_type == PR_TYPE_WRITE_EXCLUSIVE_REGONLY) ||
+ (was_type == PR_TYPE_EXCLUSIVE_ACCESS_REGONLY) ||
+ (was_type == PR_TYPE_WRITE_EXCLUSIVE_ALLREG) ||
+ (was_type == PR_TYPE_EXCLUSIVE_ACCESS_ALLREG)) {
+ target_allocate_pr_ua(dev,
+ ASCQ_2AH_RESERVATIONS_RELEASED);
+ }
+ break;
+ case PRO_PREEMPT:
+ case PRO_PREEMPT_AND_ABORT:
+ /*
+ * if the type or scope has changed, then for every I_T nexus
+ * whose reservation key was not removed, except for the I_T nexus
+ * on which the PERSISTENT RESERVE OUT command was received, the
+ * device server shall establish a unit attention condition for
+ * the initiator port associated with that I_T nexus, with the
+ * additional sense code set to RESERVATIONS RELEASED. I
+ */
+ if (pr_prev_res_holder && pr_reg_res_holder &&
+ pr_prev_res_holder != pr_reg_res_holder &&
+ (was_type != pr_reg_res_holder->pr_res_type ||
+ was_scope != pr_reg_res_holder->pr_res_scope))
+ target_allocate_pr_ua(dev, ASCQ_2AH_RESERVATIONS_RELEASED);
+ break;
+ }
+
/* update general data */
atomic_set(&dev->t10_pr.pr_generation, pr_data.pr_generation);
dev->t10_pr.pr_aptpl_active = pr_data.pr_aptpl;
@@ -741,7 +850,7 @@ target_spc2_reserve(struct se_device *dev, struct se_session *sess)
cluster_data->reserved_node_id = 0;
}
- target_pr_sync_dlm(dev);
+ target_pr_sync_dlm(dev, -1);
target_pr_unlock_dlm(dev);
}
@@ -788,6 +788,11 @@ int target_dummy_nodlm(struct se_device *dev)
return 0;
}
+int target_prsync_nodlm(struct se_device *dev, u8 pro_sa)
+{
+ return 0;
+}
+
static void target_reserve2_nodlm(struct se_device *dev, struct se_session *sess)
{
if (sess) {
@@ -806,7 +811,7 @@ const struct target_cluster_ops nodlm_cluster_ops = {
.caw_unlock = target_caw_unlock_nodlm,
.pr_lock = target_dummy_nodlm,
.pr_unlock = target_dummy_nodlm,
- .pr_sync = target_dummy_nodlm,
+ .pr_sync = target_prsync_nodlm,
.reserve = target_reserve2_nodlm,
};
@@ -3624,7 +3624,7 @@ target_scsi3_emulate_pr_out(struct se_cmd *cmd)
}
if (!ret)
- dev->cl_ops->pr_sync(dev);
+ dev->cl_ops->pr_sync(dev, sa);
dev->cl_ops->pr_unlock(dev);
if (!ret)
@@ -171,6 +171,7 @@ void target_ua_allocate_lun(struct se_node_acl *nacl,
core_scsi3_ua_allocate(deve, asc, ascq);
rcu_read_unlock();
}
+EXPORT_SYMBOL(target_ua_allocate_lun);
void core_scsi3_ua_release_all(
struct se_dev_entry *deve)
@@ -789,7 +789,7 @@ struct target_cluster_ops {
int (*caw_unlock)(struct se_device *dev, void *lock);
int (*pr_lock)(struct se_device *dev);
int (*pr_unlock)(struct se_device *dev);
- int (*pr_sync)(struct se_device *dev);
+ int (*pr_sync)(struct se_device *dev, u8 pro_sa);
void (*reserve)(struct se_device *dev, struct se_session *sess);
};
Establish Unit Attention condition on the peer nodes in the cluster on PR OUT commands according to SPC-4. Pass PR OUT Service Action to pr_sync to allow peer nodes to know the reason of the update of PR state. Signed-off-by: Dmitry Bogdanov <d.bogdanov@yadro.com> --- drivers/target/target_cluster_dlm.c | 115 +++++++++++++++++++++++++++- drivers/target/target_core_device.c | 7 +- drivers/target/target_core_pr.c | 2 +- drivers/target/target_core_ua.c | 1 + include/target/target_core_base.h | 2 +- 5 files changed, 121 insertions(+), 6 deletions(-)