mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-30 19:51:31 -04:00
Lots of virtio work which wasn't quite ready for last merge window. Plus
I dived into lguest again, reworking the pagetable code so we can move the switcher page: our fixmaps sometimes take more than 2MB now... Cheers, Rusty. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iQIcBAABAgAGBQJRga7lAAoJENkgDmzRrbjx/yIQAKpqIBtxOJeYH3SY+Uoe7Cfp toNYcpJEldvb0UcWN8M2cSZpHoxl1SUoq9djwcM29tcKa7EZAjHaGtb/Q1qMTDgv +B3WAfiGU2pmXFxLAkbrlLNGnysy24JspqJQ5hcYV84EiBxQdZp+nCYgOphd+GMK ww16vo9ya8jFjzt3GeRp/Heb3vEzV4Cp6BC3i0m8A3WNpEpbRb66pqXNk5o8ggJO SxQOKSXmUM+0m+jKSul5xn3e2Ls2LOrZZ8/DIHA+gW66N4Zab7n2/j1Q9VRxb4lh FqnR7KwgBX8OCh9IsBDqQYS7MohvMYge6eUdLtFrq84jvMleMEhrC8q9v2tucFUb 5t18CLwvyK7Gdg6UCKiZ7YSPcuURAILO16al9bh5IseeBDsuX+43VsvQoBmFn9k6 cLOVTZ6BlOmahK5PyRYFSvLa9Rxzr/05Mr7oYq9UgshD9io78dnqczFYIORF53rW zD7C4HuTZfYJFfNd0wAJ0RfVXnf8QvDlMdo7zPC26DSXNWqj8OexCY0qqSWUB+2F vcfJP6NkV4fZB8aawWIFUVwc64yqtt2uPVLa7ATZWqk16PgKrchGewmw3tiEwOgu 1l7xgffTRRUIJsqaCZoXdgw3yezcKRjuUBcOxL09lDAAhc+NxWNvzZBsKp66DwDk yZQKn0OdXnuf0CeEOfFf =1tYL -----END PGP SIGNATURE----- Merge tag 'virtio-next-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rusty/linux Pull virtio & lguest updates from Rusty Russell: "Lots of virtio work which wasn't quite ready for last merge window. Plus I dived into lguest again, reworking the pagetable code so we can move the switcher page: our fixmaps sometimes take more than 2MB now..." Ugh. Annoying conflicts with the tcm_vhost -> vhost_scsi rename. Hopefully correctly resolved. * tag 'virtio-next-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rusty/linux: (57 commits) caif_virtio: Remove bouncing email addresses lguest: improve code readability in lg_cpu_start. virtio-net: fill only rx queues which are being used lguest: map Switcher below fixmap. lguest: cache last cpu we ran on. lguest: map Switcher text whenever we allocate a new pagetable. lguest: don't share Switcher PTE pages between guests. lguest: expost switcher_pages array (as lg_switcher_pages). lguest: extract shadow PTE walking / allocating. lguest: make check_gpte et. al return bool. lguest: assume Switcher text is a single page. lguest: rename switcher_page to switcher_pages. lguest: remove RESERVE_MEM constant. lguest: check vaddr not pgd for Switcher protection. lguest: prepare to make SWITCHER_ADDR a variable. virtio: console: replace EMFILE with EBUSY for already-open port virtio-scsi: reset virtqueue affinity when doing cpu hotplug virtio-scsi: introduce multiqueue support virtio-scsi: push vq lock/unlock into virtscsi_vq_done virtio-scsi: pass struct virtio_scsi to virtqueue completion function ...
This commit is contained in:
commit
736a2dd257
60 changed files with 4481 additions and 4074 deletions
|
|
@ -13,6 +13,8 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mempool.h>
|
||||
|
|
@ -20,12 +22,14 @@
|
|||
#include <linux/virtio_ids.h>
|
||||
#include <linux/virtio_config.h>
|
||||
#include <linux/virtio_scsi.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <scsi/scsi_host.h>
|
||||
#include <scsi/scsi_device.h>
|
||||
#include <scsi/scsi_cmnd.h>
|
||||
|
||||
#define VIRTIO_SCSI_MEMPOOL_SZ 64
|
||||
#define VIRTIO_SCSI_EVENT_LEN 8
|
||||
#define VIRTIO_SCSI_VQ_BASE 2
|
||||
|
||||
/* Command queue element */
|
||||
struct virtio_scsi_cmd {
|
||||
|
|
@ -57,27 +61,61 @@ struct virtio_scsi_vq {
|
|||
struct virtqueue *vq;
|
||||
};
|
||||
|
||||
/* Per-target queue state */
|
||||
/*
|
||||
* Per-target queue state.
|
||||
*
|
||||
* This struct holds the data needed by the queue steering policy. When a
|
||||
* target is sent multiple requests, we need to drive them to the same queue so
|
||||
* that FIFO processing order is kept. However, if a target was idle, we can
|
||||
* choose a queue arbitrarily. In this case the queue is chosen according to
|
||||
* the current VCPU, so the driver expects the number of request queues to be
|
||||
* equal to the number of VCPUs. This makes it easy and fast to select the
|
||||
* queue, and also lets the driver optimize the IRQ affinity for the virtqueues
|
||||
* (each virtqueue's affinity is set to the CPU that "owns" the queue).
|
||||
*
|
||||
* An interesting effect of this policy is that only writes to req_vq need to
|
||||
* take the tgt_lock. Read can be done outside the lock because:
|
||||
*
|
||||
* - writes of req_vq only occur when atomic_inc_return(&tgt->reqs) returns 1.
|
||||
* In that case, no other CPU is reading req_vq: even if they were in
|
||||
* virtscsi_queuecommand_multi, they would be spinning on tgt_lock.
|
||||
*
|
||||
* - reads of req_vq only occur when the target is not idle (reqs != 0).
|
||||
* A CPU that enters virtscsi_queuecommand_multi will not modify req_vq.
|
||||
*
|
||||
* Similarly, decrements of reqs are never concurrent with writes of req_vq.
|
||||
* Thus they can happen outside the tgt_lock, provided of course we make reqs
|
||||
* an atomic_t.
|
||||
*/
|
||||
struct virtio_scsi_target_state {
|
||||
/* Protects sg. Lock hierarchy is tgt_lock -> vq_lock. */
|
||||
/* This spinlock never held at the same time as vq_lock. */
|
||||
spinlock_t tgt_lock;
|
||||
|
||||
/* For sglist construction when adding commands to the virtqueue. */
|
||||
struct scatterlist sg[];
|
||||
/* Count of outstanding requests. */
|
||||
atomic_t reqs;
|
||||
|
||||
/* Currently active virtqueue for requests sent to this target. */
|
||||
struct virtio_scsi_vq *req_vq;
|
||||
};
|
||||
|
||||
/* Driver instance state */
|
||||
struct virtio_scsi {
|
||||
struct virtio_device *vdev;
|
||||
|
||||
struct virtio_scsi_vq ctrl_vq;
|
||||
struct virtio_scsi_vq event_vq;
|
||||
struct virtio_scsi_vq req_vq;
|
||||
|
||||
/* Get some buffers ready for event vq */
|
||||
struct virtio_scsi_event_node event_list[VIRTIO_SCSI_EVENT_LEN];
|
||||
|
||||
struct virtio_scsi_target_state *tgt[];
|
||||
u32 num_queues;
|
||||
|
||||
/* If the affinity hint is set for virtqueues */
|
||||
bool affinity_hint_set;
|
||||
|
||||
/* CPU hotplug notifier */
|
||||
struct notifier_block nb;
|
||||
|
||||
struct virtio_scsi_vq ctrl_vq;
|
||||
struct virtio_scsi_vq event_vq;
|
||||
struct virtio_scsi_vq req_vqs[];
|
||||
};
|
||||
|
||||
static struct kmem_cache *virtscsi_cmd_cache;
|
||||
|
|
@ -107,11 +145,13 @@ static void virtscsi_compute_resid(struct scsi_cmnd *sc, u32 resid)
|
|||
*
|
||||
* Called with vq_lock held.
|
||||
*/
|
||||
static void virtscsi_complete_cmd(void *buf)
|
||||
static void virtscsi_complete_cmd(struct virtio_scsi *vscsi, void *buf)
|
||||
{
|
||||
struct virtio_scsi_cmd *cmd = buf;
|
||||
struct scsi_cmnd *sc = cmd->sc;
|
||||
struct virtio_scsi_cmd_resp *resp = &cmd->resp.cmd;
|
||||
struct virtio_scsi_target_state *tgt =
|
||||
scsi_target(sc->device)->hostdata;
|
||||
|
||||
dev_dbg(&sc->device->sdev_gendev,
|
||||
"cmd %p response %u status %#02x sense_len %u\n",
|
||||
|
|
@ -166,32 +206,71 @@ static void virtscsi_complete_cmd(void *buf)
|
|||
|
||||
mempool_free(cmd, virtscsi_cmd_pool);
|
||||
sc->scsi_done(sc);
|
||||
|
||||
atomic_dec(&tgt->reqs);
|
||||
}
|
||||
|
||||
static void virtscsi_vq_done(struct virtqueue *vq, void (*fn)(void *buf))
|
||||
static void virtscsi_vq_done(struct virtio_scsi *vscsi,
|
||||
struct virtio_scsi_vq *virtscsi_vq,
|
||||
void (*fn)(struct virtio_scsi *vscsi, void *buf))
|
||||
{
|
||||
void *buf;
|
||||
unsigned int len;
|
||||
unsigned long flags;
|
||||
struct virtqueue *vq = virtscsi_vq->vq;
|
||||
|
||||
spin_lock_irqsave(&virtscsi_vq->vq_lock, flags);
|
||||
do {
|
||||
virtqueue_disable_cb(vq);
|
||||
while ((buf = virtqueue_get_buf(vq, &len)) != NULL)
|
||||
fn(buf);
|
||||
fn(vscsi, buf);
|
||||
} while (!virtqueue_enable_cb(vq));
|
||||
spin_unlock_irqrestore(&virtscsi_vq->vq_lock, flags);
|
||||
}
|
||||
|
||||
static void virtscsi_req_done(struct virtqueue *vq)
|
||||
{
|
||||
struct Scsi_Host *sh = virtio_scsi_host(vq->vdev);
|
||||
struct virtio_scsi *vscsi = shost_priv(sh);
|
||||
unsigned long flags;
|
||||
int index = vq->index - VIRTIO_SCSI_VQ_BASE;
|
||||
struct virtio_scsi_vq *req_vq = &vscsi->req_vqs[index];
|
||||
|
||||
spin_lock_irqsave(&vscsi->req_vq.vq_lock, flags);
|
||||
virtscsi_vq_done(vq, virtscsi_complete_cmd);
|
||||
spin_unlock_irqrestore(&vscsi->req_vq.vq_lock, flags);
|
||||
/*
|
||||
* Read req_vq before decrementing the reqs field in
|
||||
* virtscsi_complete_cmd.
|
||||
*
|
||||
* With barriers:
|
||||
*
|
||||
* CPU #0 virtscsi_queuecommand_multi (CPU #1)
|
||||
* ------------------------------------------------------------
|
||||
* lock vq_lock
|
||||
* read req_vq
|
||||
* read reqs (reqs = 1)
|
||||
* write reqs (reqs = 0)
|
||||
* increment reqs (reqs = 1)
|
||||
* write req_vq
|
||||
*
|
||||
* Possible reordering without barriers:
|
||||
*
|
||||
* CPU #0 virtscsi_queuecommand_multi (CPU #1)
|
||||
* ------------------------------------------------------------
|
||||
* lock vq_lock
|
||||
* read reqs (reqs = 1)
|
||||
* write reqs (reqs = 0)
|
||||
* increment reqs (reqs = 1)
|
||||
* write req_vq
|
||||
* read (wrong) req_vq
|
||||
*
|
||||
* We do not need a full smp_rmb, because req_vq is required to get
|
||||
* to tgt->reqs: tgt is &vscsi->tgt[sc->device->id], where sc is stored
|
||||
* in the virtqueue as the user token.
|
||||
*/
|
||||
smp_read_barrier_depends();
|
||||
|
||||
virtscsi_vq_done(vscsi, req_vq, virtscsi_complete_cmd);
|
||||
};
|
||||
|
||||
static void virtscsi_complete_free(void *buf)
|
||||
static void virtscsi_complete_free(struct virtio_scsi *vscsi, void *buf)
|
||||
{
|
||||
struct virtio_scsi_cmd *cmd = buf;
|
||||
|
||||
|
|
@ -205,11 +284,8 @@ static void virtscsi_ctrl_done(struct virtqueue *vq)
|
|||
{
|
||||
struct Scsi_Host *sh = virtio_scsi_host(vq->vdev);
|
||||
struct virtio_scsi *vscsi = shost_priv(sh);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&vscsi->ctrl_vq.vq_lock, flags);
|
||||
virtscsi_vq_done(vq, virtscsi_complete_free);
|
||||
spin_unlock_irqrestore(&vscsi->ctrl_vq.vq_lock, flags);
|
||||
virtscsi_vq_done(vscsi, &vscsi->ctrl_vq, virtscsi_complete_free);
|
||||
};
|
||||
|
||||
static int virtscsi_kick_event(struct virtio_scsi *vscsi,
|
||||
|
|
@ -223,8 +299,8 @@ static int virtscsi_kick_event(struct virtio_scsi *vscsi,
|
|||
|
||||
spin_lock_irqsave(&vscsi->event_vq.vq_lock, flags);
|
||||
|
||||
err = virtqueue_add_buf(vscsi->event_vq.vq, &sg, 0, 1, event_node,
|
||||
GFP_ATOMIC);
|
||||
err = virtqueue_add_inbuf(vscsi->event_vq.vq, &sg, 1, event_node,
|
||||
GFP_ATOMIC);
|
||||
if (!err)
|
||||
virtqueue_kick(vscsi->event_vq.vq);
|
||||
|
||||
|
|
@ -254,7 +330,7 @@ static void virtscsi_cancel_event_work(struct virtio_scsi *vscsi)
|
|||
}
|
||||
|
||||
static void virtscsi_handle_transport_reset(struct virtio_scsi *vscsi,
|
||||
struct virtio_scsi_event *event)
|
||||
struct virtio_scsi_event *event)
|
||||
{
|
||||
struct scsi_device *sdev;
|
||||
struct Scsi_Host *shost = virtio_scsi_host(vscsi->vdev);
|
||||
|
|
@ -332,7 +408,7 @@ static void virtscsi_handle_event(struct work_struct *work)
|
|||
virtscsi_kick_event(vscsi, event_node);
|
||||
}
|
||||
|
||||
static void virtscsi_complete_event(void *buf)
|
||||
static void virtscsi_complete_event(struct virtio_scsi *vscsi, void *buf)
|
||||
{
|
||||
struct virtio_scsi_event_node *event_node = buf;
|
||||
|
||||
|
|
@ -344,82 +420,65 @@ static void virtscsi_event_done(struct virtqueue *vq)
|
|||
{
|
||||
struct Scsi_Host *sh = virtio_scsi_host(vq->vdev);
|
||||
struct virtio_scsi *vscsi = shost_priv(sh);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&vscsi->event_vq.vq_lock, flags);
|
||||
virtscsi_vq_done(vq, virtscsi_complete_event);
|
||||
spin_unlock_irqrestore(&vscsi->event_vq.vq_lock, flags);
|
||||
virtscsi_vq_done(vscsi, &vscsi->event_vq, virtscsi_complete_event);
|
||||
};
|
||||
|
||||
static void virtscsi_map_sgl(struct scatterlist *sg, unsigned int *p_idx,
|
||||
struct scsi_data_buffer *sdb)
|
||||
{
|
||||
struct sg_table *table = &sdb->table;
|
||||
struct scatterlist *sg_elem;
|
||||
unsigned int idx = *p_idx;
|
||||
int i;
|
||||
|
||||
for_each_sg(table->sgl, sg_elem, table->nents, i)
|
||||
sg[idx++] = *sg_elem;
|
||||
|
||||
*p_idx = idx;
|
||||
}
|
||||
|
||||
/**
|
||||
* virtscsi_map_cmd - map a scsi_cmd to a virtqueue scatterlist
|
||||
* @vscsi : virtio_scsi state
|
||||
* virtscsi_add_cmd - add a virtio_scsi_cmd to a virtqueue
|
||||
* @vq : the struct virtqueue we're talking about
|
||||
* @cmd : command structure
|
||||
* @out_num : number of read-only elements
|
||||
* @in_num : number of write-only elements
|
||||
* @req_size : size of the request buffer
|
||||
* @resp_size : size of the response buffer
|
||||
*
|
||||
* Called with tgt_lock held.
|
||||
* @gfp : flags to use for memory allocations
|
||||
*/
|
||||
static void virtscsi_map_cmd(struct virtio_scsi_target_state *tgt,
|
||||
struct virtio_scsi_cmd *cmd,
|
||||
unsigned *out_num, unsigned *in_num,
|
||||
size_t req_size, size_t resp_size)
|
||||
static int virtscsi_add_cmd(struct virtqueue *vq,
|
||||
struct virtio_scsi_cmd *cmd,
|
||||
size_t req_size, size_t resp_size, gfp_t gfp)
|
||||
{
|
||||
struct scsi_cmnd *sc = cmd->sc;
|
||||
struct scatterlist *sg = tgt->sg;
|
||||
unsigned int idx = 0;
|
||||
struct scatterlist *sgs[4], req, resp;
|
||||
struct sg_table *out, *in;
|
||||
unsigned out_num = 0, in_num = 0;
|
||||
|
||||
out = in = NULL;
|
||||
|
||||
if (sc && sc->sc_data_direction != DMA_NONE) {
|
||||
if (sc->sc_data_direction != DMA_FROM_DEVICE)
|
||||
out = &scsi_out(sc)->table;
|
||||
if (sc->sc_data_direction != DMA_TO_DEVICE)
|
||||
in = &scsi_in(sc)->table;
|
||||
}
|
||||
|
||||
/* Request header. */
|
||||
sg_set_buf(&sg[idx++], &cmd->req, req_size);
|
||||
sg_init_one(&req, &cmd->req, req_size);
|
||||
sgs[out_num++] = &req;
|
||||
|
||||
/* Data-out buffer. */
|
||||
if (sc && sc->sc_data_direction != DMA_FROM_DEVICE)
|
||||
virtscsi_map_sgl(sg, &idx, scsi_out(sc));
|
||||
|
||||
*out_num = idx;
|
||||
if (out)
|
||||
sgs[out_num++] = out->sgl;
|
||||
|
||||
/* Response header. */
|
||||
sg_set_buf(&sg[idx++], &cmd->resp, resp_size);
|
||||
sg_init_one(&resp, &cmd->resp, resp_size);
|
||||
sgs[out_num + in_num++] = &resp;
|
||||
|
||||
/* Data-in buffer */
|
||||
if (sc && sc->sc_data_direction != DMA_TO_DEVICE)
|
||||
virtscsi_map_sgl(sg, &idx, scsi_in(sc));
|
||||
if (in)
|
||||
sgs[out_num + in_num++] = in->sgl;
|
||||
|
||||
*in_num = idx - *out_num;
|
||||
return virtqueue_add_sgs(vq, sgs, out_num, in_num, cmd, gfp);
|
||||
}
|
||||
|
||||
static int virtscsi_kick_cmd(struct virtio_scsi_target_state *tgt,
|
||||
struct virtio_scsi_vq *vq,
|
||||
static int virtscsi_kick_cmd(struct virtio_scsi_vq *vq,
|
||||
struct virtio_scsi_cmd *cmd,
|
||||
size_t req_size, size_t resp_size, gfp_t gfp)
|
||||
{
|
||||
unsigned int out_num, in_num;
|
||||
unsigned long flags;
|
||||
int err;
|
||||
bool needs_kick = false;
|
||||
|
||||
spin_lock_irqsave(&tgt->tgt_lock, flags);
|
||||
virtscsi_map_cmd(tgt, cmd, &out_num, &in_num, req_size, resp_size);
|
||||
|
||||
spin_lock(&vq->vq_lock);
|
||||
err = virtqueue_add_buf(vq->vq, tgt->sg, out_num, in_num, cmd, gfp);
|
||||
spin_unlock(&tgt->tgt_lock);
|
||||
spin_lock_irqsave(&vq->vq_lock, flags);
|
||||
err = virtscsi_add_cmd(vq->vq, cmd, req_size, resp_size, gfp);
|
||||
if (!err)
|
||||
needs_kick = virtqueue_kick_prepare(vq->vq);
|
||||
|
||||
|
|
@ -430,10 +489,10 @@ static int virtscsi_kick_cmd(struct virtio_scsi_target_state *tgt,
|
|||
return err;
|
||||
}
|
||||
|
||||
static int virtscsi_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *sc)
|
||||
static int virtscsi_queuecommand(struct virtio_scsi *vscsi,
|
||||
struct virtio_scsi_vq *req_vq,
|
||||
struct scsi_cmnd *sc)
|
||||
{
|
||||
struct virtio_scsi *vscsi = shost_priv(sh);
|
||||
struct virtio_scsi_target_state *tgt = vscsi->tgt[sc->device->id];
|
||||
struct virtio_scsi_cmd *cmd;
|
||||
int ret;
|
||||
|
||||
|
|
@ -467,7 +526,7 @@ static int virtscsi_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *sc)
|
|||
BUG_ON(sc->cmd_len > VIRTIO_SCSI_CDB_SIZE);
|
||||
memcpy(cmd->req.cmd.cdb, sc->cmnd, sc->cmd_len);
|
||||
|
||||
if (virtscsi_kick_cmd(tgt, &vscsi->req_vq, cmd,
|
||||
if (virtscsi_kick_cmd(req_vq, cmd,
|
||||
sizeof cmd->req.cmd, sizeof cmd->resp.cmd,
|
||||
GFP_ATOMIC) == 0)
|
||||
ret = 0;
|
||||
|
|
@ -478,14 +537,62 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int virtscsi_queuecommand_single(struct Scsi_Host *sh,
|
||||
struct scsi_cmnd *sc)
|
||||
{
|
||||
struct virtio_scsi *vscsi = shost_priv(sh);
|
||||
struct virtio_scsi_target_state *tgt =
|
||||
scsi_target(sc->device)->hostdata;
|
||||
|
||||
atomic_inc(&tgt->reqs);
|
||||
return virtscsi_queuecommand(vscsi, &vscsi->req_vqs[0], sc);
|
||||
}
|
||||
|
||||
static struct virtio_scsi_vq *virtscsi_pick_vq(struct virtio_scsi *vscsi,
|
||||
struct virtio_scsi_target_state *tgt)
|
||||
{
|
||||
struct virtio_scsi_vq *vq;
|
||||
unsigned long flags;
|
||||
u32 queue_num;
|
||||
|
||||
spin_lock_irqsave(&tgt->tgt_lock, flags);
|
||||
|
||||
/*
|
||||
* The memory barrier after atomic_inc_return matches
|
||||
* the smp_read_barrier_depends() in virtscsi_req_done.
|
||||
*/
|
||||
if (atomic_inc_return(&tgt->reqs) > 1)
|
||||
vq = ACCESS_ONCE(tgt->req_vq);
|
||||
else {
|
||||
queue_num = smp_processor_id();
|
||||
while (unlikely(queue_num >= vscsi->num_queues))
|
||||
queue_num -= vscsi->num_queues;
|
||||
|
||||
tgt->req_vq = vq = &vscsi->req_vqs[queue_num];
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&tgt->tgt_lock, flags);
|
||||
return vq;
|
||||
}
|
||||
|
||||
static int virtscsi_queuecommand_multi(struct Scsi_Host *sh,
|
||||
struct scsi_cmnd *sc)
|
||||
{
|
||||
struct virtio_scsi *vscsi = shost_priv(sh);
|
||||
struct virtio_scsi_target_state *tgt =
|
||||
scsi_target(sc->device)->hostdata;
|
||||
struct virtio_scsi_vq *req_vq = virtscsi_pick_vq(vscsi, tgt);
|
||||
|
||||
return virtscsi_queuecommand(vscsi, req_vq, sc);
|
||||
}
|
||||
|
||||
static int virtscsi_tmf(struct virtio_scsi *vscsi, struct virtio_scsi_cmd *cmd)
|
||||
{
|
||||
DECLARE_COMPLETION_ONSTACK(comp);
|
||||
struct virtio_scsi_target_state *tgt = vscsi->tgt[cmd->sc->device->id];
|
||||
int ret = FAILED;
|
||||
|
||||
cmd->comp = ∁
|
||||
if (virtscsi_kick_cmd(tgt, &vscsi->ctrl_vq, cmd,
|
||||
if (virtscsi_kick_cmd(&vscsi->ctrl_vq, cmd,
|
||||
sizeof cmd->req.tmf, sizeof cmd->resp.tmf,
|
||||
GFP_NOIO) < 0)
|
||||
goto out;
|
||||
|
|
@ -547,18 +654,57 @@ static int virtscsi_abort(struct scsi_cmnd *sc)
|
|||
return virtscsi_tmf(vscsi, cmd);
|
||||
}
|
||||
|
||||
static struct scsi_host_template virtscsi_host_template = {
|
||||
static int virtscsi_target_alloc(struct scsi_target *starget)
|
||||
{
|
||||
struct virtio_scsi_target_state *tgt =
|
||||
kmalloc(sizeof(*tgt), GFP_KERNEL);
|
||||
if (!tgt)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&tgt->tgt_lock);
|
||||
atomic_set(&tgt->reqs, 0);
|
||||
tgt->req_vq = NULL;
|
||||
|
||||
starget->hostdata = tgt;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void virtscsi_target_destroy(struct scsi_target *starget)
|
||||
{
|
||||
struct virtio_scsi_target_state *tgt = starget->hostdata;
|
||||
kfree(tgt);
|
||||
}
|
||||
|
||||
static struct scsi_host_template virtscsi_host_template_single = {
|
||||
.module = THIS_MODULE,
|
||||
.name = "Virtio SCSI HBA",
|
||||
.proc_name = "virtio_scsi",
|
||||
.queuecommand = virtscsi_queuecommand,
|
||||
.this_id = -1,
|
||||
.queuecommand = virtscsi_queuecommand_single,
|
||||
.eh_abort_handler = virtscsi_abort,
|
||||
.eh_device_reset_handler = virtscsi_device_reset,
|
||||
|
||||
.can_queue = 1024,
|
||||
.dma_boundary = UINT_MAX,
|
||||
.use_clustering = ENABLE_CLUSTERING,
|
||||
.target_alloc = virtscsi_target_alloc,
|
||||
.target_destroy = virtscsi_target_destroy,
|
||||
};
|
||||
|
||||
static struct scsi_host_template virtscsi_host_template_multi = {
|
||||
.module = THIS_MODULE,
|
||||
.name = "Virtio SCSI HBA",
|
||||
.proc_name = "virtio_scsi",
|
||||
.this_id = -1,
|
||||
.queuecommand = virtscsi_queuecommand_multi,
|
||||
.eh_abort_handler = virtscsi_abort,
|
||||
.eh_device_reset_handler = virtscsi_device_reset,
|
||||
|
||||
.can_queue = 1024,
|
||||
.dma_boundary = UINT_MAX,
|
||||
.use_clustering = ENABLE_CLUSTERING,
|
||||
.target_alloc = virtscsi_target_alloc,
|
||||
.target_destroy = virtscsi_target_destroy,
|
||||
};
|
||||
|
||||
#define virtscsi_config_get(vdev, fld) \
|
||||
|
|
@ -578,6 +724,64 @@ static struct scsi_host_template virtscsi_host_template = {
|
|||
&__val, sizeof(__val)); \
|
||||
})
|
||||
|
||||
static void __virtscsi_set_affinity(struct virtio_scsi *vscsi, bool affinity)
|
||||
{
|
||||
int i;
|
||||
int cpu;
|
||||
|
||||
/* In multiqueue mode, when the number of cpu is equal
|
||||
* to the number of request queues, we let the qeueues
|
||||
* to be private to one cpu by setting the affinity hint
|
||||
* to eliminate the contention.
|
||||
*/
|
||||
if ((vscsi->num_queues == 1 ||
|
||||
vscsi->num_queues != num_online_cpus()) && affinity) {
|
||||
if (vscsi->affinity_hint_set)
|
||||
affinity = false;
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
if (affinity) {
|
||||
i = 0;
|
||||
for_each_online_cpu(cpu) {
|
||||
virtqueue_set_affinity(vscsi->req_vqs[i].vq, cpu);
|
||||
i++;
|
||||
}
|
||||
|
||||
vscsi->affinity_hint_set = true;
|
||||
} else {
|
||||
for (i = 0; i < vscsi->num_queues - VIRTIO_SCSI_VQ_BASE; i++)
|
||||
virtqueue_set_affinity(vscsi->req_vqs[i].vq, -1);
|
||||
|
||||
vscsi->affinity_hint_set = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void virtscsi_set_affinity(struct virtio_scsi *vscsi, bool affinity)
|
||||
{
|
||||
get_online_cpus();
|
||||
__virtscsi_set_affinity(vscsi, affinity);
|
||||
put_online_cpus();
|
||||
}
|
||||
|
||||
static int virtscsi_cpu_callback(struct notifier_block *nfb,
|
||||
unsigned long action, void *hcpu)
|
||||
{
|
||||
struct virtio_scsi *vscsi = container_of(nfb, struct virtio_scsi, nb);
|
||||
switch(action) {
|
||||
case CPU_ONLINE:
|
||||
case CPU_ONLINE_FROZEN:
|
||||
case CPU_DEAD:
|
||||
case CPU_DEAD_FROZEN:
|
||||
__virtscsi_set_affinity(vscsi, true);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static void virtscsi_init_vq(struct virtio_scsi_vq *virtscsi_vq,
|
||||
struct virtqueue *vq)
|
||||
{
|
||||
|
|
@ -585,24 +789,6 @@ static void virtscsi_init_vq(struct virtio_scsi_vq *virtscsi_vq,
|
|||
virtscsi_vq->vq = vq;
|
||||
}
|
||||
|
||||
static struct virtio_scsi_target_state *virtscsi_alloc_tgt(
|
||||
struct virtio_device *vdev, int sg_elems)
|
||||
{
|
||||
struct virtio_scsi_target_state *tgt;
|
||||
gfp_t gfp_mask = GFP_KERNEL;
|
||||
|
||||
/* We need extra sg elements at head and tail. */
|
||||
tgt = kmalloc(sizeof(*tgt) + sizeof(tgt->sg[0]) * (sg_elems + 2),
|
||||
gfp_mask);
|
||||
|
||||
if (!tgt)
|
||||
return NULL;
|
||||
|
||||
spin_lock_init(&tgt->tgt_lock);
|
||||
sg_init_table(tgt->sg, sg_elems + 2);
|
||||
return tgt;
|
||||
}
|
||||
|
||||
static void virtscsi_scan(struct virtio_device *vdev)
|
||||
{
|
||||
struct Scsi_Host *shost = (struct Scsi_Host *)vdev->priv;
|
||||
|
|
@ -614,46 +800,56 @@ static void virtscsi_remove_vqs(struct virtio_device *vdev)
|
|||
{
|
||||
struct Scsi_Host *sh = virtio_scsi_host(vdev);
|
||||
struct virtio_scsi *vscsi = shost_priv(sh);
|
||||
u32 i, num_targets;
|
||||
|
||||
virtscsi_set_affinity(vscsi, false);
|
||||
|
||||
/* Stop all the virtqueues. */
|
||||
vdev->config->reset(vdev);
|
||||
|
||||
num_targets = sh->max_id;
|
||||
for (i = 0; i < num_targets; i++) {
|
||||
kfree(vscsi->tgt[i]);
|
||||
vscsi->tgt[i] = NULL;
|
||||
}
|
||||
|
||||
vdev->config->del_vqs(vdev);
|
||||
}
|
||||
|
||||
static int virtscsi_init(struct virtio_device *vdev,
|
||||
struct virtio_scsi *vscsi, int num_targets)
|
||||
struct virtio_scsi *vscsi)
|
||||
{
|
||||
int err;
|
||||
struct virtqueue *vqs[3];
|
||||
u32 i, sg_elems;
|
||||
u32 i;
|
||||
u32 num_vqs;
|
||||
vq_callback_t **callbacks;
|
||||
const char **names;
|
||||
struct virtqueue **vqs;
|
||||
|
||||
vq_callback_t *callbacks[] = {
|
||||
virtscsi_ctrl_done,
|
||||
virtscsi_event_done,
|
||||
virtscsi_req_done
|
||||
};
|
||||
const char *names[] = {
|
||||
"control",
|
||||
"event",
|
||||
"request"
|
||||
};
|
||||
num_vqs = vscsi->num_queues + VIRTIO_SCSI_VQ_BASE;
|
||||
vqs = kmalloc(num_vqs * sizeof(struct virtqueue *), GFP_KERNEL);
|
||||
callbacks = kmalloc(num_vqs * sizeof(vq_callback_t *), GFP_KERNEL);
|
||||
names = kmalloc(num_vqs * sizeof(char *), GFP_KERNEL);
|
||||
|
||||
if (!callbacks || !vqs || !names) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
callbacks[0] = virtscsi_ctrl_done;
|
||||
callbacks[1] = virtscsi_event_done;
|
||||
names[0] = "control";
|
||||
names[1] = "event";
|
||||
for (i = VIRTIO_SCSI_VQ_BASE; i < num_vqs; i++) {
|
||||
callbacks[i] = virtscsi_req_done;
|
||||
names[i] = "request";
|
||||
}
|
||||
|
||||
/* Discover virtqueues and write information to configuration. */
|
||||
err = vdev->config->find_vqs(vdev, 3, vqs, callbacks, names);
|
||||
err = vdev->config->find_vqs(vdev, num_vqs, vqs, callbacks, names);
|
||||
if (err)
|
||||
return err;
|
||||
goto out;
|
||||
|
||||
virtscsi_init_vq(&vscsi->ctrl_vq, vqs[0]);
|
||||
virtscsi_init_vq(&vscsi->event_vq, vqs[1]);
|
||||
virtscsi_init_vq(&vscsi->req_vq, vqs[2]);
|
||||
for (i = VIRTIO_SCSI_VQ_BASE; i < num_vqs; i++)
|
||||
virtscsi_init_vq(&vscsi->req_vqs[i - VIRTIO_SCSI_VQ_BASE],
|
||||
vqs[i]);
|
||||
|
||||
virtscsi_set_affinity(vscsi, true);
|
||||
|
||||
virtscsi_config_set(vdev, cdb_size, VIRTIO_SCSI_CDB_SIZE);
|
||||
virtscsi_config_set(vdev, sense_size, VIRTIO_SCSI_SENSE_SIZE);
|
||||
|
|
@ -661,19 +857,12 @@ static int virtscsi_init(struct virtio_device *vdev,
|
|||
if (virtio_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG))
|
||||
virtscsi_kick_event_all(vscsi);
|
||||
|
||||
/* We need to know how many segments before we allocate. */
|
||||
sg_elems = virtscsi_config_get(vdev, seg_max) ?: 1;
|
||||
|
||||
for (i = 0; i < num_targets; i++) {
|
||||
vscsi->tgt[i] = virtscsi_alloc_tgt(vdev, sg_elems);
|
||||
if (!vscsi->tgt[i]) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
err = 0;
|
||||
|
||||
out:
|
||||
kfree(names);
|
||||
kfree(callbacks);
|
||||
kfree(vqs);
|
||||
if (err)
|
||||
virtscsi_remove_vqs(vdev);
|
||||
return err;
|
||||
|
|
@ -686,13 +875,21 @@ static int virtscsi_probe(struct virtio_device *vdev)
|
|||
int err;
|
||||
u32 sg_elems, num_targets;
|
||||
u32 cmd_per_lun;
|
||||
u32 num_queues;
|
||||
struct scsi_host_template *hostt;
|
||||
|
||||
/* We need to know how many queues before we allocate. */
|
||||
num_queues = virtscsi_config_get(vdev, num_queues) ? : 1;
|
||||
|
||||
/* Allocate memory and link the structs together. */
|
||||
num_targets = virtscsi_config_get(vdev, max_target) + 1;
|
||||
shost = scsi_host_alloc(&virtscsi_host_template,
|
||||
sizeof(*vscsi)
|
||||
+ num_targets * sizeof(struct virtio_scsi_target_state));
|
||||
|
||||
if (num_queues == 1)
|
||||
hostt = &virtscsi_host_template_single;
|
||||
else
|
||||
hostt = &virtscsi_host_template_multi;
|
||||
|
||||
shost = scsi_host_alloc(hostt,
|
||||
sizeof(*vscsi) + sizeof(vscsi->req_vqs[0]) * num_queues);
|
||||
if (!shost)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
@ -700,12 +897,20 @@ static int virtscsi_probe(struct virtio_device *vdev)
|
|||
shost->sg_tablesize = sg_elems;
|
||||
vscsi = shost_priv(shost);
|
||||
vscsi->vdev = vdev;
|
||||
vscsi->num_queues = num_queues;
|
||||
vdev->priv = shost;
|
||||
|
||||
err = virtscsi_init(vdev, vscsi, num_targets);
|
||||
err = virtscsi_init(vdev, vscsi);
|
||||
if (err)
|
||||
goto virtscsi_init_failed;
|
||||
|
||||
vscsi->nb.notifier_call = &virtscsi_cpu_callback;
|
||||
err = register_hotcpu_notifier(&vscsi->nb);
|
||||
if (err) {
|
||||
pr_err("registering cpu notifier failed\n");
|
||||
goto scsi_add_host_failed;
|
||||
}
|
||||
|
||||
cmd_per_lun = virtscsi_config_get(vdev, cmd_per_lun) ?: 1;
|
||||
shost->cmd_per_lun = min_t(u32, cmd_per_lun, shost->can_queue);
|
||||
shost->max_sectors = virtscsi_config_get(vdev, max_sectors) ?: 0xFFFF;
|
||||
|
|
@ -743,6 +948,8 @@ static void virtscsi_remove(struct virtio_device *vdev)
|
|||
|
||||
scsi_remove_host(shost);
|
||||
|
||||
unregister_hotcpu_notifier(&vscsi->nb);
|
||||
|
||||
virtscsi_remove_vqs(vdev);
|
||||
scsi_host_put(shost);
|
||||
}
|
||||
|
|
@ -759,7 +966,7 @@ static int virtscsi_restore(struct virtio_device *vdev)
|
|||
struct Scsi_Host *sh = virtio_scsi_host(vdev);
|
||||
struct virtio_scsi *vscsi = shost_priv(sh);
|
||||
|
||||
return virtscsi_init(vdev, vscsi, sh->max_id);
|
||||
return virtscsi_init(vdev, vscsi);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -794,8 +1001,7 @@ static int __init init(void)
|
|||
|
||||
virtscsi_cmd_cache = KMEM_CACHE(virtio_scsi_cmd, 0);
|
||||
if (!virtscsi_cmd_cache) {
|
||||
printk(KERN_ERR "kmem_cache_create() for "
|
||||
"virtscsi_cmd_cache failed\n");
|
||||
pr_err("kmem_cache_create() for virtscsi_cmd_cache failed\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
|
@ -804,8 +1010,7 @@ static int __init init(void)
|
|||
mempool_create_slab_pool(VIRTIO_SCSI_MEMPOOL_SZ,
|
||||
virtscsi_cmd_cache);
|
||||
if (!virtscsi_cmd_pool) {
|
||||
printk(KERN_ERR "mempool_create() for"
|
||||
"virtscsi_cmd_pool failed\n");
|
||||
pr_err("mempool_create() for virtscsi_cmd_pool failed\n");
|
||||
goto error;
|
||||
}
|
||||
ret = register_virtio_driver(&virtio_scsi_driver);
|
||||
|
|
|
|||
Loading…
Reference in a new issue