#include <linux/virtio_net.h>
#include <linux/scatterlist.h>
+static int csum = 1, gso = 1;
+module_param(csum, bool, 0444);
+module_param(gso, bool, 0444);
+
/* FIXME: MTU in config. */
#define MAX_PACKET_LEN (ETH_HLEN+ETH_DATA_LEN)
sg_init_one(sg, skb_vnet_hdr(skb), sizeof(struct virtio_net_hdr));
}
-static bool skb_xmit_done(struct virtqueue *rvq)
+static void skb_xmit_done(struct virtqueue *rvq)
{
struct virtnet_info *vi = rvq->vdev->priv;
/* In case we were waiting for output buffers. */
netif_wake_queue(vi->dev);
- return true;
}
static void receive_skb(struct net_device *dev, struct sk_buff *skb,
if (hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) {
pr_debug("Needs csum!\n");
- skb->ip_summed = CHECKSUM_PARTIAL;
- skb->csum_start = hdr->csum_start;
- skb->csum_offset = hdr->csum_offset;
- if (skb->csum_start > skb->len - 2
- || skb->csum_offset > skb->len - 2) {
- if (net_ratelimit())
- printk(KERN_WARNING "%s: csum=%u/%u len=%u\n",
- dev->name, skb->csum_start,
- skb->csum_offset, skb->len);
+ if (!skb_partial_csum_set(skb,hdr->csum_start,hdr->csum_offset))
goto frame_err;
- }
}
if (hdr->gso_type != VIRTIO_NET_HDR_GSO_NONE) {
pr_debug("GSO!\n");
- switch (hdr->gso_type) {
+ switch (hdr->gso_type & ~VIRTIO_NET_HDR_GSO_ECN) {
case VIRTIO_NET_HDR_GSO_TCPV4:
skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4;
break;
- case VIRTIO_NET_HDR_GSO_TCPV4_ECN:
- skb_shinfo(skb)->gso_type = SKB_GSO_TCP_ECN;
- break;
case VIRTIO_NET_HDR_GSO_UDP:
skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
break;
goto frame_err;
}
+ if (hdr->gso_type & VIRTIO_NET_HDR_GSO_ECN)
+ skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ECN;
+
skb_shinfo(skb)->gso_size = hdr->gso_size;
if (skb_shinfo(skb)->gso_size == 0) {
if (net_ratelimit())
vi->rvq->vq_ops->kick(vi->rvq);
}
-static bool skb_recv_done(struct virtqueue *rvq)
+static void skb_recv_done(struct virtqueue *rvq)
{
struct virtnet_info *vi = rvq->vdev->priv;
- netif_rx_schedule(vi->dev, &vi->napi);
- /* Suppress further interrupts. */
- return false;
+ /* Schedule NAPI, Suppress further interrupts if successful. */
+ if (netif_rx_schedule_prep(vi->dev, &vi->napi)) {
+ rvq->vq_ops->disable_cb(rvq);
+ __netif_rx_schedule(vi->dev, &vi->napi);
+ }
}
static int virtnet_poll(struct napi_struct *napi, int budget)
if (vi->num < vi->max / 2)
try_fill_recv(vi);
- /* All done? */
- if (!skb) {
+ /* Out of packets? */
+ if (received < budget) {
netif_rx_complete(vi->dev, napi);
- if (unlikely(!vi->rvq->vq_ops->restart(vi->rvq))
+ if (unlikely(!vi->rvq->vq_ops->enable_cb(vi->rvq))
&& netif_rx_reschedule(vi->dev, napi))
goto again;
}
}
if (skb_is_gso(skb)) {
+ hdr->hdr_len = skb_transport_header(skb) - skb->data;
hdr->gso_size = skb_shinfo(skb)->gso_size;
- if (skb_shinfo(skb)->gso_type & SKB_GSO_TCP_ECN)
- hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV4_ECN;
- else if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4)
+ if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4)
hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV4;
else if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6)
hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV6;
hdr->gso_type = VIRTIO_NET_HDR_GSO_UDP;
else
BUG();
+ if (skb_shinfo(skb)->gso_type & SKB_GSO_TCP_ECN)
+ hdr->gso_type |= VIRTIO_NET_HDR_GSO_ECN;
} else {
hdr->gso_type = VIRTIO_NET_HDR_GSO_NONE;
- hdr->gso_size = 0;
+ hdr->gso_size = hdr->hdr_len = 0;
}
vnet_hdr_to_sg(sg, skb);
static int virtnet_probe(struct virtio_device *vdev)
{
int err;
- unsigned int len;
struct net_device *dev;
struct virtnet_info *vi;
- void *token;
/* Allocate ourselves a network device with room for our info */
dev = alloc_etherdev(sizeof(struct virtnet_info));
SET_NETDEV_DEV(dev, &vdev->dev);
/* Do we support "hardware" checksums? */
- token = vdev->config->find(vdev, VIRTIO_CONFIG_NET_F, &len);
- if (virtio_use_bit(vdev, token, len, VIRTIO_NET_F_NO_CSUM)) {
+ if (csum && vdev->config->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 (virtio_use_bit(vdev, token, len, VIRTIO_NET_F_TSO4))
- dev->features |= NETIF_F_TSO;
- if (virtio_use_bit(vdev, token, len, VIRTIO_NET_F_UFO))
- dev->features |= NETIF_F_UFO;
- if (virtio_use_bit(vdev, token, len, VIRTIO_NET_F_TSO4_ECN))
- dev->features |= NETIF_F_TSO_ECN;
- if (virtio_use_bit(vdev, token, len, VIRTIO_NET_F_TSO6))
- dev->features |= NETIF_F_TSO6;
+ if (gso && vdev->config->feature(vdev, VIRTIO_NET_F_GSO)) {
+ dev->features |= NETIF_F_TSO | NETIF_F_UFO
+ | NETIF_F_TSO_ECN | NETIF_F_TSO6;
+ }
}
/* Configuration may specify what MAC to use. Otherwise random. */
- token = vdev->config->find(vdev, VIRTIO_CONFIG_NET_MAC_F, &len);
- if (token) {
- dev->addr_len = len;
- vdev->config->get(vdev, token, dev->dev_addr, len);
+ if (vdev->config->feature(vdev, VIRTIO_NET_F_MAC)) {
+ vdev->config->get(vdev,
+ offsetof(struct virtio_net_config, mac),
+ dev->dev_addr, dev->addr_len);
} else
random_ether_addr(dev->dev_addr);
vi->vdev = vdev;
/* We expect two virtqueues, receive then send. */
- vi->rvq = vdev->config->find_vq(vdev, skb_recv_done);
+ vi->rvq = vdev->config->find_vq(vdev, 0, skb_recv_done);
if (IS_ERR(vi->rvq)) {
err = PTR_ERR(vi->rvq);
goto free;
}
- vi->svq = vdev->config->find_vq(vdev, skb_xmit_done);
+ vi->svq = vdev->config->find_vq(vdev, 1, skb_xmit_done);
if (IS_ERR(vi->svq)) {
err = PTR_ERR(vi->svq);
goto free_recv;
static void virtnet_remove(struct virtio_device *vdev)
{
- unregister_netdev(vdev->priv);
- free_netdev(vdev->priv);
+ struct virtnet_info *vi = vdev->priv;
+
+ vdev->config->del_vq(vi->svq);
+ vdev->config->del_vq(vi->rvq);
+ unregister_netdev(vi->dev);
+ free_netdev(vi->dev);
}
static struct virtio_device_id id_table[] = {