nvme-fcloop: add sysfs attribute to inject command drop

Add sysfs attribute to specify parameters for dropping a command.  The
attribute takes a string of:

  <opcode>:<starting a what instance>:<number of times>

Opcode is formatted as lower 8 bits are opcode.  If a fabrics opcode, a
bit above bits 7:0 will be set.

Once set, each sqe is looked at. If the opcode matches the running
instance count is updated. If the instance count is in the range of where
to drop (based on starting and # of times), then drop the command by not
passing it to the target layer.

Signed-off-by: James Smart <james.smart@broadcom.com>
This commit is contained in:
James Smart 2020-10-16 14:28:38 -07:00 коммит произвёл Christoph Hellwig
Родитель 48332ff295
Коммит 03d99e5d63
1 изменённых файлов: 79 добавлений и 2 удалений

Просмотреть файл

@ -564,6 +564,50 @@ fcloop_call_host_done(struct nvmefc_fcp_req *fcpreq,
fcloop_tfcp_req_put(tfcp_req); fcloop_tfcp_req_put(tfcp_req);
} }
static bool drop_fabric_opcode;
#define DROP_OPCODE_MASK 0x00FF
/* fabrics opcode will have a bit set above 1st byte */
static int drop_opcode = -1;
static int drop_instance;
static int drop_amount;
static int drop_current_cnt;
/*
* Routine to parse io and determine if the io is to be dropped.
* Returns:
* 0 if io is not obstructed
* 1 if io was dropped
*/
static int check_for_drop(struct fcloop_fcpreq *tfcp_req)
{
struct nvmefc_fcp_req *fcpreq = tfcp_req->fcpreq;
struct nvme_fc_cmd_iu *cmdiu = fcpreq->cmdaddr;
struct nvme_command *sqe = &cmdiu->sqe;
if (drop_opcode == -1)
return 0;
pr_info("%s: seq opcd x%02x fctype x%02x: drop F %s op x%02x "
"inst %d start %d amt %d\n",
__func__, sqe->common.opcode, sqe->fabrics.fctype,
drop_fabric_opcode ? "y" : "n",
drop_opcode, drop_current_cnt, drop_instance, drop_amount);
if ((drop_fabric_opcode &&
(sqe->common.opcode != nvme_fabrics_command ||
sqe->fabrics.fctype != drop_opcode)) ||
(!drop_fabric_opcode && sqe->common.opcode != drop_opcode))
return 0;
if (++drop_current_cnt >= drop_instance) {
if (drop_current_cnt >= drop_instance + drop_amount)
drop_opcode = -1;
return 1;
}
return 0;
}
static void static void
fcloop_fcp_recv_work(struct work_struct *work) fcloop_fcp_recv_work(struct work_struct *work)
{ {
@ -590,10 +634,14 @@ fcloop_fcp_recv_work(struct work_struct *work)
if (unlikely(aborted)) if (unlikely(aborted))
ret = -ECANCELED; ret = -ECANCELED;
else else {
if (likely(!check_for_drop(tfcp_req)))
ret = nvmet_fc_rcv_fcp_req(tfcp_req->tport->targetport, ret = nvmet_fc_rcv_fcp_req(tfcp_req->tport->targetport,
&tfcp_req->tgt_fcp_req, &tfcp_req->tgt_fcp_req,
fcpreq->cmdaddr, fcpreq->cmdlen); fcpreq->cmdaddr, fcpreq->cmdlen);
else
pr_info("%s: dropped command ********\n", __func__);
}
if (ret) if (ret)
fcloop_call_host_done(fcpreq, tfcp_req, ret); fcloop_call_host_done(fcpreq, tfcp_req, ret);
@ -1449,6 +1497,33 @@ fcloop_delete_target_port(struct device *dev, struct device_attribute *attr,
return ret ? ret : count; return ret ? ret : count;
} }
static ssize_t
fcloop_set_cmd_drop(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
int opcode, starting, amount;
if (sscanf(buf, "%x:%d:%d", &opcode, &starting, &amount) != 3)
return -EBADRQC;
drop_current_cnt = 0;
drop_fabric_opcode = (opcode & ~DROP_OPCODE_MASK) ? true : false;
drop_opcode = (opcode & DROP_OPCODE_MASK);
drop_instance = starting;
/* the check to drop routine uses instance + count to know when
* to end. Thus, if dropping 1 instance, count should be 0.
* so subtract 1 from the count.
*/
drop_amount = amount - 1;
pr_info("%s: DROP: Starting at instance %d of%s opcode x%x drop +%d "
"instances\n",
__func__, drop_instance, drop_fabric_opcode ? " fabric" : "",
drop_opcode, drop_amount);
return count;
}
static DEVICE_ATTR(add_local_port, 0200, NULL, fcloop_create_local_port); static DEVICE_ATTR(add_local_port, 0200, NULL, fcloop_create_local_port);
static DEVICE_ATTR(del_local_port, 0200, NULL, fcloop_delete_local_port); static DEVICE_ATTR(del_local_port, 0200, NULL, fcloop_delete_local_port);
@ -1456,6 +1531,7 @@ static DEVICE_ATTR(add_remote_port, 0200, NULL, fcloop_create_remote_port);
static DEVICE_ATTR(del_remote_port, 0200, NULL, fcloop_delete_remote_port); static DEVICE_ATTR(del_remote_port, 0200, NULL, fcloop_delete_remote_port);
static DEVICE_ATTR(add_target_port, 0200, NULL, fcloop_create_target_port); static DEVICE_ATTR(add_target_port, 0200, NULL, fcloop_create_target_port);
static DEVICE_ATTR(del_target_port, 0200, NULL, fcloop_delete_target_port); static DEVICE_ATTR(del_target_port, 0200, NULL, fcloop_delete_target_port);
static DEVICE_ATTR(set_cmd_drop, 0200, NULL, fcloop_set_cmd_drop);
static struct attribute *fcloop_dev_attrs[] = { static struct attribute *fcloop_dev_attrs[] = {
&dev_attr_add_local_port.attr, &dev_attr_add_local_port.attr,
@ -1464,6 +1540,7 @@ static struct attribute *fcloop_dev_attrs[] = {
&dev_attr_del_remote_port.attr, &dev_attr_del_remote_port.attr,
&dev_attr_add_target_port.attr, &dev_attr_add_target_port.attr,
&dev_attr_del_target_port.attr, &dev_attr_del_target_port.attr,
&dev_attr_set_cmd_drop.attr,
NULL NULL
}; };