参考virtio 1.2 spec,Linux kernel version v6.0

  • speed contains the device speed, in units of 1 MBit per second, 0 to 0x7fffffff, or 0xffffffff for unknown speed.
  • duplex has the values of 0x01 for full duplex, 0x00 for half duplex and 0xff for unknown duplex state.

Both speed and duplex can change, thus the driver is expected to re-read these values after receiving a configuration change notification.

1
2
3
4
5
6
7
8
9
10
11
struct virtio_net_config { 
u8 mac[6];
le16 status;
le16 max_virtqueue_pairs;
le16 mtu;
le32 speed;
u8 duplex;
u8 rss_max_key_size;
le16 rss_max_indirection_table_length;
le32 supported_hash_types;
};

Linux kernel source code:

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
static void virtnet_config_changed_work(struct work_struct *work)
{
struct virtnet_info *vi =
container_of(work, struct virtnet_info, config_work);
u16 v;

if (virtio_cread_feature(vi->vdev, VIRTIO_NET_F_STATUS,
struct virtio_net_config, status, &v) < 0)
return;

if (v & VIRTIO_NET_S_ANNOUNCE) {
netdev_notify_peers(vi->dev);
virtnet_ack_link_announce(vi);
}

/* Ignore unknown (future) status bits */
v &= VIRTIO_NET_S_LINK_UP;

if (vi->status == v)
return;

vi->status = v;

if (vi->status & VIRTIO_NET_S_LINK_UP) {
virtnet_update_settings(vi);
netif_carrier_on(vi->dev);
netif_tx_wake_all_queues(vi->dev);
} else {
netif_carrier_off(vi->dev);
netif_tx_stop_all_queues(vi->dev);
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static void virtnet_update_settings(struct virtnet_info *vi)
{
u32 speed;
u8 duplex;

if (!virtio_has_feature(vi->vdev, VIRTIO_NET_F_SPEED_DUPLEX))
return;

virtio_cread_le(vi->vdev, struct virtio_net_config, speed, &speed);

if (ethtool_validate_speed(speed))
vi->speed = speed;

virtio_cread_le(vi->vdev, struct virtio_net_config, duplex, &duplex);

if (ethtool_validate_duplex(duplex))
vi->duplex = duplex;
}