]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/target/target_core_xcopy.c
Merge branch 'drm-fixes-4.13' of git://people.freedesktop.org/~agd5f/linux into drm...
[karo-tx-linux.git] / drivers / target / target_core_xcopy.c
index cac5a20a4de07ba554151c6099182c9e675efbed..9ee89e00cd77669922d8223f8a38f0a0a2cdf949 100644 (file)
@@ -40,6 +40,8 @@
 
 static struct workqueue_struct *xcopy_wq = NULL;
 
+static sense_reason_t target_parse_xcopy_cmd(struct xcopy_op *xop);
+
 static int target_xcopy_gen_naa_ieee(struct se_device *dev, unsigned char *buf)
 {
        int off = 0;
@@ -53,48 +55,60 @@ static int target_xcopy_gen_naa_ieee(struct se_device *dev, unsigned char *buf)
        return 0;
 }
 
-static int target_xcopy_locate_se_dev_e4(const unsigned char *dev_wwn,
-                                       struct se_device **found_dev)
+struct xcopy_dev_search_info {
+       const unsigned char *dev_wwn;
+       struct se_device *found_dev;
+};
+
+static int target_xcopy_locate_se_dev_e4_iter(struct se_device *se_dev,
+                                             void *data)
 {
-       struct se_device *se_dev;
+       struct xcopy_dev_search_info *info = data;
        unsigned char tmp_dev_wwn[XCOPY_NAA_IEEE_REGEX_LEN];
        int rc;
 
-       mutex_lock(&g_device_mutex);
-       list_for_each_entry(se_dev, &g_device_list, g_dev_node) {
+       if (!se_dev->dev_attrib.emulate_3pc)
+               return 0;
 
-               if (!se_dev->dev_attrib.emulate_3pc)
-                       continue;
+       memset(&tmp_dev_wwn[0], 0, XCOPY_NAA_IEEE_REGEX_LEN);
+       target_xcopy_gen_naa_ieee(se_dev, &tmp_dev_wwn[0]);
 
-               memset(&tmp_dev_wwn[0], 0, XCOPY_NAA_IEEE_REGEX_LEN);
-               target_xcopy_gen_naa_ieee(se_dev, &tmp_dev_wwn[0]);
+       rc = memcmp(&tmp_dev_wwn[0], info->dev_wwn, XCOPY_NAA_IEEE_REGEX_LEN);
+       if (rc != 0)
+               return 0;
 
-               rc = memcmp(&tmp_dev_wwn[0], dev_wwn, XCOPY_NAA_IEEE_REGEX_LEN);
-               if (rc != 0)
-                       continue;
+       info->found_dev = se_dev;
+       pr_debug("XCOPY 0xe4: located se_dev: %p\n", se_dev);
 
-               *found_dev = se_dev;
-               pr_debug("XCOPY 0xe4: located se_dev: %p\n", se_dev);
+       rc = target_depend_item(&se_dev->dev_group.cg_item);
+       if (rc != 0) {
+               pr_err("configfs_depend_item attempt failed: %d for se_dev: %p\n",
+                      rc, se_dev);
+               return rc;
+       }
 
-               rc = target_depend_item(&se_dev->dev_group.cg_item);
-               if (rc != 0) {
-                       pr_err("configfs_depend_item attempt failed:"
-                               " %d for se_dev: %p\n", rc, se_dev);
-                       mutex_unlock(&g_device_mutex);
-                       return rc;
-               }
+       pr_debug("Called configfs_depend_item for se_dev: %p se_dev->se_dev_group: %p\n",
+                se_dev, &se_dev->dev_group);
+       return 1;
+}
 
-               pr_debug("Called configfs_depend_item for se_dev: %p"
-                       " se_dev->se_dev_group: %p\n", se_dev,
-                       &se_dev->dev_group);
+static int target_xcopy_locate_se_dev_e4(const unsigned char *dev_wwn,
+                                       struct se_device **found_dev)
+{
+       struct xcopy_dev_search_info info;
+       int ret;
+
+       memset(&info, 0, sizeof(info));
+       info.dev_wwn = dev_wwn;
 
-               mutex_unlock(&g_device_mutex);
+       ret = target_for_each_device(target_xcopy_locate_se_dev_e4_iter, &info);
+       if (ret == 1) {
+               *found_dev = info.found_dev;
                return 0;
+       } else {
+               pr_debug_ratelimited("Unable to locate 0xe4 descriptor for EXTENDED_COPY\n");
+               return -EINVAL;
        }
-       mutex_unlock(&g_device_mutex);
-
-       pr_debug_ratelimited("Unable to locate 0xe4 descriptor for EXTENDED_COPY\n");
-       return -EINVAL;
 }
 
 static int target_xcopy_parse_tiddesc_e4(struct se_cmd *se_cmd, struct xcopy_op *xop,
@@ -311,9 +325,7 @@ static int target_xcopy_parse_segdesc_02(struct se_cmd *se_cmd, struct xcopy_op
                (unsigned long long)xop->dst_lba);
 
        if (dc != 0) {
-               xop->dbl = (desc[29] & 0xff) << 16;
-               xop->dbl |= (desc[30] & 0xff) << 8;
-               xop->dbl |= desc[31] & 0xff;
+               xop->dbl = get_unaligned_be24(&desc[29]);
 
                pr_debug("XCOPY seg desc 0x02: DC=1 w/ dbl: %u\n", xop->dbl);
        }
@@ -781,13 +793,24 @@ static int target_xcopy_write_destination(
 static void target_xcopy_do_work(struct work_struct *work)
 {
        struct xcopy_op *xop = container_of(work, struct xcopy_op, xop_work);
-       struct se_device *src_dev = xop->src_dev, *dst_dev = xop->dst_dev;
        struct se_cmd *ec_cmd = xop->xop_se_cmd;
-       sector_t src_lba = xop->src_lba, dst_lba = xop->dst_lba, end_lba;
+       struct se_device *src_dev, *dst_dev;
+       sector_t src_lba, dst_lba, end_lba;
        unsigned int max_sectors;
-       int rc;
-       unsigned short nolb = xop->nolb, cur_nolb, max_nolb, copied_nolb = 0;
+       int rc = 0;
+       unsigned short nolb, cur_nolb, max_nolb, copied_nolb = 0;
+
+       if (target_parse_xcopy_cmd(xop) != TCM_NO_SENSE)
+               goto err_free;
 
+       if (WARN_ON_ONCE(!xop->src_dev) || WARN_ON_ONCE(!xop->dst_dev))
+               goto err_free;
+
+       src_dev = xop->src_dev;
+       dst_dev = xop->dst_dev;
+       src_lba = xop->src_lba;
+       dst_lba = xop->dst_lba;
+       nolb = xop->nolb;
        end_lba = src_lba + nolb;
        /*
         * Break up XCOPY I/O into hw_max_sectors sized I/O based on the
@@ -855,6 +878,8 @@ static void target_xcopy_do_work(struct work_struct *work)
 
 out:
        xcopy_pt_undepend_remotedev(xop);
+
+err_free:
        kfree(xop);
        /*
         * Don't override an error scsi status if it has already been set
@@ -867,48 +892,22 @@ out:
        target_complete_cmd(ec_cmd, ec_cmd->scsi_status);
 }
 
-sense_reason_t target_do_xcopy(struct se_cmd *se_cmd)
+/*
+ * Returns TCM_NO_SENSE upon success or a sense code != TCM_NO_SENSE if parsing
+ * fails.
+ */
+static sense_reason_t target_parse_xcopy_cmd(struct xcopy_op *xop)
 {
-       struct se_device *dev = se_cmd->se_dev;
-       struct xcopy_op *xop = NULL;
+       struct se_cmd *se_cmd = xop->xop_se_cmd;
        unsigned char *p = NULL, *seg_desc;
-       unsigned int list_id, list_id_usage, sdll, inline_dl, sa;
+       unsigned int list_id, list_id_usage, sdll, inline_dl;
        sense_reason_t ret = TCM_INVALID_PARAMETER_LIST;
        int rc;
        unsigned short tdll;
 
-       if (!dev->dev_attrib.emulate_3pc) {
-               pr_err("EXTENDED_COPY operation explicitly disabled\n");
-               return TCM_UNSUPPORTED_SCSI_OPCODE;
-       }
-
-       sa = se_cmd->t_task_cdb[1] & 0x1f;
-       if (sa != 0x00) {
-               pr_err("EXTENDED_COPY(LID4) not supported\n");
-               return TCM_UNSUPPORTED_SCSI_OPCODE;
-       }
-
-       if (se_cmd->data_length == 0) {
-               target_complete_cmd(se_cmd, SAM_STAT_GOOD);
-               return TCM_NO_SENSE;
-       }
-       if (se_cmd->data_length < XCOPY_HDR_LEN) {
-               pr_err("XCOPY parameter truncation: length %u < hdr_len %u\n",
-                               se_cmd->data_length, XCOPY_HDR_LEN);
-               return TCM_PARAMETER_LIST_LENGTH_ERROR;
-       }
-
-       xop = kzalloc(sizeof(struct xcopy_op), GFP_KERNEL);
-       if (!xop) {
-               pr_err("Unable to allocate xcopy_op\n");
-               return TCM_OUT_OF_RESOURCES;
-       }
-       xop->xop_se_cmd = se_cmd;
-
        p = transport_kmap_data_sg(se_cmd);
        if (!p) {
                pr_err("transport_kmap_data_sg() failed in target_do_xcopy\n");
-               kfree(xop);
                return TCM_OUT_OF_RESOURCES;
        }
 
@@ -977,18 +976,57 @@ sense_reason_t target_do_xcopy(struct se_cmd *se_cmd)
        pr_debug("XCOPY: Processed %d target descriptors, length: %u\n", rc,
                                rc * XCOPY_TARGET_DESC_LEN);
        transport_kunmap_data_sg(se_cmd);
-
-       INIT_WORK(&xop->xop_work, target_xcopy_do_work);
-       queue_work(xcopy_wq, &xop->xop_work);
        return TCM_NO_SENSE;
 
 out:
        if (p)
                transport_kunmap_data_sg(se_cmd);
-       kfree(xop);
        return ret;
 }
 
+sense_reason_t target_do_xcopy(struct se_cmd *se_cmd)
+{
+       struct se_device *dev = se_cmd->se_dev;
+       struct xcopy_op *xop;
+       unsigned int sa;
+
+       if (!dev->dev_attrib.emulate_3pc) {
+               pr_err("EXTENDED_COPY operation explicitly disabled\n");
+               return TCM_UNSUPPORTED_SCSI_OPCODE;
+       }
+
+       sa = se_cmd->t_task_cdb[1] & 0x1f;
+       if (sa != 0x00) {
+               pr_err("EXTENDED_COPY(LID4) not supported\n");
+               return TCM_UNSUPPORTED_SCSI_OPCODE;
+       }
+
+       if (se_cmd->data_length == 0) {
+               target_complete_cmd(se_cmd, SAM_STAT_GOOD);
+               return TCM_NO_SENSE;
+       }
+       if (se_cmd->data_length < XCOPY_HDR_LEN) {
+               pr_err("XCOPY parameter truncation: length %u < hdr_len %u\n",
+                               se_cmd->data_length, XCOPY_HDR_LEN);
+               return TCM_PARAMETER_LIST_LENGTH_ERROR;
+       }
+
+       xop = kzalloc(sizeof(struct xcopy_op), GFP_KERNEL);
+       if (!xop)
+               goto err;
+       xop->xop_se_cmd = se_cmd;
+       INIT_WORK(&xop->xop_work, target_xcopy_do_work);
+       if (WARN_ON_ONCE(!queue_work(xcopy_wq, &xop->xop_work)))
+               goto free;
+       return TCM_NO_SENSE;
+
+free:
+       kfree(xop);
+
+err:
+       return TCM_OUT_OF_RESOURCES;
+}
+
 static sense_reason_t target_rcr_operating_parameters(struct se_cmd *se_cmd)
 {
        unsigned char *p;