]> Pileus Git - ~andy/linux/blobdiff - drivers/target/target_core_sbc.c
mlx4: allow order-0 memory allocations in RX path
[~andy/linux] / drivers / target / target_core_sbc.c
index 60d4b5185f32979739c01e04c5aa94de6fcd838f..bbc5b0ee2bdc5bb1d2c7d45ea370d8834cbfbed2 100644 (file)
@@ -596,3 +596,88 @@ u32 sbc_get_device_type(struct se_device *dev)
        return TYPE_DISK;
 }
 EXPORT_SYMBOL(sbc_get_device_type);
+
+sense_reason_t
+sbc_execute_unmap(struct se_cmd *cmd,
+       sense_reason_t (*do_unmap_fn)(struct se_cmd *, void *,
+                                     sector_t, sector_t),
+       void *priv)
+{
+       struct se_device *dev = cmd->se_dev;
+       unsigned char *buf, *ptr = NULL;
+       sector_t lba;
+       int size;
+       u32 range;
+       sense_reason_t ret = 0;
+       int dl, bd_dl;
+
+       /* We never set ANC_SUP */
+       if (cmd->t_task_cdb[1])
+               return TCM_INVALID_CDB_FIELD;
+
+       if (cmd->data_length == 0) {
+               target_complete_cmd(cmd, SAM_STAT_GOOD);
+               return 0;
+       }
+
+       if (cmd->data_length < 8) {
+               pr_warn("UNMAP parameter list length %u too small\n",
+                       cmd->data_length);
+               return TCM_PARAMETER_LIST_LENGTH_ERROR;
+       }
+
+       buf = transport_kmap_data_sg(cmd);
+       if (!buf)
+               return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+
+       dl = get_unaligned_be16(&buf[0]);
+       bd_dl = get_unaligned_be16(&buf[2]);
+
+       size = cmd->data_length - 8;
+       if (bd_dl > size)
+               pr_warn("UNMAP parameter list length %u too small, ignoring bd_dl %u\n",
+                       cmd->data_length, bd_dl);
+       else
+               size = bd_dl;
+
+       if (size / 16 > dev->dev_attrib.max_unmap_block_desc_count) {
+               ret = TCM_INVALID_PARAMETER_LIST;
+               goto err;
+       }
+
+       /* First UNMAP block descriptor starts at 8 byte offset */
+       ptr = &buf[8];
+       pr_debug("UNMAP: Sub: %s Using dl: %u bd_dl: %u size: %u"
+               " ptr: %p\n", dev->transport->name, dl, bd_dl, size, ptr);
+
+       while (size >= 16) {
+               lba = get_unaligned_be64(&ptr[0]);
+               range = get_unaligned_be32(&ptr[8]);
+               pr_debug("UNMAP: Using lba: %llu and range: %u\n",
+                                (unsigned long long)lba, range);
+
+               if (range > dev->dev_attrib.max_unmap_lba_count) {
+                       ret = TCM_INVALID_PARAMETER_LIST;
+                       goto err;
+               }
+
+               if (lba + range > dev->transport->get_blocks(dev) + 1) {
+                       ret = TCM_ADDRESS_OUT_OF_RANGE;
+                       goto err;
+               }
+
+               ret = do_unmap_fn(cmd, priv, lba, range);
+               if (ret)
+                       goto err;
+
+               ptr += 16;
+               size -= 16;
+       }
+
+err:
+       transport_kunmap_data_sg(cmd);
+       if (!ret)
+               target_complete_cmd(cmd, GOOD);
+       return ret;
+}
+EXPORT_SYMBOL(sbc_execute_unmap);