Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/nab/target...
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 12 Sep 2013 23:11:45 +0000 (16:11 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 12 Sep 2013 23:11:45 +0000 (16:11 -0700)
Pull SCSI target updates from Nicholas Bellinger:
 "Lots of activity again this round for I/O performance optimizations
  (per-cpu IDA pre-allocation for vhost + iscsi/target), and the
  addition of new fabric independent features to target-core
  (COMPARE_AND_WRITE + EXTENDED_COPY).

  The main highlights include:

   - Support for iscsi-target login multiplexing across individual
     network portals
   - Generic Per-cpu IDA logic (kent + akpm + clameter)
   - Conversion of vhost to use per-cpu IDA pre-allocation for
     descriptors, SGLs and userspace page pointer list
   - Conversion of iscsi-target + iser-target to use per-cpu IDA
     pre-allocation for descriptors
   - Add support for generic COMPARE_AND_WRITE (AtomicTestandSet)
     emulation for virtual backend drivers
   - Add support for generic EXTENDED_COPY (CopyOffload) emulation for
     virtual backend drivers.
   - Add support for fast memory registration mode to iser-target (Vu)

  The patches to add COMPARE_AND_WRITE and EXTENDED_COPY support are of
  particular significance, which make us the first and only open source
  target to support the full set of VAAI primitives.

  Currently Linux clients are lacking upstream support to actually
  utilize these primitives.  However, with server side support now in
  place for folks like MKP + ZAB working on the client, this logic once
  reserved for the highest end of storage arrays, can now be run in VMs
  on their laptops"

* 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/nab/target-pending: (50 commits)
  target/iscsi: Bump versions to v4.1.0
  target: Update copyright ownership/year information to 2013
  iscsi-target: Bump default TCP listen backlog to 256
  target: Fix >= v3.9+ regression in PR APTPL + ALUA metadata write-out
  iscsi-target; Bump default CmdSN Depth to 64
  iscsi-target: Remove unnecessary wait_for_completion in iscsi_get_thread_set
  iscsi-target: Add thread_set->ts_activate_sem + use common deallocate
  iscsi-target: Fix race with thread_pre_handler flush_signals + ISCSI_THREAD_SET_DIE
  target: remove unused including <linux/version.h>
  iser-target: introduce fast memory registration mode (FRWR)
  iser-target: generalize rdma memory registration and cleanup
  iser-target: move rdma wr processing to a shared function
  target: Enable global EXTENDED_COPY setup/release
  target: Add Third Party Copy (3PC) bit in INQUIRY response
  target: Enable EXTENDED_COPY setup in spc_parse_cdb
  target: Add support for EXTENDED_COPY copy offload emulation
  target: Avoid non-existent tg_pt_gp_mem in target_alua_state_check
  target: Add global device list for EXTENDED_COPY
  target: Make helpers non static for EXTENDED_COPY command setup
  target: Make spc_parse_naa_6h_vendor_specific non static
  ...

1  2 
drivers/scsi/qla2xxx/qla_target.c
drivers/scsi/qla2xxx/tcm_qla2xxx.c
drivers/target/iscsi/iscsi_target.c
drivers/target/iscsi/iscsi_target_login.c
drivers/target/target_core_spc.c
drivers/target/target_core_transport.c
include/scsi/scsi.h
lib/Makefile

@@@ -10,7 -10,7 +10,7 @@@
   *
   *  Forward port and refactoring to modern qla2xxx and target/configfs
   *
-  *  Copyright (C) 2010-2011 Nicholas A. Bellinger <nab@kernel.org>
+  *  Copyright (C) 2010-2013 Nicholas A. Bellinger <nab@kernel.org>
   *
   *  This program is free software; you can redistribute it and/or
   *  modify it under the terms of the GNU General Public License
@@@ -430,8 -430,13 +430,8 @@@ static int qlt_reset(struct scsi_qla_ho
        }
  
        ql_dbg(ql_dbg_tgt, vha, 0xe047,
 -          "scsi(%ld): resetting (session %p from port "
 -          "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x, "
 -          "mcmd %x, loop_id %d)\n", vha->host_no, sess,
 -          sess->port_name[0], sess->port_name[1],
 -          sess->port_name[2], sess->port_name[3],
 -          sess->port_name[4], sess->port_name[5],
 -          sess->port_name[6], sess->port_name[7],
 +          "scsi(%ld): resetting (session %p from port %8phC mcmd %x, "
 +          "loop_id %d)\n", vha->host_no, sess, sess->port_name,
            mcmd, loop_id);
  
        lun = a->u.isp24.fcp_cmnd.lun;
@@@ -462,10 -467,15 +462,10 @@@ static void qlt_schedule_sess_for_delet
        sess->expires = jiffies + dev_loss_tmo * HZ;
  
        ql_dbg(ql_dbg_tgt, sess->vha, 0xe048,
 -          "qla_target(%d): session for port %02x:%02x:%02x:"
 -          "%02x:%02x:%02x:%02x:%02x (loop ID %d) scheduled for "
 +          "qla_target(%d): session for port %8phC (loop ID %d) scheduled for "
            "deletion in %u secs (expires: %lu) immed: %d\n",
 -          sess->vha->vp_idx,
 -          sess->port_name[0], sess->port_name[1],
 -          sess->port_name[2], sess->port_name[3],
 -          sess->port_name[4], sess->port_name[5],
 -          sess->port_name[6], sess->port_name[7],
 -          sess->loop_id, dev_loss_tmo, sess->expires, immediate);
 +          sess->vha->vp_idx, sess->port_name, sess->loop_id, dev_loss_tmo,
 +          sess->expires, immediate);
  
        if (immediate)
                schedule_delayed_work(&tgt->sess_del_work, 0);
@@@ -620,9 -630,13 +620,9 @@@ static struct qla_tgt_sess *qlt_create_
        sess = kzalloc(sizeof(*sess), GFP_KERNEL);
        if (!sess) {
                ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04a,
 -                  "qla_target(%u): session allocation failed, "
 -                  "all commands from port %02x:%02x:%02x:%02x:"
 -                  "%02x:%02x:%02x:%02x will be refused", vha->vp_idx,
 -                  fcport->port_name[0], fcport->port_name[1],
 -                  fcport->port_name[2], fcport->port_name[3],
 -                  fcport->port_name[4], fcport->port_name[5],
 -                  fcport->port_name[6], fcport->port_name[7]);
 +                  "qla_target(%u): session allocation failed, all commands "
 +                  "from port %8phC will be refused", vha->vp_idx,
 +                  fcport->port_name);
  
                return NULL;
        }
        spin_unlock_irqrestore(&ha->hardware_lock, flags);
  
        ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04b,
 -          "qla_target(%d): %ssession for wwn %02x:%02x:%02x:%02x:"
 -          "%02x:%02x:%02x:%02x (loop_id %d, s_id %x:%x:%x, confirmed"
 -          " completion %ssupported) added\n",
 -          vha->vp_idx, local ?  "local " : "", fcport->port_name[0],
 -          fcport->port_name[1], fcport->port_name[2], fcport->port_name[3],
 -          fcport->port_name[4], fcport->port_name[5], fcport->port_name[6],
 -          fcport->port_name[7], fcport->loop_id, sess->s_id.b.domain,
 -          sess->s_id.b.area, sess->s_id.b.al_pa, sess->conf_compl_supported ?
 -          "" : "not ");
 +          "qla_target(%d): %ssession for wwn %8phC (loop_id %d, "
 +          "s_id %x:%x:%x, confirmed completion %ssupported) added\n",
 +          vha->vp_idx, local ?  "local " : "", fcport->port_name,
 +          fcport->loop_id, sess->s_id.b.domain, sess->s_id.b.area,
 +          sess->s_id.b.al_pa, sess->conf_compl_supported ?  "" : "not ");
  
        return sess;
  }
@@@ -712,9 -730,13 +712,9 @@@ void qlt_fc_port_added(struct scsi_qla_
                        qlt_undelete_sess(sess);
  
                        ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04c,
 -                          "qla_target(%u): %ssession for port %02x:"
 -                          "%02x:%02x:%02x:%02x:%02x:%02x:%02x (loop ID %d) "
 -                          "reappeared\n", vha->vp_idx, sess->local ? "local "
 -                          : "", sess->port_name[0], sess->port_name[1],
 -                          sess->port_name[2], sess->port_name[3],
 -                          sess->port_name[4], sess->port_name[5],
 -                          sess->port_name[6], sess->port_name[7],
 +                          "qla_target(%u): %ssession for port %8phC "
 +                          "(loop ID %d) reappeared\n", vha->vp_idx,
 +                          sess->local ? "local " : "", sess->port_name,
                            sess->loop_id);
  
                        ql_dbg(ql_dbg_tgt_mgt, vha, 0xf007,
        if (sess && sess->local) {
                ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04d,
                    "qla_target(%u): local session for "
 -                  "port %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x "
 -                  "(loop ID %d) became global\n", vha->vp_idx,
 -                  fcport->port_name[0], fcport->port_name[1],
 -                  fcport->port_name[2], fcport->port_name[3],
 -                  fcport->port_name[4], fcport->port_name[5],
 -                  fcport->port_name[6], fcport->port_name[7],
 -                  sess->loop_id);
 +                  "port %8phC (loop ID %d) became global\n", vha->vp_idx,
 +                  fcport->port_name, sess->loop_id);
                sess->local = 0;
        }
        ha->tgt.tgt_ops->put_sess(sess);
@@@ -2813,8 -2840,10 +2813,8 @@@ static int qlt_24xx_handle_els(struct s
        int res = 0;
  
        ql_dbg(ql_dbg_tgt_mgt, vha, 0xf026,
 -          "qla_target(%d): Port ID: 0x%02x:%02x:%02x"
 -          " ELS opcode: 0x%02x\n", vha->vp_idx, iocb->u.isp24.port_id[0],
 -          iocb->u.isp24.port_id[1], iocb->u.isp24.port_id[2],
 -          iocb->u.isp24.status_subcode);
 +          "qla_target(%d): Port ID: 0x%3phC ELS opcode: 0x%02x\n",
 +          vha->vp_idx, iocb->u.isp24.port_id, iocb->u.isp24.status_subcode);
  
        switch (iocb->u.isp24.status_subcode) {
        case ELS_PLOGI:
@@@ -2,12 -2,9 +2,9 @@@
   * This file contains tcm implementation using v4 configfs fabric infrastructure
   * for QLogic target mode HBAs
   *
-  * ?? Copyright 2010-2011 RisingTide Systems LLC.
+  * (c) Copyright 2010-2013 Datera, Inc.
   *
-  * Licensed to the Linux Foundation under the General Public License (GPL)
-  * version 2.
-  *
-  * Author: Nicholas A. Bellinger <nab@risingtidesystems.com>
+  * Author: Nicholas A. Bellinger <nab@daterainc.com>
   *
   * tcm_qla2xxx_parse_wwn() and tcm_qla2xxx_format_wwn() contains code from
   * the TCM_FC / Open-FCoE.org fabric module.
@@@ -360,6 -357,14 +357,14 @@@ static int tcm_qla2xxx_check_prod_write
        return QLA_TPG_ATTRIB(tpg)->prod_mode_write_protect;
  }
  
+ static int tcm_qla2xxx_check_demo_mode_login_only(struct se_portal_group *se_tpg)
+ {
+       struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,
+                               struct tcm_qla2xxx_tpg, se_tpg);
+       return QLA_TPG_ATTRIB(tpg)->demo_mode_login_only;
+ }
  static struct se_node_acl *tcm_qla2xxx_alloc_fabric_acl(
        struct se_portal_group *se_tpg)
  {
@@@ -489,38 -494,13 +494,13 @@@ static u32 tcm_qla2xxx_sess_get_index(s
        return 0;
  }
  
- /*
-  * The LIO target core uses DMA_TO_DEVICE to mean that data is going
-  * to the target (eg handling a WRITE) and DMA_FROM_DEVICE to mean
-  * that data is coming from the target (eg handling a READ).  However,
-  * this is just the opposite of what we have to tell the DMA mapping
-  * layer -- eg when handling a READ, the HBA will have to DMA the data
-  * out of memory so it can send it to the initiator, which means we
-  * need to use DMA_TO_DEVICE when we map the data.
-  */
- static enum dma_data_direction tcm_qla2xxx_mapping_dir(struct se_cmd *se_cmd)
- {
-       if (se_cmd->se_cmd_flags & SCF_BIDI)
-               return DMA_BIDIRECTIONAL;
-       switch (se_cmd->data_direction) {
-       case DMA_TO_DEVICE:
-               return DMA_FROM_DEVICE;
-       case DMA_FROM_DEVICE:
-               return DMA_TO_DEVICE;
-       case DMA_NONE:
-       default:
-               return DMA_NONE;
-       }
- }
  static int tcm_qla2xxx_write_pending(struct se_cmd *se_cmd)
  {
        struct qla_tgt_cmd *cmd = container_of(se_cmd,
                                struct qla_tgt_cmd, se_cmd);
  
        cmd->bufflen = se_cmd->data_length;
-       cmd->dma_data_direction = tcm_qla2xxx_mapping_dir(se_cmd);
+       cmd->dma_data_direction = target_reverse_dma_direction(se_cmd);
  
        cmd->sg_cnt = se_cmd->t_data_nents;
        cmd->sg = se_cmd->t_data_sg;
@@@ -656,7 -636,7 +636,7 @@@ static int tcm_qla2xxx_queue_data_in(st
                                struct qla_tgt_cmd, se_cmd);
  
        cmd->bufflen = se_cmd->data_length;
-       cmd->dma_data_direction = tcm_qla2xxx_mapping_dir(se_cmd);
+       cmd->dma_data_direction = target_reverse_dma_direction(se_cmd);
        cmd->aborted = (se_cmd->transport_state & CMD_T_ABORTED);
  
        cmd->sg_cnt = se_cmd->t_data_nents;
@@@ -680,7 -660,7 +660,7 @@@ static int tcm_qla2xxx_queue_status(str
        cmd->sg = NULL;
        cmd->sg_cnt = 0;
        cmd->offset = 0;
-       cmd->dma_data_direction = tcm_qla2xxx_mapping_dir(se_cmd);
+       cmd->dma_data_direction = target_reverse_dma_direction(se_cmd);
        cmd->aborted = (se_cmd->transport_state & CMD_T_ABORTED);
  
        if (se_cmd->data_direction == DMA_FROM_DEVICE) {
@@@ -939,11 -919,19 +919,19 @@@ DEF_QLA_TPG_ATTR_BOOL(prod_mode_write_p
  DEF_QLA_TPG_ATTRIB(prod_mode_write_protect);
  QLA_TPG_ATTR(prod_mode_write_protect, S_IRUGO | S_IWUSR);
  
+ /*
+  * Define tcm_qla2xxx_tpg_attrib_s_demo_mode_login_only
+  */
+ DEF_QLA_TPG_ATTR_BOOL(demo_mode_login_only);
+ DEF_QLA_TPG_ATTRIB(demo_mode_login_only);
+ QLA_TPG_ATTR(demo_mode_login_only, S_IRUGO | S_IWUSR);
  static struct configfs_attribute *tcm_qla2xxx_tpg_attrib_attrs[] = {
        &tcm_qla2xxx_tpg_attrib_generate_node_acls.attr,
        &tcm_qla2xxx_tpg_attrib_cache_dynamic_acls.attr,
        &tcm_qla2xxx_tpg_attrib_demo_mode_write_protect.attr,
        &tcm_qla2xxx_tpg_attrib_prod_mode_write_protect.attr,
+       &tcm_qla2xxx_tpg_attrib_demo_mode_login_only.attr,
        NULL,
  };
  
@@@ -1042,6 -1030,7 +1030,7 @@@ static struct se_portal_group *tcm_qla2
        QLA_TPG_ATTRIB(tpg)->generate_node_acls = 1;
        QLA_TPG_ATTRIB(tpg)->demo_mode_write_protect = 1;
        QLA_TPG_ATTRIB(tpg)->cache_dynamic_acls = 1;
+       QLA_TPG_ATTRIB(tpg)->demo_mode_login_only = 1;
  
        ret = core_tpg_register(&tcm_qla2xxx_fabric_configfs->tf_ops, wwn,
                                &tpg->se_tpg, tpg, TRANSPORT_TPG_TYPE_NORMAL);
@@@ -1474,11 -1463,15 +1463,11 @@@ static void tcm_qla2xxx_update_sess(str
  
  
        if (sess->loop_id != loop_id || sess->s_id.b24 != s_id.b24)
 -              pr_info("Updating session %p from port %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x loop_id %d -> %d s_id %x:%x:%x -> %x:%x:%x\n",
 -                      sess,
 -                      sess->port_name[0], sess->port_name[1],
 -                      sess->port_name[2], sess->port_name[3],
 -                      sess->port_name[4], sess->port_name[5],
 -                      sess->port_name[6], sess->port_name[7],
 -                      sess->loop_id, loop_id,
 -                      sess->s_id.b.domain, sess->s_id.b.area, sess->s_id.b.al_pa,
 -                      s_id.b.domain, s_id.b.area, s_id.b.al_pa);
 +              pr_info("Updating session %p from port %8phC loop_id %d -> %d s_id %x:%x:%x -> %x:%x:%x\n",
 +                  sess, sess->port_name,
 +                  sess->loop_id, loop_id, sess->s_id.b.domain,
 +                  sess->s_id.b.area, sess->s_id.b.al_pa, s_id.b.domain,
 +                  s_id.b.area, s_id.b.al_pa);
  
        if (sess->loop_id != loop_id) {
                /*
@@@ -1736,7 -1729,7 +1725,7 @@@ static struct target_core_fabric_ops tc
                                        tcm_qla2xxx_check_demo_write_protect,
        .tpg_check_prod_mode_write_protect =
                                        tcm_qla2xxx_check_prod_write_protect,
-       .tpg_check_demo_mode_login_only = tcm_qla2xxx_check_true,
+       .tpg_check_demo_mode_login_only = tcm_qla2xxx_check_demo_mode_login_only,
        .tpg_alloc_fabric_acl           = tcm_qla2xxx_alloc_fabric_acl,
        .tpg_release_fabric_acl         = tcm_qla2xxx_release_fabric_acl,
        .tpg_get_inst_index             = tcm_qla2xxx_tpg_get_inst_index,
@@@ -1784,7 -1777,7 +1773,7 @@@ static struct target_core_fabric_ops tc
        .tpg_check_demo_mode_cache      = tcm_qla2xxx_check_true,
        .tpg_check_demo_mode_write_protect = tcm_qla2xxx_check_true,
        .tpg_check_prod_mode_write_protect = tcm_qla2xxx_check_false,
-       .tpg_check_demo_mode_login_only = tcm_qla2xxx_check_true,
+       .tpg_check_demo_mode_login_only = tcm_qla2xxx_check_demo_mode_login_only,
        .tpg_alloc_fabric_acl           = tcm_qla2xxx_alloc_fabric_acl,
        .tpg_release_fabric_acl         = tcm_qla2xxx_release_fabric_acl,
        .tpg_get_inst_index             = tcm_qla2xxx_tpg_get_inst_index,
@@@ -1,9 -1,7 +1,7 @@@
  /*******************************************************************************
   * This file contains main functions related to the iSCSI Target Core Driver.
   *
-  * \u00a9 Copyright 2007-2011 RisingTide Systems LLC.
-  *
-  * Licensed to the Linux Foundation under the General Public License (GPL) version 2.
+  * (c) Copyright 2007-2013 Datera, Inc.
   *
   * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
   *
@@@ -63,7 -61,6 +61,6 @@@ spinlock_t sess_idr_lock
  
  struct iscsit_global *iscsit_global;
  
- struct kmem_cache *lio_cmd_cache;
  struct kmem_cache *lio_qr_cache;
  struct kmem_cache *lio_dr_cache;
  struct kmem_cache *lio_ooo_cache;
@@@ -220,11 -217,6 +217,6 @@@ int iscsit_access_np(struct iscsi_np *n
                spin_unlock_bh(&np->np_thread_lock);
                return -1;
        }
-       if (np->np_login_tpg) {
-               pr_err("np->np_login_tpg() is not NULL!\n");
-               spin_unlock_bh(&np->np_thread_lock);
-               return -1;
-       }
        spin_unlock_bh(&np->np_thread_lock);
        /*
         * Determine if the portal group is accepting storage traffic.
        /*
         * Here we serialize access across the TIQN+TPG Tuple.
         */
-       ret = mutex_lock_interruptible(&tpg->np_login_lock);
+       ret = down_interruptible(&tpg->np_login_sem);
        if ((ret != 0) || signal_pending(current))
                return -1;
  
-       spin_lock_bh(&np->np_thread_lock);
-       np->np_login_tpg = tpg;
-       spin_unlock_bh(&np->np_thread_lock);
+       spin_lock_bh(&tpg->tpg_state_lock);
+       if (tpg->tpg_state != TPG_STATE_ACTIVE) {
+               spin_unlock_bh(&tpg->tpg_state_lock);
+               up(&tpg->np_login_sem);
+               return -1;
+       }
+       spin_unlock_bh(&tpg->tpg_state_lock);
  
        return 0;
  }
  
- int iscsit_deaccess_np(struct iscsi_np *np, struct iscsi_portal_group *tpg)
+ void iscsit_login_kref_put(struct kref *kref)
+ {
+       struct iscsi_tpg_np *tpg_np = container_of(kref,
+                               struct iscsi_tpg_np, tpg_np_kref);
+       complete(&tpg_np->tpg_np_comp);
+ }
+ int iscsit_deaccess_np(struct iscsi_np *np, struct iscsi_portal_group *tpg,
+                      struct iscsi_tpg_np *tpg_np)
  {
        struct iscsi_tiqn *tiqn = tpg->tpg_tiqn;
  
-       spin_lock_bh(&np->np_thread_lock);
-       np->np_login_tpg = NULL;
-       spin_unlock_bh(&np->np_thread_lock);
+       up(&tpg->np_login_sem);
  
-       mutex_unlock(&tpg->np_login_lock);
+       if (tpg_np)
+               kref_put(&tpg_np->tpg_np_kref, iscsit_login_kref_put);
  
        if (tiqn)
                iscsit_put_tiqn_for_login(tiqn);
@@@ -410,20 -414,10 +414,10 @@@ struct iscsi_np *iscsit_add_np
  int iscsit_reset_np_thread(
        struct iscsi_np *np,
        struct iscsi_tpg_np *tpg_np,
-       struct iscsi_portal_group *tpg)
+       struct iscsi_portal_group *tpg,
+       bool shutdown)
  {
        spin_lock_bh(&np->np_thread_lock);
-       if (tpg && tpg_np) {
-               /*
-                * The reset operation need only be performed when the
-                * passed struct iscsi_portal_group has a login in progress
-                * to one of the network portals.
-                */
-               if (tpg_np->tpg_np->np_login_tpg != tpg) {
-                       spin_unlock_bh(&np->np_thread_lock);
-                       return 0;
-               }
-       }
        if (np->np_thread_state == ISCSI_NP_THREAD_INACTIVE) {
                spin_unlock_bh(&np->np_thread_lock);
                return 0;
        }
        spin_unlock_bh(&np->np_thread_lock);
  
+       if (tpg_np && shutdown) {
+               kref_put(&tpg_np->tpg_np_kref, iscsit_login_kref_put);
+               wait_for_completion(&tpg_np->tpg_np_comp);
+       }
        return 0;
  }
  
@@@ -497,7 -497,6 +497,6 @@@ static struct iscsit_transport iscsi_ta
        .iscsit_setup_np        = iscsit_setup_np,
        .iscsit_accept_np       = iscsit_accept_np,
        .iscsit_free_np         = iscsit_free_np,
-       .iscsit_alloc_cmd       = iscsit_alloc_cmd,
        .iscsit_get_login_rx    = iscsit_get_login_rx,
        .iscsit_put_login_tx    = iscsit_put_login_tx,
        .iscsit_get_dataout     = iscsit_build_r2ts_for_cmd,
@@@ -538,22 -537,13 +537,13 @@@ static int __init iscsi_target_init_mod
                goto ts_out1;
        }
  
-       lio_cmd_cache = kmem_cache_create("lio_cmd_cache",
-                       sizeof(struct iscsi_cmd), __alignof__(struct iscsi_cmd),
-                       0, NULL);
-       if (!lio_cmd_cache) {
-               pr_err("Unable to kmem_cache_create() for"
-                               " lio_cmd_cache\n");
-               goto ts_out2;
-       }
        lio_qr_cache = kmem_cache_create("lio_qr_cache",
                        sizeof(struct iscsi_queue_req),
                        __alignof__(struct iscsi_queue_req), 0, NULL);
        if (!lio_qr_cache) {
                pr_err("nable to kmem_cache_create() for"
                                " lio_qr_cache\n");
-               goto cmd_out;
+               goto ts_out2;
        }
  
        lio_dr_cache = kmem_cache_create("lio_dr_cache",
@@@ -597,8 -587,6 +587,6 @@@ dr_out
        kmem_cache_destroy(lio_dr_cache);
  qr_out:
        kmem_cache_destroy(lio_qr_cache);
- cmd_out:
-       kmem_cache_destroy(lio_cmd_cache);
  ts_out2:
        iscsi_deallocate_thread_sets();
  ts_out1:
@@@ -616,7 -604,6 +604,6 @@@ static void __exit iscsi_target_cleanup
        iscsi_thread_set_free();
        iscsit_release_discovery_tpg();
        iscsit_unregister_transport(&iscsi_target_transport);
-       kmem_cache_destroy(lio_cmd_cache);
        kmem_cache_destroy(lio_qr_cache);
        kmem_cache_destroy(lio_dr_cache);
        kmem_cache_destroy(lio_ooo_cache);
@@@ -1086,6 -1073,7 +1073,6 @@@ int iscsit_process_scsi_cmd(struct iscs
                if (cmd->reject_reason)
                        return 0;
  
 -              target_put_sess_cmd(conn->sess->se_sess, &cmd->se_cmd);
                return 1;
        }
        /*
@@@ -1123,10 -1111,14 +1110,10 @@@ after_immediate_data
                 */
                cmdsn_ret = iscsit_sequence_cmd(cmd->conn, cmd,
                                        (unsigned char *)hdr, hdr->cmdsn);
 -              if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER) {
 +              if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER)
                        return -1;
 -              } else if (cmdsn_ret == CMDSN_LOWER_THAN_EXP) {
 -                      target_put_sess_cmd(conn->sess->se_sess, &cmd->se_cmd);
 -                      return 0;
 -              }
  
 -              if (cmd->sense_reason) {
 +              if (cmd->sense_reason || cmdsn_ret == CMDSN_LOWER_THAN_EXP) {
                        int rc;
  
                        rc = iscsit_dump_data_payload(cmd->conn,
@@@ -1522,10 -1514,6 +1509,10 @@@ int iscsit_setup_nop_out(struct iscsi_c
        if (hdr->itt == RESERVED_ITT && !(hdr->opcode & ISCSI_OP_IMMEDIATE)) {
                pr_err("NOPOUT ITT is reserved, but Immediate Bit is"
                        " not set, protocol error.\n");
 +              if (!cmd)
 +                      return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR,
 +                                               (unsigned char *)hdr);
 +
                return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR,
                                         (unsigned char *)hdr);
        }
                        " greater than MaxXmitDataSegmentLength: %u, protocol"
                        " error.\n", payload_length,
                        conn->conn_ops->MaxXmitDataSegmentLength);
 +              if (!cmd)
 +                      return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR,
 +                                               (unsigned char *)hdr);
 +
                return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR,
                                         (unsigned char *)hdr);
        }
@@@ -3447,12 -3431,10 +3434,10 @@@ static int iscsit_build_sendtargets_res
                                bool inaddr_any = iscsit_check_inaddr_any(np);
  
                                len = sprintf(buf, "TargetAddress="
-                                       "%s%s%s:%hu,%hu",
-                                       (np->np_sockaddr.ss_family == AF_INET6) ?
-                                       "[" : "", (inaddr_any == false) ?
+                                       "%s:%hu,%hu",
+                                       (inaddr_any == false) ?
                                                np->np_ip : conn->local_ip,
-                                       (np->np_sockaddr.ss_family == AF_INET6) ?
-                                       "]" : "", (inaddr_any == false) ?
+                                       (inaddr_any == false) ?
                                                np->np_port : conn->local_port,
                                        tpg->tpgt);
                                len += 1;
@@@ -1,9 -1,7 +1,7 @@@
  /*******************************************************************************
   * This file contains the login functions used by the iSCSI Target driver.
   *
-  * \u00a9 Copyright 2007-2011 RisingTide Systems LLC.
-  *
-  * Licensed to the Linux Foundation under the General Public License (GPL) version 2.
+  * (c) Copyright 2007-2013 Datera, Inc.
   *
   * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
   *
@@@ -50,6 -48,7 +48,7 @@@ static struct iscsi_login *iscsi_login_
                pr_err("Unable to allocate memory for struct iscsi_login.\n");
                return NULL;
        }
+       conn->login = login;
        login->conn = conn;
        login->first_request = 1;
  
@@@ -428,7 -427,7 +427,7 @@@ static int iscsi_login_zero_tsih_s2
                                ISCSI_LOGIN_STATUS_NO_RESOURCES);
                        return -1;
                }
-               rc = strict_strtoul(param->value, 0, &mrdsl);
+               rc = kstrtoul(param->value, 0, &mrdsl);
                if (rc < 0) {
                        iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
                                ISCSI_LOGIN_STATUS_NO_RESOURCES);
@@@ -684,7 -683,7 +683,7 @@@ static void iscsi_post_login_start_time
                iscsit_start_nopin_timer(conn);
  }
  
static int iscsi_post_login_handler(
+ int iscsi_post_login_handler(
        struct iscsi_np *np,
        struct iscsi_conn *conn,
        u8 zero_tsih)
@@@ -872,7 -871,7 +871,7 @@@ int iscsit_setup_np
        struct __kernel_sockaddr_storage *sockaddr)
  {
        struct socket *sock = NULL;
-       int backlog = 5, ret, opt = 0, len;
+       int backlog = ISCSIT_TCP_BACKLOG, ret, opt = 0, len;
  
        switch (np->np_network_transport) {
        case ISCSI_TCP:
@@@ -1007,16 -1006,24 +1006,24 @@@ int iscsit_accept_np(struct iscsi_np *n
                rc = conn->sock->ops->getname(conn->sock,
                                (struct sockaddr *)&sock_in6, &err, 1);
                if (!rc) {
-                       snprintf(conn->login_ip, sizeof(conn->login_ip), "%pI6c",
-                               &sock_in6.sin6_addr.in6_u);
+                       if (!ipv6_addr_v4mapped(&sock_in6.sin6_addr))
+                               snprintf(conn->login_ip, sizeof(conn->login_ip), "[%pI6c]",
+                                       &sock_in6.sin6_addr.in6_u);
+                       else
+                               snprintf(conn->login_ip, sizeof(conn->login_ip), "%pI4",
+                                       &sock_in6.sin6_addr.s6_addr32[3]);
                        conn->login_port = ntohs(sock_in6.sin6_port);
                }
  
                rc = conn->sock->ops->getname(conn->sock,
                                (struct sockaddr *)&sock_in6, &err, 0);
                if (!rc) {
-                       snprintf(conn->local_ip, sizeof(conn->local_ip), "%pI6c",
-                               &sock_in6.sin6_addr.in6_u);
+                       if (!ipv6_addr_v4mapped(&sock_in6.sin6_addr))
+                               snprintf(conn->local_ip, sizeof(conn->local_ip), "[%pI6c]",
+                                       &sock_in6.sin6_addr.in6_u);
+                       else
+                               snprintf(conn->local_ip, sizeof(conn->local_ip), "%pI4",
+                                       &sock_in6.sin6_addr.s6_addr32[3]);
                        conn->local_port = ntohs(sock_in6.sin6_port);
                }
        } else {
@@@ -1116,6 -1123,77 +1123,77 @@@ iscsit_conn_set_transport(struct iscsi_
        return 0;
  }
  
+ void iscsi_target_login_sess_out(struct iscsi_conn *conn,
+               struct iscsi_np *np, bool zero_tsih, bool new_sess)
+ {
+       if (new_sess == false)
+               goto old_sess_out;
+       pr_err("iSCSI Login negotiation failed.\n");
+       iscsit_collect_login_stats(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+                                  ISCSI_LOGIN_STATUS_INIT_ERR);
+       if (!zero_tsih || !conn->sess)
+               goto old_sess_out;
+       if (conn->sess->se_sess)
+               transport_free_session(conn->sess->se_sess);
+       if (conn->sess->session_index != 0) {
+               spin_lock_bh(&sess_idr_lock);
+               idr_remove(&sess_idr, conn->sess->session_index);
+               spin_unlock_bh(&sess_idr_lock);
+       }
+       kfree(conn->sess->sess_ops);
+       kfree(conn->sess);
+ old_sess_out:
+       iscsi_stop_login_thread_timer(np);
+       /*
+        * If login negotiation fails check if the Time2Retain timer
+        * needs to be restarted.
+        */
+       if (!zero_tsih && conn->sess) {
+               spin_lock_bh(&conn->sess->conn_lock);
+               if (conn->sess->session_state == TARG_SESS_STATE_FAILED) {
+                       struct se_portal_group *se_tpg =
+                                       &ISCSI_TPG_C(conn)->tpg_se_tpg;
+                       atomic_set(&conn->sess->session_continuation, 0);
+                       spin_unlock_bh(&conn->sess->conn_lock);
+                       spin_lock_bh(&se_tpg->session_lock);
+                       iscsit_start_time2retain_handler(conn->sess);
+                       spin_unlock_bh(&se_tpg->session_lock);
+               } else
+                       spin_unlock_bh(&conn->sess->conn_lock);
+               iscsit_dec_session_usage_count(conn->sess);
+       }
+       if (!IS_ERR(conn->conn_rx_hash.tfm))
+               crypto_free_hash(conn->conn_rx_hash.tfm);
+       if (!IS_ERR(conn->conn_tx_hash.tfm))
+               crypto_free_hash(conn->conn_tx_hash.tfm);
+       if (conn->conn_cpumask)
+               free_cpumask_var(conn->conn_cpumask);
+       kfree(conn->conn_ops);
+       if (conn->param_list) {
+               iscsi_release_param_list(conn->param_list);
+               conn->param_list = NULL;
+       }
+       iscsi_target_nego_release(conn);
+       if (conn->sock) {
+               sock_release(conn->sock);
+               conn->sock = NULL;
+       }
+       if (conn->conn_transport->iscsit_free_conn)
+               conn->conn_transport->iscsit_free_conn(conn);
+       iscsit_put_transport(conn->conn_transport);
+       kfree(conn);
+ }
  static int __iscsi_target_login_thread(struct iscsi_np *np)
  {
        u8 *buffer, zero_tsih = 0;
        struct iscsi_login *login;
        struct iscsi_portal_group *tpg = NULL;
        struct iscsi_login_req *pdu;
+       struct iscsi_tpg_np *tpg_np;
+       bool new_sess = false;
  
        flush_signals(current);
  
                if (np->np_thread_state == ISCSI_NP_THREAD_RESET) {
                        spin_unlock_bh(&np->np_thread_lock);
                        complete(&np->np_restart_comp);
 -                      if (ret == -ENODEV) {
 -                              iscsit_put_transport(conn->conn_transport);
 -                              kfree(conn);
 -                              conn = NULL;
 +                      iscsit_put_transport(conn->conn_transport);
 +                      kfree(conn);
 +                      conn = NULL;
 +                      if (ret == -ENODEV)
                                goto out;
 -                      }
                        /* Get another socket */
                        return 1;
                }
                tpg = conn->tpg;
                goto new_sess_out;
        }
+       login->zero_tsih = zero_tsih;
  
        tpg = conn->tpg;
        if (!tpg) {
                        goto old_sess_out;
        }
  
-       if (iscsi_target_start_negotiation(login, conn) < 0)
+       ret = iscsi_target_start_negotiation(login, conn);
+       if (ret < 0)
                goto new_sess_out;
  
        if (!conn->sess) {
        if (signal_pending(current))
                goto new_sess_out;
  
-       ret = iscsi_post_login_handler(np, conn, zero_tsih);
+       if (ret == 1) {
+               tpg_np = conn->tpg_np;
  
-       if (ret < 0)
-               goto new_sess_out;
+               ret = iscsi_post_login_handler(np, conn, zero_tsih);
+               if (ret < 0)
+                       goto new_sess_out;
+               iscsit_deaccess_np(np, tpg, tpg_np);
+       }
  
-       iscsit_deaccess_np(np, tpg);
        tpg = NULL;
+       tpg_np = NULL;
        /* Get another socket */
        return 1;
  
  new_sess_out:
-       pr_err("iSCSI Login negotiation failed.\n");
-       iscsit_collect_login_stats(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
-                                 ISCSI_LOGIN_STATUS_INIT_ERR);
-       if (!zero_tsih || !conn->sess)
-               goto old_sess_out;
-       if (conn->sess->se_sess)
-               transport_free_session(conn->sess->se_sess);
-       if (conn->sess->session_index != 0) {
-               spin_lock_bh(&sess_idr_lock);
-               idr_remove(&sess_idr, conn->sess->session_index);
-               spin_unlock_bh(&sess_idr_lock);
-       }
-       kfree(conn->sess->sess_ops);
-       kfree(conn->sess);
+       new_sess = true;
  old_sess_out:
-       iscsi_stop_login_thread_timer(np);
-       /*
-        * If login negotiation fails check if the Time2Retain timer
-        * needs to be restarted.
-        */
-       if (!zero_tsih && conn->sess) {
-               spin_lock_bh(&conn->sess->conn_lock);
-               if (conn->sess->session_state == TARG_SESS_STATE_FAILED) {
-                       struct se_portal_group *se_tpg =
-                                       &ISCSI_TPG_C(conn)->tpg_se_tpg;
-                       atomic_set(&conn->sess->session_continuation, 0);
-                       spin_unlock_bh(&conn->sess->conn_lock);
-                       spin_lock_bh(&se_tpg->session_lock);
-                       iscsit_start_time2retain_handler(conn->sess);
-                       spin_unlock_bh(&se_tpg->session_lock);
-               } else
-                       spin_unlock_bh(&conn->sess->conn_lock);
-               iscsit_dec_session_usage_count(conn->sess);
-       }
-       if (!IS_ERR(conn->conn_rx_hash.tfm))
-               crypto_free_hash(conn->conn_rx_hash.tfm);
-       if (!IS_ERR(conn->conn_tx_hash.tfm))
-               crypto_free_hash(conn->conn_tx_hash.tfm);
-       if (conn->conn_cpumask)
-               free_cpumask_var(conn->conn_cpumask);
-       kfree(conn->conn_ops);
-       if (conn->param_list) {
-               iscsi_release_param_list(conn->param_list);
-               conn->param_list = NULL;
-       }
-       iscsi_target_nego_release(conn);
-       if (conn->sock) {
-               sock_release(conn->sock);
-               conn->sock = NULL;
-       }
-       if (conn->conn_transport->iscsit_free_conn)
-               conn->conn_transport->iscsit_free_conn(conn);
-       iscsit_put_transport(conn->conn_transport);
-       kfree(conn);
+       tpg_np = conn->tpg_np;
+       iscsi_target_login_sess_out(conn, np, zero_tsih, new_sess);
+       new_sess = false;
  
        if (tpg) {
-               iscsit_deaccess_np(np, tpg);
+               iscsit_deaccess_np(np, tpg, tpg_np);
                tpg = NULL;
+               tpg_np = NULL;
        }
  
  out:
@@@ -1,7 -1,7 +1,7 @@@
  /*
   * SCSI Primary Commands (SPC) parsing and emulation.
   *
-  * (c) Copyright 2002-2012 RisingTide Systems LLC.
+  * (c) Copyright 2002-2013 Datera, Inc.
   *
   * Nicholas A. Bellinger <nab@kernel.org>
   *
@@@ -35,7 -35,7 +35,7 @@@
  #include "target_core_alua.h"
  #include "target_core_pr.h"
  #include "target_core_ua.h"
+ #include "target_core_xcopy.h"
  
  static void spc_fill_alua_data(struct se_port *port, unsigned char *buf)
  {
@@@ -95,14 -95,17 +95,20 @@@ spc_emulate_inquiry_std(struct se_cmd *
         */
        spc_fill_alua_data(lun->lun_sep, buf);
  
+       /*
+        * Set Third-Party Copy (3PC) bit to indicate support for EXTENDED_COPY
+        */
+       if (dev->dev_attrib.emulate_3pc)
+               buf[5] |= 0x8;
        buf[7] = 0x2; /* CmdQue=1 */
  
 -      snprintf(&buf[8], 8, "LIO-ORG");
 -      snprintf(&buf[16], 16, "%s", dev->t10_wwn.model);
 -      snprintf(&buf[32], 4, "%s", dev->t10_wwn.revision);
 +      memcpy(&buf[8], "LIO-ORG ", 8);
 +      memset(&buf[16], 0x20, 16);
 +      memcpy(&buf[16], dev->t10_wwn.model,
 +             min_t(size_t, strlen(dev->t10_wwn.model), 16));
 +      memcpy(&buf[32], dev->t10_wwn.revision,
 +             min_t(size_t, strlen(dev->t10_wwn.revision), 4));
        buf[4] = 31; /* Set additional length to 31 */
  
        return 0;
@@@ -129,8 -132,8 +135,8 @@@ spc_emulate_evpd_80(struct se_cmd *cmd
        return 0;
  }
  
static void spc_parse_naa_6h_vendor_specific(struct se_device *dev,
-               unsigned char *buf)
+ void spc_parse_naa_6h_vendor_specific(struct se_device *dev,
+                                     unsigned char *buf)
  {
        unsigned char *p = &dev->t10_wwn.unit_serial[0];
        int cnt;
@@@ -460,6 -463,11 +466,11 @@@ spc_emulate_evpd_b0(struct se_cmd *cmd
  
        /* Set WSNZ to 1 */
        buf[4] = 0x01;
+       /*
+        * Set MAXIMUM COMPARE AND WRITE LENGTH
+        */
+       if (dev->dev_attrib.emulate_caw)
+               buf[5] = 0x01;
  
        /*
         * Set OPTIMAL TRANSFER LENGTH GRANULARITY
@@@ -1250,8 -1258,14 +1261,14 @@@ spc_parse_cdb(struct se_cmd *cmd, unsig
                *size = (cdb[6] << 24) | (cdb[7] << 16) | (cdb[8] << 8) | cdb[9];
                break;
        case EXTENDED_COPY:
-       case READ_ATTRIBUTE:
+               *size = get_unaligned_be32(&cdb[10]);
+               cmd->execute_cmd = target_do_xcopy;
+               break;
        case RECEIVE_COPY_RESULTS:
+               *size = get_unaligned_be32(&cdb[10]);
+               cmd->execute_cmd = target_do_receive_copy_results;
+               break;
+       case READ_ATTRIBUTE:
        case WRITE_ATTRIBUTE:
                *size = (cdb[10] << 24) | (cdb[11] << 16) |
                       (cdb[12] << 8) | cdb[13];
@@@ -3,7 -3,7 +3,7 @@@
   *
   * This file contains the Generic Target Engine Core.
   *
-  * (c) Copyright 2002-2012 RisingTide Systems LLC.
+  * (c) Copyright 2002-2013 Datera, Inc.
   *
   * Nicholas A. Bellinger <nab@kernel.org>
   *
@@@ -67,7 -67,6 +67,6 @@@ struct kmem_cache *t10_alua_tg_pt_gp_me
  static void transport_complete_task_attr(struct se_cmd *cmd);
  static void transport_handle_queue_full(struct se_cmd *cmd,
                struct se_device *dev);
- static int transport_generic_get_mem(struct se_cmd *cmd);
  static int transport_put_cmd(struct se_cmd *cmd);
  static void target_complete_ok_work(struct work_struct *work);
  
@@@ -232,6 -231,50 +231,50 @@@ struct se_session *transport_init_sessi
  }
  EXPORT_SYMBOL(transport_init_session);
  
+ int transport_alloc_session_tags(struct se_session *se_sess,
+                                unsigned int tag_num, unsigned int tag_size)
+ {
+       int rc;
+       se_sess->sess_cmd_map = kzalloc(tag_num * tag_size, GFP_KERNEL);
+       if (!se_sess->sess_cmd_map) {
+               pr_err("Unable to allocate se_sess->sess_cmd_map\n");
+               return -ENOMEM;
+       }
+       rc = percpu_ida_init(&se_sess->sess_tag_pool, tag_num);
+       if (rc < 0) {
+               pr_err("Unable to init se_sess->sess_tag_pool,"
+                       " tag_num: %u\n", tag_num);
+               kfree(se_sess->sess_cmd_map);
+               se_sess->sess_cmd_map = NULL;
+               return -ENOMEM;
+       }
+       return 0;
+ }
+ EXPORT_SYMBOL(transport_alloc_session_tags);
+ struct se_session *transport_init_session_tags(unsigned int tag_num,
+                                              unsigned int tag_size)
+ {
+       struct se_session *se_sess;
+       int rc;
+       se_sess = transport_init_session();
+       if (IS_ERR(se_sess))
+               return se_sess;
+       rc = transport_alloc_session_tags(se_sess, tag_num, tag_size);
+       if (rc < 0) {
+               transport_free_session(se_sess);
+               return ERR_PTR(-ENOMEM);
+       }
+       return se_sess;
+ }
+ EXPORT_SYMBOL(transport_init_session_tags);
  /*
   * Called with spin_lock_irqsave(&struct se_portal_group->session_lock called.
   */
@@@ -367,6 -410,10 +410,10 @@@ EXPORT_SYMBOL(transport_deregister_sess
  
  void transport_free_session(struct se_session *se_sess)
  {
+       if (se_sess->sess_cmd_map) {
+               percpu_ida_destroy(&se_sess->sess_tag_pool);
+               kfree(se_sess->sess_cmd_map);
+       }
        kmem_cache_free(se_sess_cache, se_sess);
  }
  EXPORT_SYMBOL(transport_free_session);
@@@ -1206,7 -1253,7 +1253,7 @@@ int transport_handle_cdb_direct
  }
  EXPORT_SYMBOL(transport_handle_cdb_direct);
  
- static sense_reason_t
+ sense_reason_t
  transport_generic_map_mem_to_cmd(struct se_cmd *cmd, struct scatterlist *sgl,
                u32 sgl_count, struct scatterlist *sgl_bidi, u32 sgl_bidi_count)
  {
@@@ -1512,6 -1559,13 +1559,13 @@@ void transport_generic_request_failure(
         * For SAM Task Attribute emulation for failed struct se_cmd
         */
        transport_complete_task_attr(cmd);
+       /*
+        * Handle special case for COMPARE_AND_WRITE failure, where the
+        * callback is expected to drop the per device ->caw_mutex.
+        */
+       if ((cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE) &&
+            cmd->transport_complete_callback)
+               cmd->transport_complete_callback(cmd);
  
        switch (sense_reason) {
        case TCM_NON_EXISTENT_LUN:
@@@ -1579,7 -1633,7 +1633,7 @@@ queue_full
  }
  EXPORT_SYMBOL(transport_generic_request_failure);
  
static void __target_execute_cmd(struct se_cmd *cmd)
+ void __target_execute_cmd(struct se_cmd *cmd)
  {
        sense_reason_t ret;
  
@@@ -1784,7 -1838,7 +1838,7 @@@ static void transport_complete_qf(struc
                ret = cmd->se_tfo->queue_data_in(cmd);
                break;
        case DMA_TO_DEVICE:
-               if (cmd->t_bidi_data_sg) {
+               if (cmd->se_cmd_flags & SCF_BIDI) {
                        ret = cmd->se_tfo->queue_data_in(cmd);
                        if (ret < 0)
                                break;
@@@ -1856,10 -1910,25 +1910,25 @@@ static void target_complete_ok_work(str
        }
        /*
         * Check for a callback, used by amongst other things
-        * XDWRITE_READ_10 emulation.
+        * XDWRITE_READ_10 and COMPARE_AND_WRITE emulation.
         */
-       if (cmd->transport_complete_callback)
-               cmd->transport_complete_callback(cmd);
+       if (cmd->transport_complete_callback) {
+               sense_reason_t rc;
+               rc = cmd->transport_complete_callback(cmd);
+               if (!rc && !(cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE_POST)) {
+                       return;
+               } else if (rc) {
+                       ret = transport_send_check_condition_and_sense(cmd,
+                                               rc, 0);
+                       if (ret == -EAGAIN || ret == -ENOMEM)
+                               goto queue_full;
+                       transport_lun_remove_cmd(cmd);
+                       transport_cmd_check_stop_to_fabric(cmd);
+                       return;
+               }
+       }
  
        switch (cmd->data_direction) {
        case DMA_FROM_DEVICE:
                /*
                 * Check if we need to send READ payload for BIDI-COMMAND
                 */
-               if (cmd->t_bidi_data_sg) {
+               if (cmd->se_cmd_flags & SCF_BIDI) {
                        spin_lock(&cmd->se_lun->lun_sep_lock);
                        if (cmd->se_lun->lun_sep) {
                                cmd->se_lun->lun_sep->sep_stats.tx_data_octets +=
@@@ -1930,10 -1999,29 +1999,29 @@@ static inline void transport_free_sgl(s
        kfree(sgl);
  }
  
+ static inline void transport_reset_sgl_orig(struct se_cmd *cmd)
+ {
+       /*
+        * Check for saved t_data_sg that may be used for COMPARE_AND_WRITE
+        * emulation, and free + reset pointers if necessary..
+        */
+       if (!cmd->t_data_sg_orig)
+               return;
+       kfree(cmd->t_data_sg);
+       cmd->t_data_sg = cmd->t_data_sg_orig;
+       cmd->t_data_sg_orig = NULL;
+       cmd->t_data_nents = cmd->t_data_nents_orig;
+       cmd->t_data_nents_orig = 0;
+ }
  static inline void transport_free_pages(struct se_cmd *cmd)
  {
-       if (cmd->se_cmd_flags & SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC)
+       if (cmd->se_cmd_flags & SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC) {
+               transport_reset_sgl_orig(cmd);
                return;
+       }
+       transport_reset_sgl_orig(cmd);
  
        transport_free_sgl(cmd->t_data_sg, cmd->t_data_nents);
        cmd->t_data_sg = NULL;
@@@ -2029,24 -2117,22 +2117,22 @@@ void transport_kunmap_data_sg(struct se
  }
  EXPORT_SYMBOL(transport_kunmap_data_sg);
  
- static int
- transport_generic_get_mem(struct se_cmd *cmd)
+ int
+ target_alloc_sgl(struct scatterlist **sgl, unsigned int *nents, u32 length,
+                bool zero_page)
  {
-       u32 length = cmd->data_length;
-       unsigned int nents;
+       struct scatterlist *sg;
        struct page *page;
-       gfp_t zero_flag;
+       gfp_t zero_flag = (zero_page) ? __GFP_ZERO : 0;
+       unsigned int nent;
        int i = 0;
  
-       nents = DIV_ROUND_UP(length, PAGE_SIZE);
-       cmd->t_data_sg = kmalloc(sizeof(struct scatterlist) * nents, GFP_KERNEL);
-       if (!cmd->t_data_sg)
+       nent = DIV_ROUND_UP(length, PAGE_SIZE);
+       sg = kmalloc(sizeof(struct scatterlist) * nent, GFP_KERNEL);
+       if (!sg)
                return -ENOMEM;
  
-       cmd->t_data_nents = nents;
-       sg_init_table(cmd->t_data_sg, nents);
-       zero_flag = cmd->se_cmd_flags & SCF_SCSI_DATA_CDB ? 0 : __GFP_ZERO;
+       sg_init_table(sg, nent);
  
        while (length) {
                u32 page_len = min_t(u32, length, PAGE_SIZE);
                if (!page)
                        goto out;
  
-               sg_set_page(&cmd->t_data_sg[i], page, page_len, 0);
+               sg_set_page(&sg[i], page, page_len, 0);
                length -= page_len;
                i++;
        }
+       *sgl = sg;
+       *nents = nent;
        return 0;
  
  out:
        while (i > 0) {
                i--;
-               __free_page(sg_page(&cmd->t_data_sg[i]));
+               __free_page(sg_page(&sg[i]));
        }
-       kfree(cmd->t_data_sg);
-       cmd->t_data_sg = NULL;
+       kfree(sg);
        return -ENOMEM;
  }
  
@@@ -2087,7 -2174,27 +2174,27 @@@ transport_generic_new_cmd(struct se_cm
         */
        if (!(cmd->se_cmd_flags & SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC) &&
            cmd->data_length) {
-               ret = transport_generic_get_mem(cmd);
+               bool zero_flag = !(cmd->se_cmd_flags & SCF_SCSI_DATA_CDB);
+               if ((cmd->se_cmd_flags & SCF_BIDI) ||
+                   (cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE)) {
+                       u32 bidi_length;
+                       if (cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE)
+                               bidi_length = cmd->t_task_nolb *
+                                             cmd->se_dev->dev_attrib.block_size;
+                       else
+                               bidi_length = cmd->data_length;
+                       ret = target_alloc_sgl(&cmd->t_bidi_data_sg,
+                                              &cmd->t_bidi_data_nents,
+                                              bidi_length, zero_flag);
+                       if (ret < 0)
+                               return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+               }
+               ret = target_alloc_sgl(&cmd->t_data_sg, &cmd->t_data_nents,
+                                      cmd->data_length, zero_flag);
                if (ret < 0)
                        return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
        }
@@@ -2134,7 -2241,6 +2241,7 @@@ static void transport_write_pending_qf(
  
  int transport_generic_free_cmd(struct se_cmd *cmd, int wait_for_tasks)
  {
 +      unsigned long flags;
        int ret = 0;
  
        if (!(cmd->se_cmd_flags & SCF_SE_LUN_CMD)) {
        } else {
                if (wait_for_tasks)
                        transport_wait_for_tasks(cmd);
 +              /*
 +               * Handle WRITE failure case where transport_generic_new_cmd()
 +               * has already added se_cmd to state_list, but fabric has
 +               * failed command before I/O submission.
 +               */
 +              if (cmd->state_active) {
 +                      spin_lock_irqsave(&cmd->t_state_lock, flags);
 +                      target_remove_from_state_list(cmd);
 +                      spin_unlock_irqrestore(&cmd->t_state_lock, flags);
 +              }
  
                if (cmd->se_lun)
                        transport_lun_remove_cmd(cmd);
@@@ -2740,6 -2836,15 +2847,15 @@@ transport_send_check_condition_and_sens
                buffer[SPC_ASC_KEY_OFFSET] = asc;
                buffer[SPC_ASCQ_KEY_OFFSET] = ascq;
                break;
+       case TCM_MISCOMPARE_VERIFY:
+               /* CURRENT ERROR */
+               buffer[0] = 0x70;
+               buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
+               buffer[SPC_SENSE_KEY_OFFSET] = MISCOMPARE;
+               /* MISCOMPARE DURING VERIFY OPERATION */
+               buffer[SPC_ASC_KEY_OFFSET] = 0x1d;
+               buffer[SPC_ASCQ_KEY_OFFSET] = 0x00;
+               break;
        case TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE:
        default:
                /* CURRENT ERROR */
diff --combined include/scsi/scsi.h
@@@ -144,6 -144,7 +144,7 @@@ enum scsi_timeouts 
  #define ACCESS_CONTROL_IN     0x86
  #define ACCESS_CONTROL_OUT    0x87
  #define READ_16               0x88
+ #define COMPARE_AND_WRITE     0x89
  #define WRITE_16              0x8a
  #define READ_ATTRIBUTE        0x8c
  #define WRITE_ATTRIBUTE             0x8d
@@@ -457,8 -458,6 +458,8 @@@ static inline int scsi_is_wlun(unsigne
                                 * other paths */
  #define DID_NEXUS_FAILURE 0x11  /* Permanent nexus failure, retry on other
                                 * paths might yield different results */
 +#define DID_ALLOC_FAILURE 0x12  /* Space allocation on the device failed */
 +#define DID_MEDIUM_ERROR  0x13  /* Medium error */
  #define DRIVER_OK       0x00  /* Driver status                           */
  
  /*
  #define TIMEOUT_ERROR   0x2007
  #define SCSI_RETURN_NOT_HANDLED   0x2008
  #define FAST_IO_FAIL  0x2009
 -#define TARGET_ERROR    0x200A
  
  /*
   * Midlevel queue return values.
diff --combined lib/Makefile
@@@ -13,19 -13,19 +13,20 @@@ lib-y := ctype.o string.o vsprintf.o cm
         sha1.o md5.o irq_regs.o reciprocal_div.o argv_split.o \
         proportions.o flex_proportions.o prio_heap.o ratelimit.o show_mem.o \
         is_single_threaded.o plist.o decompress.o kobject_uevent.o \
-        earlycpio.o percpu-refcount.o
+        earlycpio.o percpu-refcount.o percpu_ida.o
  
  obj-$(CONFIG_ARCH_HAS_DEBUG_STRICT_USER_COPY_CHECKS) += usercopy.o
  lib-$(CONFIG_MMU) += ioremap.o
  lib-$(CONFIG_SMP) += cpumask.o
  
  lib-y += kobject.o klist.o
 +obj-y += lockref.o
  
  obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \
         bust_spinlocks.o hexdump.o kasprintf.o bitmap.o scatterlist.o \
         gcd.o lcm.o list_sort.o uuid.o flex_array.o iovec.o clz_ctz.o \
-        bsearch.o find_last_bit.o find_next_bit.o llist.o memweight.o kfifo.o
+        bsearch.o find_last_bit.o find_next_bit.o llist.o memweight.o kfifo.o \
+        percpu_ida.o
  obj-y += string_helpers.o
  obj-$(CONFIG_TEST_STRING_HELPERS) += test-string_helpers.o
  obj-y += kstrtox.o