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@xxxxxxxxx> --- 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(-) diff --git a/drivers/target/target_cluster_dlm.c b/drivers/target/target_cluster_dlm.c index 4c4d337e6a1a..cb44f6c0f0ac 100644 --- a/drivers/target/target_cluster_dlm.c +++ b/drivers/target/target_cluster_dlm.c @@ -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); } diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index 9f01a28ef72f..4f36d12d2213 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -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, }; diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c index 930f0bebd75d..fd89647c5ac7 100644 --- a/drivers/target/target_core_pr.c +++ b/drivers/target/target_core_pr.c @@ -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) diff --git a/drivers/target/target_core_ua.c b/drivers/target/target_core_ua.c index 4276690fb6cb..68a7339611f3 100644 --- a/drivers/target/target_core_ua.c +++ b/drivers/target/target_core_ua.c @@ -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) diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index c49ddf0828f6..6c99fb4aa151 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -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); }; -- 2.25.1