From 1f08b833ddbdb1c8e81c4b1053c2ebb7b89cb437 Mon Sep 17 00:00:00 2001 From: Jamie Lokier Date: Fri, 8 Jan 2010 22:01:43 +0000 Subject: Add __devexit_p around reference to virtio_pci_remove This is needed to compile with CONFIG_VIRTIO_PCI=y, because virtio_pci_remove is marked __devexit. Signed-off-by: Jamie Lokier Signed-off-by: Rusty Russell --- drivers/virtio/virtio_pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c index 28d9cf7cf72f..1d5191fab62e 100644 --- a/drivers/virtio/virtio_pci.c +++ b/drivers/virtio/virtio_pci.c @@ -702,7 +702,7 @@ static struct pci_driver virtio_pci_driver = { .name = "virtio-pci", .id_table = virtio_pci_id_table, .probe = virtio_pci_probe, - .remove = virtio_pci_remove, + .remove = __devexit_p(virtio_pci_remove), #ifdef CONFIG_PM .suspend = virtio_pci_suspend, .resume = virtio_pci_resume, -- cgit v1.2.3 From 9564e138b1f6eb137f7149772438d3f3fb3277dd Mon Sep 17 00:00:00 2001 From: Adam Litke Date: Mon, 30 Nov 2009 10:14:15 -0600 Subject: virtio: Add memory statistics reporting to the balloon driver (V4) Changes since V3: - Do not do endian conversions as they will be done in the host - Report stats that reference a quantity of memory in bytes - Minor coding style updates Changes since V2: - Increase stat field size to 64 bits - Report all sizes in kb (not pages) - Drop anon_pages stat and fix endianness conversion Changes since V1: - Use a virtqueue instead of the device config space When using ballooning to manage overcommitted memory on a host, a system for guests to communicate their memory usage to the host can provide information that will minimize the impact of ballooning on the guests. The current method employs a daemon running in each guest that communicates memory statistics to a host daemon at a specified time interval. The host daemon aggregates this information and inflates and/or deflates balloons according to the level of host memory pressure. This approach is effective but overly complex since a daemon must be installed inside each guest and coordinated to communicate with the host. A simpler approach is to collect memory statistics in the virtio balloon driver and communicate them directly to the hypervisor. This patch enables the guest-side support by adding stats collection and reporting to the virtio balloon driver. Signed-off-by: Adam Litke Cc: Anthony Liguori Cc: virtualization@lists.linux-foundation.org Signed-off-by: Rusty Russell (minor fixes) --- drivers/virtio/virtio_balloon.c | 94 +++++++++++++++++++++++++++++++++++++---- include/linux/virtio_balloon.h | 15 +++++++ 2 files changed, 101 insertions(+), 8 deletions(-) diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c index 505be88c82ae..cd778b1752b5 100644 --- a/drivers/virtio/virtio_balloon.c +++ b/drivers/virtio/virtio_balloon.c @@ -28,7 +28,7 @@ struct virtio_balloon { struct virtio_device *vdev; - struct virtqueue *inflate_vq, *deflate_vq; + struct virtqueue *inflate_vq, *deflate_vq, *stats_vq; /* Where the ballooning thread waits for config to change. */ wait_queue_head_t config_change; @@ -49,6 +49,9 @@ struct virtio_balloon /* The array of pfns we tell the Host about. */ unsigned int num_pfns; u32 pfns[256]; + + /* Memory statistics */ + struct virtio_balloon_stat stats[VIRTIO_BALLOON_S_NR]; }; static struct virtio_device_id id_table[] = { @@ -154,6 +157,62 @@ static void leak_balloon(struct virtio_balloon *vb, size_t num) } } +static inline void update_stat(struct virtio_balloon *vb, int idx, + u16 tag, u64 val) +{ + BUG_ON(idx >= VIRTIO_BALLOON_S_NR); + vb->stats[idx].tag = tag; + vb->stats[idx].val = val; +} + +#define pages_to_bytes(x) ((u64)(x) << PAGE_SHIFT) + +static void update_balloon_stats(struct virtio_balloon *vb) +{ + unsigned long events[NR_VM_EVENT_ITEMS]; + struct sysinfo i; + int idx = 0; + + all_vm_events(events); + si_meminfo(&i); + + update_stat(vb, idx++, VIRTIO_BALLOON_S_SWAP_IN, + pages_to_bytes(events[PSWPIN])); + update_stat(vb, idx++, VIRTIO_BALLOON_S_SWAP_OUT, + pages_to_bytes(events[PSWPOUT])); + update_stat(vb, idx++, VIRTIO_BALLOON_S_MAJFLT, events[PGMAJFAULT]); + update_stat(vb, idx++, VIRTIO_BALLOON_S_MINFLT, events[PGFAULT]); + update_stat(vb, idx++, VIRTIO_BALLOON_S_MEMFREE, + pages_to_bytes(i.freeram)); + update_stat(vb, idx++, VIRTIO_BALLOON_S_MEMTOT, + pages_to_bytes(i.totalram)); +} + +/* + * While most virtqueues communicate guest-initiated requests to the hypervisor, + * the stats queue operates in reverse. The driver initializes the virtqueue + * with a single buffer. From that point forward, all conversations consist of + * a hypervisor request (a call to this function) which directs us to refill + * the virtqueue with a fresh stats buffer. + */ +static void stats_ack(struct virtqueue *vq) +{ + struct virtio_balloon *vb; + unsigned int len; + struct scatterlist sg; + + vb = vq->vq_ops->get_buf(vq, &len); + if (!vb) + return; + + update_balloon_stats(vb); + + sg_init_one(&sg, vb->stats, sizeof(vb->stats)); + if (vq->vq_ops->add_buf(vq, &sg, 1, 0, vb) < 0) + BUG(); + vq->vq_ops->kick(vq); +} + static void virtballoon_changed(struct virtio_device *vdev) { struct virtio_balloon *vb = vdev->priv; @@ -204,10 +263,10 @@ static int balloon(void *_vballoon) static int virtballoon_probe(struct virtio_device *vdev) { struct virtio_balloon *vb; - struct virtqueue *vqs[2]; - vq_callback_t *callbacks[] = { balloon_ack, balloon_ack }; - const char *names[] = { "inflate", "deflate" }; - int err; + struct virtqueue *vqs[3]; + vq_callback_t *callbacks[] = { balloon_ack, balloon_ack, stats_ack }; + const char *names[] = { "inflate", "deflate", "stats" }; + int err, nvqs; vdev->priv = vb = kmalloc(sizeof(*vb), GFP_KERNEL); if (!vb) { @@ -220,13 +279,29 @@ static int virtballoon_probe(struct virtio_device *vdev) init_waitqueue_head(&vb->config_change); vb->vdev = vdev; - /* We expect two virtqueues. */ - err = vdev->config->find_vqs(vdev, 2, vqs, callbacks, names); + /* We expect two virtqueues: inflate and deflate, + * and optionally stat. */ + nvqs = virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_STATS_VQ) ? 3 : 2; + err = vdev->config->find_vqs(vdev, nvqs, vqs, callbacks, names); if (err) goto out_free_vb; vb->inflate_vq = vqs[0]; vb->deflate_vq = vqs[1]; + if (virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_STATS_VQ)) { + struct scatterlist sg; + vb->stats_vq = vqs[2]; + + /* + * Prime this virtqueue with one buffer so the hypervisor can + * use it to signal us later. + */ + sg_init_one(&sg, vb->stats, sizeof vb->stats); + if (vb->stats_vq->vq_ops->add_buf(vb->stats_vq, + &sg, 1, 0, vb) < 0) + BUG(); + vb->stats_vq->vq_ops->kick(vb->stats_vq); + } vb->thread = kthread_run(balloon, vb, "vballoon"); if (IS_ERR(vb->thread)) { @@ -264,7 +339,10 @@ static void __devexit virtballoon_remove(struct virtio_device *vdev) kfree(vb); } -static unsigned int features[] = { VIRTIO_BALLOON_F_MUST_TELL_HOST }; +static unsigned int features[] = { + VIRTIO_BALLOON_F_MUST_TELL_HOST, + VIRTIO_BALLOON_F_STATS_VQ, +}; static struct virtio_driver virtio_balloon_driver = { .feature_table = features, diff --git a/include/linux/virtio_balloon.h b/include/linux/virtio_balloon.h index 1418f048cb34..a50ecd1b81a2 100644 --- a/include/linux/virtio_balloon.h +++ b/include/linux/virtio_balloon.h @@ -7,6 +7,7 @@ /* The feature bitmap for virtio balloon */ #define VIRTIO_BALLOON_F_MUST_TELL_HOST 0 /* Tell before reclaiming pages */ +#define VIRTIO_BALLOON_F_STATS_VQ 1 /* Memory Stats virtqueue */ /* Size of a PFN in the balloon interface. */ #define VIRTIO_BALLOON_PFN_SHIFT 12 @@ -18,4 +19,18 @@ struct virtio_balloon_config /* Number of pages we've actually got in balloon. */ __le32 actual; }; + +#define VIRTIO_BALLOON_S_SWAP_IN 0 /* Amount of memory swapped in */ +#define VIRTIO_BALLOON_S_SWAP_OUT 1 /* Amount of memory swapped out */ +#define VIRTIO_BALLOON_S_MAJFLT 2 /* Number of major faults */ +#define VIRTIO_BALLOON_S_MINFLT 3 /* Number of minor faults */ +#define VIRTIO_BALLOON_S_MEMFREE 4 /* Total amount of free memory */ +#define VIRTIO_BALLOON_S_MEMTOT 5 /* Total amount of memory */ +#define VIRTIO_BALLOON_S_NR 6 + +struct virtio_balloon_stat { + u16 tag; + u64 val; +} __attribute__((packed)); + #endif /* _LINUX_VIRTIO_BALLOON_H */ -- cgit v1.2.3 From 1f34c71afe5115e77a49c4e67720a66e27053e54 Mon Sep 17 00:00:00 2001 From: Adam Litke Date: Thu, 10 Dec 2009 16:35:15 -0600 Subject: virtio: Fix scheduling while atomic in virtio_balloon stats This is a fix for my earlier patch: "virtio: Add memory statistics reporting to the balloon driver (V4)". I discovered that all_vm_events() can sleep and therefore stats collection cannot be done in interrupt context. One solution is to handle the interrupt by noting that stats need to be collected and waking the existing vballoon kthread which will complete the work via stats_handle_request(). Rusty, is this a saner way of doing business? There is one issue that I would like a broader opinion on. In stats_request, I update vb->need_stats_update and then wake up the kthread. The kthread uses vb->need_stats_update as a condition variable. Do I need a memory barrier between the update and wake_up to ensure that my kthread sees the correct value? My testing suggests that it is not needed but I would like some confirmation from the experts. Signed-off-by: Adam Litke To: Rusty Russell Cc: Anthony Liguori Cc: linux-kernel@vger.kernel.org Signed-off-by: Rusty Russell --- drivers/virtio/virtio_balloon.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c index cd778b1752b5..3db3d242c3ee 100644 --- a/drivers/virtio/virtio_balloon.c +++ b/drivers/virtio/virtio_balloon.c @@ -51,6 +51,7 @@ struct virtio_balloon u32 pfns[256]; /* Memory statistics */ + int need_stats_update; struct virtio_balloon_stat stats[VIRTIO_BALLOON_S_NR]; }; @@ -193,20 +194,30 @@ static void update_balloon_stats(struct virtio_balloon *vb) * the stats queue operates in reverse. The driver initializes the virtqueue * with a single buffer. From that point forward, all conversations consist of * a hypervisor request (a call to this function) which directs us to refill - * the virtqueue with a fresh stats buffer. + * the virtqueue with a fresh stats buffer. Since stats collection can sleep, + * we notify our kthread which does the actual work via stats_handle_request(). */ -static void stats_ack(struct virtqueue *vq) +static void stats_request(struct virtqueue *vq) { struct virtio_balloon *vb; unsigned int len; - struct scatterlist sg; vb = vq->vq_ops->get_buf(vq, &len); if (!vb) return; + vb->need_stats_update = 1; + wake_up(&vb->config_change); +} + +static void stats_handle_request(struct virtio_balloon *vb) +{ + struct virtqueue *vq; + struct scatterlist sg; + vb->need_stats_update = 0; update_balloon_stats(vb); + vq = vb->stats_vq; sg_init_one(&sg, vb->stats, sizeof(vb->stats)); if (vq->vq_ops->add_buf(vq, &sg, 1, 0, vb) < 0) BUG(); @@ -249,8 +260,11 @@ static int balloon(void *_vballoon) try_to_freeze(); wait_event_interruptible(vb->config_change, (diff = towards_target(vb)) != 0 + || vb->need_stats_update || kthread_should_stop() || freezing(current)); + if (vb->need_stats_update) + stats_handle_request(vb); if (diff > 0) fill_balloon(vb, diff); else if (diff < 0) @@ -264,7 +278,7 @@ static int virtballoon_probe(struct virtio_device *vdev) { struct virtio_balloon *vb; struct virtqueue *vqs[3]; - vq_callback_t *callbacks[] = { balloon_ack, balloon_ack, stats_ack }; + vq_callback_t *callbacks[] = { balloon_ack, balloon_ack, stats_request }; const char *names[] = { "inflate", "deflate", "stats" }; int err, nvqs; -- cgit v1.2.3 From 169c246a30808588436794e96a97c61a01af9bed Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 24 Feb 2010 14:22:14 -0600 Subject: virtio: fix balloon without VIRTIO_BALLOON_F_STATS_VQ When running under qemu-kvm-0.11.0: BUG: unable to handle kernel paging request at 56e58955 ... Process vballoon (pid: 1297, ti=c7976000 task=c70a6ca0 task.ti=c7 ... Call Trace: [] ? balloon+0x1b3/0x440 [virtio_balloon] [] ? schedule+0x327/0x9d0 [] ? balloon+0x0/0x440 [virtio_balloon] [] ? kthread+0x74/0x80 [] ? kthread+0x0/0x80 [] ? kernel_thread_helper+0x6/0x30 need_stats_update should be zero-initialized. Signed-off-by: Rusty Russell Acked-by: Adam Litke --- drivers/virtio/virtio_balloon.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c index 3db3d242c3ee..369f2eebbad1 100644 --- a/drivers/virtio/virtio_balloon.c +++ b/drivers/virtio/virtio_balloon.c @@ -292,6 +292,7 @@ static int virtballoon_probe(struct virtio_device *vdev) vb->num_pages = 0; init_waitqueue_head(&vb->config_change); vb->vdev = vdev; + vb->need_stats_update = 0; /* We expect two virtqueues: inflate and deflate, * and optionally stat. */ -- cgit v1.2.3 From 28ff4ef71795fe6f6c0cc06a04911c9ca09c2c11 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 24 Feb 2010 14:22:18 -0600 Subject: lguest: remove unneeded zlib.h include in example launcher Two years ago 5bbf89fc2608 removed the horrible bzImage unpacking code. Now it's time to remove the unneeded zlib.h include, too. Signed-off-by: Rusty Russell --- Documentation/lguest/lguest.c | 1 - 1 file changed, 1 deletion(-) diff --git a/Documentation/lguest/lguest.c b/Documentation/lguest/lguest.c index 42208511b5c0..3119f5db75bd 100644 --- a/Documentation/lguest/lguest.c +++ b/Documentation/lguest/lguest.c @@ -34,7 +34,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3 From 97a545ab6ce922a0f868d192718a48a0091ebc5e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 24 Feb 2010 14:22:22 -0600 Subject: virtio: remove bogus barriers from DEBUG version of virtio_ring.c With DEBUG defined, we add an ->in_use flag to detect if the caller invokes two virtio methods in parallel. The barriers attempt to ensure timely update of the ->in_use flag. But they're voodoo: if we need these barriers it implies that the calling code doesn't have sufficient synchronization to ensure the code paths aren't invoked at the same time anyway, and we want to detect it. Also, adding barriers changes timing, so turning on debug has more chance of hiding real problems. Thanks to MST for drawing my attention to this code... CC: Michael S. Tsirkin Signed-off-by: Rusty Russell --- drivers/virtio/virtio_ring.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index fbd2ecde93e4..1ee97d402a48 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -36,10 +36,9 @@ panic("%s:in_use = %i\n", \ (_vq)->vq.name, (_vq)->in_use); \ (_vq)->in_use = __LINE__; \ - mb(); \ } while (0) #define END_USE(_vq) \ - do { BUG_ON(!(_vq)->in_use); (_vq)->in_use = 0; mb(); } while(0) + do { BUG_ON(!(_vq)->in_use); (_vq)->in_use = 0; } while(0) #else #define BAD_RING(_vq, fmt, args...) \ do { \ -- cgit v1.2.3 From d57ed95da483418e8b0433da693c9168dd0a2df6 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 28 Jan 2010 00:42:23 +0200 Subject: virtio: use smp_XX barriers on SMP virtio is communicating with a virtual "device" that actually runs on another host processor. Thus SMP barriers can be used to control memory access ordering. Where possible, we should use SMP barriers which are more lightweight than mandatory barriers, because mandatory barriers also control MMIO effects on accesses through relaxed memory I/O windows (which virtio does not use) (compare specifically smp_rmb and rmb on x86_64). We can't just use smp_mb and friends though, because we must force memory ordering even if guest is UP since host could be running on another CPU, but SMP barriers are defined to barrier() in that configuration. So, for UP fall back to mandatory barriers instead. Signed-off-by: Michael S. Tsirkin Signed-off-by: Rusty Russell --- drivers/virtio/virtio_ring.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index 1ee97d402a48..827f7e042610 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -21,6 +21,24 @@ #include #include +/* virtio guest is communicating with a virtual "device" that actually runs on + * a host processor. Memory barriers are used to control SMP effects. */ +#ifdef CONFIG_SMP +/* Where possible, use SMP barriers which are more lightweight than mandatory + * barriers, because mandatory barriers control MMIO effects on accesses + * through relaxed memory I/O windows (which virtio does not use). */ +#define virtio_mb() smp_mb() +#define virtio_rmb() smp_rmb() +#define virtio_wmb() smp_wmb() +#else +/* We must force memory ordering even if guest is UP since host could be + * running on another CPU, but SMP barriers are defined to barrier() in that + * configuration. So fall back to mandatory barriers instead. */ +#define virtio_mb() mb() +#define virtio_rmb() rmb() +#define virtio_wmb() wmb() +#endif + #ifdef DEBUG /* For development, we want to crash whenever the ring is screwed. */ #define BAD_RING(_vq, fmt, args...) \ @@ -220,13 +238,13 @@ static void vring_kick(struct virtqueue *_vq) START_USE(vq); /* Descriptors and available array need to be set before we expose the * new available array entries. */ - wmb(); + virtio_wmb(); vq->vring.avail->idx += vq->num_added; vq->num_added = 0; /* Need to update avail index before checking if we should notify */ - mb(); + virtio_mb(); if (!(vq->vring.used->flags & VRING_USED_F_NO_NOTIFY)) /* Prod other side to tell it about changes. */ @@ -285,7 +303,7 @@ static void *vring_get_buf(struct virtqueue *_vq, unsigned int *len) } /* Only get used array entries after they have been exposed by host. */ - rmb(); + virtio_rmb(); i = vq->vring.used->ring[vq->last_used_idx%vq->vring.num].id; *len = vq->vring.used->ring[vq->last_used_idx%vq->vring.num].len; @@ -323,7 +341,7 @@ static bool vring_enable_cb(struct virtqueue *_vq) /* We optimistically turn back on interrupts, then check if there was * more to do. */ vq->vring.avail->flags &= ~VRING_AVAIL_F_NO_INTERRUPT; - mb(); + virtio_mb(); if (unlikely(more_used(vq))) { END_USE(vq); return false; -- cgit v1.2.3 From 69740c8ba878f58bc3c71f74618fc2cd1da990da Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 24 Feb 2010 14:22:25 -0600 Subject: virtio_blk: add block topology support Allow reading various alignment values from the config page. This allows the guest to much better align I/O requests depending on the storage topology. Note that the formats for the config values appear a bit messed up, but we follow the formats used by ATA and SCSI so they are expected in the storage world. Signed-off-by: Christoph Hellwig Signed-off-by: Rusty Russell --- drivers/block/virtio_blk.c | 61 ++++++++++++++++++++++++++++++++++------------ include/linux/virtio_blk.h | 13 ++++++++++ 2 files changed, 59 insertions(+), 15 deletions(-) diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index 51042f0ba7e1..7eff828b2117 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -243,10 +243,12 @@ static int index_to_minor(int index) static int __devinit virtblk_probe(struct virtio_device *vdev) { struct virtio_blk *vblk; + struct request_queue *q; int err; u64 cap; - u32 v; - u32 blk_size, sg_elems; + u32 v, blk_size, sg_elems, opt_io_size; + u16 min_io_size; + u8 physical_block_exp, alignment_offset; if (index_to_minor(index) >= 1 << MINORBITS) return -ENOSPC; @@ -293,13 +295,13 @@ static int __devinit virtblk_probe(struct virtio_device *vdev) goto out_mempool; } - vblk->disk->queue = blk_init_queue(do_virtblk_request, &vblk->lock); - if (!vblk->disk->queue) { + q = vblk->disk->queue = blk_init_queue(do_virtblk_request, &vblk->lock); + if (!q) { err = -ENOMEM; goto out_put_disk; } - vblk->disk->queue->queuedata = vblk; + q->queuedata = vblk; if (index < 26) { sprintf(vblk->disk->disk_name, "vd%c", 'a' + index % 26); @@ -323,10 +325,10 @@ static int __devinit virtblk_probe(struct virtio_device *vdev) /* If barriers are supported, tell block layer that queue is ordered */ if (virtio_has_feature(vdev, VIRTIO_BLK_F_FLUSH)) - blk_queue_ordered(vblk->disk->queue, QUEUE_ORDERED_DRAIN_FLUSH, + blk_queue_ordered(q, QUEUE_ORDERED_DRAIN_FLUSH, virtblk_prepare_flush); else if (virtio_has_feature(vdev, VIRTIO_BLK_F_BARRIER)) - blk_queue_ordered(vblk->disk->queue, QUEUE_ORDERED_TAG, NULL); + blk_queue_ordered(q, QUEUE_ORDERED_TAG, NULL); /* If disk is read-only in the host, the guest should obey */ if (virtio_has_feature(vdev, VIRTIO_BLK_F_RO)) @@ -345,14 +347,14 @@ static int __devinit virtblk_probe(struct virtio_device *vdev) set_capacity(vblk->disk, cap); /* We can handle whatever the host told us to handle. */ - blk_queue_max_phys_segments(vblk->disk->queue, vblk->sg_elems-2); - blk_queue_max_hw_segments(vblk->disk->queue, vblk->sg_elems-2); + blk_queue_max_phys_segments(q, vblk->sg_elems-2); + blk_queue_max_hw_segments(q, vblk->sg_elems-2); /* No need to bounce any requests */ - blk_queue_bounce_limit(vblk->disk->queue, BLK_BOUNCE_ANY); + blk_queue_bounce_limit(q, BLK_BOUNCE_ANY); /* No real sector limit. */ - blk_queue_max_sectors(vblk->disk->queue, -1U); + blk_queue_max_sectors(q, -1U); /* Host can optionally specify maximum segment size and number of * segments. */ @@ -360,16 +362,45 @@ static int __devinit virtblk_probe(struct virtio_device *vdev) offsetof(struct virtio_blk_config, size_max), &v); if (!err) - blk_queue_max_segment_size(vblk->disk->queue, v); + blk_queue_max_segment_size(q, v); else - blk_queue_max_segment_size(vblk->disk->queue, -1U); + blk_queue_max_segment_size(q, -1U); /* Host can optionally specify the block size of the device */ err = virtio_config_val(vdev, VIRTIO_BLK_F_BLK_SIZE, offsetof(struct virtio_blk_config, blk_size), &blk_size); if (!err) - blk_queue_logical_block_size(vblk->disk->queue, blk_size); + blk_queue_logical_block_size(q, blk_size); + else + blk_size = queue_logical_block_size(q); + + /* Use topology information if available */ + err = virtio_config_val(vdev, VIRTIO_BLK_F_TOPOLOGY, + offsetof(struct virtio_blk_config, physical_block_exp), + &physical_block_exp); + if (!err && physical_block_exp) + blk_queue_physical_block_size(q, + blk_size * (1 << physical_block_exp)); + + err = virtio_config_val(vdev, VIRTIO_BLK_F_TOPOLOGY, + offsetof(struct virtio_blk_config, alignment_offset), + &alignment_offset); + if (!err && alignment_offset) + blk_queue_alignment_offset(q, blk_size * alignment_offset); + + err = virtio_config_val(vdev, VIRTIO_BLK_F_TOPOLOGY, + offsetof(struct virtio_blk_config, min_io_size), + &min_io_size); + if (!err && min_io_size) + blk_queue_io_min(q, blk_size * min_io_size); + + err = virtio_config_val(vdev, VIRTIO_BLK_F_TOPOLOGY, + offsetof(struct virtio_blk_config, opt_io_size), + &opt_io_size); + if (!err && opt_io_size) + blk_queue_io_opt(q, blk_size * opt_io_size); + add_disk(vblk->disk); return 0; @@ -412,7 +443,7 @@ static struct virtio_device_id id_table[] = { static unsigned int features[] = { VIRTIO_BLK_F_BARRIER, VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_SIZE_MAX, VIRTIO_BLK_F_GEOMETRY, VIRTIO_BLK_F_RO, VIRTIO_BLK_F_BLK_SIZE, - VIRTIO_BLK_F_SCSI, VIRTIO_BLK_F_FLUSH + VIRTIO_BLK_F_SCSI, VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_TOPOLOGY }; /* diff --git a/include/linux/virtio_blk.h b/include/linux/virtio_blk.h index fd294c56d571..e52029e98919 100644 --- a/include/linux/virtio_blk.h +++ b/include/linux/virtio_blk.h @@ -15,6 +15,7 @@ #define VIRTIO_BLK_F_BLK_SIZE 6 /* Block size of disk is available*/ #define VIRTIO_BLK_F_SCSI 7 /* Supports scsi command passthru */ #define VIRTIO_BLK_F_FLUSH 9 /* Cache flush command support */ +#define VIRTIO_BLK_F_TOPOLOGY 10 /* Topology information is available */ struct virtio_blk_config { /* The capacity (in 512-byte sectors). */ @@ -29,8 +30,20 @@ struct virtio_blk_config { __u8 heads; __u8 sectors; } geometry; + /* block size of device (if VIRTIO_BLK_F_BLK_SIZE) */ __u32 blk_size; + + /* the next 4 entries are guarded by VIRTIO_BLK_F_TOPOLOGY */ + /* exponent for physical block per logical block. */ + __u8 physical_block_exp; + /* alignment offset in logical blocks. */ + __u8 alignment_offset; + /* minimum I/O size without performance penalty in logical blocks. */ + __u16 min_io_size; + /* optimal sustained I/O size in logical blocks. */ + __u32 opt_io_size; + } __attribute__((packed)); /* -- cgit v1.2.3 From c021eac4148c16bf53baa0dd14e8ebee6f39dab5 Mon Sep 17 00:00:00 2001 From: Shirley Ma Date: Mon, 18 Jan 2010 19:15:23 +0530 Subject: virtio: Add ability to detach unused buffers from vrings There's currently no way for a virtio driver to ask for unused buffers, so it has to keep a list itself to reclaim them at shutdown. This is redundant, since virtio_ring stores that information. So add a new hook to do this. Signed-off-by: Shirley Ma Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/virtio/virtio_ring.c | 25 +++++++++++++++++++++++++ include/linux/virtio.h | 4 ++++ 2 files changed, 29 insertions(+) diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index 827f7e042610..782b7292a3d8 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -351,6 +351,30 @@ static bool vring_enable_cb(struct virtqueue *_vq) return true; } +static void *vring_detach_unused_buf(struct virtqueue *_vq) +{ + struct vring_virtqueue *vq = to_vvq(_vq); + unsigned int i; + void *buf; + + START_USE(vq); + + for (i = 0; i < vq->vring.num; i++) { + if (!vq->data[i]) + continue; + /* detach_buf clears data, so grab it now. */ + buf = vq->data[i]; + detach_buf(vq, i); + END_USE(vq); + return buf; + } + /* That should have freed everything. */ + BUG_ON(vq->num_free != vq->vring.num); + + END_USE(vq); + return NULL; +} + irqreturn_t vring_interrupt(int irq, void *_vq) { struct vring_virtqueue *vq = to_vvq(_vq); @@ -377,6 +401,7 @@ static struct virtqueue_ops vring_vq_ops = { .kick = vring_kick, .disable_cb = vring_disable_cb, .enable_cb = vring_enable_cb, + .detach_unused_buf = vring_detach_unused_buf, }; struct virtqueue *vring_new_virtqueue(unsigned int num, diff --git a/include/linux/virtio.h b/include/linux/virtio.h index 057a2e010758..f508c651e53d 100644 --- a/include/linux/virtio.h +++ b/include/linux/virtio.h @@ -51,6 +51,9 @@ struct virtqueue { * This re-enables callbacks; it returns "false" if there are pending * buffers in the queue, to detect a possible race between the driver * checking for more work, and enabling callbacks. + * @detach_unused_buf: detach first unused buffer + * vq: the struct virtqueue we're talking about. + * Returns NULL or the "data" token handed to add_buf * * Locking rules are straightforward: the driver is responsible for * locking. No two operations may be invoked simultaneously, with the exception @@ -71,6 +74,7 @@ struct virtqueue_ops { void (*disable_cb)(struct virtqueue *vq); bool (*enable_cb)(struct virtqueue *vq); + void *(*detach_unused_buf)(struct virtqueue *vq); }; /** -- cgit v1.2.3 From 3b8706240ee6084ccb46e53cd3a554356b7eeec8 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Fri, 12 Feb 2010 10:32:14 +0530 Subject: virtio: Initialize vq->data entries to NULL vq operations depend on vq->data[i] being NULL to figure out if the vq entry is in use (since the previous patch). We have to initialize them to NULL to ensure we don't work with junk data and trigger false BUG_ONs. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell Cc: Shirley Ma --- drivers/virtio/virtio_ring.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index 782b7292a3d8..0db906b3c95d 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -448,8 +448,11 @@ struct virtqueue *vring_new_virtqueue(unsigned int num, /* Put everything in free lists. */ vq->num_free = num; vq->free_head = 0; - for (i = 0; i < num-1; i++) + for (i = 0; i < num-1; i++) { vq->vring.desc[i].next = i+1; + vq->data[i] = NULL; + } + vq->data[i] = NULL; return &vq->vq; } -- cgit v1.2.3 From a23ea92474e558b071d3e43d961ec767c31faebd Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 18 Jan 2010 19:14:55 +0530 Subject: virtio: console: comment cleanup Remove old lguest-style comments. [Amit: - wingify comments acc. to kernel style - indent comments ] Signed-off-by: Rusty Russell Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 108 ++++++++++++++++++++--------------------- include/linux/virtio_console.h | 6 ++- 2 files changed, 58 insertions(+), 56 deletions(-) diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index a035ae39a359..26e238cd7d2f 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -1,18 +1,5 @@ -/*D:300 - * The Guest console driver - * - * Writing console drivers is one of the few remaining Dark Arts in Linux. - * Fortunately for us, the path of virtual consoles has been well-trodden by - * the PowerPC folks, who wrote "hvc_console.c" to generically support any - * virtual console. We use that infrastructure which only requires us to write - * the basic put_chars and get_chars functions and call the right register - * functions. - :*/ - -/*M:002 The console can be flooded: while the Guest is processing input the - * Host can send more. Buffering in the Host could alleviate this, but it is a - * difficult problem in general. :*/ -/* Copyright (C) 2006, 2007 Rusty Russell, IBM Corporation +/* + * Copyright (C) 2006, 2007, 2009 Rusty Russell, IBM Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -34,8 +21,6 @@ #include #include "hvc_console.h" -/*D:340 These represent our input and output console queues, and the virtio - * operations for them. */ static struct virtqueue *in_vq, *out_vq; static struct virtio_device *vdev; @@ -49,12 +34,14 @@ static struct hv_ops virtio_cons; /* The hvc device */ static struct hvc_struct *hvc; -/*D:310 The put_chars() callback is pretty straightforward. +/* + * The put_chars() callback is pretty straightforward. * - * We turn the characters into a scatter-gather list, add it to the output - * queue and then kick the Host. Then we sit here waiting for it to finish: - * inefficient in theory, but in practice implementations will do it - * immediately (lguest's Launcher does). */ + * We turn the characters into a scatter-gather list, add it to the + * output queue and then kick the Host. Then we sit here waiting for + * it to finish: inefficient in theory, but in practice + * implementations will do it immediately (lguest's Launcher does). + */ static int put_chars(u32 vtermno, const char *buf, int count) { struct scatterlist sg[1]; @@ -63,8 +50,10 @@ static int put_chars(u32 vtermno, const char *buf, int count) /* This is a convenient routine to initialize a single-elem sg list */ sg_init_one(sg, buf, count); - /* add_buf wants a token to identify this buffer: we hand it any - * non-NULL pointer, since there's only ever one buffer. */ + /* + * add_buf wants a token to identify this buffer: we hand it + * any non-NULL pointer, since there's only ever one buffer. + */ if (out_vq->vq_ops->add_buf(out_vq, sg, 1, 0, (void *)1) >= 0) { /* Tell Host to go! */ out_vq->vq_ops->kick(out_vq); @@ -77,8 +66,10 @@ static int put_chars(u32 vtermno, const char *buf, int count) return count; } -/* Create a scatter-gather list representing our input buffer and put it in the - * queue. */ +/* + * Create a scatter-gather list representing our input buffer and put + * it in the queue. + */ static void add_inbuf(void) { struct scatterlist sg[1]; @@ -90,12 +81,14 @@ static void add_inbuf(void) in_vq->vq_ops->kick(in_vq); } -/*D:350 get_chars() is the callback from the hvc_console infrastructure when - * an interrupt is received. +/* + * get_chars() is the callback from the hvc_console infrastructure + * when an interrupt is received. * - * Most of the code deals with the fact that the hvc_console() infrastructure - * only asks us for 16 bytes at a time. We keep in_offset and in_used fields - * for partially-filled buffers. */ + * Most of the code deals with the fact that the hvc_console() + * infrastructure only asks us for 16 bytes at a time. We keep + * in_offset and in_used fields for partially-filled buffers. + */ static int get_chars(u32 vtermno, char *buf, int count) { /* If we don't have an input queue yet, we can't get input. */ @@ -123,14 +116,16 @@ static int get_chars(u32 vtermno, char *buf, int count) return count; } -/*:*/ -/*D:320 Console drivers are initialized very early so boot messages can go out, - * so we do things slightly differently from the generic virtio initialization - * of the net and block drivers. +/* + * Console drivers are initialized very early so boot messages can go + * out, so we do things slightly differently from the generic virtio + * initialization of the net and block drivers. * - * At this stage, the console is output-only. It's too early to set up a - * virtqueue, so we let the drivers do some boutique early-output thing. */ + * At this stage, the console is output-only. It's too early to set + * up a virtqueue, so we let the drivers do some boutique early-output + * thing. + */ int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int)) { virtio_cons.put_chars = put_chars; @@ -157,8 +152,8 @@ static void virtcons_apply_config(struct virtio_device *dev) } /* - * we support only one console, the hvc struct is a global var - * We set the configuration at this point, since we now have a tty + * we support only one console, the hvc struct is a global var We set + * the configuration at this point, since we now have a tty */ static int notifier_add_vio(struct hvc_struct *hp, int data) { @@ -179,13 +174,17 @@ static void hvc_handle_input(struct virtqueue *vq) hvc_kick(); } -/*D:370 Once we're further in boot, we get probed like any other virtio device. - * At this stage we set up the output virtqueue. +/* + * Once we're further in boot, we get probed like any other virtio + * device. At this stage we set up the output virtqueue. * - * To set up and manage our virtual console, we call hvc_alloc(). Since we - * never remove the console device we never need this pointer again. + * To set up and manage our virtual console, we call hvc_alloc(). + * Since we never remove the console device we never need this pointer + * again. * - * Finally we put our input buffer in the input queue, ready to receive. */ + * Finally we put our input buffer in the input queue, ready to + * receive. + */ static int __devinit virtcons_probe(struct virtio_device *dev) { vq_callback_t *callbacks[] = { hvc_handle_input, NULL}; @@ -203,8 +202,6 @@ static int __devinit virtcons_probe(struct virtio_device *dev) } /* Find the queues. */ - /* FIXME: This is why we want to wean off hvc: we do nothing - * when input comes in. */ err = vdev->config->find_vqs(vdev, 2, vqs, callbacks, names); if (err) goto free; @@ -219,15 +216,18 @@ static int __devinit virtcons_probe(struct virtio_device *dev) virtio_cons.notifier_del = notifier_del_vio; virtio_cons.notifier_hangup = notifier_del_vio; - /* The first argument of hvc_alloc() is the virtual console number, so - * we use zero. The second argument is the parameter for the - * notification mechanism (like irq number). We currently leave this - * as zero, virtqueues have implicit notifications. + /* + * The first argument of hvc_alloc() is the virtual console + * number, so we use zero. The second argument is the + * parameter for the notification mechanism (like irq + * number). We currently leave this as zero, virtqueues have + * implicit notifications. * - * The third argument is a "struct hv_ops" containing the put_chars() - * get_chars(), notifier_add() and notifier_del() pointers. - * The final argument is the output buffer size: we can do any size, - * so we put PAGE_SIZE here. */ + * The third argument is a "struct hv_ops" containing the + * put_chars(), get_chars(), notifier_add() and notifier_del() + * pointers. The final argument is the output buffer size: we + * can do any size, so we put PAGE_SIZE here. + */ hvc = hvc_alloc(0, 0, &virtio_cons, PAGE_SIZE); if (IS_ERR(hvc)) { err = PTR_ERR(hvc); diff --git a/include/linux/virtio_console.h b/include/linux/virtio_console.h index fe885174cc1f..9e0da40beae0 100644 --- a/include/linux/virtio_console.h +++ b/include/linux/virtio_console.h @@ -3,8 +3,10 @@ #include #include #include -/* This header, excluding the #ifdef __KERNEL__ part, is BSD licensed so - * anyone can use the definitions to implement compatible drivers/servers. */ +/* + * This header, excluding the #ifdef __KERNEL__ part, is BSD licensed so + * anyone can use the definitions to implement compatible drivers/servers. + */ /* Feature bits */ #define VIRTIO_CONSOLE_F_SIZE 0 /* Does host provide console size? */ -- cgit v1.2.3 From 971f3390003619ea4ac0b20ee93dfd3209025790 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 18 Jan 2010 19:14:56 +0530 Subject: virtio: console: statically initialize virtio_cons That way, we can make it const as is good kernel style. We use a separate indirection for the early console, rather than mugging ops.put_chars. We rename it hv_ops, too. Signed-off-by: Rusty Russell Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 60 ++++++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 26e238cd7d2f..1d844a43a6bf 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -28,12 +28,12 @@ static struct virtio_device *vdev; static unsigned int in_len; static char *in, *inbuf; -/* The operations for our console. */ -static struct hv_ops virtio_cons; - /* The hvc device */ static struct hvc_struct *hvc; +/* This is the very early arch-specified put chars function. */ +static int (*early_put_chars)(u32, const char *, int); + /* * The put_chars() callback is pretty straightforward. * @@ -47,6 +47,9 @@ static int put_chars(u32 vtermno, const char *buf, int count) struct scatterlist sg[1]; unsigned int len; + if (unlikely(early_put_chars)) + return early_put_chars(vtermno, buf, count); + /* This is a convenient routine to initialize a single-elem sg list */ sg_init_one(sg, buf, count); @@ -117,21 +120,6 @@ static int get_chars(u32 vtermno, char *buf, int count) return count; } -/* - * Console drivers are initialized very early so boot messages can go - * out, so we do things slightly differently from the generic virtio - * initialization of the net and block drivers. - * - * At this stage, the console is output-only. It's too early to set - * up a virtqueue, so we let the drivers do some boutique early-output - * thing. - */ -int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int)) -{ - virtio_cons.put_chars = put_chars; - return hvc_instantiate(0, 0, &virtio_cons); -} - /* * virtio console configuration. This supports: * - console resize @@ -174,6 +162,30 @@ static void hvc_handle_input(struct virtqueue *vq) hvc_kick(); } +/* The operations for the console. */ +static struct hv_ops hv_ops = { + .get_chars = get_chars, + .put_chars = put_chars, + .notifier_add = notifier_add_vio, + .notifier_del = notifier_del_vio, + .notifier_hangup = notifier_del_vio, +}; + +/* + * Console drivers are initialized very early so boot messages can go + * out, so we do things slightly differently from the generic virtio + * initialization of the net and block drivers. + * + * At this stage, the console is output-only. It's too early to set + * up a virtqueue, so we let the drivers do some boutique early-output + * thing. + */ +int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int)) +{ + early_put_chars = put_chars; + return hvc_instantiate(0, 0, &hv_ops); +} + /* * Once we're further in boot, we get probed like any other virtio * device. At this stage we set up the output virtqueue. @@ -209,13 +221,6 @@ static int __devinit virtcons_probe(struct virtio_device *dev) in_vq = vqs[0]; out_vq = vqs[1]; - /* Start using the new console output. */ - virtio_cons.get_chars = get_chars; - virtio_cons.put_chars = put_chars; - virtio_cons.notifier_add = notifier_add_vio; - virtio_cons.notifier_del = notifier_del_vio; - virtio_cons.notifier_hangup = notifier_del_vio; - /* * The first argument of hvc_alloc() is the virtual console * number, so we use zero. The second argument is the @@ -228,7 +233,7 @@ static int __devinit virtcons_probe(struct virtio_device *dev) * pointers. The final argument is the output buffer size: we * can do any size, so we put PAGE_SIZE here. */ - hvc = hvc_alloc(0, 0, &virtio_cons, PAGE_SIZE); + hvc = hvc_alloc(0, 0, &hv_ops, PAGE_SIZE); if (IS_ERR(hvc)) { err = PTR_ERR(hvc); goto free_vqs; @@ -236,6 +241,9 @@ static int __devinit virtcons_probe(struct virtio_device *dev) /* Register the input buffer the first time. */ add_inbuf(); + + /* Start using the new console output. */ + early_put_chars = NULL; return 0; free_vqs: -- cgit v1.2.3 From 1dff399616a79b8ef5d61ad68f2ef1e1f590b465 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 28 Nov 2009 12:20:26 +0530 Subject: hvc_console: make the ops pointer const. This is nicer for modern R/O protection. And noone needs it non-const, so constify the callers as well. Signed-off-by: Rusty Russell Signed-off-by: Amit Shah To: Christian Borntraeger Cc: linuxppc-dev@ozlabs.org --- drivers/char/hvc_beat.c | 2 +- drivers/char/hvc_console.c | 7 ++++--- drivers/char/hvc_console.h | 7 ++++--- drivers/char/hvc_iseries.c | 2 +- drivers/char/hvc_iucv.c | 2 +- drivers/char/hvc_rtas.c | 2 +- drivers/char/hvc_udbg.c | 2 +- drivers/char/hvc_vio.c | 2 +- drivers/char/hvc_xen.c | 2 +- drivers/char/virtio_console.c | 2 +- 10 files changed, 16 insertions(+), 14 deletions(-) diff --git a/drivers/char/hvc_beat.c b/drivers/char/hvc_beat.c index 0afc8b82212e..6913fc33270c 100644 --- a/drivers/char/hvc_beat.c +++ b/drivers/char/hvc_beat.c @@ -84,7 +84,7 @@ static int hvc_beat_put_chars(uint32_t vtermno, const char *buf, int cnt) return cnt; } -static struct hv_ops hvc_beat_get_put_ops = { +static const struct hv_ops hvc_beat_get_put_ops = { .get_chars = hvc_beat_get_chars, .put_chars = hvc_beat_put_chars, }; diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c index 416d3423150d..d8dac5820f0e 100644 --- a/drivers/char/hvc_console.c +++ b/drivers/char/hvc_console.c @@ -125,7 +125,7 @@ static struct hvc_struct *hvc_get_by_index(int index) * console interfaces but can still be used as a tty device. This has to be * static because kmalloc will not work during early console init. */ -static struct hv_ops *cons_ops[MAX_NR_HVC_CONSOLES]; +static const struct hv_ops *cons_ops[MAX_NR_HVC_CONSOLES]; static uint32_t vtermnos[MAX_NR_HVC_CONSOLES] = {[0 ... MAX_NR_HVC_CONSOLES - 1] = -1}; @@ -247,7 +247,7 @@ static void destroy_hvc_struct(struct kref *kref) * vty adapters do NOT get an hvc_instantiate() callback since they * appear after early console init. */ -int hvc_instantiate(uint32_t vtermno, int index, struct hv_ops *ops) +int hvc_instantiate(uint32_t vtermno, int index, const struct hv_ops *ops) { struct hvc_struct *hp; @@ -749,7 +749,8 @@ static const struct tty_operations hvc_ops = { }; struct hvc_struct __devinit *hvc_alloc(uint32_t vtermno, int data, - struct hv_ops *ops, int outbuf_size) + const struct hv_ops *ops, + int outbuf_size) { struct hvc_struct *hp; int i; diff --git a/drivers/char/hvc_console.h b/drivers/char/hvc_console.h index 10950ca706d8..52ddf4d3716c 100644 --- a/drivers/char/hvc_console.h +++ b/drivers/char/hvc_console.h @@ -55,7 +55,7 @@ struct hvc_struct { int outbuf_size; int n_outbuf; uint32_t vtermno; - struct hv_ops *ops; + const struct hv_ops *ops; int irq_requested; int data; struct winsize ws; @@ -76,11 +76,12 @@ struct hv_ops { }; /* Register a vterm and a slot index for use as a console (console_init) */ -extern int hvc_instantiate(uint32_t vtermno, int index, struct hv_ops *ops); +extern int hvc_instantiate(uint32_t vtermno, int index, + const struct hv_ops *ops); /* register a vterm for hvc tty operation (module_init or hotplug add) */ extern struct hvc_struct * __devinit hvc_alloc(uint32_t vtermno, int data, - struct hv_ops *ops, int outbuf_size); + const struct hv_ops *ops, int outbuf_size); /* remove a vterm from hvc tty operation (module_exit or hotplug remove) */ extern int hvc_remove(struct hvc_struct *hp); diff --git a/drivers/char/hvc_iseries.c b/drivers/char/hvc_iseries.c index 936d05bf37fa..fd0242676a2a 100644 --- a/drivers/char/hvc_iseries.c +++ b/drivers/char/hvc_iseries.c @@ -197,7 +197,7 @@ done: return sent; } -static struct hv_ops hvc_get_put_ops = { +static const struct hv_ops hvc_get_put_ops = { .get_chars = get_chars, .put_chars = put_chars, .notifier_add = notifier_add_irq, diff --git a/drivers/char/hvc_iucv.c b/drivers/char/hvc_iucv.c index fe62bd0e17b7..21681a81cc35 100644 --- a/drivers/char/hvc_iucv.c +++ b/drivers/char/hvc_iucv.c @@ -922,7 +922,7 @@ static int hvc_iucv_pm_restore_thaw(struct device *dev) /* HVC operations */ -static struct hv_ops hvc_iucv_ops = { +static const struct hv_ops hvc_iucv_ops = { .get_chars = hvc_iucv_get_chars, .put_chars = hvc_iucv_put_chars, .notifier_add = hvc_iucv_notifier_add, diff --git a/drivers/char/hvc_rtas.c b/drivers/char/hvc_rtas.c index 88590d040046..61c4a61558d9 100644 --- a/drivers/char/hvc_rtas.c +++ b/drivers/char/hvc_rtas.c @@ -71,7 +71,7 @@ static int hvc_rtas_read_console(uint32_t vtermno, char *buf, int count) return i; } -static struct hv_ops hvc_rtas_get_put_ops = { +static const struct hv_ops hvc_rtas_get_put_ops = { .get_chars = hvc_rtas_read_console, .put_chars = hvc_rtas_write_console, }; diff --git a/drivers/char/hvc_udbg.c b/drivers/char/hvc_udbg.c index bd63ba878a56..b0957e61a7be 100644 --- a/drivers/char/hvc_udbg.c +++ b/drivers/char/hvc_udbg.c @@ -58,7 +58,7 @@ static int hvc_udbg_get(uint32_t vtermno, char *buf, int count) return i; } -static struct hv_ops hvc_udbg_ops = { +static const struct hv_ops hvc_udbg_ops = { .get_chars = hvc_udbg_get, .put_chars = hvc_udbg_put, }; diff --git a/drivers/char/hvc_vio.c b/drivers/char/hvc_vio.c index 10be343d6ae7..27370e99c66f 100644 --- a/drivers/char/hvc_vio.c +++ b/drivers/char/hvc_vio.c @@ -77,7 +77,7 @@ static int filtered_get_chars(uint32_t vtermno, char *buf, int count) return got; } -static struct hv_ops hvc_get_put_ops = { +static const struct hv_ops hvc_get_put_ops = { .get_chars = filtered_get_chars, .put_chars = hvc_put_chars, .notifier_add = notifier_add_irq, diff --git a/drivers/char/hvc_xen.c b/drivers/char/hvc_xen.c index b1a71638c772..60446f82a3fc 100644 --- a/drivers/char/hvc_xen.c +++ b/drivers/char/hvc_xen.c @@ -122,7 +122,7 @@ static int read_console(uint32_t vtermno, char *buf, int len) return recv; } -static struct hv_ops hvc_ops = { +static const struct hv_ops hvc_ops = { .get_chars = read_console, .put_chars = write_console, .notifier_add = notifier_add_irq, diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 1d844a43a6bf..791be4e91d15 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -163,7 +163,7 @@ static void hvc_handle_input(struct virtqueue *vq) } /* The operations for the console. */ -static struct hv_ops hv_ops = { +static const struct hv_ops hv_ops = { .get_chars = get_chars, .put_chars = put_chars, .notifier_add = notifier_add_vio, -- cgit v1.2.3 From f550804ab92e37a08d2622522a0f11252a2158ea Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Mon, 18 Jan 2010 19:14:59 +0530 Subject: virtio: console: We support only one device at a time We support only one virtio_console device at a time. If multiple are found, error out if one is already initialized. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 791be4e91d15..bfc0abf825ed 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -204,6 +204,11 @@ static int __devinit virtcons_probe(struct virtio_device *dev) struct virtqueue *vqs[2]; int err; + if (vdev) { + dev_warn(&vdev->dev, + "Multiple virtio-console devices not supported yet\n"); + return -EEXIST; + } vdev = dev; /* This is the scratch page we use to receive console input */ -- cgit v1.2.3 From 21206ede8826fd9d2eb72e05b429f3ccb1bdaff5 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 18 Jan 2010 19:15:00 +0530 Subject: virtio: console: port encapsulation We are heading towards a multiple-"port" system, so as part of weaning off globals we encapsulate the information into 'struct port'. Signed-off-by: Rusty Russell Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 107 +++++++++++++++++++++++------------------- 1 file changed, 58 insertions(+), 49 deletions(-) diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index bfc0abf825ed..9ea9223c5c5c 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -21,15 +21,19 @@ #include #include "hvc_console.h" -static struct virtqueue *in_vq, *out_vq; -static struct virtio_device *vdev; - -/* This is our input buffer, and how much data is left in it. */ -static unsigned int in_len; -static char *in, *inbuf; +struct port { + struct virtqueue *in_vq, *out_vq; + struct virtio_device *vdev; + /* This is our input buffer, and how much data is left in it. */ + char *inbuf; + unsigned int used_len, offset; + + /* The hvc device */ + struct hvc_struct *hvc; +}; -/* The hvc device */ -static struct hvc_struct *hvc; +/* We have one port ready to go immediately, for a console. */ +static struct port console; /* This is the very early arch-specified put chars function. */ static int (*early_put_chars)(u32, const char *, int); @@ -46,22 +50,21 @@ static int put_chars(u32 vtermno, const char *buf, int count) { struct scatterlist sg[1]; unsigned int len; + struct port *port; if (unlikely(early_put_chars)) return early_put_chars(vtermno, buf, count); + port = &console; + /* This is a convenient routine to initialize a single-elem sg list */ sg_init_one(sg, buf, count); - /* - * add_buf wants a token to identify this buffer: we hand it - * any non-NULL pointer, since there's only ever one buffer. - */ - if (out_vq->vq_ops->add_buf(out_vq, sg, 1, 0, (void *)1) >= 0) { + /* This shouldn't fail: if it does, we lose chars. */ + if (port->out_vq->vq_ops->add_buf(port->out_vq, sg, 1, 0, port) >= 0) { /* Tell Host to go! */ - out_vq->vq_ops->kick(out_vq); - /* Chill out until it's done with the buffer. */ - while (!out_vq->vq_ops->get_buf(out_vq, &len)) + port->out_vq->vq_ops->kick(port->out_vq); + while (!port->out_vq->vq_ops->get_buf(port->out_vq, &len)) cpu_relax(); } @@ -73,15 +76,15 @@ static int put_chars(u32 vtermno, const char *buf, int count) * Create a scatter-gather list representing our input buffer and put * it in the queue. */ -static void add_inbuf(void) +static void add_inbuf(struct port *port) { struct scatterlist sg[1]; - sg_init_one(sg, inbuf, PAGE_SIZE); + sg_init_one(sg, port->inbuf, PAGE_SIZE); - /* We should always be able to add one buffer to an empty queue. */ - if (in_vq->vq_ops->add_buf(in_vq, sg, 0, 1, inbuf) < 0) + /* Should always be able to add one buffer to an empty queue. */ + if (port->in_vq->vq_ops->add_buf(port->in_vq, sg, 0, 1, port) < 0) BUG(); - in_vq->vq_ops->kick(in_vq); + port->in_vq->vq_ops->kick(port->in_vq); } /* @@ -94,28 +97,31 @@ static void add_inbuf(void) */ static int get_chars(u32 vtermno, char *buf, int count) { + struct port *port; + + port = &console; + /* If we don't have an input queue yet, we can't get input. */ - BUG_ON(!in_vq); + BUG_ON(!port->in_vq); - /* No buffer? Try to get one. */ - if (!in_len) { - in = in_vq->vq_ops->get_buf(in_vq, &in_len); - if (!in) + /* No more in buffer? See if they've (re)used it. */ + if (port->offset == port->used_len) { + if (!port->in_vq->vq_ops->get_buf(port->in_vq, &port->used_len)) return 0; + port->offset = 0; } /* You want more than we have to give? Well, try wanting less! */ - if (in_len < count) - count = in_len; + if (port->offset + count > port->used_len) + count = port->used_len - port->offset; /* Copy across to their buffer and increment offset. */ - memcpy(buf, in, count); - in += count; - in_len -= count; + memcpy(buf, port->inbuf + port->offset, count); + port->offset += count; /* Finished? Re-register buffer so Host will use it again. */ - if (in_len == 0) - add_inbuf(); + if (port->offset == port->used_len) + add_inbuf(port); return count; } @@ -135,7 +141,7 @@ static void virtcons_apply_config(struct virtio_device *dev) dev->config->get(dev, offsetof(struct virtio_console_config, rows), &ws.ws_row, sizeof(u16)); - hvc_resize(hvc, ws); + hvc_resize(console.hvc, ws); } } @@ -146,7 +152,7 @@ static void virtcons_apply_config(struct virtio_device *dev) static int notifier_add_vio(struct hvc_struct *hp, int data) { hp->irq_requested = 1; - virtcons_apply_config(vdev); + virtcons_apply_config(console.vdev); return 0; } @@ -158,7 +164,7 @@ static void notifier_del_vio(struct hvc_struct *hp, int data) static void hvc_handle_input(struct virtqueue *vq) { - if (hvc_poll(hvc)) + if (hvc_poll(console.hvc)) hvc_kick(); } @@ -197,23 +203,26 @@ int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int)) * Finally we put our input buffer in the input queue, ready to * receive. */ -static int __devinit virtcons_probe(struct virtio_device *dev) +static int __devinit virtcons_probe(struct virtio_device *vdev) { vq_callback_t *callbacks[] = { hvc_handle_input, NULL}; const char *names[] = { "input", "output" }; struct virtqueue *vqs[2]; + struct port *port; int err; - if (vdev) { - dev_warn(&vdev->dev, + port = &console; + if (port->vdev) { + dev_warn(&port->vdev->dev, "Multiple virtio-console devices not supported yet\n"); return -EEXIST; } - vdev = dev; + port->vdev = vdev; /* This is the scratch page we use to receive console input */ - inbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (!inbuf) { + port->used_len = 0; + port->inbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!port->inbuf) { err = -ENOMEM; goto fail; } @@ -223,8 +232,8 @@ static int __devinit virtcons_probe(struct virtio_device *dev) if (err) goto free; - in_vq = vqs[0]; - out_vq = vqs[1]; + port->in_vq = vqs[0]; + port->out_vq = vqs[1]; /* * The first argument of hvc_alloc() is the virtual console @@ -238,14 +247,14 @@ static int __devinit virtcons_probe(struct virtio_device *dev) * pointers. The final argument is the output buffer size: we * can do any size, so we put PAGE_SIZE here. */ - hvc = hvc_alloc(0, 0, &hv_ops, PAGE_SIZE); - if (IS_ERR(hvc)) { - err = PTR_ERR(hvc); + port->hvc = hvc_alloc(0, 0, &hv_ops, PAGE_SIZE); + if (IS_ERR(port->hvc)) { + err = PTR_ERR(port->hvc); goto free_vqs; } /* Register the input buffer the first time. */ - add_inbuf(); + add_inbuf(port); /* Start using the new console output. */ early_put_chars = NULL; @@ -254,7 +263,7 @@ static int __devinit virtcons_probe(struct virtio_device *dev) free_vqs: vdev->config->del_vqs(vdev); free: - kfree(inbuf); + kfree(port->inbuf); fail: return err; } -- cgit v1.2.3 From fdb9a054554e1e435e927c9a47a999f026abd408 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Mon, 18 Jan 2010 19:15:01 +0530 Subject: virtio: console: encapsulate buffer information in a struct Collect port buffer, used_len, offset fields into a single structure. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 70 +++++++++++++++++++++++++++++++++---------- 1 file changed, 55 insertions(+), 15 deletions(-) diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 9ea9223c5c5c..699fc98ec8d9 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -21,12 +21,24 @@ #include #include "hvc_console.h" +struct port_buffer { + char *buf; + + /* size of the buffer in *buf above */ + size_t size; + + /* used length of the buffer */ + size_t len; + /* offset in the buf from which to consume data */ + size_t offset; +}; + struct port { struct virtqueue *in_vq, *out_vq; struct virtio_device *vdev; - /* This is our input buffer, and how much data is left in it. */ - char *inbuf; - unsigned int used_len, offset; + + /* The current buffer from which data has to be fed to readers */ + struct port_buffer *inbuf; /* The hvc device */ struct hvc_struct *hvc; @@ -38,6 +50,33 @@ static struct port console; /* This is the very early arch-specified put chars function. */ static int (*early_put_chars)(u32, const char *, int); +static void free_buf(struct port_buffer *buf) +{ + kfree(buf->buf); + kfree(buf); +} + +static struct port_buffer *alloc_buf(size_t buf_size) +{ + struct port_buffer *buf; + + buf = kmalloc(sizeof(*buf), GFP_KERNEL); + if (!buf) + goto fail; + buf->buf = kzalloc(buf_size, GFP_KERNEL); + if (!buf->buf) + goto free_buf; + buf->len = 0; + buf->offset = 0; + buf->size = buf_size; + return buf; + +free_buf: + kfree(buf); +fail: + return NULL; +} + /* * The put_chars() callback is pretty straightforward. * @@ -79,7 +118,7 @@ static int put_chars(u32 vtermno, const char *buf, int count) static void add_inbuf(struct port *port) { struct scatterlist sg[1]; - sg_init_one(sg, port->inbuf, PAGE_SIZE); + sg_init_one(sg, port->inbuf->buf, PAGE_SIZE); /* Should always be able to add one buffer to an empty queue. */ if (port->in_vq->vq_ops->add_buf(port->in_vq, sg, 0, 1, port) < 0) @@ -98,6 +137,7 @@ static void add_inbuf(struct port *port) static int get_chars(u32 vtermno, char *buf, int count) { struct port *port; + unsigned int len; port = &console; @@ -105,22 +145,23 @@ static int get_chars(u32 vtermno, char *buf, int count) BUG_ON(!port->in_vq); /* No more in buffer? See if they've (re)used it. */ - if (port->offset == port->used_len) { - if (!port->in_vq->vq_ops->get_buf(port->in_vq, &port->used_len)) + if (port->inbuf->offset == port->inbuf->len) { + if (!port->in_vq->vq_ops->get_buf(port->in_vq, &len)) return 0; - port->offset = 0; + port->inbuf->offset = 0; + port->inbuf->len = len; } /* You want more than we have to give? Well, try wanting less! */ - if (port->offset + count > port->used_len) - count = port->used_len - port->offset; + if (port->inbuf->offset + count > port->inbuf->len) + count = port->inbuf->len - port->inbuf->offset; /* Copy across to their buffer and increment offset. */ - memcpy(buf, port->inbuf + port->offset, count); - port->offset += count; + memcpy(buf, port->inbuf->buf + port->inbuf->offset, count); + port->inbuf->offset += count; /* Finished? Re-register buffer so Host will use it again. */ - if (port->offset == port->used_len) + if (port->inbuf->offset == port->inbuf->len) add_inbuf(port); return count; @@ -220,8 +261,7 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) port->vdev = vdev; /* This is the scratch page we use to receive console input */ - port->used_len = 0; - port->inbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); + port->inbuf = alloc_buf(PAGE_SIZE); if (!port->inbuf) { err = -ENOMEM; goto fail; @@ -263,7 +303,7 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) free_vqs: vdev->config->del_vqs(vdev); free: - kfree(port->inbuf); + free_buf(port->inbuf); fail: return err; } -- cgit v1.2.3 From e27b519807e04d950802cb89f7b22933d8d2f837 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Mon, 18 Jan 2010 19:15:02 +0530 Subject: virtio: console: ensure add_inbuf can work for multiple ports as well add_inbuf() assumed one port and one inbuf per port. Remove that assumption. Also move the function so that put_chars and get_chars are together. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 699fc98ec8d9..1dbd46cb1a26 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -77,6 +77,22 @@ fail: return NULL; } +/* + * Create a scatter-gather list representing our input buffer and put + * it in the queue. + * + * Callers should take appropriate locks. + */ +static void add_inbuf(struct virtqueue *vq, struct port_buffer *buf) +{ + struct scatterlist sg[1]; + sg_init_one(sg, buf->buf, buf->size); + + if (vq->vq_ops->add_buf(vq, sg, 0, 1, buf) < 0) + BUG(); + vq->vq_ops->kick(vq); +} + /* * The put_chars() callback is pretty straightforward. * @@ -111,21 +127,6 @@ static int put_chars(u32 vtermno, const char *buf, int count) return count; } -/* - * Create a scatter-gather list representing our input buffer and put - * it in the queue. - */ -static void add_inbuf(struct port *port) -{ - struct scatterlist sg[1]; - sg_init_one(sg, port->inbuf->buf, PAGE_SIZE); - - /* Should always be able to add one buffer to an empty queue. */ - if (port->in_vq->vq_ops->add_buf(port->in_vq, sg, 0, 1, port) < 0) - BUG(); - port->in_vq->vq_ops->kick(port->in_vq); -} - /* * get_chars() is the callback from the hvc_console infrastructure * when an interrupt is received. @@ -162,7 +163,7 @@ static int get_chars(u32 vtermno, char *buf, int count) /* Finished? Re-register buffer so Host will use it again. */ if (port->inbuf->offset == port->inbuf->len) - add_inbuf(port); + add_inbuf(port->in_vq, port->inbuf); return count; } @@ -294,7 +295,7 @@ static int __devinit virtcons_p