You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
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:
@@ -6,6 +6,3 @@ kvm/
|
||||
- Kernel Virtual Machine. See also http://linux-kvm.org
|
||||
uml/
|
||||
- User Mode Linux, builds/runs Linux kernel as a userspace program.
|
||||
virtio.txt
|
||||
- Text version of draft virtio spec.
|
||||
See http://ozlabs.org/~rusty/virtio-spec
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8743,6 +8743,7 @@ F: drivers/virtio/
|
||||
F: drivers/net/virtio_net.c
|
||||
F: drivers/block/virtio_blk.c
|
||||
F: include/linux/virtio_*.h
|
||||
F: include/uapi/linux/virtio_*.h
|
||||
|
||||
VIRTIO HOST (VHOST)
|
||||
M: "Michael S. Tsirkin" <mst@redhat.com>
|
||||
|
||||
@@ -11,18 +11,11 @@
|
||||
|
||||
#define GUEST_PL 1
|
||||
|
||||
/* Every guest maps the core switcher code. */
|
||||
#define SHARED_SWITCHER_PAGES \
|
||||
DIV_ROUND_UP(end_switcher_text - start_switcher_text, PAGE_SIZE)
|
||||
/* Pages for switcher itself, then two pages per cpu */
|
||||
#define TOTAL_SWITCHER_PAGES (SHARED_SWITCHER_PAGES + 2 * nr_cpu_ids)
|
||||
/* Page for Switcher text itself, then two pages per cpu */
|
||||
#define TOTAL_SWITCHER_PAGES (1 + 2 * nr_cpu_ids)
|
||||
|
||||
/* We map at -4M (-2M for PAE) for ease of mapping (one PTE page). */
|
||||
#ifdef CONFIG_X86_PAE
|
||||
#define SWITCHER_ADDR 0xFFE00000
|
||||
#else
|
||||
#define SWITCHER_ADDR 0xFFC00000
|
||||
#endif
|
||||
/* Where we map the Switcher, in both Host and Guest. */
|
||||
extern unsigned long switcher_addr;
|
||||
|
||||
/* Found in switcher.S */
|
||||
extern unsigned long default_idt_entries[];
|
||||
|
||||
@@ -110,7 +110,7 @@ new_segment:
|
||||
if (!sg)
|
||||
sg = sglist;
|
||||
else {
|
||||
sg->page_link &= ~0x02;
|
||||
sg_unmark_end(sg);
|
||||
sg = sg_next(sg);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -143,7 +143,7 @@ new_segment:
|
||||
* termination bit to avoid doing a full
|
||||
* sg_init_table() in drivers for each command.
|
||||
*/
|
||||
(*sg)->page_link &= ~0x02;
|
||||
sg_unmark_end(*sg);
|
||||
*sg = sg_next(*sg);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -124,7 +124,7 @@ obj-$(CONFIG_PPC_PS3) += ps3/
|
||||
obj-$(CONFIG_OF) += of/
|
||||
obj-$(CONFIG_SSB) += ssb/
|
||||
obj-$(CONFIG_BCMA) += bcma/
|
||||
obj-$(CONFIG_VHOST_NET) += vhost/
|
||||
obj-$(CONFIG_VHOST_RING) += vhost/
|
||||
obj-$(CONFIG_VLYNQ) += vlynq/
|
||||
obj-$(CONFIG_STAGING) += staging/
|
||||
obj-y += platform/
|
||||
|
||||
+73
-89
@@ -100,96 +100,103 @@ static inline struct virtblk_req *virtblk_alloc_req(struct virtio_blk *vblk,
|
||||
return vbr;
|
||||
}
|
||||
|
||||
static void virtblk_add_buf_wait(struct virtio_blk *vblk,
|
||||
struct virtblk_req *vbr,
|
||||
unsigned long out,
|
||||
unsigned long in)
|
||||
static int __virtblk_add_req(struct virtqueue *vq,
|
||||
struct virtblk_req *vbr,
|
||||
struct scatterlist *data_sg,
|
||||
bool have_data)
|
||||
{
|
||||
DEFINE_WAIT(wait);
|
||||
struct scatterlist hdr, status, cmd, sense, inhdr, *sgs[6];
|
||||
unsigned int num_out = 0, num_in = 0;
|
||||
int type = vbr->out_hdr.type & ~VIRTIO_BLK_T_OUT;
|
||||
|
||||
for (;;) {
|
||||
sg_init_one(&hdr, &vbr->out_hdr, sizeof(vbr->out_hdr));
|
||||
sgs[num_out++] = &hdr;
|
||||
|
||||
/*
|
||||
* If this is a packet command we need a couple of additional headers.
|
||||
* Behind the normal outhdr we put a segment with the scsi command
|
||||
* block, and before the normal inhdr we put the sense data and the
|
||||
* inhdr with additional status information.
|
||||
*/
|
||||
if (type == VIRTIO_BLK_T_SCSI_CMD) {
|
||||
sg_init_one(&cmd, vbr->req->cmd, vbr->req->cmd_len);
|
||||
sgs[num_out++] = &cmd;
|
||||
}
|
||||
|
||||
if (have_data) {
|
||||
if (vbr->out_hdr.type & VIRTIO_BLK_T_OUT)
|
||||
sgs[num_out++] = data_sg;
|
||||
else
|
||||
sgs[num_out + num_in++] = data_sg;
|
||||
}
|
||||
|
||||
if (type == VIRTIO_BLK_T_SCSI_CMD) {
|
||||
sg_init_one(&sense, vbr->req->sense, SCSI_SENSE_BUFFERSIZE);
|
||||
sgs[num_out + num_in++] = &sense;
|
||||
sg_init_one(&inhdr, &vbr->in_hdr, sizeof(vbr->in_hdr));
|
||||
sgs[num_out + num_in++] = &inhdr;
|
||||
}
|
||||
|
||||
sg_init_one(&status, &vbr->status, sizeof(vbr->status));
|
||||
sgs[num_out + num_in++] = &status;
|
||||
|
||||
return virtqueue_add_sgs(vq, sgs, num_out, num_in, vbr, GFP_ATOMIC);
|
||||
}
|
||||
|
||||
static void virtblk_add_req(struct virtblk_req *vbr, bool have_data)
|
||||
{
|
||||
struct virtio_blk *vblk = vbr->vblk;
|
||||
DEFINE_WAIT(wait);
|
||||
int ret;
|
||||
|
||||
spin_lock_irq(vblk->disk->queue->queue_lock);
|
||||
while (unlikely((ret = __virtblk_add_req(vblk->vq, vbr, vbr->sg,
|
||||
have_data)) < 0)) {
|
||||
prepare_to_wait_exclusive(&vblk->queue_wait, &wait,
|
||||
TASK_UNINTERRUPTIBLE);
|
||||
|
||||
spin_lock_irq(vblk->disk->queue->queue_lock);
|
||||
if (virtqueue_add_buf(vblk->vq, vbr->sg, out, in, vbr,
|
||||
GFP_ATOMIC) < 0) {
|
||||
spin_unlock_irq(vblk->disk->queue->queue_lock);
|
||||
io_schedule();
|
||||
} else {
|
||||
virtqueue_kick(vblk->vq);
|
||||
spin_unlock_irq(vblk->disk->queue->queue_lock);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
finish_wait(&vblk->queue_wait, &wait);
|
||||
}
|
||||
|
||||
static inline void virtblk_add_req(struct virtblk_req *vbr,
|
||||
unsigned int out, unsigned int in)
|
||||
{
|
||||
struct virtio_blk *vblk = vbr->vblk;
|
||||
|
||||
spin_lock_irq(vblk->disk->queue->queue_lock);
|
||||
if (unlikely(virtqueue_add_buf(vblk->vq, vbr->sg, out, in, vbr,
|
||||
GFP_ATOMIC) < 0)) {
|
||||
spin_unlock_irq(vblk->disk->queue->queue_lock);
|
||||
virtblk_add_buf_wait(vblk, vbr, out, in);
|
||||
return;
|
||||
io_schedule();
|
||||
spin_lock_irq(vblk->disk->queue->queue_lock);
|
||||
|
||||
finish_wait(&vblk->queue_wait, &wait);
|
||||
}
|
||||
|
||||
virtqueue_kick(vblk->vq);
|
||||
spin_unlock_irq(vblk->disk->queue->queue_lock);
|
||||
}
|
||||
|
||||
static int virtblk_bio_send_flush(struct virtblk_req *vbr)
|
||||
static void virtblk_bio_send_flush(struct virtblk_req *vbr)
|
||||
{
|
||||
unsigned int out = 0, in = 0;
|
||||
|
||||
vbr->flags |= VBLK_IS_FLUSH;
|
||||
vbr->out_hdr.type = VIRTIO_BLK_T_FLUSH;
|
||||
vbr->out_hdr.sector = 0;
|
||||
vbr->out_hdr.ioprio = 0;
|
||||
sg_set_buf(&vbr->sg[out++], &vbr->out_hdr, sizeof(vbr->out_hdr));
|
||||
sg_set_buf(&vbr->sg[out + in++], &vbr->status, sizeof(vbr->status));
|
||||
|
||||
virtblk_add_req(vbr, out, in);
|
||||
|
||||
return 0;
|
||||
virtblk_add_req(vbr, false);
|
||||
}
|
||||
|
||||
static int virtblk_bio_send_data(struct virtblk_req *vbr)
|
||||
static void virtblk_bio_send_data(struct virtblk_req *vbr)
|
||||
{
|
||||
struct virtio_blk *vblk = vbr->vblk;
|
||||
unsigned int num, out = 0, in = 0;
|
||||
struct bio *bio = vbr->bio;
|
||||
bool have_data;
|
||||
|
||||
vbr->flags &= ~VBLK_IS_FLUSH;
|
||||
vbr->out_hdr.type = 0;
|
||||
vbr->out_hdr.sector = bio->bi_sector;
|
||||
vbr->out_hdr.ioprio = bio_prio(bio);
|
||||
|
||||
sg_set_buf(&vbr->sg[out++], &vbr->out_hdr, sizeof(vbr->out_hdr));
|
||||
|
||||
num = blk_bio_map_sg(vblk->disk->queue, bio, vbr->sg + out);
|
||||
|
||||
sg_set_buf(&vbr->sg[num + out + in++], &vbr->status,
|
||||
sizeof(vbr->status));
|
||||
|
||||
if (num) {
|
||||
if (bio->bi_rw & REQ_WRITE) {
|
||||
if (blk_bio_map_sg(vblk->disk->queue, bio, vbr->sg)) {
|
||||
have_data = true;
|
||||
if (bio->bi_rw & REQ_WRITE)
|
||||
vbr->out_hdr.type |= VIRTIO_BLK_T_OUT;
|
||||
out += num;
|
||||
} else {
|
||||
else
|
||||
vbr->out_hdr.type |= VIRTIO_BLK_T_IN;
|
||||
in += num;
|
||||
}
|
||||
}
|
||||
} else
|
||||
have_data = false;
|
||||
|
||||
virtblk_add_req(vbr, out, in);
|
||||
|
||||
return 0;
|
||||
virtblk_add_req(vbr, have_data);
|
||||
}
|
||||
|
||||
static void virtblk_bio_send_data_work(struct work_struct *work)
|
||||
@@ -298,7 +305,7 @@ static void virtblk_done(struct virtqueue *vq)
|
||||
static bool do_req(struct request_queue *q, struct virtio_blk *vblk,
|
||||
struct request *req)
|
||||
{
|
||||
unsigned long num, out = 0, in = 0;
|
||||
unsigned int num;
|
||||
struct virtblk_req *vbr;
|
||||
|
||||
vbr = virtblk_alloc_req(vblk, GFP_ATOMIC);
|
||||
@@ -335,40 +342,15 @@ static bool do_req(struct request_queue *q, struct virtio_blk *vblk,
|
||||
}
|
||||
}
|
||||
|
||||
sg_set_buf(&vblk->sg[out++], &vbr->out_hdr, sizeof(vbr->out_hdr));
|
||||
|
||||
/*
|
||||
* If this is a packet command we need a couple of additional headers.
|
||||
* Behind the normal outhdr we put a segment with the scsi command
|
||||
* block, and before the normal inhdr we put the sense data and the
|
||||
* inhdr with additional status information before the normal inhdr.
|
||||
*/
|
||||
if (vbr->req->cmd_type == REQ_TYPE_BLOCK_PC)
|
||||
sg_set_buf(&vblk->sg[out++], vbr->req->cmd, vbr->req->cmd_len);
|
||||
|
||||
num = blk_rq_map_sg(q, vbr->req, vblk->sg + out);
|
||||
|
||||
if (vbr->req->cmd_type == REQ_TYPE_BLOCK_PC) {
|
||||
sg_set_buf(&vblk->sg[num + out + in++], vbr->req->sense, SCSI_SENSE_BUFFERSIZE);
|
||||
sg_set_buf(&vblk->sg[num + out + in++], &vbr->in_hdr,
|
||||
sizeof(vbr->in_hdr));
|
||||
}
|
||||
|
||||
sg_set_buf(&vblk->sg[num + out + in++], &vbr->status,
|
||||
sizeof(vbr->status));
|
||||
|
||||
num = blk_rq_map_sg(q, vbr->req, vblk->sg);
|
||||
if (num) {
|
||||
if (rq_data_dir(vbr->req) == WRITE) {
|
||||
if (rq_data_dir(vbr->req) == WRITE)
|
||||
vbr->out_hdr.type |= VIRTIO_BLK_T_OUT;
|
||||
out += num;
|
||||
} else {
|
||||
else
|
||||
vbr->out_hdr.type |= VIRTIO_BLK_T_IN;
|
||||
in += num;
|
||||
}
|
||||
}
|
||||
|
||||
if (virtqueue_add_buf(vblk->vq, vblk->sg, out, in, vbr,
|
||||
GFP_ATOMIC) < 0) {
|
||||
if (__virtblk_add_req(vblk->vq, vbr, vblk->sg, num) < 0) {
|
||||
mempool_free(vbr, vblk->pool);
|
||||
return false;
|
||||
}
|
||||
@@ -539,6 +521,7 @@ static void virtblk_config_changed_work(struct work_struct *work)
|
||||
struct virtio_device *vdev = vblk->vdev;
|
||||
struct request_queue *q = vblk->disk->queue;
|
||||
char cap_str_2[10], cap_str_10[10];
|
||||
char *envp[] = { "RESIZE=1", NULL };
|
||||
u64 capacity, size;
|
||||
|
||||
mutex_lock(&vblk->config_lock);
|
||||
@@ -568,6 +551,7 @@ static void virtblk_config_changed_work(struct work_struct *work)
|
||||
|
||||
set_capacity(vblk->disk, capacity);
|
||||
revalidate_disk(vblk->disk);
|
||||
kobject_uevent_env(&disk_to_dev(vblk->disk)->kobj, KOBJ_CHANGE, envp);
|
||||
done:
|
||||
mutex_unlock(&vblk->config_lock);
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ static void register_buffer(u8 *buf, size_t size)
|
||||
sg_init_one(&sg, buf, size);
|
||||
|
||||
/* There should always be room for one buffer. */
|
||||
if (virtqueue_add_buf(vq, &sg, 0, 1, buf, GFP_KERNEL) < 0)
|
||||
if (virtqueue_add_inbuf(vq, &sg, 1, buf, GFP_KERNEL) < 0)
|
||||
BUG();
|
||||
|
||||
virtqueue_kick(vq);
|
||||
|
||||
@@ -78,8 +78,8 @@ struct ports_driver_data {
|
||||
};
|
||||
static struct ports_driver_data pdrvdata;
|
||||
|
||||
DEFINE_SPINLOCK(pdrvdata_lock);
|
||||
DECLARE_COMPLETION(early_console_added);
|
||||
static DEFINE_SPINLOCK(pdrvdata_lock);
|
||||
static DECLARE_COMPLETION(early_console_added);
|
||||
|
||||
/* This struct holds information that's relevant only for console ports */
|
||||
struct console {
|
||||
@@ -503,7 +503,7 @@ static int add_inbuf(struct virtqueue *vq, struct port_buffer *buf)
|
||||
|
||||
sg_init_one(sg, buf->buf, buf->size);
|
||||
|
||||
ret = virtqueue_add_buf(vq, sg, 0, 1, buf, GFP_ATOMIC);
|
||||
ret = virtqueue_add_inbuf(vq, sg, 1, buf, GFP_ATOMIC);
|
||||
virtqueue_kick(vq);
|
||||
if (!ret)
|
||||
ret = vq->num_free;
|
||||
@@ -572,7 +572,7 @@ static ssize_t __send_control_msg(struct ports_device *portdev, u32 port_id,
|
||||
sg_init_one(sg, &cpkt, sizeof(cpkt));
|
||||
|
||||
spin_lock(&portdev->c_ovq_lock);
|
||||
if (virtqueue_add_buf(vq, sg, 1, 0, &cpkt, GFP_ATOMIC) == 0) {
|
||||
if (virtqueue_add_outbuf(vq, sg, 1, &cpkt, GFP_ATOMIC) == 0) {
|
||||
virtqueue_kick(vq);
|
||||
while (!virtqueue_get_buf(vq, &len))
|
||||
cpu_relax();
|
||||
@@ -622,7 +622,7 @@ static ssize_t __send_to_port(struct port *port, struct scatterlist *sg,
|
||||
|
||||
reclaim_consumed_buffers(port);
|
||||
|
||||
err = virtqueue_add_buf(out_vq, sg, nents, 0, data, GFP_ATOMIC);
|
||||
err = virtqueue_add_outbuf(out_vq, sg, nents, data, GFP_ATOMIC);
|
||||
|
||||
/* Tell Host to go! */
|
||||
virtqueue_kick(out_vq);
|
||||
@@ -1040,7 +1040,7 @@ static int port_fops_open(struct inode *inode, struct file *filp)
|
||||
spin_lock_irq(&port->inbuf_lock);
|
||||
if (port->guest_connected) {
|
||||
spin_unlock_irq(&port->inbuf_lock);
|
||||
ret = -EMFILE;
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -1202,7 +1202,7 @@ int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int))
|
||||
return hvc_instantiate(0, 0, &hv_ops);
|
||||
}
|
||||
|
||||
int init_port_console(struct port *port)
|
||||
static int init_port_console(struct port *port)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
||||
@@ -5,10 +5,9 @@ config LGUEST
|
||||
---help---
|
||||
This is a very simple module which allows you to run
|
||||
multiple instances of the same Linux kernel, using the
|
||||
"lguest" command found in the Documentation/virtual/lguest
|
||||
directory.
|
||||
"lguest" command found in the tools/lguest directory.
|
||||
|
||||
Note that "lguest" is pronounced to rhyme with "fell quest",
|
||||
not "rustyvisor". See Documentation/virtual/lguest/lguest.txt.
|
||||
not "rustyvisor". See tools/lguest/lguest.txt.
|
||||
|
||||
If unsure, say N. If curious, say M. If masochistic, say Y.
|
||||
|
||||
+32
-35
@@ -20,9 +20,9 @@
|
||||
#include <asm/asm-offsets.h>
|
||||
#include "lg.h"
|
||||
|
||||
|
||||
unsigned long switcher_addr;
|
||||
struct page **lg_switcher_pages;
|
||||
static struct vm_struct *switcher_vma;
|
||||
static struct page **switcher_page;
|
||||
|
||||
/* This One Big lock protects all inter-guest data structures. */
|
||||
DEFINE_MUTEX(lguest_lock);
|
||||
@@ -52,13 +52,21 @@ static __init int map_switcher(void)
|
||||
* easy.
|
||||
*/
|
||||
|
||||
/* We assume Switcher text fits into a single page. */
|
||||
if (end_switcher_text - start_switcher_text > PAGE_SIZE) {
|
||||
printk(KERN_ERR "lguest: switcher text too large (%zu)\n",
|
||||
end_switcher_text - start_switcher_text);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* We allocate an array of struct page pointers. map_vm_area() wants
|
||||
* this, rather than just an array of pages.
|
||||
*/
|
||||
switcher_page = kmalloc(sizeof(switcher_page[0])*TOTAL_SWITCHER_PAGES,
|
||||
GFP_KERNEL);
|
||||
if (!switcher_page) {
|
||||
lg_switcher_pages = kmalloc(sizeof(lg_switcher_pages[0])
|
||||
* TOTAL_SWITCHER_PAGES,
|
||||
GFP_KERNEL);
|
||||
if (!lg_switcher_pages) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
@@ -68,32 +76,29 @@ static __init int map_switcher(void)
|
||||
* so we make sure they're zeroed.
|
||||
*/
|
||||
for (i = 0; i < TOTAL_SWITCHER_PAGES; i++) {
|
||||
switcher_page[i] = alloc_page(GFP_KERNEL|__GFP_ZERO);
|
||||
if (!switcher_page[i]) {
|
||||
lg_switcher_pages[i] = alloc_page(GFP_KERNEL|__GFP_ZERO);
|
||||
if (!lg_switcher_pages[i]) {
|
||||
err = -ENOMEM;
|
||||
goto free_some_pages;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* First we check that the Switcher won't overlap the fixmap area at
|
||||
* the top of memory. It's currently nowhere near, but it could have
|
||||
* very strange effects if it ever happened.
|
||||
* We place the Switcher underneath the fixmap area, which is the
|
||||
* highest virtual address we can get. This is important, since we
|
||||
* tell the Guest it can't access this memory, so we want its ceiling
|
||||
* as high as possible.
|
||||
*/
|
||||
if (SWITCHER_ADDR + (TOTAL_SWITCHER_PAGES+1)*PAGE_SIZE > FIXADDR_START){
|
||||
err = -ENOMEM;
|
||||
printk("lguest: mapping switcher would thwack fixmap\n");
|
||||
goto free_pages;
|
||||
}
|
||||
switcher_addr = FIXADDR_START - (TOTAL_SWITCHER_PAGES+1)*PAGE_SIZE;
|
||||
|
||||
/*
|
||||
* Now we reserve the "virtual memory area" we want: 0xFFC00000
|
||||
* (SWITCHER_ADDR). We might not get it in theory, but in practice
|
||||
* it's worked so far. The end address needs +1 because __get_vm_area
|
||||
* allocates an extra guard page, so we need space for that.
|
||||
* Now we reserve the "virtual memory area" we want. We might
|
||||
* not get it in theory, but in practice it's worked so far.
|
||||
* The end address needs +1 because __get_vm_area allocates an
|
||||
* extra guard page, so we need space for that.
|
||||
*/
|
||||
switcher_vma = __get_vm_area(TOTAL_SWITCHER_PAGES * PAGE_SIZE,
|
||||
VM_ALLOC, SWITCHER_ADDR, SWITCHER_ADDR
|
||||
VM_ALLOC, switcher_addr, switcher_addr
|
||||
+ (TOTAL_SWITCHER_PAGES+1) * PAGE_SIZE);
|
||||
if (!switcher_vma) {
|
||||
err = -ENOMEM;
|
||||
@@ -103,12 +108,12 @@ static __init int map_switcher(void)
|
||||
|
||||
/*
|
||||
* This code actually sets up the pages we've allocated to appear at
|
||||
* SWITCHER_ADDR. map_vm_area() takes the vma we allocated above, the
|
||||
* switcher_addr. map_vm_area() takes the vma we allocated above, the
|
||||
* kind of pages we're mapping (kernel pages), and a pointer to our
|
||||
* array of struct pages. It increments that pointer, but we don't
|
||||
* care.
|
||||
*/
|
||||
pagep = switcher_page;
|
||||
pagep = lg_switcher_pages;
|
||||
err = map_vm_area(switcher_vma, PAGE_KERNEL_EXEC, &pagep);
|
||||
if (err) {
|
||||
printk("lguest: map_vm_area failed: %i\n", err);
|
||||
@@ -133,8 +138,8 @@ free_pages:
|
||||
i = TOTAL_SWITCHER_PAGES;
|
||||
free_some_pages:
|
||||
for (--i; i >= 0; i--)
|
||||
__free_pages(switcher_page[i], 0);
|
||||
kfree(switcher_page);
|
||||
__free_pages(lg_switcher_pages[i], 0);
|
||||
kfree(lg_switcher_pages);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
@@ -149,8 +154,8 @@ static void unmap_switcher(void)
|
||||
vunmap(switcher_vma->addr);
|
||||
/* Now we just need to free the pages we copied the switcher into */
|
||||
for (i = 0; i < TOTAL_SWITCHER_PAGES; i++)
|
||||
__free_pages(switcher_page[i], 0);
|
||||
kfree(switcher_page);
|
||||
__free_pages(lg_switcher_pages[i], 0);
|
||||
kfree(lg_switcher_pages);
|
||||
}
|
||||
|
||||
/*H:032
|
||||
@@ -323,15 +328,10 @@ static int __init init(void)
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
/* Now we set up the pagetable implementation for the Guests. */
|
||||
err = init_pagetables(switcher_page, SHARED_SWITCHER_PAGES);
|
||||
if (err)
|
||||
goto unmap;
|
||||
|
||||
/* We might need to reserve an interrupt vector. */
|
||||
err = init_interrupts();
|
||||
if (err)
|
||||
goto free_pgtables;
|
||||
goto unmap;
|
||||
|
||||
/* /dev/lguest needs to be registered. */
|
||||
err = lguest_device_init();
|
||||
@@ -346,8 +346,6 @@ static int __init init(void)
|
||||
|
||||
free_interrupts:
|
||||
free_interrupts();
|
||||
free_pgtables:
|
||||
free_pagetables();
|
||||
unmap:
|
||||
unmap_switcher();
|
||||
out:
|
||||
@@ -359,7 +357,6 @@ static void __exit fini(void)
|
||||
{
|
||||
lguest_device_remove();
|
||||
free_interrupts();
|
||||
free_pagetables();
|
||||
unmap_switcher();
|
||||
|
||||
lguest_arch_host_fini();
|
||||
|
||||
+3
-3
@@ -14,11 +14,10 @@
|
||||
|
||||
#include <asm/lguest.h>
|
||||
|
||||
void free_pagetables(void);
|
||||
int init_pagetables(struct page **switcher_page, unsigned int pages);
|
||||
|
||||
struct pgdir {
|
||||
unsigned long gpgdir;
|
||||
bool switcher_mapped;
|
||||
int last_host_cpu;
|
||||
pgd_t *pgdir;
|
||||
};
|
||||
|
||||
@@ -124,6 +123,7 @@ bool lguest_address_ok(const struct lguest *lg,
|
||||
unsigned long addr, unsigned long len);
|
||||
void __lgread(struct lg_cpu *, void *, unsigned long, unsigned);
|
||||
void __lgwrite(struct lg_cpu *, unsigned long, const void *, unsigned);
|
||||
extern struct page **lg_switcher_pages;
|
||||
|
||||
/*H:035
|
||||
* Using memory-copy operations like that is usually inconvient, so we
|
||||
|
||||
@@ -250,13 +250,13 @@ static ssize_t read(struct file *file, char __user *user, size_t size,loff_t*o)
|
||||
*/
|
||||
static int lg_cpu_start(struct lg_cpu *cpu, unsigned id, unsigned long start_ip)
|
||||
{
|
||||
/* We have a limited number the number of CPUs in the lguest struct. */
|
||||
/* We have a limited number of CPUs in the lguest struct. */
|
||||
if (id >= ARRAY_SIZE(cpu->lg->cpus))
|
||||
return -EINVAL;
|
||||
|
||||
/* Set up this CPU's id, and pointer back to the lguest struct. */
|
||||
cpu->id = id;
|
||||
cpu->lg = container_of((cpu - id), struct lguest, cpus[0]);
|
||||
cpu->lg = container_of(cpu, struct lguest, cpus[id]);
|
||||
cpu->lg->nr_cpus++;
|
||||
|
||||
/* Each CPU has a timer it can set. */
|
||||
@@ -270,7 +270,7 @@ static int lg_cpu_start(struct lg_cpu *cpu, unsigned id, unsigned long start_ip)
|
||||
if (!cpu->regs_page)
|
||||
return -ENOMEM;
|
||||
|
||||
/* We actually put the registers at the bottom of the page. */
|
||||
/* We actually put the registers at the end of the page. */
|
||||
cpu->regs = (void *)cpu->regs_page + PAGE_SIZE - sizeof(*cpu->regs);
|
||||
|
||||
/*
|
||||
|
||||
+307
-266
File diff suppressed because it is too large
Load Diff
@@ -59,14 +59,13 @@ static struct {
|
||||
/* Offset from where switcher.S was compiled to where we've copied it */
|
||||
static unsigned long switcher_offset(void)
|
||||
{
|
||||
return SWITCHER_ADDR - (unsigned long)start_switcher_text;
|
||||
return switcher_addr - (unsigned long)start_switcher_text;
|
||||
}
|
||||
|
||||
/* This cpu's struct lguest_pages. */
|
||||
/* This cpu's struct lguest_pages (after the Switcher text page) */
|
||||
static struct lguest_pages *lguest_pages(unsigned int cpu)
|
||||
{
|
||||
return &(((struct lguest_pages *)
|
||||
(SWITCHER_ADDR + SHARED_SWITCHER_PAGES*PAGE_SIZE))[cpu]);
|
||||
return &(((struct lguest_pages *)(switcher_addr + PAGE_SIZE))[cpu]);
|
||||
}
|
||||
|
||||
static DEFINE_PER_CPU(struct lg_cpu *, lg_last_cpu);
|
||||
|
||||
@@ -40,3 +40,17 @@ config CAIF_HSI
|
||||
The caif low level driver for CAIF over HSI.
|
||||
Be aware that if you enable this then you also need to
|
||||
enable a low-level HSI driver.
|
||||
|
||||
config CAIF_VIRTIO
|
||||
tristate "CAIF virtio transport driver"
|
||||
depends on CAIF
|
||||
select VHOST_RING
|
||||
select VIRTIO
|
||||
select GENERIC_ALLOCATOR
|
||||
default n
|
||||
---help---
|
||||
The caif driver for CAIF over Virtio.
|
||||
|
||||
if CAIF_VIRTIO
|
||||
source "drivers/vhost/Kconfig"
|
||||
endif
|
||||
|
||||
@@ -9,3 +9,6 @@ obj-$(CONFIG_CAIF_SPI_SLAVE) += cfspi_slave.o
|
||||
|
||||
# HSI interface
|
||||
obj-$(CONFIG_CAIF_HSI) += caif_hsi.o
|
||||
|
||||
# Virtio interface
|
||||
obj-$(CONFIG_CAIF_VIRTIO) += caif_virtio.o
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+41
-36
@@ -39,7 +39,6 @@ module_param(gso, bool, 0444);
|
||||
#define MAX_PACKET_LEN (ETH_HLEN + VLAN_HLEN + ETH_DATA_LEN)
|
||||
#define GOOD_COPY_LEN 128
|
||||
|
||||
#define VIRTNET_SEND_COMMAND_SG_MAX 2
|
||||
#define VIRTNET_DRIVER_VERSION "1.0.0"
|
||||
|
||||
struct virtnet_stats {
|
||||
@@ -444,7 +443,7 @@ static int add_recvbuf_small(struct receive_queue *rq, gfp_t gfp)
|
||||
|
||||
skb_to_sgvec(skb, rq->sg + 1, 0, skb->len);
|
||||
|
||||
err = virtqueue_add_buf(rq->vq, rq->sg, 0, 2, skb, gfp);
|
||||
err = virtqueue_add_inbuf(rq->vq, rq->sg, 2, skb, gfp);
|
||||
if (err < 0)
|
||||
dev_kfree_skb(skb);
|
||||
|
||||
@@ -489,8 +488,8 @@ static int add_recvbuf_big(struct receive_queue *rq, gfp_t gfp)
|
||||
|
||||
/* chain first in list head */
|
||||
first->private = (unsigned long)list;
|
||||
err = virtqueue_add_buf(rq->vq, rq->sg, 0, MAX_SKB_FRAGS + 2,
|
||||
first, gfp);
|
||||
err = virtqueue_add_inbuf(rq->vq, rq->sg, MAX_SKB_FRAGS + 2,
|
||||
first, gfp);
|
||||
if (err < 0)
|
||||
give_pages(rq, first);
|
||||
|
||||
@@ -508,7 +507,7 @@ static int add_recvbuf_mergeable(struct receive_queue *rq, gfp_t gfp)
|
||||
|
||||
sg_init_one(rq->sg, page_address(page), PAGE_SIZE);
|
||||
|
||||
err = virtqueue_add_buf(rq->vq, rq->sg, 0, 1, page, gfp);
|
||||
err = virtqueue_add_inbuf(rq->vq, rq->sg, 1, page, gfp);
|
||||
if (err < 0)
|
||||
give_pages(rq, page);
|
||||
|
||||
@@ -582,7 +581,7 @@ static void refill_work(struct work_struct *work)
|
||||
bool still_empty;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < vi->max_queue_pairs; i++) {
|
||||
for (i = 0; i < vi->curr_queue_pairs; i++) {
|
||||
struct receive_queue *rq = &vi->rq[i];
|
||||
|
||||
napi_disable(&rq->napi);
|
||||
@@ -637,7 +636,7 @@ static int virtnet_open(struct net_device *dev)
|
||||
struct virtnet_info *vi = netdev_priv(dev);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < vi->max_queue_pairs; i++) {
|
||||
for (i = 0; i < vi->curr_queue_pairs; i++) {
|
||||
/* Make sure we have some buffers: if oom use wq. */
|
||||
if (!try_fill_recv(&vi->rq[i], GFP_KERNEL))
|
||||
schedule_delayed_work(&vi->refill, 0);
|
||||
@@ -711,8 +710,7 @@ static int xmit_skb(struct send_queue *sq, struct sk_buff *skb)
|
||||
sg_set_buf(sq->sg, &hdr->hdr, sizeof hdr->hdr);
|
||||
|
||||
num_sg = skb_to_sgvec(skb, sq->sg + 1, 0, skb->len) + 1;
|
||||
return virtqueue_add_buf(sq->vq, sq->sg, num_sg,
|
||||
0, skb, GFP_ATOMIC);
|
||||
return virtqueue_add_outbuf(sq->vq, sq->sg, num_sg, skb, GFP_ATOMIC);
|
||||
}
|
||||
|
||||
static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
@@ -767,32 +765,35 @@ static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
* never fail unless improperly formated.
|
||||
*/
|
||||
static bool virtnet_send_command(struct virtnet_info *vi, u8 class, u8 cmd,
|
||||
struct scatterlist *data, int out, int in)
|
||||
struct scatterlist *out,
|
||||
struct scatterlist *in)
|
||||
{
|
||||
struct scatterlist *s, sg[VIRTNET_SEND_COMMAND_SG_MAX + 2];
|
||||
struct scatterlist *sgs[4], hdr, stat;
|
||||
struct virtio_net_ctrl_hdr ctrl;
|
||||
virtio_net_ctrl_ack status = ~0;
|
||||
unsigned int tmp;
|
||||
int i;
|
||||
unsigned out_num = 0, in_num = 0, tmp;
|
||||
|
||||
/* Caller should know better */
|
||||
BUG_ON(!virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VQ) ||
|
||||
(out + in > VIRTNET_SEND_COMMAND_SG_MAX));
|
||||
|
||||
out++; /* Add header */
|
||||
in++; /* Add return status */
|
||||
BUG_ON(!virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VQ));
|
||||
|
||||
ctrl.class = class;
|
||||
ctrl.cmd = cmd;
|
||||
/* Add header */
|
||||
sg_init_one(&hdr, &ctrl, sizeof(ctrl));
|
||||
sgs[out_num++] = &hdr;
|
||||
|
||||
sg_init_table(sg, out + in);
|
||||
if (out)
|
||||
sgs[out_num++] = out;
|
||||
if (in)
|
||||
sgs[out_num + in_num++] = in;
|
||||
|
||||
sg_set_buf(&sg[0], &ctrl, sizeof(ctrl));
|
||||
for_each_sg(data, s, out + in - 2, i)
|
||||
sg_set_buf(&sg[i + 1], sg_virt(s), s->length);
|
||||
sg_set_buf(&sg[out + in - 1], &status, sizeof(status));
|
||||
/* Add return status. */
|
||||
sg_init_one(&stat, &status, sizeof(status));
|
||||
sgs[out_num + in_num++] = &stat;
|
||||
|
||||
BUG_ON(virtqueue_add_buf(vi->cvq, sg, out, in, vi, GFP_ATOMIC) < 0);
|
||||
BUG_ON(out_num + in_num > ARRAY_SIZE(sgs));
|
||||
BUG_ON(virtqueue_add_sgs(vi->cvq, sgs, out_num, in_num, vi, GFP_ATOMIC)
|
||||
< 0);
|
||||
|
||||
virtqueue_kick(vi->cvq);
|
||||
|
||||
@@ -821,7 +822,7 @@ static int virtnet_set_mac_address(struct net_device *dev, void *p)
|
||||
sg_init_one(&sg, addr->sa_data, dev->addr_len);
|
||||
if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_MAC,
|
||||
VIRTIO_NET_CTRL_MAC_ADDR_SET,
|
||||
&sg, 1, 0)) {
|
||||
&sg, NULL)) {
|
||||
dev_warn(&vdev->dev,
|
||||
"Failed to set mac address by vq command.\n");
|
||||
return -EINVAL;
|
||||
@@ -889,8 +890,7 @@ static void virtnet_ack_link_announce(struct virtnet_info *vi)
|
||||
{
|
||||
rtnl_lock();
|
||||
if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_ANNOUNCE,
|
||||
VIRTIO_NET_CTRL_ANNOUNCE_ACK, NULL,
|
||||
0, 0))
|
||||
VIRTIO_NET_CTRL_ANNOUNCE_ACK, NULL, NULL))
|
||||
dev_warn(&vi->dev->dev, "Failed to ack link announce.\n");
|
||||
rtnl_unlock();
|
||||
}
|
||||
@@ -900,6 +900,7 @@ static int virtnet_set_queues(struct virtnet_info *vi, u16 queue_pairs)
|
||||
struct scatterlist sg;
|
||||
struct virtio_net_ctrl_mq s;
|
||||
struct net_device *dev = vi->dev;
|
||||
int i;
|
||||
|
||||
if (!vi->has_cvq || !virtio_has_feature(vi->vdev, VIRTIO_NET_F_MQ))
|
||||
return 0;
|
||||
@@ -908,12 +909,16 @@ static int virtnet_set_queues(struct virtnet_info *vi, u16 queue_pairs)
|
||||
sg_init_one(&sg, &s, sizeof(s));
|
||||
|
||||
if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_MQ,
|
||||
VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, &sg, 1, 0)){
|
||||
VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, &sg, NULL)) {
|
||||
dev_warn(&dev->dev, "Fail to set num of queue pairs to %d\n",
|
||||
queue_pairs);
|
||||
return -EINVAL;
|
||||
} else
|
||||
} else {
|
||||
for (i = vi->curr_queue_pairs; i < queue_pairs; i++)
|
||||
if (!try_fill_recv(&vi->rq[i], GFP_KERNEL))
|
||||
schedule_delayed_work(&vi->refill, 0);
|
||||
vi->curr_queue_pairs = queue_pairs;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -955,7 +960,7 @@ static void virtnet_set_rx_mode(struct net_device *dev)
|
||||
|
||||
if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_RX,
|
||||
VIRTIO_NET_CTRL_RX_PROMISC,
|
||||
sg, 1, 0))
|
||||
sg, NULL))
|
||||
dev_warn(&dev->dev, "Failed to %sable promisc mode.\n",
|
||||
promisc ? "en" : "dis");
|
||||
|
||||
@@ -963,7 +968,7 @@ static void virtnet_set_rx_mode(struct net_device *dev)
|
||||
|
||||
if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_RX,
|
||||
VIRTIO_NET_CTRL_RX_ALLMULTI,
|
||||
sg, 1, 0))
|
||||
sg, NULL))
|
||||
dev_warn(&dev->dev, "Failed to %sable allmulti mode.\n",
|
||||
allmulti ? "en" : "dis");
|
||||
|
||||
@@ -1000,7 +1005,7 @@ static void virtnet_set_rx_mode(struct net_device *dev)
|
||||
|
||||
if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_MAC,
|
||||
VIRTIO_NET_CTRL_MAC_TABLE_SET,
|
||||
sg, 2, 0))
|
||||
sg, NULL))
|
||||
dev_warn(&dev->dev, "Failed to set MAC fitler table.\n");
|
||||
|
||||
kfree(buf);
|
||||
@@ -1015,7 +1020,7 @@ static int virtnet_vlan_rx_add_vid(struct net_device *dev,
|
||||
sg_init_one(&sg, &vid, sizeof(vid));
|
||||
|
||||
if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_VLAN,
|
||||
VIRTIO_NET_CTRL_VLAN_ADD, &sg, 1, 0))
|
||||
VIRTIO_NET_CTRL_VLAN_ADD, &sg, NULL))
|
||||
dev_warn(&dev->dev, "Failed to add VLAN ID %d.\n", vid);
|
||||
return 0;
|
||||
}
|
||||
@@ -1029,7 +1034,7 @@ static int virtnet_vlan_rx_kill_vid(struct net_device *dev,
|
||||
sg_init_one(&sg, &vid, sizeof(vid));
|
||||
|
||||
if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_VLAN,
|
||||
VIRTIO_NET_CTRL_VLAN_DEL, &sg, 1, 0))
|
||||
VIRTIO_NET_CTRL_VLAN_DEL, &sg, NULL))
|
||||
dev_warn(&dev->dev, "Failed to kill VLAN ID %d.\n", vid);
|
||||
return 0;
|
||||
}
|
||||
@@ -1570,7 +1575,7 @@ static int virtnet_probe(struct virtio_device *vdev)
|
||||
}
|
||||
|
||||
/* Last of all, set up some receive buffers. */
|
||||
for (i = 0; i < vi->max_queue_pairs; i++) {
|
||||
for (i = 0; i < vi->curr_queue_pairs; i++) {
|
||||
try_fill_recv(&vi->rq[i], GFP_KERNEL);
|
||||
|
||||
/* If we didn't even get one input buffer, we're useless. */
|
||||
@@ -1694,7 +1699,7 @@ static int virtnet_restore(struct virtio_device *vdev)
|
||||
|
||||
netif_device_attach(vi->dev);
|
||||
|
||||
for (i = 0; i < vi->max_queue_pairs; i++)
|
||||
for (i = 0; i < vi->curr_queue_pairs; i++)
|
||||
if (!try_fill_recv(&vi->rq[i], GFP_KERNEL))
|
||||
schedule_delayed_work(&vi->refill, 0);
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user