Skip to content

Commit 79f1b7d

Browse files
mikechristieBrian Maly
authored andcommitted
vhost-scsi: Reduce response iov mem use
We have to save N iov entries to copy the virtio_scsi_cmd_resp struct back to the guest's buffer. The difficulty is that we can't assume the virtio_scsi_cmd_resp will be in 1 iov because older virtio specs allowed you to break it up. The worst case is that the guest was doing something like breaking up the virtio_scsi_cmd_resp struct into 108 (the struct is 108 bytes) byte sized vecs like: iov[0].iov_base = ((unsigned char *)virtio_scsi_cmd_resp)[0] iov[0].iov_len = 1 iov[1].iov_base = ((unsigned char *)virtio_scsi_cmd_resp)[1] iov[1].iov_len = 1 .... iov[107].iov_base = ((unsigned char *)virtio_scsi_cmd_resp)[107] iov[1].iov_len = 1 Right now we allocate UIO_MAXIOV vecs which is 1024 and so for a small device with just 1 queue and 128 commands per queue, we are wasting 1.8 MB = (1024 current entries - 108) * 16 bytes per entry * 128 cmds The most common case is going to be where the initiator puts the entire virtio_scsi_cmd_resp in the first iov and does not split it. We've always done it this way for Linux and the windows driver looks like it's always done the same. It's highly unlikely anyone has ever split the response and if they did it might just be where they have the sense in a second iov but that doesn't seem likely as well. So to optimize for the common implementation, this has us only pre-allocate the single iovec. If we do hit the split up response case this has us allocate the needed iovec when needed. Signed-off-by: Mike Christie <[email protected]> Message-Id: <[email protected]> Signed-off-by: Michael S. Tsirkin <[email protected]> Acked-by: Stefan Hajnoczi <[email protected]> (cherry picked from commit 9d8960672d63db4b3b04542f5622748b345c637a) Orabug: 37980690 Conflicts: - commit de4eda9 ("use less confusing names for iov_iter direction initializers") is not merged in UEK7 so we are still using READ instead of ITER_DEST in vhost_scsi_complete_cmd_work. Signed-off-by: Mike Christie <[email protected]> Reviewed-by: Dongli Zhang <[email protected]> Signed-off-by: Brian Maly <[email protected]>
1 parent 36a0fcb commit 79f1b7d

File tree

1 file changed

+60
-22
lines changed

1 file changed

+60
-22
lines changed

drivers/vhost/scsi.c

Lines changed: 60 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@
4545
#define VHOST_SCSI_PREALLOC_SGLS 2048
4646
#define VHOST_SCSI_PREALLOC_UPAGES 2048
4747
#define VHOST_SCSI_PREALLOC_PROT_SGLS 2048
48+
/*
49+
* For the legacy descriptor case we allocate an iov per byte in the
50+
* virtio_scsi_cmd_resp struct.
51+
*/
52+
#define VHOST_SCSI_MAX_RESP_IOVS sizeof(struct virtio_scsi_cmd_resp)
4853

4954
static unsigned int vhost_scsi_inline_sg_cnt = VHOST_SCSI_PREALLOC_SGLS;
5055

@@ -106,8 +111,6 @@ struct vhost_scsi_inflight {
106111
struct vhost_scsi_cmd {
107112
/* Descriptor from vhost_get_vq_desc() for virt_queue segment */
108113
int tvc_vq_desc;
109-
/* virtio-scsi response incoming iovecs */
110-
int tvc_in_iovs;
111114
/* The number of scatterlists associated with this cmd */
112115
u32 tvc_sgl_count;
113116
u32 tvc_prot_sgl_count;
@@ -118,8 +121,12 @@ struct vhost_scsi_cmd {
118121
struct sg_table table;
119122
struct scatterlist *prot_sgl;
120123
struct sg_table prot_table;
121-
/* Pointer to response header iovec */
122-
struct iovec *tvc_resp_iov;
124+
/* Fast path response header iovec used when only one vec is needed */
125+
struct iovec tvc_resp_iov;
126+
/* Number of iovs for response */
127+
unsigned int tvc_resp_iovs_cnt;
128+
/* Pointer to response header iovecs if more than one is needed */
129+
struct iovec *tvc_resp_iovs;
123130
/* Pointer to vhost_virtqueue for the cmd */
124131
struct vhost_virtqueue *tvc_vq;
125132
/* The TCM I/O descriptor that is accessed via container_of() */
@@ -401,6 +408,8 @@ static void vhost_scsi_release_cmd_res(struct se_cmd *se_cmd)
401408
sg_free_table_chained(&tv_cmd->prot_table, vs->inline_sg_cnt);
402409
}
403410

411+
if (tv_cmd->tvc_resp_iovs != &tv_cmd->tvc_resp_iov)
412+
kfree(tv_cmd->tvc_resp_iovs);
404413
sbitmap_clear_bit(&svq->scsi_tags, se_cmd->map_tag);
405414
vhost_scsi_put_inflight(inflight);
406415
}
@@ -651,8 +660,8 @@ static void vhost_scsi_complete_cmd_work(struct vhost_work *work)
651660
se_cmd->scsi_sense_length);
652661
}
653662

654-
iov_iter_init(&iov_iter, READ, cmd->tvc_resp_iov,
655-
cmd->tvc_in_iovs, sizeof(v_rsp));
663+
iov_iter_init(&iov_iter, READ, cmd->tvc_resp_iovs,
664+
cmd->tvc_resp_iovs_cnt, sizeof(v_rsp));
656665
ret = copy_to_iter(&v_rsp, sizeof(v_rsp), &iov_iter);
657666
if (likely(ret == sizeof(v_rsp))) {
658667
struct vhost_scsi_virtqueue *q;
@@ -679,7 +688,6 @@ vhost_scsi_get_cmd(struct vhost_virtqueue *vq, u64 scsi_tag)
679688
struct vhost_scsi_virtqueue, vq);
680689
struct vhost_scsi_cmd *cmd;
681690
struct scatterlist *sgl, *prot_sgl;
682-
struct iovec *tvc_resp_iov;
683691
int tag;
684692

685693
tag = sbitmap_get(&svq->scsi_tags);
@@ -691,13 +699,11 @@ vhost_scsi_get_cmd(struct vhost_virtqueue *vq, u64 scsi_tag)
691699
cmd = &svq->scsi_cmds[tag];
692700
sgl = cmd->sgl;
693701
prot_sgl = cmd->prot_sgl;
694-
tvc_resp_iov = cmd->tvc_resp_iov;
695702
memset(cmd, 0, sizeof(*cmd));
696703
cmd->sgl = sgl;
697704
cmd->prot_sgl = prot_sgl;
698705
cmd->tvc_se_cmd.map_tag = tag;
699706
cmd->inflight = vhost_scsi_get_inflight(vq);
700-
cmd->tvc_resp_iov = tvc_resp_iov;
701707

702708
return cmd;
703709
}
@@ -1143,6 +1149,43 @@ vhost_scsi_get_req(struct vhost_virtqueue *vq, struct vhost_scsi_ctx *vc,
11431149
return ret;
11441150
}
11451151

1152+
static int
1153+
vhost_scsi_setup_resp_iovs(struct vhost_scsi_cmd *cmd, struct iovec *in_iovs,
1154+
unsigned int in_iovs_cnt)
1155+
{
1156+
int i, cnt;
1157+
1158+
if (!in_iovs_cnt)
1159+
return 0;
1160+
/*
1161+
* Initiator's normally just put the virtio_scsi_cmd_resp in the first
1162+
* iov, but just in case they wedged in some data with it we check for
1163+
* greater than or equal to the response struct.
1164+
*/
1165+
if (in_iovs[0].iov_len >= sizeof(struct virtio_scsi_cmd_resp)) {
1166+
cmd->tvc_resp_iovs = &cmd->tvc_resp_iov;
1167+
cmd->tvc_resp_iovs_cnt = 1;
1168+
} else {
1169+
/*
1170+
* Legacy descriptor layouts didn't specify that we must put
1171+
* the entire response in one iov. Worst case we have a
1172+
* iov per byte.
1173+
*/
1174+
cnt = min(VHOST_SCSI_MAX_RESP_IOVS, in_iovs_cnt);
1175+
cmd->tvc_resp_iovs = kcalloc(cnt, sizeof(struct iovec),
1176+
GFP_KERNEL);
1177+
if (!cmd->tvc_resp_iovs)
1178+
return -ENOMEM;
1179+
1180+
cmd->tvc_resp_iovs_cnt = cnt;
1181+
}
1182+
1183+
for (i = 0; i < cmd->tvc_resp_iovs_cnt; i++)
1184+
cmd->tvc_resp_iovs[i] = in_iovs[i];
1185+
1186+
return 0;
1187+
}
1188+
11461189
static u16 vhost_buf_to_lun(u8 *lun_buf)
11471190
{
11481191
return ((lun_buf[2] << 8) | lun_buf[3]) & 0x3FFF;
@@ -1160,7 +1203,7 @@ vhost_scsi_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq)
11601203
struct iov_iter in_iter, prot_iter, data_iter;
11611204
u64 tag;
11621205
u32 exp_data_len, data_direction;
1163-
int ret, prot_bytes, i, c = 0;
1206+
int ret, prot_bytes, c = 0;
11641207
u16 lun;
11651208
u8 task_attr;
11661209
bool t10_pi = vhost_has_feature(vq, VIRTIO_SCSI_F_T10_PI);
@@ -1322,9 +1365,13 @@ vhost_scsi_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq)
13221365
goto err;
13231366
}
13241367
cmd->tvc_vq = vq;
1325-
for (i = 0; i < vc.in ; i++)
1326-
cmd->tvc_resp_iov[i] = vq->iov[vc.out + i];
1327-
cmd->tvc_in_iovs = vc.in;
1368+
1369+
ret = vhost_scsi_setup_resp_iovs(cmd, &vq->iov[vc.out], vc.in);
1370+
if (ret) {
1371+
vq_err(vq, "Failed to alloc recv iovs\n");
1372+
vhost_scsi_release_cmd_res(&cmd->tvc_se_cmd);
1373+
goto err;
1374+
}
13281375

13291376
pr_debug("vhost_scsi got command opcode: %#02x, lun: %d\n",
13301377
cdb[0], lun);
@@ -1698,7 +1745,6 @@ static void vhost_scsi_destroy_vq_cmds(struct vhost_virtqueue *vq)
16981745

16991746
kfree(tv_cmd->sgl);
17001747
kfree(tv_cmd->prot_sgl);
1701-
kfree(tv_cmd->tvc_resp_iov);
17021748
}
17031749

17041750
sbitmap_free(&svq->scsi_tags);
@@ -1747,14 +1793,6 @@ static int vhost_scsi_setup_vq_cmds(struct vhost_virtqueue *vq, int max_cmds)
17471793
}
17481794
}
17491795

1750-
tv_cmd->tvc_resp_iov = kcalloc(UIO_MAXIOV,
1751-
sizeof(struct iovec),
1752-
GFP_KERNEL);
1753-
if (!tv_cmd->tvc_resp_iov) {
1754-
pr_err("Unable to allocate tv_cmd->tvc_resp_iov\n");
1755-
goto out;
1756-
}
1757-
17581796
if (vhost_has_feature(vq, VIRTIO_SCSI_F_T10_PI) &&
17591797
vs->inline_sg_cnt) {
17601798
tv_cmd->prot_sgl = kcalloc(vs->inline_sg_cnt,

0 commit comments

Comments
 (0)