diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/block/virtio_blk.c | 44 | ||||
-rw-r--r-- | drivers/lguest/lguest_device.c | 68 | ||||
-rw-r--r-- | drivers/lguest/lguest_user.c | 4 | ||||
-rw-r--r-- | drivers/net/virtio_net.c | 96 | ||||
-rw-r--r-- | drivers/virtio/virtio.c | 38 | ||||
-rw-r--r-- | drivers/virtio/virtio_balloon.c | 12 | ||||
-rw-r--r-- | drivers/virtio/virtio_pci.c | 34 | ||||
-rw-r--r-- | drivers/virtio/virtio_ring.c | 5 |
8 files changed, 208 insertions, 93 deletions
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index 0cfbe8c594a5..84e064ffee52 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -35,7 +35,7 @@ struct virtblk_req struct list_head list; struct request *req; struct virtio_blk_outhdr out_hdr; - struct virtio_blk_inhdr in_hdr; + u8 status; }; static void blk_done(struct virtqueue *vq) @@ -48,7 +48,7 @@ static void blk_done(struct virtqueue *vq) spin_lock_irqsave(&vblk->lock, flags); while ((vbr = vblk->vq->vq_ops->get_buf(vblk->vq, &len)) != NULL) { int uptodate; - switch (vbr->in_hdr.status) { + switch (vbr->status) { case VIRTIO_BLK_S_OK: uptodate = 1; break; @@ -101,7 +101,7 @@ static bool do_req(struct request_queue *q, struct virtio_blk *vblk, sg_init_table(vblk->sg, VIRTIO_MAX_SG); sg_set_buf(&vblk->sg[0], &vbr->out_hdr, sizeof(vbr->out_hdr)); num = blk_rq_map_sg(q, vbr->req, vblk->sg+1); - sg_set_buf(&vblk->sg[num+1], &vbr->in_hdr, sizeof(vbr->in_hdr)); + sg_set_buf(&vblk->sg[num+1], &vbr->status, sizeof(vbr->status)); if (rq_data_dir(vbr->req) == WRITE) { vbr->out_hdr.type |= VIRTIO_BLK_T_OUT; @@ -157,10 +157,25 @@ static int virtblk_ioctl(struct inode *inode, struct file *filp, /* We provide getgeo only to please some old bootloader/partitioning tools */ static int virtblk_getgeo(struct block_device *bd, struct hd_geometry *geo) { - /* some standard values, similar to sd */ - geo->heads = 1 << 6; - geo->sectors = 1 << 5; - geo->cylinders = get_capacity(bd->bd_disk) >> 11; + struct virtio_blk *vblk = bd->bd_disk->private_data; + struct virtio_blk_geometry vgeo; + int err; + + /* see if the host passed in geometry config */ + err = virtio_config_val(vblk->vdev, VIRTIO_BLK_F_GEOMETRY, + offsetof(struct virtio_blk_config, geometry), + &vgeo); + + if (!err) { + geo->heads = vgeo.heads; + geo->sectors = vgeo.sectors; + geo->cylinders = vgeo.cylinders; + } else { + /* some standard values, similar to sd */ + geo->heads = 1 << 6; + geo->sectors = 1 << 5; + geo->cylinders = get_capacity(bd->bd_disk) >> 11; + } return 0; } @@ -242,12 +257,12 @@ static int virtblk_probe(struct virtio_device *vdev) index++; /* If barriers are supported, tell block layer that queue is ordered */ - if (vdev->config->feature(vdev, VIRTIO_BLK_F_BARRIER)) + if (virtio_has_feature(vdev, VIRTIO_BLK_F_BARRIER)) blk_queue_ordered(vblk->disk->queue, QUEUE_ORDERED_TAG, NULL); /* Host must always specify the capacity. */ - __virtio_config_val(vdev, offsetof(struct virtio_blk_config, capacity), - &cap); + vdev->config->get(vdev, offsetof(struct virtio_blk_config, capacity), + &cap, sizeof(cap)); /* If capacity is too big, truncate with warning. */ if ((sector_t)cap != cap) { @@ -289,7 +304,6 @@ out: static void virtblk_remove(struct virtio_device *vdev) { struct virtio_blk *vblk = vdev->priv; - int major = vblk->disk->major; /* Nothing should be pending. */ BUG_ON(!list_empty(&vblk->reqs)); @@ -299,7 +313,6 @@ static void virtblk_remove(struct virtio_device *vdev) blk_cleanup_queue(vblk->disk->queue); put_disk(vblk->disk); - unregister_blkdev(major, "virtblk"); mempool_destroy(vblk->pool); vdev->config->del_vq(vblk->vq); kfree(vblk); @@ -310,7 +323,14 @@ static struct virtio_device_id id_table[] = { { 0 }, }; +static unsigned int features[] = { + VIRTIO_BLK_F_BARRIER, VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_SIZE_MAX, + VIRTIO_BLK_F_GEOMETRY, +}; + static struct virtio_driver virtio_blk = { + .feature_table = features, + .feature_table_size = ARRAY_SIZE(features), .driver.name = KBUILD_MODNAME, .driver.owner = THIS_MODULE, .id_table = id_table, diff --git a/drivers/lguest/lguest_device.c b/drivers/lguest/lguest_device.c index 2bc9bf7e88e5..8080249957af 100644 --- a/drivers/lguest/lguest_device.c +++ b/drivers/lguest/lguest_device.c @@ -85,27 +85,34 @@ static unsigned desc_size(const struct lguest_device_desc *desc) + desc->config_len; } -/* This tests (and acknowleges) a feature bit. */ -static bool lg_feature(struct virtio_device *vdev, unsigned fbit) +/* This gets the device's feature bits. */ +static u32 lg_get_features(struct virtio_device *vdev) { + unsigned int i; + u32 features = 0; struct lguest_device_desc *desc = to_lgdev(vdev)->desc; - u8 *features; - - /* Obviously if they ask for a feature off the end of our feature - * bitmap, it's not set. */ - if (fbit / 8 > desc->feature_len) - return false; - - /* The feature bitmap comes after the virtqueues. */ - features = lg_features(desc); - if (!(features[fbit / 8] & (1 << (fbit % 8)))) - return false; - - /* We set the matching bit in the other half of the bitmap to tell the - * Host we want to use this feature. We don't use this yet, but we - * could in future. */ - features[desc->feature_len + fbit / 8] |= (1 << (fbit % 8)); - return true; + u8 *in_features = lg_features(desc); + + /* We do this the slow but generic way. */ + for (i = 0; i < min(desc->feature_len * 8, 32); i++) + if (in_features[i / 8] & (1 << (i % 8))) + features |= (1 << i); + + return features; +} + +static void lg_set_features(struct virtio_device *vdev, u32 features) +{ + unsigned int i; + struct lguest_device_desc *desc = to_lgdev(vdev)->desc; + /* Second half of bitmap is features we accept. */ + u8 *out_features = lg_features(desc) + desc->feature_len; + + memset(out_features, 0, desc->feature_len); + for (i = 0; i < min(desc->feature_len * 8, 32); i++) { + if (features & (1 << i)) + out_features[i / 8] |= (1 << (i % 8)); + } } /* Once they've found a field, getting a copy of it is easy. */ @@ -137,20 +144,26 @@ static u8 lg_get_status(struct virtio_device *vdev) return to_lgdev(vdev)->desc->status; } +/* To notify on status updates, we (ab)use the NOTIFY hypercall, with the + * descriptor address of the device. A zero status means "reset". */ +static void set_status(struct virtio_device *vdev, u8 status) +{ + unsigned long offset = (void *)to_lgdev(vdev)->desc - lguest_devices; + + /* We set the status. */ + to_lgdev(vdev)->desc->status = status; + hcall(LHCALL_NOTIFY, (max_pfn<<PAGE_SHIFT) + offset, 0, 0); +} + static void lg_set_status(struct virtio_device *vdev, u8 status) { BUG_ON(!status); - to_lgdev(vdev)->desc->status = status; + set_status(vdev, status); } -/* To reset the device, we (ab)use the NOTIFY hypercall, with the descriptor - * address of the device. The Host will zero the status and all the - * features. */ static void lg_reset(struct virtio_device *vdev) { - unsigned long offset = (void *)to_lgdev(vdev)->desc - lguest_devices; - - hcall(LHCALL_NOTIFY, (max_pfn<<PAGE_SHIFT) + offset, 0, 0); + set_status(vdev, 0); } /* @@ -286,7 +299,8 @@ static void lg_del_vq(struct virtqueue *vq) /* The ops structure which hooks everything together. */ static struct virtio_config_ops lguest_config_ops = { - .feature = lg_feature, + .get_features = lg_get_features, + .set_features = lg_set_features, .get = lg_get, .set = lg_set, .get_status = lg_get_status, diff --git a/drivers/lguest/lguest_user.c b/drivers/lguest/lguest_user.c index 645e6e040bfb..e73a000473cc 100644 --- a/drivers/lguest/lguest_user.c +++ b/drivers/lguest/lguest_user.c @@ -102,7 +102,7 @@ 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. */ - if (id >= NR_CPUS) + if (id >= ARRAY_SIZE(cpu->lg->cpus)) return -EINVAL; /* Set up this CPU's id, and pointer back to the lguest struct. */ @@ -251,8 +251,6 @@ static ssize_t write(struct file *file, const char __user *in, if (!lg || (cpu_id >= lg->nr_cpus)) return -EINVAL; cpu = &lg->cpus[cpu_id]; - if (!cpu) - return -EINVAL; /* Once the Guest is dead, you can only read() why it died. */ if (lg->dead) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 555b70c8b863..f926b5ab3d09 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -41,6 +41,9 @@ struct virtnet_info struct net_device *dev; struct napi_struct napi; + /* The skb we couldn't send because buffers were full. */ + struct sk_buff *last_xmit_skb; + /* Number of input buffers, and max we've ever had. */ unsigned int num, max; @@ -142,10 +145,10 @@ drop: static void try_fill_recv(struct virtnet_info *vi) { struct sk_buff *skb; - struct scatterlist sg[1+MAX_SKB_FRAGS]; + struct scatterlist sg[2+MAX_SKB_FRAGS]; int num, err; - sg_init_table(sg, 1+MAX_SKB_FRAGS); + sg_init_table(sg, 2+MAX_SKB_FRAGS); for (;;) { skb = netdev_alloc_skb(vi->dev, MAX_PACKET_LEN); if (unlikely(!skb)) @@ -221,23 +224,22 @@ static void free_old_xmit_skbs(struct virtnet_info *vi) while ((skb = vi->svq->vq_ops->get_buf(vi->svq, &len)) != NULL) { pr_debug("Sent skb %p\n", skb); __skb_unlink(skb, &vi->send); - vi->dev->stats.tx_bytes += len; + vi->dev->stats.tx_bytes += skb->len; vi->dev->stats.tx_packets++; kfree_skb(skb); } } -static int start_xmit(struct sk_buff *skb, struct net_device *dev) +static int xmit_skb(struct virtnet_info *vi, struct sk_buff *skb) { - struct virtnet_info *vi = netdev_priv(dev); - int num, err; - struct scatterlist sg[1+MAX_SKB_FRAGS]; + int num; + struct scatterlist sg[2+MAX_SKB_FRAGS]; struct virtio_net_hdr *hdr; const unsigned char *dest = ((struct ethhdr *)skb->data)->h_dest; - sg_init_table(sg, 1+MAX_SKB_FRAGS); + sg_init_table(sg, 2+MAX_SKB_FRAGS); - pr_debug("%s: xmit %p " MAC_FMT "\n", dev->name, skb, + pr_debug("%s: xmit %p " MAC_FMT "\n", vi->dev->name, skb, dest[0], dest[1], dest[2], dest[3], dest[4], dest[5]); @@ -272,30 +274,51 @@ static int start_xmit(struct sk_buff *skb, struct net_device *dev) vnet_hdr_to_sg(sg, skb); num = skb_to_sgvec(skb, sg+1, 0, skb->len) + 1; - __skb_queue_head(&vi->send, skb); + + return vi->svq->vq_ops->add_buf(vi->svq, sg, num, 0, skb); +} + +static int start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct virtnet_info *vi = netdev_priv(dev); again: /* Free up any pending old buffers before queueing new ones. */ free_old_xmit_skbs(vi); - err = vi->svq->vq_ops->add_buf(vi->svq, sg, num, 0, skb); - if (err) { - pr_debug("%s: virtio not prepared to send\n", dev->name); - netif_stop_queue(dev); - - /* Activate callback for using skbs: if this returns false it - * means some were used in the meantime. */ - if (unlikely(!vi->svq->vq_ops->enable_cb(vi->svq))) { - vi->svq->vq_ops->disable_cb(vi->svq); - netif_start_queue(dev); - goto again; + + /* If we has a buffer left over from last time, send it now. */ + if (vi->last_xmit_skb) { + if (xmit_skb(vi, vi->last_xmit_skb) != 0) { + /* Drop this skb: we only queue one. */ + vi->dev->stats.tx_dropped++; + kfree_skb(skb); + goto stop_queue; } - __skb_unlink(skb, &vi->send); + vi->last_xmit_skb = NULL; + } - return NETDEV_TX_BUSY; + /* Put new one in send queue and do transmit */ + __skb_queue_head(&vi->send, skb); + if (xmit_skb(vi, skb) != 0) { + vi->last_xmit_skb = skb; + goto stop_queue; } +done: vi->svq->vq_ops->kick(vi->svq); - - return 0; + return NETDEV_TX_OK; + +stop_queue: + pr_debug("%s: virtio not prepared to send\n", dev->name); + netif_stop_queue(dev); + + /* Activate callback for using skbs: if this returns false it + * means some were used in the meantime. */ + if (unlikely(!vi->svq->vq_ops->enable_cb(vi->svq))) { + vi->svq->vq_ops->disable_cb(vi->svq); + netif_start_queue(dev); + goto again; + } + goto done; } #ifdef CONFIG_NET_POLL_CONTROLLER @@ -355,17 +378,26 @@ static int virtnet_probe(struct virtio_device *vdev) SET_NETDEV_DEV(dev, &vdev->dev); /* Do we support "hardware" checksums? */ - if (csum && vdev->config->feature(vdev, VIRTIO_NET_F_CSUM)) { + if (csum && virtio_has_feature(vdev, VIRTIO_NET_F_CSUM)) { /* This opens up the world of extra features. */ dev->features |= NETIF_F_HW_CSUM|NETIF_F_SG|NETIF_F_FRAGLIST; - if (gso && vdev->config->feature(vdev, VIRTIO_NET_F_GSO)) { + if (gso && virtio_has_feature(vdev, VIRTIO_NET_F_GSO)) { dev->features |= NETIF_F_TSO | NETIF_F_UFO | NETIF_F_TSO_ECN | NETIF_F_TSO6; } + /* Individual feature bits: what can host handle? */ + if (gso && virtio_has_feature(vdev, VIRTIO_NET_F_HOST_TSO4)) + dev->features |= NETIF_F_TSO; + if (gso && virtio_has_feature(vdev, VIRTIO_NET_F_HOST_TSO6)) + dev->features |= NETIF_F_TSO6; + if (gso && virtio_has_feature(vdev, VIRTIO_NET_F_HOST_ECN)) + dev->features |= NETIF_F_TSO_ECN; + if (gso && virtio_has_feature(vdev, VIRTIO_NET_F_HOST_UFO)) + dev->features |= NETIF_F_UFO; } /* Configuration may specify what MAC to use. Otherwise random. */ - if (vdev->config->feature(vdev, VIRTIO_NET_F_MAC)) { + if (virtio_has_feature(vdev, VIRTIO_NET_F_MAC)) { vdev->config->get(vdev, offsetof(struct virtio_net_config, mac), dev->dev_addr, dev->addr_len); @@ -454,7 +486,15 @@ static struct virtio_device_id id_table[] = { { 0 }, }; +static unsigned int features[] = { + VIRTIO_NET_F_CSUM, VIRTIO_NET_F_GSO, VIRTIO_NET_F_MAC, + VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_HOST_TSO6, + VIRTIO_NET_F_HOST_ECN, +}; + static struct virtio_driver virtio_net = { + .feature_table = features, + .feature_table_size = ARRAY_SIZE(features), .driver.name = KBUILD_MODNAME, .driver.owner = THIS_MODULE, .id_table = id_table, diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c index b535483bc556..13866789b356 100644 --- a/drivers/virtio/virtio.c +++ b/drivers/virtio/virtio.c @@ -80,19 +80,51 @@ static void add_status(struct virtio_device *dev, unsigned status) dev->config->set_status(dev, dev->config->get_status(dev) | status); } +void virtio_check_driver_offered_feature(const struct virtio_device *vdev, + unsigned int fbit) +{ + unsigned int i; + struct virtio_driver *drv = container_of(vdev->dev.driver, + struct virtio_driver, driver); + + for (i = 0; i < drv->feature_table_size; i++) + if (drv->feature_table[i] == fbit) + return; + BUG(); +} +EXPORT_SYMBOL_GPL(virtio_check_driver_offered_feature); + static int virtio_dev_probe(struct device *_d) { - int err; + int err, i; struct virtio_device *dev = container_of(_d,struct virtio_device,dev); struct virtio_driver *drv = container_of(dev->dev.driver, struct virtio_driver, driver); + u32 device_features; + /* We have a driver! */ add_status(dev, VIRTIO_CONFIG_S_DRIVER); + + /* Figure out what features the device supports. */ + device_features = dev->config->get_features(dev); + + /* Features supported by both device and driver into dev->features. */ + memset(dev->features, 0, sizeof(dev->features)); + for (i = 0; i < drv->feature_table_size; i++) { + unsigned int f = drv->feature_table[i]; + BUG_ON(f >= 32); + if (device_features & (1 << f)) + set_bit(f, dev->features); + } + err = drv->probe(dev); if (err) add_status(dev, VIRTIO_CONFIG_S_FAILED); - else + else { add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK); + /* They should never have set feature bits beyond 32 */ + dev->config->set_features(dev, dev->features[0]); + } return err; } @@ -114,6 +146,8 @@ static int virtio_dev_remove(struct device *_d) int register_virtio_driver(struct virtio_driver *driver) { + /* Catch this early. */ + BUG_ON(driver->feature_table_size && !driver->feature_table); driver->driver.bus = &virtio_bus; driver->driver.probe = virtio_dev_probe; driver->driver.remove = virtio_dev_remove; diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c index 0b3efc31ee6d..bfef604160d1 100644 --- a/drivers/virtio/virtio_balloon.c +++ b/drivers/virtio/virtio_balloon.c @@ -155,9 +155,9 @@ static void virtballoon_changed(struct virtio_device *vdev) static inline s64 towards_target(struct virtio_balloon *vb) { u32 v; - __virtio_config_val(vb->vdev, - offsetof(struct virtio_balloon_config, num_pages), - &v); + vb->vdev->config->get(vb->vdev, + offsetof(struct virtio_balloon_config, num_pages), + &v, sizeof(v)); return v - vb->num_pages; } @@ -227,7 +227,7 @@ static int virtballoon_probe(struct virtio_device *vdev) } vb->tell_host_first - = vdev->config->feature(vdev, VIRTIO_BALLOON_F_MUST_TELL_HOST); + = virtio_has_feature(vdev, VIRTIO_BALLOON_F_MUST_TELL_HOST); return 0; @@ -259,7 +259,11 @@ static void virtballoon_remove(struct virtio_device *vdev) kfree(vb); } +static unsigned int features[] = { VIRTIO_BALLOON_F_MUST_TELL_HOST }; + static struct virtio_driver virtio_balloon = { + .feature_table = features, + .feature_table_size = ARRAY_SIZE(features), .driver.name = KBUILD_MODNAME, .driver.owner = THIS_MODULE, .id_table = id_table, diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c index c0df924766a7..27e9fc9117cd 100644 --- a/drivers/virtio/virtio_pci.c +++ b/drivers/virtio/virtio_pci.c @@ -87,23 +87,22 @@ static struct virtio_pci_device *to_vp_device(struct virtio_device *vdev) return container_of(vdev, struct virtio_pci_device, vdev); } -/* virtio config->feature() implementation */ -static bool vp_feature(struct virtio_device *vdev, unsigned bit) +/* virtio config->get_features() implementation */ +static u32 vp_get_features(struct virtio_device *vdev) +{ + struct virtio_pci_device *vp_dev = to_vp_device(vdev); + + /* When someone needs more than 32 feature bits, we'll need to + * steal a bit to indicate that the rest are somewhere else. */ + return ioread32(vp_dev->ioaddr + VIRTIO_PCI_HOST_FEATURES); +} + +/* virtio config->set_features() implementation */ +static void vp_set_features(struct virtio_device *vdev, u32 features) { struct virtio_pci_device *vp_dev = to_vp_device(vdev); - u32 mask; - - /* Since this function is supposed to have the side effect of - * enabling a queried feature, we simulate that by doing a read - * from the host feature bitmask and then writing to the guest - * feature bitmask */ - mask = ioread32(vp_dev->ioaddr + VIRTIO_PCI_HOST_FEATURES); - if (mask & (1 << bit)) { - mask |= (1 << bit); - iowrite32(mask, vp_dev->ioaddr + VIRTIO_PCI_GUEST_FEATURES); - } - return !!(mask & (1 << bit)); + iowrite32(features, vp_dev->ioaddr + VIRTIO_PCI_GUEST_FEATURES); } /* virtio config->get() implementation */ @@ -145,14 +144,14 @@ static void vp_set_status(struct virtio_device *vdev, u8 status) struct virtio_pci_device *vp_dev = to_vp_device(vdev); /* We should never be setting status to 0. */ BUG_ON(status == 0); - return iowrite8(status, vp_dev->ioaddr + VIRTIO_PCI_STATUS); + iowrite8(status, vp_dev->ioaddr + VIRTIO_PCI_STATUS); } static void vp_reset(struct virtio_device *vdev) { struct virtio_pci_device *vp_dev = to_vp_device(vdev); /* 0 status means a reset. */ - return iowrite8(0, vp_dev->ioaddr + VIRTIO_PCI_STATUS); + iowrite8(0, vp_dev->ioaddr + VIRTIO_PCI_STATUS); } /* the notify function used when creating a virt queue */ @@ -293,7 +292,6 @@ static void vp_del_vq(struct virtqueue *vq) } static struct virtio_config_ops virtio_pci_config_ops = { - .feature = vp_feature, .get = vp_get, .set = vp_set, .get_status = vp_get_status, @@ -301,6 +299,8 @@ static struct virtio_config_ops virtio_pci_config_ops = { .reset = vp_reset, .find_vq = vp_find_vq, .del_vq = vp_del_vq, + .get_features = vp_get_features, + .set_features = vp_set_features, }; /* the PCI probing function */ diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index c2fa5c630813..937a49d6772c 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -184,6 +184,11 @@ static void *vring_get_buf(struct virtqueue *_vq, unsigned int *len) START_USE(vq); + if (unlikely(vq->broken)) { + END_USE(vq); + return NULL; + } + if (!more_used(vq)) { pr_debug("No more buffers in queue\n"); END_USE(vq); |