Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3ccd6e83 authored by Nicholas Bellinger's avatar Nicholas Bellinger
Browse files

target: Fix PR registration + APTPL RCU conversion regression



This patch fixes a v4.2+ regression introduced by commit 79dc9c9e
where lookup of t10_pr_registration->pr_reg_deve and associated
->pr_kref get was missing from __core_scsi3_do_alloc_registration(),
which is responsible for setting DEF_PR_REG_ACTIVE.

This would result in REGISTER operations completing successfully,
but subsequent core_scsi3_pr_seq_non_holder() checking would fail
with !DEF_PR_REG_ACTIVE -> RESERVATION CONFLICT status.

Update __core_scsi3_add_registration() to drop ->pr_kref reference
after registration and any optional ALL_TG_PT=1 processing has
completed.  Update core_scsi3_decode_spec_i_port() to release
the new parent local_pr_reg->pr_kref as well.

Also, update __core_scsi3_check_aptpl_registration() to perform
the same target_nacl_find_deve() lookup + ->pr_kref get, now that
__core_scsi3_add_registration() expects to drop the reference.

Finally, since there are cases when se_dev_entry->se_lun_acl can
still be dereferenced in core_scsi3_lunacl_undepend_item() while
holding ->pr_kref, go ahead and move explicit rcu_assign_pointer()
NULL assignments within core_disable_device_list_for_node() until
after orig->pr_comp finishes.

Reported-by: default avatarScott L. Lykens <scott@lykens.org>
Tested-by: default avatarScott L. Lykens <scott@lykens.org>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Hannes Reinecke <hare@suse.de>
Cc: Lee Duncan <lduncan@suse.com>
Cc: <stable@vger.kernel.org> # v4.2+
Signed-off-by: default avatarNicholas Bellinger <nab@linux-iscsi.org>
parent 9fd60088
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -427,8 +427,6 @@ void core_disable_device_list_for_node(

	hlist_del_rcu(&orig->link);
	clear_bit(DEF_PR_REG_ACTIVE, &orig->deve_flags);
	rcu_assign_pointer(orig->se_lun, NULL);
	rcu_assign_pointer(orig->se_lun_acl, NULL);
	orig->lun_flags = 0;
	orig->creation_time = 0;
	orig->attach_count--;
@@ -439,6 +437,9 @@ void core_disable_device_list_for_node(
	kref_put(&orig->pr_kref, target_pr_kref_release);
	wait_for_completion(&orig->pr_comp);

	rcu_assign_pointer(orig->se_lun, NULL);
	rcu_assign_pointer(orig->se_lun_acl, NULL);

	kfree_rcu(orig, rcu_head);

	core_scsi3_free_pr_reg_from_nacl(dev, nacl);
+67 −24
Original line number Diff line number Diff line
@@ -618,7 +618,7 @@ static struct t10_pr_registration *__core_scsi3_do_alloc_registration(
	struct se_device *dev,
	struct se_node_acl *nacl,
	struct se_lun *lun,
	struct se_dev_entry *deve,
	struct se_dev_entry *dest_deve,
	u64 mapped_lun,
	unsigned char *isid,
	u64 sa_res_key,
@@ -640,7 +640,29 @@ static struct t10_pr_registration *__core_scsi3_do_alloc_registration(
	INIT_LIST_HEAD(&pr_reg->pr_reg_atp_mem_list);
	atomic_set(&pr_reg->pr_res_holders, 0);
	pr_reg->pr_reg_nacl = nacl;
	pr_reg->pr_reg_deve = deve;
	/*
	 * For destination registrations for ALL_TG_PT=1 and SPEC_I_PT=1,
	 * the se_dev_entry->pr_ref will have been already obtained by
	 * core_get_se_deve_from_rtpi() or __core_scsi3_alloc_registration().
	 *
	 * Otherwise, locate se_dev_entry now and obtain a reference until
	 * registration completes in __core_scsi3_add_registration().
	 */
	if (dest_deve) {
		pr_reg->pr_reg_deve = dest_deve;
	} else {
		rcu_read_lock();
		pr_reg->pr_reg_deve = target_nacl_find_deve(nacl, mapped_lun);
		if (!pr_reg->pr_reg_deve) {
			rcu_read_unlock();
			pr_err("Unable to locate PR deve %s mapped_lun: %llu\n",
				nacl->initiatorname, mapped_lun);
			kmem_cache_free(t10_pr_reg_cache, pr_reg);
			return NULL;
		}
		kref_get(&pr_reg->pr_reg_deve->pr_kref);
		rcu_read_unlock();
	}
	pr_reg->pr_res_mapped_lun = mapped_lun;
	pr_reg->pr_aptpl_target_lun = lun->unpacked_lun;
	pr_reg->tg_pt_sep_rtpi = lun->lun_rtpi;
@@ -936,17 +958,29 @@ static int __core_scsi3_check_aptpl_registration(
		    !(strcmp(pr_reg->pr_tport, t_port)) &&
		     (pr_reg->pr_reg_tpgt == tpgt) &&
		     (pr_reg->pr_aptpl_target_lun == target_lun)) {
			/*
			 * Obtain the ->pr_reg_deve pointer + reference, that
			 * is released by __core_scsi3_add_registration() below.
			 */
			rcu_read_lock();
			pr_reg->pr_reg_deve = target_nacl_find_deve(nacl, mapped_lun);
			if (!pr_reg->pr_reg_deve) {
				pr_err("Unable to locate PR APTPL %s mapped_lun:"
					" %llu\n", nacl->initiatorname, mapped_lun);
				rcu_read_unlock();
				continue;
			}
			kref_get(&pr_reg->pr_reg_deve->pr_kref);
			rcu_read_unlock();

			pr_reg->pr_reg_nacl = nacl;
			pr_reg->tg_pt_sep_rtpi = lun->lun_rtpi;

			list_del(&pr_reg->pr_reg_aptpl_list);
			spin_unlock(&pr_tmpl->aptpl_reg_lock);
			/*
			 * At this point all of the pointers in *pr_reg will
			 * be setup, so go ahead and add the registration.
			 */

			__core_scsi3_add_registration(dev, nacl, pr_reg, 0, 0);
			/*
			 * If this registration is the reservation holder,
@@ -1044,18 +1078,11 @@ static void __core_scsi3_add_registration(

	__core_scsi3_dump_registration(tfo, dev, nacl, pr_reg, register_type);
	spin_unlock(&pr_tmpl->registration_lock);

	rcu_read_lock();
	deve = pr_reg->pr_reg_deve;
	if (deve)
		set_bit(DEF_PR_REG_ACTIVE, &deve->deve_flags);
	rcu_read_unlock();

	/*
	 * Skip extra processing for ALL_TG_PT=0 or REGISTER_AND_MOVE.
	 */
	if (!pr_reg->pr_reg_all_tg_pt || register_move)
		return;
		goto out;
	/*
	 * Walk pr_reg->pr_reg_atp_list and add registrations for ALL_TG_PT=1
	 * allocated in __core_scsi3_alloc_registration()
@@ -1075,19 +1102,31 @@ static void __core_scsi3_add_registration(
		__core_scsi3_dump_registration(tfo, dev, nacl_tmp, pr_reg_tmp,
					       register_type);
		spin_unlock(&pr_tmpl->registration_lock);

		/*
		 * Drop configfs group dependency reference and deve->pr_kref
		 * obtained from  __core_scsi3_alloc_registration() code.
		 */
		rcu_read_lock();
		deve = pr_reg_tmp->pr_reg_deve;
		if (deve)
		if (deve) {
			set_bit(DEF_PR_REG_ACTIVE, &deve->deve_flags);
			core_scsi3_lunacl_undepend_item(deve);
			pr_reg_tmp->pr_reg_deve = NULL;
		}
		rcu_read_unlock();

	}
out:
	/*
		 * Drop configfs group dependency reference from
		 * __core_scsi3_alloc_registration()
	 * Drop deve->pr_kref obtained in __core_scsi3_do_alloc_registration()
	 */
		core_scsi3_lunacl_undepend_item(pr_reg_tmp->pr_reg_deve);
	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();
}

static int core_scsi3_alloc_registration(
@@ -1785,9 +1824,11 @@ core_scsi3_decode_spec_i_port(
			dest_node_acl->initiatorname, i_buf, (dest_se_deve) ?
			dest_se_deve->mapped_lun : 0);

		if (!dest_se_deve)
		if (!dest_se_deve) {
			kref_put(&local_pr_reg->pr_reg_deve->pr_kref,
				 target_pr_kref_release);
			continue;

		}
		core_scsi3_lunacl_undepend_item(dest_se_deve);
		core_scsi3_nodeacl_undepend_item(dest_node_acl);
		core_scsi3_tpg_undepend_item(dest_tpg);
@@ -1823,9 +1864,11 @@ core_scsi3_decode_spec_i_port(

		kmem_cache_free(t10_pr_reg_cache, dest_pr_reg);

		if (!dest_se_deve)
		if (!dest_se_deve) {
			kref_put(&local_pr_reg->pr_reg_deve->pr_kref,
				 target_pr_kref_release);
			continue;

		}
		core_scsi3_lunacl_undepend_item(dest_se_deve);
		core_scsi3_nodeacl_undepend_item(dest_node_acl);
		core_scsi3_tpg_undepend_item(dest_tpg);