diff options
author | Paolo Bonzini <pbonzini@redhat.com> | 2012-06-13 10:56:34 -0400 |
---|---|---|
committer | James Bottomley <JBottomley@Parallels.com> | 2012-07-20 03:58:58 -0400 |
commit | 2bd37f0fde99cbf8b78fb55f1128e8c3a63cf1da (patch) | |
tree | 669df2ebb154fff1af3590bd2bf815083857e01d /drivers/scsi/virtio_scsi.c | |
parent | bce750b1633927be3eecf821f4d17975c3ba5b6a (diff) |
[SCSI] virtio-scsi: split scatterlist per target
To improve performance for I/O to different targets, add a separate
scatterlist for each of them.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
Diffstat (limited to 'drivers/scsi/virtio_scsi.c')
-rw-r--r-- | drivers/scsi/virtio_scsi.c | 141 |
1 files changed, 94 insertions, 47 deletions
diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c index facfc90ef005..9fc5e67a0ca5 100644 --- a/drivers/scsi/virtio_scsi.c +++ b/drivers/scsi/virtio_scsi.c | |||
@@ -50,18 +50,24 @@ struct virtio_scsi_vq { | |||
50 | struct virtqueue *vq; | 50 | struct virtqueue *vq; |
51 | }; | 51 | }; |
52 | 52 | ||
53 | /* Per-target queue state */ | ||
54 | struct virtio_scsi_target_state { | ||
55 | /* Protects sg. Lock hierarchy is tgt_lock -> vq_lock. */ | ||
56 | spinlock_t tgt_lock; | ||
57 | |||
58 | /* For sglist construction when adding commands to the virtqueue. */ | ||
59 | struct scatterlist sg[]; | ||
60 | }; | ||
61 | |||
53 | /* Driver instance state */ | 62 | /* Driver instance state */ |
54 | struct virtio_scsi { | 63 | struct virtio_scsi { |
55 | /* Protects sg[]. The lock hierarchy is sg_lock -> vq_lock. */ | ||
56 | spinlock_t sg_lock; | ||
57 | |||
58 | struct virtio_device *vdev; | 64 | struct virtio_device *vdev; |
65 | |||
59 | struct virtio_scsi_vq ctrl_vq; | 66 | struct virtio_scsi_vq ctrl_vq; |
60 | struct virtio_scsi_vq event_vq; | 67 | struct virtio_scsi_vq event_vq; |
61 | struct virtio_scsi_vq req_vq; | 68 | struct virtio_scsi_vq req_vq; |
62 | 69 | ||
63 | /* For sglist construction when adding commands to the virtqueue. */ | 70 | struct virtio_scsi_target_state *tgt[]; |
64 | struct scatterlist sg[]; | ||
65 | }; | 71 | }; |
66 | 72 | ||
67 | static struct kmem_cache *virtscsi_cmd_cache; | 73 | static struct kmem_cache *virtscsi_cmd_cache; |
@@ -230,25 +236,17 @@ static void virtscsi_map_sgl(struct scatterlist *sg, unsigned int *p_idx, | |||
230 | * @req_size : size of the request buffer | 236 | * @req_size : size of the request buffer |
231 | * @resp_size : size of the response buffer | 237 | * @resp_size : size of the response buffer |
232 | * | 238 | * |
233 | * Called with vq_lock held. | 239 | * Called with tgt_lock held. |
234 | */ | 240 | */ |
235 | static void virtscsi_map_cmd(struct virtio_scsi *vscsi, | 241 | static void virtscsi_map_cmd(struct virtio_scsi_target_state *tgt, |
236 | struct virtio_scsi_cmd *cmd, | 242 | struct virtio_scsi_cmd *cmd, |
237 | unsigned *out_num, unsigned *in_num, | 243 | unsigned *out_num, unsigned *in_num, |
238 | size_t req_size, size_t resp_size) | 244 | size_t req_size, size_t resp_size) |
239 | { | 245 | { |
240 | struct scsi_cmnd *sc = cmd->sc; | 246 | struct scsi_cmnd *sc = cmd->sc; |
241 | struct scatterlist *sg = vscsi->sg; | 247 | struct scatterlist *sg = tgt->sg; |
242 | unsigned int idx = 0; | 248 | unsigned int idx = 0; |
243 | 249 | ||
244 | if (sc) { | ||
245 | struct Scsi_Host *shost = virtio_scsi_host(vscsi->vdev); | ||
246 | BUG_ON(scsi_sg_count(sc) > shost->sg_tablesize); | ||
247 | |||
248 | /* TODO: check feature bit and fail if unsupported? */ | ||
249 | BUG_ON(sc->sc_data_direction == DMA_BIDIRECTIONAL); | ||
250 | } | ||
251 | |||
252 | /* Request header. */ | 250 | /* Request header. */ |
253 | sg_set_buf(&sg[idx++], &cmd->req, req_size); | 251 | sg_set_buf(&sg[idx++], &cmd->req, req_size); |
254 | 252 | ||
@@ -268,7 +266,8 @@ static void virtscsi_map_cmd(struct virtio_scsi *vscsi, | |||
268 | *in_num = idx - *out_num; | 266 | *in_num = idx - *out_num; |
269 | } | 267 | } |
270 | 268 | ||
271 | static int virtscsi_kick_cmd(struct virtio_scsi *vscsi, struct virtio_scsi_vq *vq, | 269 | static int virtscsi_kick_cmd(struct virtio_scsi_target_state *tgt, |
270 | struct virtio_scsi_vq *vq, | ||
272 | struct virtio_scsi_cmd *cmd, | 271 | struct virtio_scsi_cmd *cmd, |
273 | size_t req_size, size_t resp_size, gfp_t gfp) | 272 | size_t req_size, size_t resp_size, gfp_t gfp) |
274 | { | 273 | { |
@@ -276,12 +275,12 @@ static int virtscsi_kick_cmd(struct virtio_scsi *vscsi, struct virtio_scsi_vq *v | |||
276 | unsigned long flags; | 275 | unsigned long flags; |
277 | int ret; | 276 | int ret; |
278 | 277 | ||
279 | spin_lock_irqsave(&vscsi->sg_lock, flags); | 278 | spin_lock_irqsave(&tgt->tgt_lock, flags); |
280 | virtscsi_map_cmd(vscsi, cmd, &out_num, &in_num, req_size, resp_size); | 279 | virtscsi_map_cmd(tgt, cmd, &out_num, &in_num, req_size, resp_size); |
281 | 280 | ||
282 | spin_lock(&vq->vq_lock); | 281 | spin_lock(&vq->vq_lock); |
283 | ret = virtqueue_add_buf(vq->vq, vscsi->sg, out_num, in_num, cmd, gfp); | 282 | ret = virtqueue_add_buf(vq->vq, tgt->sg, out_num, in_num, cmd, gfp); |
284 | spin_unlock(&vscsi->sg_lock); | 283 | spin_unlock(&tgt->tgt_lock); |
285 | if (ret >= 0) | 284 | if (ret >= 0) |
286 | ret = virtqueue_kick_prepare(vq->vq); | 285 | ret = virtqueue_kick_prepare(vq->vq); |
287 | 286 | ||
@@ -295,9 +294,16 @@ static int virtscsi_kick_cmd(struct virtio_scsi *vscsi, struct virtio_scsi_vq *v | |||
295 | static int virtscsi_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *sc) | 294 | static int virtscsi_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *sc) |
296 | { | 295 | { |
297 | struct virtio_scsi *vscsi = shost_priv(sh); | 296 | struct virtio_scsi *vscsi = shost_priv(sh); |
297 | struct virtio_scsi_target_state *tgt = vscsi->tgt[sc->device->id]; | ||
298 | struct virtio_scsi_cmd *cmd; | 298 | struct virtio_scsi_cmd *cmd; |
299 | int ret; | 299 | int ret; |
300 | 300 | ||
301 | struct Scsi_Host *shost = virtio_scsi_host(vscsi->vdev); | ||
302 | BUG_ON(scsi_sg_count(sc) > shost->sg_tablesize); | ||
303 | |||
304 | /* TODO: check feature bit and fail if unsupported? */ | ||
305 | BUG_ON(sc->sc_data_direction == DMA_BIDIRECTIONAL); | ||
306 | |||
301 | dev_dbg(&sc->device->sdev_gendev, | 307 | dev_dbg(&sc->device->sdev_gendev, |
302 | "cmd %p CDB: %#02x\n", sc, sc->cmnd[0]); | 308 | "cmd %p CDB: %#02x\n", sc, sc->cmnd[0]); |
303 | 309 | ||
@@ -322,7 +328,7 @@ static int virtscsi_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *sc) | |||
322 | BUG_ON(sc->cmd_len > VIRTIO_SCSI_CDB_SIZE); | 328 | BUG_ON(sc->cmd_len > VIRTIO_SCSI_CDB_SIZE); |
323 | memcpy(cmd->req.cmd.cdb, sc->cmnd, sc->cmd_len); | 329 | memcpy(cmd->req.cmd.cdb, sc->cmnd, sc->cmd_len); |
324 | 330 | ||
325 | if (virtscsi_kick_cmd(vscsi, &vscsi->req_vq, cmd, | 331 | if (virtscsi_kick_cmd(tgt, &vscsi->req_vq, cmd, |
326 | sizeof cmd->req.cmd, sizeof cmd->resp.cmd, | 332 | sizeof cmd->req.cmd, sizeof cmd->resp.cmd, |
327 | GFP_ATOMIC) >= 0) | 333 | GFP_ATOMIC) >= 0) |
328 | ret = 0; | 334 | ret = 0; |
@@ -334,10 +340,11 @@ out: | |||
334 | static int virtscsi_tmf(struct virtio_scsi *vscsi, struct virtio_scsi_cmd *cmd) | 340 | static int virtscsi_tmf(struct virtio_scsi *vscsi, struct virtio_scsi_cmd *cmd) |
335 | { | 341 | { |
336 | DECLARE_COMPLETION_ONSTACK(comp); | 342 | DECLARE_COMPLETION_ONSTACK(comp); |
343 | struct virtio_scsi_target_state *tgt = vscsi->tgt[cmd->sc->device->id]; | ||
337 | int ret = FAILED; | 344 | int ret = FAILED; |
338 | 345 | ||
339 | cmd->comp = ∁ | 346 | cmd->comp = ∁ |
340 | if (virtscsi_kick_cmd(vscsi, &vscsi->ctrl_vq, cmd, | 347 | if (virtscsi_kick_cmd(tgt, &vscsi->ctrl_vq, cmd, |
341 | sizeof cmd->req.tmf, sizeof cmd->resp.tmf, | 348 | sizeof cmd->req.tmf, sizeof cmd->resp.tmf, |
342 | GFP_NOIO) < 0) | 349 | GFP_NOIO) < 0) |
343 | goto out; | 350 | goto out; |
@@ -437,11 +444,49 @@ static void virtscsi_init_vq(struct virtio_scsi_vq *virtscsi_vq, | |||
437 | virtscsi_vq->vq = vq; | 444 | virtscsi_vq->vq = vq; |
438 | } | 445 | } |
439 | 446 | ||
447 | static struct virtio_scsi_target_state *virtscsi_alloc_tgt( | ||
448 | struct virtio_device *vdev, int sg_elems) | ||
449 | { | ||
450 | struct virtio_scsi_target_state *tgt; | ||
451 | gfp_t gfp_mask = GFP_KERNEL; | ||
452 | |||
453 | /* We need extra sg elements at head and tail. */ | ||
454 | tgt = kmalloc(sizeof(*tgt) + sizeof(tgt->sg[0]) * (sg_elems + 2), | ||
455 | gfp_mask); | ||
456 | |||
457 | if (!tgt) | ||
458 | return NULL; | ||
459 | |||
460 | spin_lock_init(&tgt->tgt_lock); | ||
461 | sg_init_table(tgt->sg, sg_elems + 2); | ||
462 | return tgt; | ||
463 | } | ||
464 | |||
465 | static void virtscsi_remove_vqs(struct virtio_device *vdev) | ||
466 | { | ||
467 | struct Scsi_Host *sh = virtio_scsi_host(vdev); | ||
468 | struct virtio_scsi *vscsi = shost_priv(sh); | ||
469 | u32 i, num_targets; | ||
470 | |||
471 | /* Stop all the virtqueues. */ | ||
472 | vdev->config->reset(vdev); | ||
473 | |||
474 | num_targets = sh->max_id; | ||
475 | for (i = 0; i < num_targets; i++) { | ||
476 | kfree(vscsi->tgt[i]); | ||
477 | vscsi->tgt[i] = NULL; | ||
478 | } | ||
479 | |||
480 | vdev->config->del_vqs(vdev); | ||
481 | } | ||
482 | |||
440 | static int virtscsi_init(struct virtio_device *vdev, | 483 | static int virtscsi_init(struct virtio_device *vdev, |
441 | struct virtio_scsi *vscsi) | 484 | struct virtio_scsi *vscsi, int num_targets) |
442 | { | 485 | { |
443 | int err; | 486 | int err; |
444 | struct virtqueue *vqs[3]; | 487 | struct virtqueue *vqs[3]; |
488 | u32 i, sg_elems; | ||
489 | |||
445 | vq_callback_t *callbacks[] = { | 490 | vq_callback_t *callbacks[] = { |
446 | virtscsi_ctrl_done, | 491 | virtscsi_ctrl_done, |
447 | virtscsi_event_done, | 492 | virtscsi_event_done, |
@@ -464,7 +509,23 @@ static int virtscsi_init(struct virtio_device *vdev, | |||
464 | 509 | ||
465 | virtscsi_config_set(vdev, cdb_size, VIRTIO_SCSI_CDB_SIZE); | 510 | virtscsi_config_set(vdev, cdb_size, VIRTIO_SCSI_CDB_SIZE); |
466 | virtscsi_config_set(vdev, sense_size, VIRTIO_SCSI_SENSE_SIZE); | 511 | virtscsi_config_set(vdev, sense_size, VIRTIO_SCSI_SENSE_SIZE); |
467 | return 0; | 512 | |
513 | /* We need to know how many segments before we allocate. */ | ||
514 | sg_elems = virtscsi_config_get(vdev, seg_max) ?: 1; | ||
515 | |||
516 | for (i = 0; i < num_targets; i++) { | ||
517 | vscsi->tgt[i] = virtscsi_alloc_tgt(vdev, sg_elems); | ||
518 | if (!vscsi->tgt[i]) { | ||
519 | err = -ENOMEM; | ||
520 | goto out; | ||
521 | } | ||
522 | } | ||
523 | err = 0; | ||
524 | |||
525 | out: | ||
526 | if (err) | ||
527 | virtscsi_remove_vqs(vdev); | ||
528 | return err; | ||
468 | } | 529 | } |
469 | 530 | ||
470 | static int __devinit virtscsi_probe(struct virtio_device *vdev) | 531 | static int __devinit virtscsi_probe(struct virtio_device *vdev) |
@@ -472,31 +533,25 @@ static int __devinit virtscsi_probe(struct virtio_device *vdev) | |||
472 | struct Scsi_Host *shost; | 533 | struct Scsi_Host *shost; |
473 | struct virtio_scsi *vscsi; | 534 | struct virtio_scsi *vscsi; |
474 | int err; | 535 | int err; |
475 | u32 sg_elems; | 536 | u32 sg_elems, num_targets; |
476 | u32 cmd_per_lun; | 537 | u32 cmd_per_lun; |
477 | 538 | ||
478 | /* We need to know how many segments before we allocate. | ||
479 | * We need an extra sg elements at head and tail. | ||
480 | */ | ||
481 | sg_elems = virtscsi_config_get(vdev, seg_max) ?: 1; | ||
482 | |||
483 | /* Allocate memory and link the structs together. */ | 539 | /* Allocate memory and link the structs together. */ |
540 | num_targets = virtscsi_config_get(vdev, max_target) + 1; | ||
484 | shost = scsi_host_alloc(&virtscsi_host_template, | 541 | shost = scsi_host_alloc(&virtscsi_host_template, |
485 | sizeof(*vscsi) + sizeof(vscsi->sg[0]) * (sg_elems + 2)); | 542 | sizeof(*vscsi) |
543 | + num_targets * sizeof(struct virtio_scsi_target_state)); | ||
486 | 544 | ||
487 | if (!shost) | 545 | if (!shost) |
488 | return -ENOMEM; | 546 | return -ENOMEM; |
489 | 547 | ||
548 | sg_elems = virtscsi_config_get(vdev, seg_max) ?: 1; | ||
490 | shost->sg_tablesize = sg_elems; | 549 | shost->sg_tablesize = sg_elems; |
491 | vscsi = shost_priv(shost); | 550 | vscsi = shost_priv(shost); |
492 | vscsi->vdev = vdev; | 551 | vscsi->vdev = vdev; |
493 | vdev->priv = shost; | 552 | vdev->priv = shost; |
494 | 553 | ||
495 | /* Random initializations. */ | 554 | err = virtscsi_init(vdev, vscsi, num_targets); |
496 | spin_lock_init(&vscsi->sg_lock); | ||
497 | sg_init_table(vscsi->sg, sg_elems + 2); | ||
498 | |||
499 | err = virtscsi_init(vdev, vscsi); | ||
500 | if (err) | 555 | if (err) |
501 | goto virtscsi_init_failed; | 556 | goto virtscsi_init_failed; |
502 | 557 | ||
@@ -504,7 +559,7 @@ static int __devinit virtscsi_probe(struct virtio_device *vdev) | |||
504 | shost->cmd_per_lun = min_t(u32, cmd_per_lun, shost->can_queue); | 559 | shost->cmd_per_lun = min_t(u32, cmd_per_lun, shost->can_queue); |
505 | shost->max_sectors = virtscsi_config_get(vdev, max_sectors) ?: 0xFFFF; | 560 | shost->max_sectors = virtscsi_config_get(vdev, max_sectors) ?: 0xFFFF; |
506 | shost->max_lun = virtscsi_config_get(vdev, max_lun) + 1; | 561 | shost->max_lun = virtscsi_config_get(vdev, max_lun) + 1; |
507 | shost->max_id = virtscsi_config_get(vdev, max_target) + 1; | 562 | shost->max_id = num_targets; |
508 | shost->max_channel = 0; | 563 | shost->max_channel = 0; |
509 | shost->max_cmd_len = VIRTIO_SCSI_CDB_SIZE; | 564 | shost->max_cmd_len = VIRTIO_SCSI_CDB_SIZE; |
510 | err = scsi_add_host(shost, &vdev->dev); | 565 | err = scsi_add_host(shost, &vdev->dev); |
@@ -522,14 +577,6 @@ virtscsi_init_failed: | |||
522 | return err; | 577 | return err; |
523 | } | 578 | } |
524 | 579 | ||
525 | static void virtscsi_remove_vqs(struct virtio_device *vdev) | ||
526 | { | ||
527 | /* Stop all the virtqueues. */ | ||
528 | vdev->config->reset(vdev); | ||
529 | |||
530 | vdev->config->del_vqs(vdev); | ||
531 | } | ||
532 | |||
533 | static void __devexit virtscsi_remove(struct virtio_device *vdev) | 580 | static void __devexit virtscsi_remove(struct virtio_device *vdev) |
534 | { | 581 | { |
535 | struct Scsi_Host *shost = virtio_scsi_host(vdev); | 582 | struct Scsi_Host *shost = virtio_scsi_host(vdev); |
@@ -552,7 +599,7 @@ static int virtscsi_restore(struct virtio_device *vdev) | |||
552 | struct Scsi_Host *sh = virtio_scsi_host(vdev); | 599 | struct Scsi_Host *sh = virtio_scsi_host(vdev); |
553 | struct virtio_scsi *vscsi = shost_priv(sh); | 600 | struct virtio_scsi *vscsi = shost_priv(sh); |
554 | 601 | ||
555 | return virtscsi_init(vdev, vscsi); | 602 | return virtscsi_init(vdev, vscsi, sh->max_id); |
556 | } | 603 | } |
557 | #endif | 604 | #endif |
558 | 605 | ||