]> git.ipfire.org Git - people/ms/linux.git/commitdiff
Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/nab/target...
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 20 Dec 2014 02:02:22 +0000 (18:02 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 20 Dec 2014 02:02:22 +0000 (18:02 -0800)
Pull SCSI target fixes from Nicholas Bellinger:
 "The highlights this merge window include:

   - Allow target fabric drivers to function as built-in.  (Roland)
   - Fix tcm_loop multi-TPG endpoint nexus bug.  (Hannes)
   - Move per device config_item_type into se_subsystem_api, allowing
     configfs attributes to be defined at module_init time.  (Jerome +
     nab)
   - Convert existing IBLOCK/FILEIO/RAMDISK/PSCSI/TCMU drivers to use
     external configfs attributes.  (nab)
   - A number of iser-target fixes related to active session + network
     portal shutdown stability during extended stress testing.  (Sagi +
     Slava)
   - Dynamic allocation of T10-PI contexts for iser-target, fixing a
     potentially bogus iscsi_np->tpg_np pointer reference in >= v3.14
     code.  (Sagi)
   - iser-target performance + scalability improvements.  (Sagi)
   - Fixes for SPC-4 Persistent Reservation AllRegistrants spec
     compliance.  (Ilias + James + nab)
   - Avoid potential short kern_sendmsg() in iscsi-target for now until
     Al's conversion to use msghdr iteration is merged post -rc1.
     (Viro)

  Also, Sagi has requested a number of iser-target patches (9) that
  address stability issues he's encountered during extended stress
  testing be considered for v3.10.y + v3.14.y code.  Given the amount of
  LOC involved, it will certainly require extra backporting effort.

  Apologies in advance to Greg-KH & Co on this.  Sagi and I will be
  working post-merge to ensure they each get applied correctly"

* 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/nab/target-pending: (53 commits)
  target: Allow AllRegistrants to re-RESERVE existing reservation
  uapi/linux/target_core_user.h: fix headers_install.sh badness
  iscsi-target: Fail connection on short sendmsg writes
  iscsi-target: nullify session in failed login sequence
  target: Avoid dropping AllRegistrants reservation during unregister
  target: Fix R_HOLDER bit usage for AllRegistrants
  iscsi-target: Drop left-over bogus iscsi_np->tpg_np
  iser-target: Fix wc->wr_id cast warning
  iser-target: Remove code duplication
  iser-target: Adjust log levels and prettify some prints
  iser-target: Use debug_level parameter to control logging level
  iser-target: Fix logout sequence
  iser-target: Don't wait for session commands from completion context
  iser-target: Reduce CQ lock contention by batch polling
  iser-target: Introduce isert_poll_budget
  iser-target: Remove an atomic operation from the IO path
  iser-target: Remove redundant call to isert_conn_terminate
  iser-target: Use single CQ for TX and RX
  iser-target: Centralize completion elements to a context
  iser-target: Cast wr_id with uintptr_t instead of unsinged long
  ...

1  2 
drivers/target/iscsi/iscsi_target_util.c
drivers/target/loopback/tcm_loop.c
drivers/target/target_core_pr.c

index 7c6a95bcb35e4ec043679338211f2435f98ccd33,0b68c2ebce95523473d575cab8cb52fe0f410d7d..bcd88ec99793ba554496369d8853ee391cef8c11
@@@ -1326,19 -1326,21 +1326,19 @@@ static int iscsit_do_rx_data
        struct iscsi_conn *conn,
        struct iscsi_data_count *count)
  {
 -      int data = count->data_length, rx_loop = 0, total_rx = 0, iov_len;
 -      struct kvec *iov_p;
 +      int data = count->data_length, rx_loop = 0, total_rx = 0;
        struct msghdr msg;
  
        if (!conn || !conn->sock || !conn->conn_ops)
                return -1;
  
        memset(&msg, 0, sizeof(struct msghdr));
 -
 -      iov_p = count->iov;
 -      iov_len = count->iov_count;
 +      iov_iter_kvec(&msg.msg_iter, READ | ITER_KVEC,
 +                    count->iov, count->iov_count, data);
  
        while (total_rx < data) {
 -              rx_loop = kernel_recvmsg(conn->sock, &msg, iov_p, iov_len,
 -                                      (data - total_rx), MSG_WAITALL);
 +              rx_loop = sock_recvmsg(conn->sock, &msg,
 +                                    (data - total_rx), MSG_WAITALL);
                if (rx_loop <= 0) {
                        pr_debug("rx_loop: %d total_rx: %d\n",
                                rx_loop, total_rx);
@@@ -1356,15 -1358,15 +1356,15 @@@ static int iscsit_do_tx_data
        struct iscsi_conn *conn,
        struct iscsi_data_count *count)
  {
-       int data = count->data_length, total_tx = 0, tx_loop = 0, iov_len;
+       int ret, iov_len;
        struct kvec *iov_p;
        struct msghdr msg;
  
        if (!conn || !conn->sock || !conn->conn_ops)
                return -1;
  
-       if (data <= 0) {
-               pr_err("Data length is: %d\n", data);
+       if (count->data_length <= 0) {
+               pr_err("Data length is: %d\n", count->data_length);
                return -1;
        }
  
        iov_p = count->iov;
        iov_len = count->iov_count;
  
-       while (total_tx < data) {
-               tx_loop = kernel_sendmsg(conn->sock, &msg, iov_p, iov_len,
-                                       (data - total_tx));
-               if (tx_loop <= 0) {
-                       pr_debug("tx_loop: %d total_tx %d\n",
-                               tx_loop, total_tx);
-                       return tx_loop;
-               }
-               total_tx += tx_loop;
-               pr_debug("tx_loop: %d, total_tx: %d, data: %d\n",
-                                       tx_loop, total_tx, data);
+       ret = kernel_sendmsg(conn->sock, &msg, iov_p, iov_len,
+                            count->data_length);
+       if (ret != count->data_length) {
+               pr_err("Unexpected ret: %d send data %d\n",
+                      ret, count->data_length);
+               return -EPIPE;
        }
+       pr_debug("ret: %d, sent data: %d\n", ret, count->data_length);
  
-       return total_tx;
+       return ret;
  }
  
  int rx_data(
index 4d1b7224a7f2559d200ec11aaced60a8d67c4a6c,0be83e788df2fd80039c721729b1cde3859e3c86..7172a71f9f0be562e6acd3dc9224ef4d04b31826
@@@ -110,6 -110,58 +110,6 @@@ static struct device_driver tcm_loop_dr
   */
  struct device *tcm_loop_primary;
  
 -/*
 - * Copied from drivers/scsi/libfc/fc_fcp.c:fc_change_queue_depth() and
 - * drivers/scsi/libiscsi.c:iscsi_change_queue_depth()
 - */
 -static int tcm_loop_change_queue_depth(
 -      struct scsi_device *sdev,
 -      int depth,
 -      int reason)
 -{
 -      switch (reason) {
 -      case SCSI_QDEPTH_DEFAULT:
 -              scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth);
 -              break;
 -      case SCSI_QDEPTH_QFULL:
 -              scsi_track_queue_full(sdev, depth);
 -              break;
 -      case SCSI_QDEPTH_RAMP_UP:
 -              scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth);
 -              break;
 -      default:
 -              return -EOPNOTSUPP;
 -      }
 -      return sdev->queue_depth;
 -}
 -
 -static int tcm_loop_change_queue_type(struct scsi_device *sdev, int tag)
 -{
 -      if (sdev->tagged_supported) {
 -              scsi_set_tag_type(sdev, tag);
 -
 -              if (tag)
 -                      scsi_activate_tcq(sdev, sdev->queue_depth);
 -              else
 -                      scsi_deactivate_tcq(sdev, sdev->queue_depth);
 -      } else
 -              tag = 0;
 -
 -      return tag;
 -}
 -
 -/*
 - * Locate the SAM Task Attr from struct scsi_cmnd *
 - */
 -static int tcm_loop_sam_attr(struct scsi_cmnd *sc, int tag)
 -{
 -      if (sc->device->tagged_supported &&
 -          sc->device->ordered_tags && tag >= 0)
 -              return MSG_ORDERED_TAG;
 -
 -      return MSG_SIMPLE_TAG;
 -}
 -
  static void tcm_loop_submission_work(struct work_struct *work)
  {
        struct tcm_loop_cmd *tl_cmd =
                set_host_byte(sc, DID_TRANSPORT_DISRUPTED);
                goto out_done;
        }
-       tl_nexus = tl_hba->tl_nexus;
+       tl_nexus = tl_tpg->tl_nexus;
        if (!tl_nexus) {
                scmd_printk(KERN_ERR, sc, "TCM_Loop I_T Nexus"
                                " does not exist\n");
  
        rc = target_submit_cmd_map_sgls(se_cmd, tl_nexus->se_sess, sc->cmnd,
                        &tl_cmd->tl_sense_buf[0], tl_cmd->sc->device->lun,
 -                      transfer_length, tcm_loop_sam_attr(sc, tl_cmd->sc_cmd_tag),
 +                      transfer_length, MSG_SIMPLE_TAG,
                        sc->sc_data_direction, 0,
                        scsi_sglist(sc), scsi_sg_count(sc),
                        sgl_bidi, sgl_bidi_count,
@@@ -218,16 -270,26 +218,26 @@@ static int tcm_loop_queuecommand(struc
   * to struct scsi_device
   */
  static int tcm_loop_issue_tmr(struct tcm_loop_tpg *tl_tpg,
-                             struct tcm_loop_nexus *tl_nexus,
                              int lun, int task, enum tcm_tmreq_table tmr)
  {
        struct se_cmd *se_cmd = NULL;
        struct se_session *se_sess;
        struct se_portal_group *se_tpg;
+       struct tcm_loop_nexus *tl_nexus;
        struct tcm_loop_cmd *tl_cmd = NULL;
        struct tcm_loop_tmr *tl_tmr = NULL;
        int ret = TMR_FUNCTION_FAILED, rc;
  
+       /*
+        * Locate the tl_nexus and se_sess pointers
+        */
+       tl_nexus = tl_tpg->tl_nexus;
+       if (!tl_nexus) {
+               pr_err("Unable to perform device reset without"
+                               " active I_T Nexus\n");
+               return ret;
+       }
        tl_cmd = kmem_cache_zalloc(tcm_loop_cmd_cache, GFP_KERNEL);
        if (!tl_cmd) {
                pr_err("Unable to allocate memory for tl_cmd\n");
  
        se_cmd = &tl_cmd->tl_se_cmd;
        se_tpg = &tl_tpg->tl_se_tpg;
-       se_sess = tl_nexus->se_sess;
+       se_sess = tl_tpg->tl_nexus->se_sess;
        /*
         * Initialize struct se_cmd descriptor from target_core_mod infrastructure
         */
@@@ -288,7 -350,6 +298,6 @@@ release
  static int tcm_loop_abort_task(struct scsi_cmnd *sc)
  {
        struct tcm_loop_hba *tl_hba;
-       struct tcm_loop_nexus *tl_nexus;
        struct tcm_loop_tpg *tl_tpg;
        int ret = FAILED;
  
         * Locate the tcm_loop_hba_t pointer
         */
        tl_hba = *(struct tcm_loop_hba **)shost_priv(sc->device->host);
-       /*
-        * Locate the tl_nexus and se_sess pointers
-        */
-       tl_nexus = tl_hba->tl_nexus;
-       if (!tl_nexus) {
-               pr_err("Unable to perform device reset without"
-                               " active I_T Nexus\n");
-               return FAILED;
-       }
-       /*
-        * Locate the tl_tpg pointer from TargetID in sc->device->id
-        */
        tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id];
-       ret = tcm_loop_issue_tmr(tl_tpg, tl_nexus, sc->device->lun,
+       ret = tcm_loop_issue_tmr(tl_tpg, sc->device->lun,
                                 sc->request->tag, TMR_ABORT_TASK);
        return (ret == TMR_FUNCTION_COMPLETE) ? SUCCESS : FAILED;
  }
  static int tcm_loop_device_reset(struct scsi_cmnd *sc)
  {
        struct tcm_loop_hba *tl_hba;
-       struct tcm_loop_nexus *tl_nexus;
        struct tcm_loop_tpg *tl_tpg;
        int ret = FAILED;
  
         * Locate the tcm_loop_hba_t pointer
         */
        tl_hba = *(struct tcm_loop_hba **)shost_priv(sc->device->host);
-       /*
-        * Locate the tl_nexus and se_sess pointers
-        */
-       tl_nexus = tl_hba->tl_nexus;
-       if (!tl_nexus) {
-               pr_err("Unable to perform device reset without"
-                               " active I_T Nexus\n");
-               return FAILED;
-       }
-       /*
-        * Locate the tl_tpg pointer from TargetID in sc->device->id
-        */
        tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id];
-       ret = tcm_loop_issue_tmr(tl_tpg, tl_nexus, sc->device->lun,
+       ret = tcm_loop_issue_tmr(tl_tpg, sc->device->lun,
                                 0, TMR_LUN_RESET);
        return (ret == TMR_FUNCTION_COMPLETE) ? SUCCESS : FAILED;
  }
@@@ -379,13 -415,27 +363,13 @@@ static int tcm_loop_slave_alloc(struct 
        return 0;
  }
  
 -static int tcm_loop_slave_configure(struct scsi_device *sd)
 -{
 -      if (sd->tagged_supported) {
 -              scsi_activate_tcq(sd, sd->queue_depth);
 -              scsi_adjust_queue_depth(sd, MSG_SIMPLE_TAG,
 -                                      sd->host->cmd_per_lun);
 -      } else {
 -              scsi_adjust_queue_depth(sd, 0,
 -                                      sd->host->cmd_per_lun);
 -      }
 -
 -      return 0;
 -}
 -
  static struct scsi_host_template tcm_loop_driver_template = {
        .show_info              = tcm_loop_show_info,
        .proc_name              = "tcm_loopback",
        .name                   = "TCM_Loopback",
        .queuecommand           = tcm_loop_queuecommand,
 -      .change_queue_depth     = tcm_loop_change_queue_depth,
 -      .change_queue_type      = tcm_loop_change_queue_type,
 +      .change_queue_depth     = scsi_change_queue_depth,
 +      .change_queue_type      = scsi_change_queue_type,
        .eh_abort_handler = tcm_loop_abort_task,
        .eh_device_reset_handler = tcm_loop_device_reset,
        .eh_target_reset_handler = tcm_loop_target_reset,
        .max_sectors            = 0xFFFF,
        .use_clustering         = DISABLE_CLUSTERING,
        .slave_alloc            = tcm_loop_slave_alloc,
 -      .slave_configure        = tcm_loop_slave_configure,
        .module                 = THIS_MODULE,
 +      .use_blk_tags           = 1,
 +      .track_queue_depth      = 1,
  };
  
  static int tcm_loop_driver_probe(struct device *dev)
@@@ -940,8 -989,8 +924,8 @@@ static int tcm_loop_make_nexus
        struct tcm_loop_nexus *tl_nexus;
        int ret = -ENOMEM;
  
-       if (tl_tpg->tl_hba->tl_nexus) {
-               pr_debug("tl_tpg->tl_hba->tl_nexus already exists\n");
+       if (tl_tpg->tl_nexus) {
+               pr_debug("tl_tpg->tl_nexus already exists\n");
                return -EEXIST;
        }
        se_tpg = &tl_tpg->tl_se_tpg;
         */
        __transport_register_session(se_tpg, tl_nexus->se_sess->se_node_acl,
                        tl_nexus->se_sess, tl_nexus);
-       tl_tpg->tl_hba->tl_nexus = tl_nexus;
+       tl_tpg->tl_nexus = tl_nexus;
        pr_debug("TCM_Loop_ConfigFS: Established I_T Nexus to emulated"
                " %s Initiator Port: %s\n", tcm_loop_dump_proto_id(tl_hba),
                name);
@@@ -992,12 -1041,8 +976,8 @@@ static int tcm_loop_drop_nexus
  {
        struct se_session *se_sess;
        struct tcm_loop_nexus *tl_nexus;
-       struct tcm_loop_hba *tl_hba = tpg->tl_hba;
  
-       if (!tl_hba)
-               return -ENODEV;
-       tl_nexus = tl_hba->tl_nexus;
+       tl_nexus = tpg->tl_nexus;
        if (!tl_nexus)
                return -ENODEV;
  
        }
  
        pr_debug("TCM_Loop_ConfigFS: Removing I_T Nexus to emulated"
-               " %s Initiator Port: %s\n", tcm_loop_dump_proto_id(tl_hba),
+               " %s Initiator Port: %s\n", tcm_loop_dump_proto_id(tpg->tl_hba),
                tl_nexus->se_sess->se_node_acl->initiatorname);
        /*
         * Release the SCSI I_T Nexus to the emulated SAS Target Port
         */
        transport_deregister_session(tl_nexus->se_sess);
-       tpg->tl_hba->tl_nexus = NULL;
+       tpg->tl_nexus = NULL;
        kfree(tl_nexus);
        return 0;
  }
@@@ -1035,7 -1080,7 +1015,7 @@@ static ssize_t tcm_loop_tpg_show_nexus
        struct tcm_loop_nexus *tl_nexus;
        ssize_t ret;
  
-       tl_nexus = tl_tpg->tl_hba->tl_nexus;
+       tl_nexus = tl_tpg->tl_nexus;
        if (!tl_nexus)
                return -ENODEV;
  
index 4c261c33cf55e62394db8b22cf312fdbb352d8f7,f75a4baf6e568e6608f46a608282e049ab6cf835..d56f2aaba9af9a6bb4b89d5c1080d426cba84e63
@@@ -76,7 -76,7 +76,7 @@@ enum preempt_type 
  };
  
  static void __core_scsi3_complete_pro_release(struct se_device *, struct se_node_acl *,
-                       struct t10_pr_registration *, int);
+                                             struct t10_pr_registration *, int, int);
  
  static sense_reason_t
  target_scsi2_reservation_check(struct se_cmd *cmd)
@@@ -459,7 -459,7 +459,7 @@@ static int core_scsi3_pr_seq_non_holder
        case ACCESS_CONTROL_OUT:
        case INQUIRY:
        case LOG_SENSE:
 -      case READ_MEDIA_SERIAL_NUMBER:
 +      case SERVICE_ACTION_IN_12:
        case REPORT_LUNS:
        case REQUEST_SENSE:
        case PERSISTENT_RESERVE_IN:
@@@ -1177,7 -1177,7 +1177,7 @@@ static int core_scsi3_check_implicit_re
                 *    service action with the SERVICE ACTION RESERVATION KEY
                 *    field set to zero (see 5.7.11.3).
                 */
-               __core_scsi3_complete_pro_release(dev, nacl, pr_reg, 0);
+               __core_scsi3_complete_pro_release(dev, nacl, pr_reg, 0, 1);
                ret = 1;
                /*
                 * For 'All Registrants' reservation types, all existing
@@@ -1219,7 -1219,8 +1219,8 @@@ static void __core_scsi3_free_registrat
  
        pr_reg->pr_reg_deve->def_pr_registered = 0;
        pr_reg->pr_reg_deve->pr_res_key = 0;
-       list_del(&pr_reg->pr_reg_list);
+       if (!list_empty(&pr_reg->pr_reg_list))
+               list_del(&pr_reg->pr_reg_list);
        /*
         * Caller accessing *pr_reg using core_scsi3_locate_pr_reg(),
         * so call core_scsi3_put_pr_reg() to decrement our reference.
@@@ -1271,6 -1272,7 +1272,7 @@@ void core_scsi3_free_pr_reg_from_nacl
  {
        struct t10_reservation *pr_tmpl = &dev->t10_pr;
        struct t10_pr_registration *pr_reg, *pr_reg_tmp, *pr_res_holder;
+       bool free_reg = false;
        /*
         * If the passed se_node_acl matches the reservation holder,
         * release the reservation.
        spin_lock(&dev->dev_reservation_lock);
        pr_res_holder = dev->dev_pr_res_holder;
        if ((pr_res_holder != NULL) &&
-           (pr_res_holder->pr_reg_nacl == nacl))
-               __core_scsi3_complete_pro_release(dev, nacl, pr_res_holder, 0);
+           (pr_res_holder->pr_reg_nacl == nacl)) {
+               __core_scsi3_complete_pro_release(dev, nacl, pr_res_holder, 0, 1);
+               free_reg = true;
+       }
        spin_unlock(&dev->dev_reservation_lock);
        /*
         * Release any registration associated with the struct se_node_acl.
         */
        spin_lock(&pr_tmpl->registration_lock);
+       if (pr_res_holder && free_reg)
+               __core_scsi3_free_registration(dev, pr_res_holder, NULL, 0);
        list_for_each_entry_safe(pr_reg, pr_reg_tmp,
                        &pr_tmpl->registration_list, pr_reg_list) {
  
@@@ -1307,7 -1314,7 +1314,7 @@@ void core_scsi3_free_all_registrations
        if (pr_res_holder != NULL) {
                struct se_node_acl *pr_res_nacl = pr_res_holder->pr_reg_nacl;
                __core_scsi3_complete_pro_release(dev, pr_res_nacl,
-                               pr_res_holder, 0);
+                                                 pr_res_holder, 0, 0);
        }
        spin_unlock(&dev->dev_reservation_lock);
  
@@@ -1429,14 -1436,12 +1436,12 @@@ core_scsi3_decode_spec_i_port
        struct target_core_fabric_ops *tmp_tf_ops;
        unsigned char *buf;
        unsigned char *ptr, *i_str = NULL, proto_ident, tmp_proto_ident;
-       char *iport_ptr = NULL, dest_iport[64], i_buf[PR_REG_ISID_ID_LEN];
+       char *iport_ptr = NULL, i_buf[PR_REG_ISID_ID_LEN];
        sense_reason_t ret;
        u32 tpdl, tid_len = 0;
        int dest_local_nexus;
        u32 dest_rtpi = 0;
  
-       memset(dest_iport, 0, 64);
        local_se_deve = se_sess->se_node_acl->device_list[cmd->orig_fe_lun];
        /*
         * Allocate a struct pr_transport_id_holder and setup the
@@@ -2105,13 -2110,13 +2110,13 @@@ core_scsi3_emulate_pro_register(struct 
                /*
                 * sa_res_key=0 Unregister Reservation Key for registered I_T Nexus.
                 */
-               pr_holder = core_scsi3_check_implicit_release(
-                               cmd->se_dev, pr_reg);
+               type = pr_reg->pr_res_type;
+               pr_holder = core_scsi3_check_implicit_release(cmd->se_dev,
+                                                             pr_reg);
                if (pr_holder < 0) {
                        ret = TCM_RESERVATION_CONFLICT;
                        goto out;
                }
-               type = pr_reg->pr_res_type;
  
                spin_lock(&pr_tmpl->registration_lock);
                /*
@@@ -2269,6 -2274,7 +2274,7 @@@ core_scsi3_pro_reserve(struct se_cmd *c
        spin_lock(&dev->dev_reservation_lock);
        pr_res_holder = dev->dev_pr_res_holder;
        if (pr_res_holder) {
+               int pr_res_type = pr_res_holder->pr_res_type;
                /*
                 * From spc4r17 Section 5.7.9: Reserving:
                 *
                 * the logical unit, then the command shall be completed with
                 * RESERVATION CONFLICT status.
                 */
-               if (pr_res_holder != pr_reg) {
+               if ((pr_res_holder != pr_reg) &&
+                   (pr_res_type != PR_TYPE_WRITE_EXCLUSIVE_ALLREG) &&
+                   (pr_res_type != PR_TYPE_EXCLUSIVE_ACCESS_ALLREG)) {
                        struct se_node_acl *pr_res_nacl = pr_res_holder->pr_reg_nacl;
                        pr_err("SPC-3 PR: Attempted RESERVE from"
                                " [%s]: %s while reservation already held by"
@@@ -2385,23 -2393,59 +2393,59 @@@ static void __core_scsi3_complete_pro_r
        struct se_device *dev,
        struct se_node_acl *se_nacl,
        struct t10_pr_registration *pr_reg,
-       int explicit)
+       int explicit,
+       int unreg)
  {
        struct target_core_fabric_ops *tfo = se_nacl->se_tpg->se_tpg_tfo;
        char i_buf[PR_REG_ISID_ID_LEN];
+       int pr_res_type = 0, pr_res_scope = 0;
  
        memset(i_buf, 0, PR_REG_ISID_ID_LEN);
        core_pr_dump_initiator_port(pr_reg, i_buf, PR_REG_ISID_ID_LEN);
        /*
         * Go ahead and release the current PR reservation holder.
-        */
-       dev->dev_pr_res_holder = NULL;
+        * If an All Registrants reservation is currently active and
+        * a unregister operation is requested, replace the current
+        * dev_pr_res_holder with another active registration.
+        */
+       if (dev->dev_pr_res_holder) {
+               pr_res_type = dev->dev_pr_res_holder->pr_res_type;
+               pr_res_scope = dev->dev_pr_res_holder->pr_res_scope;
+               dev->dev_pr_res_holder->pr_res_type = 0;
+               dev->dev_pr_res_holder->pr_res_scope = 0;
+               dev->dev_pr_res_holder->pr_res_holder = 0;
+               dev->dev_pr_res_holder = NULL;
+       }
+       if (!unreg)
+               goto out;
  
-       pr_debug("SPC-3 PR [%s] Service Action: %s RELEASE cleared"
-               " reservation holder TYPE: %s ALL_TG_PT: %d\n",
-               tfo->get_fabric_name(), (explicit) ? "explicit" : "implicit",
-               core_scsi3_pr_dump_type(pr_reg->pr_res_type),
-               (pr_reg->pr_reg_all_tg_pt) ? 1 : 0);
+       spin_lock(&dev->t10_pr.registration_lock);
+       list_del_init(&pr_reg->pr_reg_list);
+       /*
+        * If the I_T nexus is a reservation holder, the persistent reservation
+        * is of an all registrants type, and the I_T nexus is the last remaining
+        * registered I_T nexus, then the device server shall also release the
+        * persistent reservation.
+        */
+       if (!list_empty(&dev->t10_pr.registration_list) &&
+           ((pr_res_type == PR_TYPE_WRITE_EXCLUSIVE_ALLREG) ||
+            (pr_res_type == PR_TYPE_EXCLUSIVE_ACCESS_ALLREG))) {
+               dev->dev_pr_res_holder =
+                       list_entry(dev->t10_pr.registration_list.next,
+                                  struct t10_pr_registration, pr_reg_list);
+               dev->dev_pr_res_holder->pr_res_type = pr_res_type;
+               dev->dev_pr_res_holder->pr_res_scope = pr_res_scope;
+               dev->dev_pr_res_holder->pr_res_holder = 1;
+       }
+       spin_unlock(&dev->t10_pr.registration_lock);
+ out:
+       if (!dev->dev_pr_res_holder) {
+               pr_debug("SPC-3 PR [%s] Service Action: %s RELEASE cleared"
+                       " reservation holder TYPE: %s ALL_TG_PT: %d\n",
+                       tfo->get_fabric_name(), (explicit) ? "explicit" :
+                       "implicit", core_scsi3_pr_dump_type(pr_res_type),
+                       (pr_reg->pr_reg_all_tg_pt) ? 1 : 0);
+       }
        pr_debug("SPC-3 PR [%s] RELEASE Node: %s%s\n",
                tfo->get_fabric_name(), se_nacl->initiatorname,
                i_buf);
@@@ -2532,7 -2576,7 +2576,7 @@@ core_scsi3_emulate_pro_release(struct s
         *    server shall not establish a unit attention condition.
         */
        __core_scsi3_complete_pro_release(dev, se_sess->se_node_acl,
-                       pr_reg, 1);
+                                         pr_reg, 1, 0);
  
        spin_unlock(&dev->dev_reservation_lock);
  
@@@ -2620,7 -2664,7 +2664,7 @@@ core_scsi3_emulate_pro_clear(struct se_
        if (pr_res_holder) {
                struct se_node_acl *pr_res_nacl = pr_res_holder->pr_reg_nacl;
                __core_scsi3_complete_pro_release(dev, pr_res_nacl,
-                       pr_res_holder, 0);
+                                                 pr_res_holder, 0, 0);
        }
        spin_unlock(&dev->dev_reservation_lock);
        /*
@@@ -2679,7 -2723,7 +2723,7 @@@ static void __core_scsi3_complete_pro_p
         */
        if (dev->dev_pr_res_holder)
                __core_scsi3_complete_pro_release(dev, nacl,
-                               dev->dev_pr_res_holder, 0);
+                                                 dev->dev_pr_res_holder, 0, 0);
  
        dev->dev_pr_res_holder = pr_reg;
        pr_reg->pr_res_holder = 1;
@@@ -2924,8 -2968,8 +2968,8 @@@ core_scsi3_pro_preempt(struct se_cmd *c
         */
        if (pr_reg_n != pr_res_holder)
                __core_scsi3_complete_pro_release(dev,
-                               pr_res_holder->pr_reg_nacl,
-                               dev->dev_pr_res_holder, 0);
+                                                 pr_res_holder->pr_reg_nacl,
+                                                 dev->dev_pr_res_holder, 0, 0);
        /*
         * b) Remove the registrations for all I_T nexuses identified
         *    by the SERVICE ACTION RESERVATION KEY field, except the
@@@ -3059,7 -3103,7 +3103,7 @@@ core_scsi3_emulate_pro_register_and_mov
        struct t10_reservation *pr_tmpl = &dev->t10_pr;
        unsigned char *buf;
        unsigned char *initiator_str;
-       char *iport_ptr = NULL, dest_iport[64], i_buf[PR_REG_ISID_ID_LEN];
+       char *iport_ptr = NULL, i_buf[PR_REG_ISID_ID_LEN];
        u32 tid_len, tmp_tid_len;
        int new_reg = 0, type, scope, matching_iname;
        sense_reason_t ret;
                return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
        }
  
-       memset(dest_iport, 0, 64);
        memset(i_buf, 0, PR_REG_ISID_ID_LEN);
        se_tpg = se_sess->se_tpg;
        tf_ops = se_tpg->se_tpg_tfo;
@@@ -3389,7 -3432,7 +3432,7 @@@ after_iport_check
         *    holder (i.e., the I_T nexus on which the
         */
        __core_scsi3_complete_pro_release(dev, pr_res_nacl,
-                       dev->dev_pr_res_holder, 0);
+                                         dev->dev_pr_res_holder, 0, 0);
        /*
         * g) Move the persistent reservation to the specified I_T nexus using
         *    the same scope and type as the persistent reservation released in
@@@ -3837,7 -3880,8 +3880,8 @@@ core_scsi3_pri_read_full_status(struct 
        unsigned char *buf;
        u32 add_desc_len = 0, add_len = 0, desc_len, exp_desc_len;
        u32 off = 8; /* off into first Full Status descriptor */
-       int format_code = 0;
+       int format_code = 0, pr_res_type = 0, pr_res_scope = 0;
+       bool all_reg = false;
  
        if (cmd->data_length < 8) {
                pr_err("PRIN SA READ_FULL_STATUS SCSI Data Length: %u"
        buf[2] = ((dev->t10_pr.pr_generation >> 8) & 0xff);
        buf[3] = (dev->t10_pr.pr_generation & 0xff);
  
+       spin_lock(&dev->dev_reservation_lock);
+       if (dev->dev_pr_res_holder) {
+               struct t10_pr_registration *pr_holder = dev->dev_pr_res_holder;
+               if (pr_holder->pr_res_type == PR_TYPE_WRITE_EXCLUSIVE_ALLREG ||
+                   pr_holder->pr_res_type == PR_TYPE_EXCLUSIVE_ACCESS_ALLREG) {
+                       all_reg = true;
+                       pr_res_type = pr_holder->pr_res_type;
+                       pr_res_scope = pr_holder->pr_res_scope;
+               }
+       }
+       spin_unlock(&dev->dev_reservation_lock);
        spin_lock(&pr_tmpl->registration_lock);
        list_for_each_entry_safe(pr_reg, pr_reg_tmp,
                        &pr_tmpl->registration_list, pr_reg_list) {
                 * reservation holder for PR_HOLDER bit.
                 *
                 * Also, if this registration is the reservation
-                * holder, fill in SCOPE and TYPE in the next byte.
+                * holder or there is an All Registrants reservation
+                * active, fill in SCOPE and TYPE in the next byte.
                 */
                if (pr_reg->pr_res_holder) {
                        buf[off++] |= 0x01;
                        buf[off++] = (pr_reg->pr_res_scope & 0xf0) |
                                     (pr_reg->pr_res_type & 0x0f);
-               } else
+               } else if (all_reg) {
+                       buf[off++] |= 0x01;
+                       buf[off++] = (pr_res_scope & 0xf0) |
+                                    (pr_res_type & 0x0f);
+               } else {
                        off += 2;
+               }
  
                off += 4; /* Skip over reserved area */
                /*