virtio-net

源码解析

这里我们就介绍两个源文件:

1
2
./unikraft/plat/drivers/include/virtio/virtio_net.h
./unikraft/plat/drivers/virtio/virtio_net.c

头文件virtio_net.h

这部分其实和unikraft本身关系不大,主要用于了解virtio网络设备相关背景

上来首先介绍了virtio的feature bitmap.

Feature bits

定义了 Guest 和 Host 支持的功能,例如 VIRTIO_NET_F_CSUM bit 表示网络设备是否支持 checksum offload。feature bits 机制提供了未来扩充功能的灵活性,以及兼容旧设备的能力。

这里是涉及到网络环境涉及的超参数宏

  • VIRTIO_NET_F_CSUM (0) 设备进行部分校验数据包。“checksum offload”是现代网卡的常见功能。
  • VIRTIO_NET_F_GUEST_CSUM (1) 驱动进行部分校验数据包。
  • VIRTIO_NET_F_CTRL_GUEST_OFFLOADS (2) 没有代码使用过,offload配置通道,好像和 XDP LRO/CSUM 相关。Control channel offloads reconfiguration support.Dynamic offload configuration。
  • VIRTIO_NET_F_MAC (5) 设备有指定的MAC
  • VIRTIO_NET_F_GUEST_TSO4 (7) 驱动支持TSOv4
  • VIRTIO_NET_F_GUEST_TSO6 (8) 驱动支持 TSOv6
  • VIRTIO_NET_F_GUEST_ECN (9) 驱动支持带有ECN 的 TSO
  • VIRTIO_NET_F_GUEST_UFO (10) 驱动支持 UFO. VIRTIO_NET_F_HOST_TSO4 (11) 设备支持 TSOv4.
  • VIRTIO_NET_F_HOST_TSO6 (12) 设备支持 TSOv6.
  • VIRTIO_NET_F_HOST_ECN (13) 设备支持有 ECN 的 TSO
  • VIRTIO_NET_F_HOST_UFO (14) 设备支持 UFO
  • VIRTIO_NET_F_MRG_RXBUF (15) 驱动支持 Merge Buffer
  • VIRTIO_NET_F_STATUS (16) 支持Configuration status 域,config change有关
  • VIRTIO_NET_F_CTRL_VQ (17) 支持Control queue
  • VIRTIO_NET_F_CTRL_RX (18) 支持 通过Control queue 设置 RX mode,如混杂模式,组播,广播等
  • VIRTIO_NET_F_CTRL_VLAN (19) Control queue 设置vlan过滤表
  • VIRTIO_NET_F_GUEST_ANNOUNCE(21) 驱动支持发送 gratuitous packets(Gratuitous ARP)
  • VIRTIO_NET_F_MQ(22) 设备支持多队列,并且RR方式接收报文
  • VIRTIO_NET_F_CTRL_MAC_ADDR(23) 支持通过Control Queue设置MAC地址

里面涉及的概念补充如下:

MTU:最大传输单元(Maximum Transmission Unit,MTU)用来通知对方所能接受数据服务单元的最大尺寸,说明发送方能够接受的有效载荷大小。

ECN:显式拥塞通知Explicit Congestion Notification(ECN)是TCP/IP协议的扩展,在RFC 3168 (2001) 中进行了定义。ECN支持端到端的网络拥塞通知。

virtio_net_config

这个数据结构主要管理configuration布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
struct virtio_net_config {
/* The config defining mac address (if VIRTIO_NET_F_MAC) */
__u8 mac[UK_NETDEV_HWADDR_LEN]; // 物理地址
/* See VIRTIO_NET_F_STATUS and VIRTIO_NET_S_* above */
__u16 status; // virtio_net_config.status available,主要是关于设备是否可用
/* Maximum number of each of transmit and receive queues;
* see VIRTIO_NET_F_MQ and VIRTIO_NET_CTRL_MQ.
* Legal values are between 1 and 0x8000
*/

/* Receive Flow
Steering,字段指定在协商VIRTIO_NET_F_MQ后可以配置的传输和接收VirtQueue
分别为receiveq1…receiveqN和transmitq1…transmitqN的最大数量

*/
__u16 max_virtqueue_pairs;
/* Default maximum transmit unit advice */
__u16 mtu; // 网络能够传输的最大数据包大小,以字节为单位
/*
* speed, in units of 1Mb. All values 0 to INT_MAX are legal.
* Any other value stands for unknown.
*/
__u32 speed;
/*
* 0x00 - half duplex
* 0x01 - full duplex
* Any other value stands for unknown.
*/
__u8 duplex; // 全双工和半双工通信
} __packed;

后面都是定义一些前后端交互时报文的限制条件,我们在具体实现中进行讨论

2.2 virtio_net.c

这部分主要解析和uknetdev的关系

在uknetdev中曾经定义了网络驱动设备的接收和传输队列,但是没有实现,在virtio_net.c进行了明确定义。

1
2
3
4
5
6
7
/**
* @internal Queue structs that are defined internally by each driver
* The datatype is introduced here for having type checking on the
* API code
*/
struct uk_netdev_tx_queue;
struct uk_netdev_rx_queue;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* @internal structure to represent the transmit queue.
*/
struct uk_netdev_tx_queue {
/* The virtqueue reference */
struct virtqueue *vq; // 前后端通信队列
/* The hw queue identifier */
uint16_t hwvq_id; // hw = hardware
/* The user queue identifier */
uint16_t lqueue_id;
/* The nr. of descriptor limit */
uint16_t max_nb_desc;
/* The nr. of descriptor user configured */
uint16_t nb_desc;
/* The flag to interrupt on the transmit queue */
uint8_t intr_enabled;
/* Reference to the uk_netdev */
struct uk_netdev *ndev;
/* The scatter list and its associated fragements */
struct uk_sglist sg; // sg = signal ? 信号量对应的语义片段
struct uk_sglist_seg sgsegs[NET_MAX_FRAGMENTS];
};
  • virtio_net_device
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
struct virtio_net_device {
/* Virtio Device */
struct virtio_dev *vdev;
/* List of all the virtqueue in the pci device */
struct virtqueue *vq;
struct uk_netdev netdev;
/* Count of the number of the virtqueues */
__u16 max_vqueue_pairs;
/* List of the Rx/Tx queue 维护网络设备的消息传输和接收队列*/
__u16 rx_vqueue_cnt;
struct uk_netdev_rx_queue *rxqs;
__u16 tx_vqueue_cnt;
struct uk_netdev_tx_queue *txqs;
/* The netdevice identifier */
__u16 uid;
/* The max mtu */
__u16 max_mtu;
/* The mtu */
__u16 mtu;
/* The hw address of the netdevice */
struct uk_hwaddr hw_addr; // 网络硬件地址
/* Netdev state */
__u8 state; // 在netdev_core.h中有定义uk_netdev_state
/* RX promiscuous mode. */
__u8 promisc : 1;
};
  • virtio_netdev_recv 这里定义了一个rx_one的函数,提前放上来说
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    static int virtio_netdev_recv(struct uk_netdev *dev __unused,
    struct uk_netdev_rx_queue *queue,
    struct uk_netbuf **pkt)
    {
    int status = 0x0;
    int rc = 0;

    UK_ASSERT(dev && queue);
    UK_ASSERT(pkt);

    /* Queue interrupts have to be off when calling receive */
    UK_ASSERT(!(queue->intr_enabled & VTNET_INTR_EN));

    rc = virtio_netdev_rxq_dequeue(queue, pkt);
    if (unlikely(rc < 0)) {
    uk_pr_err("Failed to dequeue the packet: %d\n", rc);
    goto err_exit;
    }
    // 检验报文接收状态
    status |= (*pkt) ? UK_NETDEV_STATUS_SUCCESS : 0x0;
    status |= virtio_netdev_rx_fillup(queue, (queue->nb_desc - rc), 1);

    /* Enable interrupt only when user had previously enabled it */
    if (queue->intr_enabled & VTNET_INTR_USR_EN_MASK) {
    /* Need to enable the interrupt on the last packet */
    rc = virtqueue_intr_enable(queue->vq);
    if (rc == 1 && !(*pkt)) {
    /**
    * Packet arrive after reading the queue and before
    * enabling the interrupt
    */

    // 报文到达时间在开启中断之前到达,在执行read操作以后,再读
    rc = virtio_netdev_rxq_dequeue(queue, pkt);
    if (unlikely(rc < 0)) {
    uk_pr_err("Failed to dequeue the packet: %d\n",
    rc);
    goto err_exit;
    }
    status |= UK_NETDEV_STATUS_SUCCESS;

    /*
    * Since we received something, we need to fillup
    * and notify
    */
    status |= virtio_netdev_rx_fillup(queue,
    (queue->nb_desc - rc),
    1);

    /* Need to enable the interrupt on the last packet */
    rc = virtqueue_intr_enable(queue->vq);
    status |= (rc == 1) ? UK_NETDEV_STATUS_MORE : 0x0;
    } else if (*pkt) {
    /* When we originally got a packet and there is more */
    status |= (rc == 1) ? UK_NETDEV_STATUS_MORE : 0x0;
    }
    } else if (*pkt) {
    /**
    * For polling case, we report always there are further
    * packets unless the queue is empty.
    */
    status |= UK_NETDEV_STATUS_MORE;
    }
    return status;

    err_exit:
    UK_ASSERT(rc < 0);
    return rc;
    }

virtio_netdev操作

这里函数主要分成两类,一类是virtio_net_*主要是virtio层面设备的管理,信息获取等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static int virtio_net_drv_init(struct uk_alloc *drv_allocator);
static int virtio_net_add_dev(struct virtio_dev *vdev);
static void virtio_net_info_get(struct uk_netdev *dev,
struct uk_netdev_info *dev_info);

// Disable interrupts on the virtqueue.
static int virtio_net_rx_intr_disable(struct uk_netdev *n,
struct uk_netdev_rx_queue *queue);
static int virtio_net_rx_intr_enable(struct uk_netdev *n,
struct uk_netdev_rx_queue *queue);

static const struct uk_hwaddr *virtio_net_mac_get(struct uk_netdev *n);
static __u16 virtio_net_mtu_get(struct uk_netdev *n);
static unsigned virtio_net_promisc_get(struct uk_netdev *n);

这里就举一个添加设备的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
static int virtio_net_add_dev(struct virtio_dev *vdev)
{
struct virtio_net_device *vndev; // 前端设备
int rc = 0;

UK_ASSERT(vdev != NULL);

vndev = uk_calloc(a, 1, sizeof(*vndev));
if (!vndev) {
rc = -ENOMEM;
goto err_out;
}
vndev->vdev = vdev;
/* register netdev */
vndev->netdev.rx_one = virtio_netdev_recv; // 接收到的packet
vndev->netdev.tx_one = virtio_netdev_xmit; // 传输packet
vndev->netdev.ops = &virtio_netdev_ops; // 驱动设备行为

rc = uk_netdev_drv_register(&vndev->netdev, a, drv_name); // 返回一个唯一注册id
if (rc < 0) {
uk_pr_err("Failed to register virtio-net device with libuknet\n");
goto err_netdev_data;
}
vndev->uid = rc;
rc = 0;
vndev->max_mtu = UK_ETH_PAYLOAD_MAXLEN;
vndev->mtu = vndev->max_mtu;
vndev->promisc = 0;

/**
* TODO:
* Adding multiqueue support for the virtio net driver.
*/
vndev->max_vqueue_pairs = 1;
uk_pr_debug("virtio-net device registered with libuknet\n");

exit:
return rc;
err_netdev_data:
uk_free(a, vndev);
err_out:
goto exit;
}

virtio_netdev_*

直接和netdev扯上关系的部分的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
static int virtio_netdev_configure(struct uk_netdev *n,
const struct uk_netdev_conf *conf);

static int virtio_netdev_rxtx_alloc(struct virtio_net_device *vndev,
const struct uk_netdev_conf *conf);
static int virtio_netdev_feature_negotiate(struct uk_netdev *n);
static struct uk_netdev_tx_queue *virtio_netdev_tx_queue_setup(
struct uk_netdev *n, uint16_t queue_id,
uint16_t nb_desc,
struct uk_netdev_txqueue_conf *conf);
static int virtio_netdev_vqueue_setup(struct virtio_net_device *vndev,
uint16_t queue_id, uint16_t nr_desc,
virtq_type_t queue_type,
struct uk_alloc *a);
static struct uk_netdev_rx_queue *virtio_netdev_rx_queue_setup(
struct uk_netdev *n,
uint16_t queue_id, uint16_t nb_desc,
struct uk_netdev_rxqueue_conf *conf);

static void virtio_netdev_xmit_free(struct uk_netdev_tx_queue *txq);
static int virtio_netdev_xmit(struct uk_netdev *dev,
struct uk_netdev_tx_queue *queue,
struct uk_netbuf *pkt);
static int virtio_netdev_recv(struct uk_netdev *dev,
struct uk_netdev_rx_queue *queue,
struct uk_netbuf **pkt);

static int virtio_netdev_rxq_info_get(struct uk_netdev *dev, __u16 queue_id,
struct uk_netdev_queue_info *qinfo);
static int virtio_netdev_txq_info_get(struct uk_netdev *dev, __u16 queue_id,
struct uk_netdev_queue_info *qinfo);
static int virtio_netdev_rxq_dequeue(struct uk_netdev_rx_queue *rxq,
struct uk_netbuf **netbuf);
static int virtio_netdev_rxq_enqueue(struct uk_netdev_rx_queue *rxq,
struct uk_netbuf *netbuf);
static int virtio_netdev_recv_done(struct virtqueue *vq, void *priv);
static int virtio_netdev_rx_fillup(struct uk_netdev_rx_queue *rxq,
__u16 num, int notify);

这些函数,最终都要绑定在uk_netdev_ops

netdev_core.h中的定义如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
* A structure containing the functions exported by a driver.
*/
struct uk_netdev_ops {
/** RX queue interrupts. */
uk_netdev_rxq_intr_enable_t rxq_intr_enable; /* optional */
uk_netdev_rxq_intr_disable_t rxq_intr_disable; /* optional */

/** Set/Get hardware address. */
uk_netdev_hwaddr_get_t hwaddr_get; /* recommended */
uk_netdev_hwaddr_set_t hwaddr_set; /* optional */

/** Set/Get MTU. */
uk_netdev_mtu_get_t mtu_get;
uk_netdev_mtu_set_t mtu_set; /* optional */

/** Promiscuous mode. */
uk_netdev_promiscuous_set_t promiscuous_set; /* optional */
uk_netdev_promiscuous_get_t promiscuous_get;

/** Device/driver capabilities and info. */
uk_netdev_info_get_t info_get;
uk_netdev_txq_info_get_t txq_info_get;
uk_netdev_rxq_info_get_t rxq_info_get;
uk_netdev_einfo_get_t einfo_get; /* optional */

/** Device life cycle. */
uk_netdev_probe_t probe; /* recommended */
uk_netdev_configure_t configure;
uk_netdev_txq_configure_t txq_configure;
uk_netdev_rxq_configure_t rxq_configure;
uk_netdev_start_t start;
};

在virtio_net.c中的实现为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static const struct uk_netdev_ops virtio_netdev_ops = {
.probe = virtio_netdev_feature_negotiate,
.configure = virtio_netdev_configure,
.rxq_configure = virtio_netdev_rx_queue_setup,
.txq_configure = virtio_netdev_tx_queue_setup,
.start = virtio_net_start,
.rxq_intr_enable = virtio_net_rx_intr_enable,
.rxq_intr_disable = virtio_net_rx_intr_disable,
.info_get = virtio_net_info_get,
.promiscuous_get = virtio_net_promisc_get,
.hwaddr_get = virtio_net_mac_get,
.mtu_get = virtio_net_mtu_get,
.txq_info_get = virtio_netdev_txq_info_get,
.rxq_info_get = virtio_netdev_rxq_info_get,
};
  • virtio_netdev_feature_negotiate:网络设备bus相关feature map,抽取部分关键代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    /**
    * Read device feature bits, and write the subset of feature bits
    * understood by the OS and driver to the device. During this step the
    * driver MAY read (but MUST NOT write) the device-specific
    * configuration fields to check that it can support the device before
    * accepting it.
    */
    host_features = virtio_feature_get(vndev->vdev);

    /**
    * Hardware address
    * NOTE: For now, we require a provided hw address. In principle,
    * we could generate one if none was given.
    * 设置网络设备的mac硬件地址
    */

    VIRTIO_FEATURE_SET(drv_features, VIRTIO_NET_F_MAC);

    /**
    * MTU information (needed)
    */
    VIRTIO_FEATURE_SET(drv_features, VIRTIO_NET_F_STATUS);

    /**
    * Gratuitous ARP
    * NOTE: We tell that we will do gratuitous ARPs ourselves.
    * ARP信息,ARP就是根据IP地址解析物理地址
    */
    VIRTIO_FEATURE_SET(drv_features, VIRTIO_NET_F_GUEST_ANNOUNCE);

    /**
    * Partial checksumming
    * NOTE: This enables sending and receiving of packets marked with
    * VIRTIO_NET_HDR_F_DATA_VALID and VIRTIO_NET_HDR_F_NEEDS_CSUM
    * 校验和相关特征值设置
    */
    VIRTIO_FEATURE_SET(drv_features, VIRTIO_NET_F_CSUM);
    VIRTIO_FEATURE_SET(drv_features, VIRTIO_NET_F_GUEST_CSUM);

    /**
    * Announce our enabled driver features back to the backend device
    * 将feature bits映射到具体设备上
    */
    vndev->vdev->features = drv_features;
    virtio_feature_set(vndev->vdev, vndev->vdev->features);
  • virtio_netdev_configure:主要是对virtio netdev里的配置初始化,主要是接收和发送队列数设置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    /**
    * netdev_core.h, nb = number
    * A structure used to configure a network device.
    */
    struct uk_netdev_conf {
    uint16_t nb_rx_queues;
    uint16_t nb_tx_queues;
    };

    static int virtio_netdev_configure(struct uk_netdev *n,
    const struct uk_netdev_conf *conf)
    {
    int rc = 0;
    struct virtio_net_device *vndev;

    UK_ASSERT(n);
    UK_ASSERT(conf);
    vndev = to_virtionetdev(n);

    rc = virtio_netdev_rxtx_alloc(vndev, conf); // 给virtio netdev设置接受发送队列数上限
    if (rc < 0) {
    uk_pr_err("%p: Failed to initialize rx and tx rings: %d\n",
    n, rc);
    }

    /* Initialize the count of the virtio-net device */
    vndev->rx_vqueue_cnt = 0;
    vndev->tx_vqueue_cnt = 0;

    return rc;
    }
  • uk_netdev_rx_queue

初始化接收队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

typedef enum {
VNET_RX,
VNET_TX,
} virtq_type_t;

static struct uk_netdev_rx_queue *virtio_netdev_rx_queue_setup(
struct uk_netdev *n, uint16_t queue_id,
uint16_t nb_desc,
struct uk_netdev_rxqueue_conf *conf)
{
struct virtio_net_device *vndev;
struct uk_netdev_rx_queue *rxq = NULL;
int rc;

UK_ASSERT(n);
UK_ASSERT(conf);
UK_ASSERT(conf->alloc_rxpkts);

vndev = to_virtionetdev(n);
if (queue_id >= vndev->max_vqueue_pairs) {
uk_pr_err("Invalid virtqueue identifier: %"__PRIu16"\n",
queue_id);
rc = -EINVAL;
goto err_exit;
}
/* Setup the virtqueue with the descriptor */
rc = virtio_netdev_vqueue_setup(vndev, queue_id, nb_desc, VNET_RX,
conf->a);
if (rc < 0) {
uk_pr_err("Failed to set up virtqueue %"__PRIu16": %d\n",
queue_id, rc);
goto err_exit;
}
rxq = &vndev->rxqs[rc];
rxq->alloc_rxpkts = conf->alloc_rxpkts;
rxq->alloc_rxpkts_argp = conf->alloc_rxpkts_argp;

/* Allocate receive buffers for this queue */
virtio_netdev_rx_fillup(rxq, rxq->nb_desc, 0);

exit:
return rxq;

err_exit:
rxq = ERR2PTR(rc);
goto exit;
}

这里涉及到了virtio_netdev_vqueue_setup函数。

这个函数的主要目的是初始化队列virtio_vqueue_setup(vqueue)中的,接收和发送队列都需要调用这个函数。完成的任务包括为队列绑定callback,队列描述符的初始化,rxqs成员变量初始化。

在绑定callback时调用了virtio_netdev_recv_done函数,这个函数主要用于终止队列ring的打断virtqueue_intr_disable,并触发uk_netdev_drv_rx_event,表示完成接收事件后的动作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
/**
* This function setup the vring infrastructure.
* @param vndev
* Reference to the virtio net device.
* @param queue_id
* User queue identifier
* @param nr_desc
* User configured number of descriptors.
* @param queue_type
* Queue type.
* @param a
* Reference to the allocator.
*/

static int virtio_netdev_vqueue_setup(struct virtio_net_device *vndev,
uint16_t queue_id, uint16_t nr_desc, virtq_type_t queue_type,
struct uk_alloc *a)
{
int rc = 0;
int id = 0;
virtqueue_callback_t callback;
uint16_t max_desc, hwvq_id;
struct virtqueue *vq;

if (queue_type == VNET_RX) {
id = vndev->rx_vqueue_cnt;
callback = virtio_netdev_recv_done;
max_desc = vndev->rxqs[id].max_nb_desc;
hwvq_id = vndev->rxqs[id].hwvq_id;
} else {
id = vndev->tx_vqueue_cnt;
/* We don't support the callback from the txqueue yet */
callback = NULL;
max_desc = vndev->txqs[id].max_nb_desc;
hwvq_id = vndev->txqs[id].hwvq_id;
}

if (unlikely(max_desc < nr_desc)) {
uk_pr_err("Max allowed desc: %"__PRIu16" Requested desc:%"__PRIu16"\n",
max_desc, nr_desc);
return -ENOBUFS;
}

nr_desc = (nr_desc != 0) ? nr_desc : max_desc;
uk_pr_debug("Configuring the %d descriptors\n", nr_desc);

/* Check if the descriptor is a power of 2 */
if (unlikely(nr_desc & (nr_desc - 1))) {
uk_pr_err("Expect descriptor count as a power 2\n");
return -EINVAL;
}

/*

*/
vq = virtio_vqueue_setup(vndev->vdev, hwvq_id, nr_desc, callback, a);
if (unlikely(PTRISERR(vq))) {
uk_pr_err("Failed to set up virtqueue %"__PRIu16"\n",
queue_id);
rc = PTR2ERR(vq);
return rc;
}

if (queue_type == VNET_RX) {
vq->priv = &vndev->rxqs[id];
vndev->rxqs[id].ndev = &vndev->netdev;
vndev->rxqs[id].vq = vq;
vndev->rxqs[id].nb_desc = nr_desc;
vndev->rxqs[id].lqueue_id = queue_id;
vndev->rx_vqueue_cnt++;
} else {
vndev->txqs[id].vq = vq;
vndev->txqs[id].ndev = &vndev->netdev;
vndev->txqs[id].nb_desc = nr_desc;
vndev->txqs[id].lqueue_id = queue_id;
vndev->tx_vqueue_cnt++;
}
return id;
}

uk_netdev_tx_queue和这个读队列的操作基本一致,就不再赘述

  • virtio_net_start:这个函数就是禁用接收和发送队列的中断选项
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
static int virtio_net_start(struct uk_netdev *n)
{
struct virtio_net_device *d;
int i = 0;

UK_ASSERT(n != NULL);
d = to_virtionetdev(n);

/*
* By default, interrupts are disabled and it is up to the user or
* network stack to manually enable them with a call to
* enable_tx|rx_intr()
*/
for (i = 0; i < d->rx_vqueue_cnt; i++) {
virtqueue_intr_disable(d->rxqs[i].vq);
d->rxqs[i].intr_enabled = 0;
}

for (i = 0; i < d->tx_vqueue_cnt; i++) {
virtqueue_intr_disable(d->txqs[i].vq);
d->txqs[i].intr_enabled = 0;
}

/*
* Set the DRIVER_OK status bit. At this point the device is "live".
*/
virtio_dev_drv_up(d->vdev);
uk_pr_info(DRIVER_NAME": %"__PRIu16" started\n", d->uid);

return 0;
}
  • virtio_net_rx_intr_enable, virtio_net_rx_intr_disable:管理接收发送队列的enable与disable
    it
  • virtio_net_info_get:获取virtio_netdev的最大接收传输队列,对齐标志以及feature bit。
  • virtio_net_promisc_get:是否promisc模式
  • virtio_net_mac_get:获取设备硬件mac地址
  • virtio_net_mtu_get:获取网络最大传输单元
  • virtio_netdev_rxq_info_get:获取接收队列信息,主要是描述符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
static int virtio_netdev_rxq_info_get(struct uk_netdev *dev,
__u16 queue_id,
struct uk_netdev_queue_info *qinfo)
{
struct virtio_net_device *vndev;
struct uk_netdev_rx_queue *rxq;
int rc = 0;

UK_ASSERT(dev);
UK_ASSERT(qinfo);
vndev = to_virtionetdev(dev);
if (unlikely(queue_id >= vndev->max_vqueue_pairs)) {
uk_pr_err("Invalid virtqueue id: %"__PRIu16"\n", queue_id);
rc = -EINVAL;
goto exit;
}
rxq = &vndev->rxqs[queue_id];
qinfo->nb_min = 1;
qinfo->nb_max = rxq->max_nb_desc;
qinfo->nb_is_power_of_two = 1;

exit:
return rc;

}