target: Fix PR registration + APTPL RCU conversion regression
authorNicholas Bellinger <nab@linux-iscsi.org>
Sun, 13 Sep 2015 09:30:46 +0000 (02:30 -0700)
committerNicholas Bellinger <nab@linux-iscsi.org>
Fri, 25 Sep 2015 06:17:07 +0000 (23:17 -0700)
This patch fixes a v4.2+ regression introduced by commit 79dc9c9e86
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: Scott L. Lykens <scott@lykens.org>
Tested-by: Scott 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: Nicholas Bellinger <nab@linux-iscsi.org>
drivers/target/target_core_device.c
drivers/target/target_core_pr.c

index dcc424ac35d45bfd2cc38bb348c851fc4336796d..abf20763b0b640931b9fc6288bdab4d6182b72f0 100644 (file)
@@ -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);
index 5ab7100de17eb5694b162403ef43585c4bbceaf1..e7933115087ab2fab461ee4b10c2012398eb73e0 100644 (file)
@@ -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();
-
-               /*
-                * Drop configfs group dependency reference from
-                * __core_scsi3_alloc_registration()
-                */
-               core_scsi3_lunacl_undepend_item(pr_reg_tmp->pr_reg_deve);
        }
+out:
+       /*
+        * 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();
 }
 
 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 @@ out:
 
                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);