Notify other nodes in cluster that PR data updated. On notification do the following under PR cluster lock to read PR data: Update an existing registrants. Remove an outdated registrants (absent in cluster data). Create new registrants. Update Reservation status. Signed-off-by: Dmitry Bogdanov <d.bogdanov@xxxxxxxxx> --- drivers/target/target_cluster_dlm.c | 304 ++++++++++++++++++++++++ drivers/target/target_core_device.c | 1 + drivers/target/target_core_fabric_lib.c | 3 +- drivers/target/target_core_internal.h | 5 - drivers/target/target_core_pr.c | 8 +- drivers/target/target_core_pr.h | 1 + include/target/target_core_base.h | 5 + 7 files changed, 320 insertions(+), 7 deletions(-) diff --git a/drivers/target/target_cluster_dlm.c b/drivers/target/target_cluster_dlm.c index 14465764eaaf..6b7cb3175e3a 100644 --- a/drivers/target/target_cluster_dlm.c +++ b/drivers/target/target_cluster_dlm.c @@ -17,6 +17,7 @@ struct target_cluster_data { struct mutex pr_lock_mutex; struct dlm_ckv_lock *pr_lock; struct dlm_ckv_kv *pr_data; + struct dlm_ckv_notify *pr_sync_notify; int reserved_node_id; struct dlm_ckv_kv **pr_reg_kv; size_t pr_reg_kv_len; @@ -96,6 +97,7 @@ struct async_group { struct completion compl; }; +static void target_pr_sync_cb(void *arg); static int pr_reg_realloc(struct target_cluster_data *cluster_data, size_t nr_registrants); @@ -143,10 +145,17 @@ static int target_init_dlm(struct se_device *dev) if (!cluster_data->pr_data) goto fail; + cluster_data->pr_sync_notify = dlm_ckv_create_notification( + cluster_data->bucket, "pr_sync", target_pr_sync_cb); + if (!cluster_data->pr_sync_notify) + goto fail; + dev->cluster_data = cluster_data; return err; fail: + if (cluster_data->pr_sync_notify) + dlm_ckv_free_notification(cluster_data->pr_sync_notify); if (cluster_data->pr_lock) dlm_ckv_free_lock(cluster_data->pr_lock); if (cluster_data->pr_data) @@ -167,6 +176,7 @@ static int target_cleanup_dlm(struct se_device *dev) dlm_ckv_free_kv(cluster_data->pr_reg_kv[i]); kfree(cluster_data->pr_reg_kv); + dlm_ckv_free_notification(cluster_data->pr_sync_notify); dlm_ckv_free_lock(cluster_data->pr_lock); dlm_ckv_free_kv(cluster_data->pr_data); res = dlm_ckv_close_bucket(cluster_data->bucket); @@ -379,11 +389,305 @@ static int target_pr_sync_dlm(struct se_device *dev) skip_pr_reg: + /* request to update PR data on other nodes in cluster */ + dlm_ckv_notify(cluster_data->pr_sync_notify); + done: return res; } +static int target_update_pr_reg(struct se_device *dev, + struct t10_pr_registration *pr_reg, + const struct pr_lvb *pr_data, + const struct pr_reg_lvb *pr_reg_data, + struct t10_pr_registration **pr_reg_res_holder) +{ + /* update existing registrant */ + pr_reg->pr_res_key = pr_reg_data->key; + pr_reg->pr_reg_all_tg_pt = pr_reg_data->is_all_tg_pt; + + if (pr_reg_data->is_holder) + *pr_reg_res_holder = pr_reg; + + return 0; +} + +static struct t10_pr_registration * +target_create_pr_reg(struct se_device *dev, + struct pr_lvb *pr_data, + struct pr_reg_lvb *pr_reg_data) +{ + struct t10_pr_registration *pr_reg = NULL; + struct se_lun *lun, *lun_tmp; + struct se_lun_acl *lacl_tmp; + struct se_node_acl *nacl_tmp; + struct se_dev_entry *deve; + char i_buf[PR_REG_ISID_ID_LEN]; + u8 initiatorname[PR_REG_ISID_LEN]; + char *isid = NULL; + + target_parse_pr_out_transport_id(pr_reg_data->tid, initiatorname, &isid); + + spin_lock(&dev->se_port_lock); + list_for_each_entry_safe(lun, lun_tmp, &dev->dev_sep_list, lun_dev_link) { + if (!percpu_ref_tryget_live(&lun->lun_ref)) + continue; + + if (lun->lun_tpg->tpg_rtpi != pr_reg_data->rtpi) { + percpu_ref_put(&lun->lun_ref); + continue; + } + spin_unlock(&dev->se_port_lock); + + spin_lock(&lun->lun_deve_lock); + list_for_each_entry(deve, &lun->lun_deve_list, lun_link) { + if (!deve->se_lun_acl) + continue; + + lacl_tmp = deve->se_lun_acl; + nacl_tmp = lacl_tmp->se_lun_nacl; + + if (strcmp(nacl_tmp->initiatorname, initiatorname)) + continue; + + kref_get(&deve->pr_kref); + pr_reg = __core_scsi3_do_alloc_registration( + dev, nacl_tmp, + nacl_tmp->initiatorname, lun->unpacked_lun, + pr_reg_data->rtpi, + deve, lacl_tmp->mapped_lun, + isid, + pr_reg_data->key, + pr_reg_data->is_all_tg_pt, + pr_data->pr_aptpl); + + percpu_ref_put(&lun->lun_ref); + spin_unlock(&lun->lun_deve_lock); + goto out; + } + percpu_ref_put(&lun->lun_ref); + spin_unlock(&lun->lun_deve_lock); + spin_lock(&dev->se_port_lock); + } + spin_unlock(&dev->se_port_lock); +out: + + if (!pr_reg) { + /* target not yet configured, + * allocate registration like for APTPL case + */ + pr_reg = __core_scsi3_do_alloc_registration( + dev, NULL, initiatorname, 0, + pr_reg_data->rtpi, + NULL, 0, + isid, + pr_reg_data->key, + pr_reg_data->is_all_tg_pt, + pr_data->pr_aptpl); + } + + if (pr_reg) { + spin_lock(&dev->t10_pr.registration_lock); + memcpy(pr_reg->pr_tid, pr_reg_data->tid, PR_REG_TID_LEN); + list_add_tail(&pr_reg->pr_reg_list, + &dev->t10_pr.registration_list); + + /* + * Drop deve->pr_kref obtained in __core_scsi3_do_alloc_registration() + */ + rcu_read_lock(); + deve = pr_reg->pr_reg_deve; + if (deve) { + set_bit(DEF_PR_REG_ACTIVE, &deve->deve_flags); + kref_put(&deve->pr_kref, target_pr_kref_release); + pr_reg->pr_reg_deve = NULL; + } + rcu_read_unlock(); + + memset(&i_buf[0], 0, PR_REG_ISID_ID_LEN); + core_pr_dump_initiator_port(pr_reg, i_buf, + PR_REG_ISID_ID_LEN); + pr_debug("SPC-3 PR Service Action: REGISTER Initiator Node: %s%s\n", + initiatorname, i_buf); + pr_debug("SPC-3 PR registration on Target Port: %d\n", + pr_reg->tg_pt_sep_rtpi); + pr_debug("SPC-3 PR for %s TCM Subsystem %s Object Target Port(s)\n", + (pr_reg->pr_reg_all_tg_pt) ? "ALL" : "SINGLE", + dev->transport->name); + pr_debug("SPC-3 PR SA Res Key: 0x%016llx PRgeneration: 0x%08x APTPL: %d\n", + pr_reg->pr_res_key, + pr_reg->pr_res_generation, + pr_reg->pr_reg_aptpl); + spin_unlock(&dev->t10_pr.registration_lock); + return pr_reg; + } + + pr_warn("TARGET_CORE[%d]: PR data from cluster is invalid: could not find local nacl/deve for Initiator %s - RTPI %d\n", + dev->dev_index, initiatorname, pr_reg_data->rtpi); + + return NULL; +} + +static void target_pr_sync_cb(void *arg) +{ + struct se_device *dev = arg; + struct target_cluster_data *cluster_data = dev->cluster_data; + struct t10_pr_registration *pr_reg, *pr_reg_tmp; + struct t10_pr_registration *pr_reg_res_holder = NULL; + struct t10_pr_registration *pr_prev_res_holder = NULL; + struct pr_reg_lvb *pr_reg_data = NULL; + LIST_HEAD(to_be_deleted_list); + struct async_group grp; + struct pr_lvb pr_data; + bool res_to_delete = false; + bool was_held; + u8 was_type; + u8 was_scope; + bool found; + int i = 0; + int res; + + res = dlm_ckv_get(cluster_data->pr_data, (char *)&pr_data, sizeof(pr_data)); + if (res) + goto done; + + if (!pr_data.version) { + pr_info("TARGET_CORE[%d]: PR data from cluster is invalid\n", + dev->dev_index); + goto done; + } + + pr_reg_data = kzalloc(sizeof(struct pr_reg_lvb) * pr_data.nr_registrants, + GFP_KERNEL); + if (!pr_reg_data) { + res = -ENOMEM; + goto done; + } + + res = pr_reg_realloc(cluster_data, pr_data.nr_registrants); + if (res) + goto done; + + if (pr_data.nr_registrants == 0) + goto skip_pr_reg; + + refcount_set(&grp.pending, 1); /* 1 for a loop */ + atomic_set(&grp.status, 0); + init_completion(&grp.compl); + + for (i = 0; i < pr_data.nr_registrants; ++i) { + refcount_inc(&grp.pending); + res = dlm_ckv_get_async(cluster_data->pr_reg_kv[i], (char *)(pr_reg_data + i), + sizeof(struct pr_reg_lvb), group_compl_cb, &grp); + if (res) { + refcount_dec(&grp.pending); + break; + } + } + group_compl_cb(&grp, 0); + res = wait_for_completion_timeout(&grp.compl, 60 * HZ); + if (!res) { + pr_err("TARGET_CORE[%d]: timeout of waiting for dlm_ckv_get_async\n", + dev->dev_index); + goto done; + } + res = atomic_read(&grp.status); + if (res) { + pr_err("TARGET_CORE[%d]: fail of group for dlm_ckv_get_async %d\n", + dev->dev_index, res); + goto done; + } + +skip_pr_reg: + /* + * Update existing registrations + */ + spin_lock(&dev->t10_pr.registration_lock); + + was_held = !!dev->dev_pr_res_holder; + pr_prev_res_holder = dev->dev_pr_res_holder; + if (was_held) { + was_scope = pr_prev_res_holder->pr_res_scope; + was_type = pr_prev_res_holder->pr_res_type; + } + + list_for_each_entry_safe(pr_reg, pr_reg_tmp, &dev->t10_pr.registration_list, + pr_reg_list) { + found = false; + + for (i = 0; i < pr_data.nr_registrants; ++i) { + if (!pr_reg_data[i].version) + continue; + + if (pr_reg->tg_pt_sep_rtpi == pr_reg_data[i].rtpi && + !target_cmp_pr_transport_id(pr_reg, pr_reg_data[i].tid)) { + found = true; + /* mark existing registrants */ + pr_reg_data[i].version = 0; + target_update_pr_reg(dev, pr_reg, &pr_data, + &pr_reg_data[i], + &pr_reg_res_holder); + break; + } + } + + if (!found) + list_move_tail(&pr_reg->pr_reg_list, &to_be_deleted_list); + } + + /* deregister obsolete entries */ + list_for_each_entry_safe(pr_reg, pr_reg_tmp, &to_be_deleted_list, + pr_reg_list) { + if (dev->dev_pr_res_holder != pr_reg) + __core_scsi3_free_registration(dev, pr_reg, NULL, 0); + else + res_to_delete = true; + } + spin_unlock(&dev->t10_pr.registration_lock); + + /* register new entries */ + for (i = 0; i < pr_data.nr_registrants; ++i) { + /* skip existing registrants */ + if (!pr_reg_data[i].version) + continue; + + pr_reg = target_create_pr_reg(dev, &pr_data, &pr_reg_data[i]); + if (!pr_reg) + pr_err("TARGET_CORE[%d]: can not create new registration\n", + dev->dev_index); + else if (pr_reg_data[i].is_holder) + pr_reg_res_holder = pr_reg; + } + + /* update general data */ + atomic_set(&dev->t10_pr.pr_generation, pr_data.pr_generation); + dev->t10_pr.pr_aptpl_active = pr_data.pr_aptpl; + + /* update reservation */ + spin_lock(&dev->dev_reservation_lock); + if (pr_prev_res_holder && + ((pr_reg_res_holder != pr_prev_res_holder) || + (was_type != pr_reg_res_holder->pr_res_type))) + __core_scsi3_complete_pro_release(dev, pr_prev_res_holder, 0, 0); + + if (pr_reg_res_holder) + __core_scsi3_set_reservation(dev, pr_reg_res_holder, + pr_data.pr_scope, pr_data.pr_type); + spin_unlock(&dev->dev_reservation_lock); + + if (res_to_delete) { + spin_lock(&dev->t10_pr.registration_lock); + __core_scsi3_free_registration(dev, pr_prev_res_holder, NULL, 0); + spin_unlock(&dev->t10_pr.registration_lock); + } + + core_scsi3_update_and_write_aptpl(dev, dev->t10_pr.pr_aptpl_active); + +done: + kfree(pr_reg_data); +} + const struct target_cluster_ops dlm_cluster_ops = { .name = "dlm", .owner = THIS_MODULE, diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index f5da3fc17ad1..297bf5985f48 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -355,6 +355,7 @@ void target_pr_kref_release(struct kref *kref) pr_kref); complete(&deve->pr_comp); } +EXPORT_SYMBOL(target_pr_kref_release); /* * Establish UA condition on SCSI device - all LUNs diff --git a/drivers/target/target_core_fabric_lib.c b/drivers/target/target_core_fabric_lib.c index 8232d872db35..2937bf4f6865 100644 --- a/drivers/target/target_core_fabric_lib.c +++ b/drivers/target/target_core_fabric_lib.c @@ -396,7 +396,7 @@ int target_cmp_pr_transport_id( return memcmp(pr_reg->pr_tid, tid, len1); } - +EXPORT_SYMBOL(target_cmp_pr_transport_id); int target_gen_pr_transport_id( struct t10_pr_registration *pr_reg, @@ -444,3 +444,4 @@ u32 target_parse_pr_out_transport_id( *port_nexus_ptr = NULL; return 24; } +EXPORT_SYMBOL(target_parse_pr_out_transport_id); diff --git a/drivers/target/target_core_internal.h b/drivers/target/target_core_internal.h index 8669ff6f48e9..89f0ddca35ed 100644 --- a/drivers/target/target_core_internal.h +++ b/drivers/target/target_core_internal.h @@ -65,7 +65,6 @@ extern struct t10_alua_lu_gp *default_lu_gp; /* target_core_device.c */ struct se_dev_entry *core_get_se_deve_from_rtpi(struct se_node_acl *, u16); -void target_pr_kref_release(struct kref *); void core_free_device_list_for_node(struct se_node_acl *, struct se_portal_group *); void core_update_device_list_access(u64, bool, struct se_node_acl *); @@ -107,14 +106,10 @@ int target_fabric_setup_cits(struct target_fabric_configfs *); /* target_core_fabric_lib.c */ int target_get_pr_transport_id_len(struct t10_pr_registration *pr_reg); -int target_cmp_pr_transport_id(struct t10_pr_registration *pr_reg, - unsigned char *tid); int target_gen_pr_transport_id(struct t10_pr_registration *pr_reg, int proto_id, const char *initiatorname, unsigned char *isid); -u32 target_parse_pr_out_transport_id( - const char *buf, char *initiatorname, char **port_nexus_ptr); /* target_core_hba.c */ struct se_hba *core_alloc_hba(const char *, u32, u32); diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c index a9fe192f5fe4..fe2f64826e9e 100644 --- a/drivers/target/target_core_pr.c +++ b/drivers/target/target_core_pr.c @@ -52,6 +52,7 @@ void core_pr_dump_initiator_port( snprintf(buf, size, ",i,0x%s", pr_reg->pr_reg_isid); } +EXPORT_SYMBOL(core_pr_dump_initiator_port); enum register_type { REGISTER, @@ -677,6 +678,7 @@ struct t10_pr_registration *__core_scsi3_do_alloc_registration( return pr_reg; } +EXPORT_SYMBOL(__core_scsi3_do_alloc_registration); static int core_scsi3_lunacl_depend_item(struct se_dev_entry *); static void core_scsi3_lunacl_undepend_item(struct se_dev_entry *); @@ -1296,6 +1298,7 @@ void __core_scsi3_free_registration( */ list_add_tail(&pr_reg->pr_reg_abort_list, preempt_and_abort_list); } +EXPORT_SYMBOL(__core_scsi3_free_registration); void core_scsi3_free_all_registrations( struct se_device *dev) @@ -1925,7 +1928,7 @@ static int __core_scsi3_write_aptpl_to_file( * Clear the APTPL metadata if APTPL has been disabled, otherwise * write out the updated metadata to struct file for this SCSI device. */ -static sense_reason_t core_scsi3_update_and_write_aptpl(struct se_device *dev, bool aptpl) +sense_reason_t core_scsi3_update_and_write_aptpl(struct se_device *dev, bool aptpl) { unsigned char *buf; int rc, len = PR_APTPL_BUF_LEN; @@ -1965,6 +1968,7 @@ static sense_reason_t core_scsi3_update_and_write_aptpl(struct se_device *dev, b pr_debug("SPC-3 PR: Set APTPL Bit Activated\n"); return 0; } +EXPORT_SYMBOL(core_scsi3_update_and_write_aptpl); static sense_reason_t core_scsi3_emulate_pro_register(struct se_cmd *cmd, u64 res_key, u64 sa_res_key, @@ -2214,6 +2218,7 @@ void __core_scsi3_set_reservation(struct se_device *dev, pr_reg->pr_iport, i_buf); } +EXPORT_SYMBOL(__core_scsi3_set_reservation); static sense_reason_t core_scsi3_pro_reserve(struct se_cmd *cmd, int type, int scope, u64 res_key) @@ -2430,6 +2435,7 @@ void __core_scsi3_complete_pro_release( */ pr_reg->pr_res_holder = pr_reg->pr_res_type = pr_reg->pr_res_scope = 0; } +EXPORT_SYMBOL(__core_scsi3_complete_pro_release); static sense_reason_t core_scsi3_emulate_pro_release(struct se_cmd *cmd, int type, int scope, diff --git a/drivers/target/target_core_pr.h b/drivers/target/target_core_pr.h index 589fdac5470f..e7eff50a01f2 100644 --- a/drivers/target/target_core_pr.h +++ b/drivers/target/target_core_pr.h @@ -68,6 +68,7 @@ extern int core_scsi3_alloc_aptpl_registration( extern int core_scsi3_check_aptpl_registration(struct se_device *, struct se_portal_group *, struct se_lun *, struct se_node_acl *, u64); +sense_reason_t core_scsi3_update_and_write_aptpl(struct se_device *dev, bool aptpl); extern void core_scsi3_free_all_registrations(struct se_device *); extern unsigned char *core_scsi3_pr_dump_type(int); diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index 9edf3c1ac204..b0f0a6b93f6e 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -1004,5 +1004,10 @@ void target_cluster_impl_unregister(const struct target_cluster_ops *ops); int target_get_pr_transport_id(struct t10_pr_registration *pr_reg, unsigned char *buf); +u32 target_parse_pr_out_transport_id( + const char *buf, char *initiatorname, char **port_nexus_ptr); +int target_cmp_pr_transport_id(struct t10_pr_registration *pr_reg, + unsigned char *tid); +void target_pr_kref_release(struct kref *kref); #endif /* TARGET_CORE_BASE_H */ -- 2.25.1