aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephen Rothwell <sfr@canb.auug.org.au>2017-05-30 13:43:54 +1000
committerStephen Rothwell <sfr@canb.auug.org.au>2017-05-30 13:43:54 +1000
commitd517ae1be54d40a42e2b02d109f625b763e4a268 (patch)
tree10f7d739f963d7580338caf4f9bf6e62acb785dd
parent55277d058c9139f8ddc384c0bd5ee1b7c9191ae2 (diff)
parentcae4e246117d9a38abab75210c5fd573a11a63a1 (diff)
Merge remote-tracking branch 'target-updates/for-next'
-rw-r--r--drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c175
-rw-r--r--drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.h25
-rw-r--r--drivers/scsi/ibmvscsi_tgt/libsrp.h5
-rw-r--r--drivers/target/target_core_rd.c4
-rw-r--r--drivers/target/target_core_transport.c23
-rw-r--r--drivers/target/target_core_user.c46
-rw-r--r--drivers/vhost/scsi.c11
7 files changed, 241 insertions, 48 deletions
diff --git a/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c
index d390325c99ec..35710524d059 100644
--- a/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c
+++ b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c
@@ -155,6 +155,9 @@ static long ibmvscsis_unregister_command_q(struct scsi_info *vscsi)
qrc = h_free_crq(vscsi->dds.unit_id);
switch (qrc) {
case H_SUCCESS:
+ spin_lock_bh(&vscsi->intr_lock);
+ vscsi->flags &= ~PREP_FOR_SUSPEND_FLAGS;
+ spin_unlock_bh(&vscsi->intr_lock);
break;
case H_HARDWARE:
@@ -422,6 +425,9 @@ static void ibmvscsis_disconnect(struct work_struct *work)
new_state = vscsi->new_state;
vscsi->new_state = 0;
+ vscsi->flags |= DISCONNECT_SCHEDULED;
+ vscsi->flags &= ~SCHEDULE_DISCONNECT;
+
pr_debug("disconnect: flags 0x%x, state 0x%hx\n", vscsi->flags,
vscsi->state);
@@ -802,6 +808,13 @@ static long ibmvscsis_establish_new_q(struct scsi_info *vscsi)
long rc = ADAPT_SUCCESS;
uint format;
+ rc = h_vioctl(vscsi->dds.unit_id, H_ENABLE_PREPARE_FOR_SUSPEND, 30000,
+ 0, 0, 0, 0);
+ if (rc == H_SUCCESS)
+ vscsi->flags |= PREP_FOR_SUSPEND_ENABLED;
+ else if (rc != H_NOT_FOUND)
+ pr_err("Error from Enable Prepare for Suspend: %ld\n", rc);
+
vscsi->flags &= PRESERVE_FLAG_FIELDS;
vscsi->rsp_q_timer.timer_pops = 0;
vscsi->debit = 0;
@@ -951,6 +964,63 @@ static void ibmvscsis_free_cmd_resources(struct scsi_info *vscsi,
}
/**
+ * ibmvscsis_ready_for_suspend() - Helper function to call VIOCTL
+ * @vscsi: Pointer to our adapter structure
+ * @idle: Indicates whether we were called from adapter_idle. This
+ * is important to know if we need to do a disconnect, since if
+ * we're called from adapter_idle, we're still processing the
+ * current disconnect, so we can't just call post_disconnect.
+ *
+ * This function is called when the adapter is idle when phyp has sent
+ * us a Prepare for Suspend Transport Event.
+ *
+ * EXECUTION ENVIRONMENT:
+ * Process or interrupt environment called with interrupt lock held
+ */
+static long ibmvscsis_ready_for_suspend(struct scsi_info *vscsi, bool idle)
+{
+ long rc = 0;
+ struct viosrp_crq *crq;
+
+ /* See if there is a Resume event in the queue */
+ crq = vscsi->cmd_q.base_addr + vscsi->cmd_q.index;
+
+ pr_debug("ready_suspend: flags 0x%x, state 0x%hx crq_valid:%x\n",
+ vscsi->flags, vscsi->state, (int)crq->valid);
+
+ if (!(vscsi->flags & PREP_FOR_SUSPEND_ABORTED) && !(crq->valid)) {
+ rc = h_vioctl(vscsi->dds.unit_id, H_READY_FOR_SUSPEND, 0, 0, 0,
+ 0, 0);
+ if (rc) {
+ pr_err("Ready for Suspend Vioctl failed: %ld\n", rc);
+ rc = 0;
+ }
+ } else if (((vscsi->flags & PREP_FOR_SUSPEND_OVERWRITE) &&
+ (vscsi->flags & PREP_FOR_SUSPEND_ABORTED)) ||
+ ((crq->valid) && ((crq->valid != VALID_TRANS_EVENT) ||
+ (crq->format != RESUME_FROM_SUSP)))) {
+ if (idle) {
+ vscsi->state = ERR_DISCONNECT_RECONNECT;
+ ibmvscsis_reset_queue(vscsi);
+ rc = -1;
+ } else if (vscsi->state == CONNECTED) {
+ ibmvscsis_post_disconnect(vscsi,
+ ERR_DISCONNECT_RECONNECT, 0);
+ }
+
+ vscsi->flags &= ~PREP_FOR_SUSPEND_OVERWRITE;
+
+ if ((crq->valid) && ((crq->valid != VALID_TRANS_EVENT) ||
+ (crq->format != RESUME_FROM_SUSP)))
+ pr_err("Invalid element in CRQ after Prepare for Suspend");
+ }
+
+ vscsi->flags &= ~(PREP_FOR_SUSPEND_PENDING | PREP_FOR_SUSPEND_ABORTED);
+
+ return rc;
+}
+
+/**
* ibmvscsis_trans_event() - Handle a Transport Event
* @vscsi: Pointer to our adapter structure
* @crq: Pointer to CRQ entry containing the Transport Event
@@ -974,18 +1044,8 @@ static long ibmvscsis_trans_event(struct scsi_info *vscsi,
case PARTNER_FAILED:
case PARTNER_DEREGISTER:
ibmvscsis_delete_client_info(vscsi, true);
- break;
-
- default:
- rc = ERROR;
- dev_err(&vscsi->dev, "trans_event: invalid format %d\n",
- (uint)crq->format);
- ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT,
- RESPONSE_Q_DOWN);
- break;
- }
-
- if (rc == ADAPT_SUCCESS) {
+ if (crq->format == MIGRATED)
+ vscsi->flags &= ~PREP_FOR_SUSPEND_OVERWRITE;
switch (vscsi->state) {
case NO_QUEUE:
case ERR_DISCONNECTED:
@@ -1034,6 +1094,60 @@ static long ibmvscsis_trans_event(struct scsi_info *vscsi,
vscsi->flags |= (RESPONSE_Q_DOWN | TRANS_EVENT);
break;
}
+ break;
+
+ case PREPARE_FOR_SUSPEND:
+ pr_debug("Prep for Suspend, crq status = 0x%x\n",
+ (int)crq->status);
+ switch (vscsi->state) {
+ case ERR_DISCONNECTED:
+ case WAIT_CONNECTION:
+ case CONNECTED:
+ ibmvscsis_ready_for_suspend(vscsi, false);
+ break;
+ case SRP_PROCESSING:
+ vscsi->resume_state = vscsi->state;
+ vscsi->flags |= PREP_FOR_SUSPEND_PENDING;
+ if (crq->status == CRQ_ENTRY_OVERWRITTEN)
+ vscsi->flags |= PREP_FOR_SUSPEND_OVERWRITE;
+ ibmvscsis_post_disconnect(vscsi, WAIT_IDLE, 0);
+ break;
+ case NO_QUEUE:
+ case UNDEFINED:
+ case UNCONFIGURING:
+ case WAIT_ENABLED:
+ case ERR_DISCONNECT:
+ case ERR_DISCONNECT_RECONNECT:
+ case WAIT_IDLE:
+ pr_err("Invalid state for Prepare for Suspend Trans Event: 0x%x\n",
+ vscsi->state);
+ break;
+ }
+ break;
+
+ case RESUME_FROM_SUSP:
+ pr_debug("Resume from Suspend, crq status = 0x%x\n",
+ (int)crq->status);
+ if (vscsi->flags & PREP_FOR_SUSPEND_PENDING) {
+ vscsi->flags |= PREP_FOR_SUSPEND_ABORTED;
+ } else {
+ if ((crq->status == CRQ_ENTRY_OVERWRITTEN) ||
+ (vscsi->flags & PREP_FOR_SUSPEND_OVERWRITE)) {
+ ibmvscsis_post_disconnect(vscsi,
+ ERR_DISCONNECT_RECONNECT,
+ 0);
+ vscsi->flags &= ~PREP_FOR_SUSPEND_OVERWRITE;
+ }
+ }
+ break;
+
+ default:
+ rc = ERROR;
+ dev_err(&vscsi->dev, "trans_event: invalid format %d\n",
+ (uint)crq->format);
+ ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT,
+ RESPONSE_Q_DOWN);
+ break;
}
rc = vscsi->flags & SCHEDULE_DISCONNECT;
@@ -1170,6 +1284,8 @@ static struct ibmvscsis_cmd *ibmvscsis_get_free_cmd(struct scsi_info *vscsi)
cmd = list_first_entry_or_null(&vscsi->free_cmd,
struct ibmvscsis_cmd, list);
if (cmd) {
+ if (cmd->abort_cmd)
+ cmd->abort_cmd = NULL;
cmd->flags &= ~(DELAY_SEND);
list_del(&cmd->list);
cmd->iue = iue;
@@ -1199,6 +1315,7 @@ static struct ibmvscsis_cmd *ibmvscsis_get_free_cmd(struct scsi_info *vscsi)
static void ibmvscsis_adapter_idle(struct scsi_info *vscsi)
{
int free_qs = false;
+ long rc = 0;
pr_debug("adapter_idle: flags 0x%x, state 0x%hx\n", vscsi->flags,
vscsi->state);
@@ -1238,7 +1355,14 @@ static void ibmvscsis_adapter_idle(struct scsi_info *vscsi)
vscsi->rsp_q_timer.timer_pops = 0;
vscsi->debit = 0;
vscsi->credit = 0;
- if (vscsi->flags & TRANS_EVENT) {
+ if (vscsi->flags & PREP_FOR_SUSPEND_PENDING) {
+ vscsi->state = vscsi->resume_state;
+ vscsi->resume_state = 0;
+ rc = ibmvscsis_ready_for_suspend(vscsi, true);
+ vscsi->flags &= ~DISCONNECT_SCHEDULED;
+ if (rc)
+ break;
+ } else if (vscsi->flags & TRANS_EVENT) {
vscsi->state = WAIT_CONNECTION;
vscsi->flags &= PRESERVE_FLAG_FIELDS;
} else {
@@ -1774,6 +1898,7 @@ static void ibmvscsis_send_messages(struct scsi_info *vscsi)
if (cmd->abort_cmd) {
retry = true;
cmd->abort_cmd->flags &= ~(DELAY_SEND);
+ cmd->abort_cmd = NULL;
}
/*
@@ -1788,6 +1913,25 @@ static void ibmvscsis_send_messages(struct scsi_info *vscsi)
list_del(&cmd->list);
ibmvscsis_free_cmd_resources(vscsi,
cmd);
+ /*
+ * With a successfully aborted op
+ * through LIO we want to increment the
+ * the vscsi credit so that when we dont
+ * send a rsp to the original scsi abort
+ * op (h_send_crq), but the tm rsp to
+ * the abort is sent, the credit is
+ * correctly sent with the abort tm rsp.
+ * We would need 1 for the abort tm rsp
+ * and 1 credit for the aborted scsi op.
+ * Thus we need to increment here.
+ * Also we want to increment the credit
+ * here because we want to make sure
+ * cmd is actually released first
+ * otherwise the client will think it
+ * it can send a new cmd, and we could
+ * find ourselves short of cmd elements.
+ */
+ vscsi->credit += 1;
} else {
iue = cmd->iue;
@@ -2962,10 +3106,7 @@ static long srp_build_response(struct scsi_info *vscsi,
rsp->opcode = SRP_RSP;
- if (vscsi->credit > 0 && vscsi->state == SRP_PROCESSING)
- rsp->req_lim_delta = cpu_to_be32(vscsi->credit);
- else
- rsp->req_lim_delta = cpu_to_be32(1 + vscsi->credit);
+ rsp->req_lim_delta = cpu_to_be32(1 + vscsi->credit);
rsp->tag = cmd->rsp.tag;
rsp->flags = 0;
diff --git a/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.h b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.h
index b4391a8de456..cc96c2731134 100644
--- a/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.h
+++ b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.h
@@ -262,6 +262,14 @@ struct scsi_info {
#define DISCONNECT_SCHEDULED 0x00800
/* remove function is sleeping */
#define CFG_SLEEPING 0x01000
+ /* Register for Prepare for Suspend Transport Events */
+#define PREP_FOR_SUSPEND_ENABLED 0x02000
+ /* Prepare for Suspend event sent */
+#define PREP_FOR_SUSPEND_PENDING 0x04000
+ /* Resume from Suspend event sent */
+#define PREP_FOR_SUSPEND_ABORTED 0x08000
+ /* Prepare for Suspend event overwrote another CRQ entry */
+#define PREP_FOR_SUSPEND_OVERWRITE 0x10000
u32 flags;
/* adapter lock */
spinlock_t intr_lock;
@@ -272,6 +280,7 @@ struct scsi_info {
/* used in crq, to tag what iu the response is for */
u64 empty_iu_tag;
uint new_state;
+ uint resume_state;
/* control block for the response queue timer */
struct timer_cb rsp_q_timer;
/* keep last client to enable proper accounting */
@@ -324,8 +333,13 @@ struct scsi_info {
#define TARGET_STOP(VSCSI) (long)(((VSCSI)->state & DONT_PROCESS_STATE) | \
((VSCSI)->flags & BLOCK))
+#define PREP_FOR_SUSPEND_FLAGS (PREP_FOR_SUSPEND_ENABLED | \
+ PREP_FOR_SUSPEND_PENDING | \
+ PREP_FOR_SUSPEND_ABORTED | \
+ PREP_FOR_SUSPEND_OVERWRITE)
+
/* flag bit that are not reset during disconnect */
-#define PRESERVE_FLAG_FIELDS 0
+#define PRESERVE_FLAG_FIELDS (PREP_FOR_SUSPEND_FLAGS)
#define vio_iu(IUE) ((union viosrp_iu *)((IUE)->sbuf->buf))
@@ -333,8 +347,15 @@ struct scsi_info {
#define WRITE_CMD(cdb) (((cdb)[0] & 0x1F) == 0xA)
#ifndef H_GET_PARTNER_INFO
-#define H_GET_PARTNER_INFO 0x0000000000000008LL
+#define H_GET_PARTNER_INFO 0x0000000000000008LL
+#endif
+#ifndef H_ENABLE_PREPARE_FOR_SUSPEND
+#define H_ENABLE_PREPARE_FOR_SUSPEND 0x000000000000001DLL
#endif
+#ifndef H_READY_FOR_SUSPEND
+#define H_READY_FOR_SUSPEND 0x000000000000001ELL
+#endif
+
#define h_copy_rdma(l, sa, sb, da, db) \
plpar_hcall_norets(H_COPY_RDMA, l, sa, sb, da, db)
diff --git a/drivers/scsi/ibmvscsi_tgt/libsrp.h b/drivers/scsi/ibmvscsi_tgt/libsrp.h
index 4696f331453e..9fec55b36322 100644
--- a/drivers/scsi/ibmvscsi_tgt/libsrp.h
+++ b/drivers/scsi/ibmvscsi_tgt/libsrp.h
@@ -30,10 +30,13 @@ enum srp_trans_event {
UNUSED_FORMAT = 0,
PARTNER_FAILED = 1,
PARTNER_DEREGISTER = 2,
- MIGRATED = 6
+ MIGRATED = 6,
+ PREPARE_FOR_SUSPEND = 9,
+ RESUME_FROM_SUSP = 0xA
};
enum srp_status {
+ CRQ_ENTRY_OVERWRITTEN = 0x20,
HEADER_DESCRIPTOR = 0xF1,
PING = 0xF5,
PING_RESPONSE = 0xF6
diff --git a/drivers/target/target_core_rd.c b/drivers/target/target_core_rd.c
index 20253d04103f..d12967690054 100644
--- a/drivers/target/target_core_rd.c
+++ b/drivers/target/target_core_rd.c
@@ -554,7 +554,7 @@ static ssize_t rd_set_configfs_dev_params(struct se_device *dev,
struct rd_dev *rd_dev = RD_DEV(dev);
char *orig, *ptr, *opts;
substring_t args[MAX_OPT_ARGS];
- int ret = 0, arg, token;
+ int arg, token;
opts = kstrdup(page, GFP_KERNEL);
if (!opts)
@@ -589,7 +589,7 @@ static ssize_t rd_set_configfs_dev_params(struct se_device *dev,
}
kfree(orig);
- return (!ret) ? count : ret;
+ return count;
}
static ssize_t rd_show_configfs_dev_params(struct se_device *dev, char *b)
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index 37f57357d4a0..6025935036c9 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -1160,15 +1160,28 @@ target_cmd_size_check(struct se_cmd *cmd, unsigned int size)
if (cmd->unknown_data_length) {
cmd->data_length = size;
} else if (size != cmd->data_length) {
- pr_warn("TARGET_CORE[%s]: Expected Transfer Length:"
+ pr_warn_ratelimited("TARGET_CORE[%s]: Expected Transfer Length:"
" %u does not match SCSI CDB Length: %u for SAM Opcode:"
" 0x%02x\n", cmd->se_tfo->get_fabric_name(),
cmd->data_length, size, cmd->t_task_cdb[0]);
- if (cmd->data_direction == DMA_TO_DEVICE &&
- cmd->se_cmd_flags & SCF_SCSI_DATA_CDB) {
- pr_err("Rejecting underflow/overflow WRITE data\n");
- return TCM_INVALID_CDB_FIELD;
+ if (cmd->data_direction == DMA_TO_DEVICE) {
+ if (cmd->se_cmd_flags & SCF_SCSI_DATA_CDB) {
+ pr_err_ratelimited("Rejecting underflow/overflow"
+ " for WRITE data CDB\n");
+ return TCM_INVALID_CDB_FIELD;
+ }
+ /*
+ * Some fabric drivers like iscsi-target still expect to
+ * always reject overflow writes. Reject this case until
+ * full fabric driver level support for overflow writes
+ * is introduced tree-wide.
+ */
+ if (size > cmd->data_length) {
+ pr_err_ratelimited("Rejecting overflow for"
+ " WRITE control CDB\n");
+ return TCM_INVALID_CDB_FIELD;
+ }
}
/*
* Reject READ_* or WRITE_* with overflow/underflow for
diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c
index 9045837f748b..beb5f098f32d 100644
--- a/drivers/target/target_core_user.c
+++ b/drivers/target/target_core_user.c
@@ -97,7 +97,7 @@ struct tcmu_hba {
struct tcmu_dev {
struct list_head node;
-
+ struct kref kref;
struct se_device se_dev;
char *name;
@@ -969,6 +969,7 @@ static struct se_device *tcmu_alloc_device(struct se_hba *hba, const char *name)
udev = kzalloc(sizeof(struct tcmu_dev), GFP_KERNEL);
if (!udev)
return NULL;
+ kref_init(&udev->kref);
udev->name = kstrdup(name, GFP_KERNEL);
if (!udev->name) {
@@ -1145,6 +1146,24 @@ static int tcmu_open(struct uio_info *info, struct inode *inode)
return 0;
}
+static void tcmu_dev_call_rcu(struct rcu_head *p)
+{
+ struct se_device *dev = container_of(p, struct se_device, rcu_head);
+ struct tcmu_dev *udev = TCMU_DEV(dev);
+
+ kfree(udev->uio_info.name);
+ kfree(udev->name);
+ kfree(udev);
+}
+
+static void tcmu_dev_kref_release(struct kref *kref)
+{
+ struct tcmu_dev *udev = container_of(kref, struct tcmu_dev, kref);
+ struct se_device *dev = &udev->se_dev;
+
+ call_rcu(&dev->rcu_head, tcmu_dev_call_rcu);
+}
+
static int tcmu_release(struct uio_info *info, struct inode *inode)
{
struct tcmu_dev *udev = container_of(info, struct tcmu_dev, uio_info);
@@ -1152,7 +1171,8 @@ static int tcmu_release(struct uio_info *info, struct inode *inode)
clear_bit(TCMU_DEV_BIT_OPEN, &udev->flags);
pr_debug("close\n");
-
+ /* release ref from configure */
+ kref_put(&udev->kref, tcmu_dev_kref_release);
return 0;
}
@@ -1272,6 +1292,12 @@ static int tcmu_configure_device(struct se_device *dev)
dev->dev_attrib.hw_max_sectors = 128;
dev->dev_attrib.hw_queue_depth = 128;
+ /*
+ * Get a ref incase userspace does a close on the uio device before
+ * LIO has initiated tcmu_free_device.
+ */
+ kref_get(&udev->kref);
+
ret = tcmu_netlink_event(TCMU_CMD_ADDED_DEVICE, udev->uio_info.name,
udev->uio_info.uio_dev->minor);
if (ret)
@@ -1284,11 +1310,13 @@ static int tcmu_configure_device(struct se_device *dev)
return 0;
err_netlink:
+ kref_put(&udev->kref, tcmu_dev_kref_release);
uio_unregister_device(&udev->uio_info);
err_register:
vfree(udev->mb_addr);
err_vzalloc:
kfree(info->name);
+ info->name = NULL;
return ret;
}
@@ -1302,14 +1330,6 @@ static int tcmu_check_and_free_pending_cmd(struct tcmu_cmd *cmd)
return -EINVAL;
}
-static void tcmu_dev_call_rcu(struct rcu_head *p)
-{
- struct se_device *dev = container_of(p, struct se_device, rcu_head);
- struct tcmu_dev *udev = TCMU_DEV(dev);
-
- kfree(udev);
-}
-
static bool tcmu_dev_configured(struct tcmu_dev *udev)
{
return udev->uio_info.uio_dev ? true : false;
@@ -1364,10 +1384,10 @@ static void tcmu_free_device(struct se_device *dev)
udev->uio_info.uio_dev->minor);
uio_unregister_device(&udev->uio_info);
- kfree(udev->uio_info.name);
- kfree(udev->name);
}
- call_rcu(&dev->rcu_head, tcmu_dev_call_rcu);
+
+ /* release ref from init */
+ kref_put(&udev->kref, tcmu_dev_kref_release);
}
enum {
diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c
index fd6c8b66f06f..679f8960db4b 100644
--- a/drivers/vhost/scsi.c
+++ b/drivers/vhost/scsi.c
@@ -496,14 +496,12 @@ static void vhost_scsi_evt_work(struct vhost_work *work)
struct vhost_scsi *vs = container_of(work, struct vhost_scsi,
vs_event_work);
struct vhost_virtqueue *vq = &vs->vqs[VHOST_SCSI_VQ_EVT].vq;
- struct vhost_scsi_evt *evt;
+ struct vhost_scsi_evt *evt, *t;
struct llist_node *llnode;
mutex_lock(&vq->mutex);
llnode = llist_del_all(&vs->vs_event_list);
- while (llnode) {
- evt = llist_entry(llnode, struct vhost_scsi_evt, list);
- llnode = llist_next(llnode);
+ llist_for_each_entry_safe(evt, t, llnode, list) {
vhost_scsi_do_evt_work(vs, evt);
vhost_scsi_free_evt(vs, evt);
}
@@ -529,10 +527,7 @@ static void vhost_scsi_complete_cmd_work(struct vhost_work *work)
bitmap_zero(signal, VHOST_SCSI_MAX_VQ);
llnode = llist_del_all(&vs->vs_completion_list);
- while (llnode) {
- cmd = llist_entry(llnode, struct vhost_scsi_cmd,
- tvc_completion_list);
- llnode = llist_next(llnode);
+ llist_for_each_entry(cmd, llnode, tvc_completion_list) {
se_cmd = &cmd->tvc_se_cmd;
pr_debug("%s tv_cmd %p resid %u status %#02x\n", __func__,