f0c391e09b
According to the virtio specifications, the host present the supported features to the guest, and the guest should answer woth the features it supports. This allows the host to disable support for features that the guest is not going to use. Minix tells to the host it doesn't support any extended features, while it uses at least VIRTIO_NET_F_MAC and VIRTIO_NET_F_CTRL_VQ. For the latter it seems it only allocate the queue while not using it later. However starting with QEMU 1.4.0, with multiqueue support added, the control queue is not allocated on the host side if the guest doesn't tell it supports this feature. This cause virtio-net to crash on the Minix side. This patch fixes that by correctly telling the features that are used by the Minix guest for more than printing a debug message. This fixes virtio-net on QEMU 1.4.x. Change-Id: I8bbf757c09d24e0f5fe5835531a1c9203b714bd7
680 lines
15 KiB
C
680 lines
15 KiB
C
/* virtio net driver for MINIX 3
|
|
*
|
|
* Copyright (c) 2013, A. Welzel, <arne.welzel@gmail.com>
|
|
*
|
|
* This software is released under the BSD license. See the LICENSE file
|
|
* included in the main directory of this source distribution for the
|
|
* license terms and conditions.
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <sys/types.h>
|
|
|
|
#include <net/gen/ether.h>
|
|
#include <net/gen/eth_io.h>
|
|
|
|
#include <minix/drivers.h>
|
|
#include <minix/netdriver.h>
|
|
#include <minix/sysutil.h>
|
|
#include <minix/virtio.h>
|
|
|
|
#include <sys/queue.h>
|
|
|
|
#include "virtio_net.h"
|
|
|
|
#define dput(s) do { dprintf(s); printf("\n"); } while (0)
|
|
#define dprintf(s) do { \
|
|
printf("%s: ", name); \
|
|
printf s; \
|
|
} while (0)
|
|
|
|
static struct virtio_device *net_dev;
|
|
|
|
static const char *const name = "virtio-net";
|
|
|
|
enum queue {RX_Q, TX_Q, CTRL_Q};
|
|
|
|
/* Number of packets to work with */
|
|
/* TODO: This should be an argument to the driver and possibly also
|
|
* depend on the queue sizes offered by this device.
|
|
*/
|
|
#define BUF_PACKETS 64
|
|
/* Maximum size of a packet */
|
|
#define MAX_PACK_SIZE ETH_MAX_PACK_SIZE
|
|
/* Buffer size needed for the payload of BUF_PACKETS */
|
|
#define PACKET_BUF_SZ (BUF_PACKETS * MAX_PACK_SIZE)
|
|
|
|
struct packet {
|
|
int idx;
|
|
struct virtio_net_hdr *vhdr;
|
|
phys_bytes phdr;
|
|
char *vdata;
|
|
phys_bytes pdata;
|
|
STAILQ_ENTRY(packet) next;
|
|
};
|
|
|
|
/* Allocated data chunks */
|
|
static char *data_vir;
|
|
static phys_bytes data_phys;
|
|
static struct virtio_net_hdr *hdrs_vir;
|
|
static phys_bytes hdrs_phys;
|
|
static struct packet *packets;
|
|
static int in_rx;
|
|
static int started;
|
|
|
|
/* Packets on this list can be given to the host */
|
|
static STAILQ_HEAD(free_list, packet) free_list;
|
|
|
|
/* Packets on this list are to be given to inet */
|
|
static STAILQ_HEAD(recv_list, packet) recv_list;
|
|
|
|
/* State about pending inet messages */
|
|
static int rx_pending;
|
|
static message pending_rx_msg;
|
|
static int tx_pending;
|
|
static message pending_tx_msg;
|
|
|
|
/* Various state data */
|
|
static u8_t virtio_net_mac[6];
|
|
static eth_stat_t virtio_net_stats;
|
|
static int spurious_interrupt;
|
|
|
|
|
|
/* Prototypes */
|
|
static int virtio_net_probe(int skip);
|
|
static int virtio_net_config(void);
|
|
static int virtio_net_alloc_bufs(void);
|
|
static void virtio_net_init_queues(void);
|
|
|
|
static void virtio_net_refill_rx_queue(void);
|
|
static void virtio_net_check_queues(void);
|
|
static void virtio_net_check_pending(void);
|
|
|
|
static void virtio_net_fetch_iovec(iovec_s_t *iov, message *m);
|
|
static int virtio_net_cpy_to_user(message *m);
|
|
static int virtio_net_cpy_from_user(message *m);
|
|
|
|
static void virtio_net_intr(message *m);
|
|
static void virtio_net_write(message *m);
|
|
static void virtio_net_read(message *m);
|
|
static void virtio_net_conf(message *m);
|
|
static void virtio_net_getstat(message *m);
|
|
|
|
static void virtio_net_notify(message *m);
|
|
static void virtio_net_msg(message *m);
|
|
static void virtio_net_main_loop(void);
|
|
|
|
static void sef_local_startup(void);
|
|
static int sef_cb_init_fresh(int type, sef_init_info_t *info);
|
|
static void sef_cb_signal_handler(int signo);
|
|
|
|
|
|
/* TODO: Features are pretty much ignored */
|
|
struct virtio_feature netf[] = {
|
|
{ "partial csum", VIRTIO_NET_F_CSUM, 0, 0 },
|
|
{ "given mac", VIRTIO_NET_F_MAC, 0, 1 },
|
|
{ "status ", VIRTIO_NET_F_STATUS, 0, 0 },
|
|
{ "control channel", VIRTIO_NET_F_CTRL_VQ, 0, 1 },
|
|
{ "control channel rx", VIRTIO_NET_F_CTRL_RX, 0, 0 }
|
|
};
|
|
|
|
static int
|
|
virtio_net_probe(int skip)
|
|
{
|
|
/* virtio-net has at least 2 queues */
|
|
int queues = 2;
|
|
net_dev= virtio_setup_device(0x00001, name, netf,
|
|
sizeof(netf) / sizeof(netf[0]),
|
|
1 /* threads */, skip);
|
|
if (net_dev == NULL)
|
|
return ENXIO;
|
|
|
|
/* If the host supports the control queue, allocate it as well */
|
|
if (virtio_host_supports(net_dev, VIRTIO_NET_F_CTRL_VQ))
|
|
queues += 1;
|
|
|
|
if (virtio_alloc_queues(net_dev, queues) != OK) {
|
|
virtio_free_device(net_dev);
|
|
return ENOMEM;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
static int
|
|
virtio_net_config(void)
|
|
{
|
|
u32_t mac14;
|
|
u32_t mac56;
|
|
int i;
|
|
|
|
if (virtio_host_supports(net_dev, VIRTIO_NET_F_MAC)) {
|
|
dprintf(("Mac set by host: "));
|
|
mac14 = virtio_sread32(net_dev, 0);
|
|
mac56 = virtio_sread32(net_dev, 4);
|
|
*(u32_t*)virtio_net_mac = mac14;
|
|
*(u16_t*)(virtio_net_mac + 4) = mac56;
|
|
|
|
for (i = 0; i < 6; i++)
|
|
printf("%02x%s", virtio_net_mac[i],
|
|
i == 5 ? "\n" : ":");
|
|
} else {
|
|
dput(("No mac"));
|
|
}
|
|
|
|
if (virtio_host_supports(net_dev, VIRTIO_NET_F_STATUS)) {
|
|
dput(("Current Status %x", (u32_t)virtio_sread16(net_dev, 6)));
|
|
} else {
|
|
dput(("No status"));
|
|
}
|
|
|
|
if (virtio_host_supports(net_dev, VIRTIO_NET_F_CTRL_VQ))
|
|
dput(("Host supports control channel"));
|
|
|
|
if (virtio_host_supports(net_dev, VIRTIO_NET_F_CTRL_RX))
|
|
dput(("Host supports control channel for RX"));
|
|
|
|
return OK;
|
|
}
|
|
|
|
static int
|
|
virtio_net_alloc_bufs(void)
|
|
{
|
|
data_vir = alloc_contig(PACKET_BUF_SZ, 0, &data_phys);
|
|
|
|
if (!data_vir)
|
|
return ENOMEM;
|
|
|
|
hdrs_vir = alloc_contig(BUF_PACKETS * sizeof(hdrs_vir[0]),
|
|
0, &hdrs_phys);
|
|
|
|
if (!hdrs_vir) {
|
|
free_contig(data_vir, PACKET_BUF_SZ);
|
|
return ENOMEM;
|
|
}
|
|
|
|
packets = malloc(BUF_PACKETS * sizeof(packets[0]));
|
|
|
|
if (!packets) {
|
|
free_contig(data_vir, PACKET_BUF_SZ);
|
|
free_contig(hdrs_vir, BUF_PACKETS * sizeof(hdrs_vir[0]));
|
|
return ENOMEM;
|
|
}
|
|
|
|
memset(data_vir, 0, PACKET_BUF_SZ);
|
|
memset(hdrs_vir, 0, BUF_PACKETS * sizeof(hdrs_vir[0]));
|
|
memset(packets, 0, BUF_PACKETS * sizeof(packets[0]));
|
|
|
|
return OK;
|
|
}
|
|
|
|
static void
|
|
virtio_net_init_queues(void)
|
|
{
|
|
int i;
|
|
STAILQ_INIT(&free_list);
|
|
STAILQ_INIT(&recv_list);
|
|
|
|
for (i = 0; i < BUF_PACKETS; i++) {
|
|
packets[i].idx = i;
|
|
packets[i].vhdr = &hdrs_vir[i];
|
|
packets[i].phdr = hdrs_phys + i * sizeof(hdrs_vir[i]);
|
|
packets[i].vdata = data_vir + i * MAX_PACK_SIZE;
|
|
packets[i].pdata = data_phys + i * MAX_PACK_SIZE;
|
|
STAILQ_INSERT_HEAD(&free_list, &packets[i], next);
|
|
}
|
|
}
|
|
|
|
static void
|
|
virtio_net_refill_rx_queue(void)
|
|
{
|
|
struct vumap_phys phys[2];
|
|
struct packet *p;
|
|
|
|
while ((in_rx < BUF_PACKETS / 2) && !STAILQ_EMPTY(&free_list)) {
|
|
|
|
/* peek */
|
|
p = STAILQ_FIRST(&free_list);
|
|
/* remove */
|
|
STAILQ_REMOVE_HEAD(&free_list, next);
|
|
|
|
phys[0].vp_addr = p->phdr;
|
|
assert(!(phys[0].vp_addr & 1));
|
|
phys[0].vp_size = sizeof(struct virtio_net_hdr);
|
|
|
|
phys[1].vp_addr = p->pdata;
|
|
assert(!(phys[1].vp_addr & 1));
|
|
phys[1].vp_size = MAX_PACK_SIZE;
|
|
|
|
/* RX queue needs write */
|
|
phys[0].vp_addr |= 1;
|
|
phys[1].vp_addr |= 1;
|
|
|
|
virtio_to_queue(net_dev, RX_Q, phys, 2, p);
|
|
in_rx++;
|
|
|
|
}
|
|
|
|
if (in_rx == 0 && STAILQ_EMPTY(&free_list)) {
|
|
dput(("warning: rx queue underflow!"));
|
|
virtio_net_stats.ets_fifoUnder++;
|
|
}
|
|
}
|
|
|
|
static void
|
|
virtio_net_check_queues(void)
|
|
{
|
|
struct packet *p;
|
|
|
|
/* Put the received packets into the recv list */
|
|
while (virtio_from_queue(net_dev, RX_Q, (void **)&p) == 0) {
|
|
STAILQ_INSERT_TAIL(&recv_list, p, next);
|
|
in_rx--;
|
|
virtio_net_stats.ets_packetR++;
|
|
}
|
|
|
|
/* Packets from the TX queue just indicated they are free to
|
|
* be reused now. inet already knows about them as being sent.
|
|
*/
|
|
while (virtio_from_queue(net_dev, TX_Q, (void **)&p) == 0) {
|
|
memset(p->vhdr, 0, sizeof(*p->vhdr));
|
|
memset(p->vdata, 0, MAX_PACK_SIZE);
|
|
STAILQ_INSERT_HEAD(&free_list, p, next);
|
|
virtio_net_stats.ets_packetT++;
|
|
}
|
|
}
|
|
|
|
static void
|
|
virtio_net_check_pending(void)
|
|
{
|
|
int dst = 0xDEAD;
|
|
int r;
|
|
|
|
message reply;
|
|
reply.m_type = DL_TASK_REPLY;
|
|
reply.DL_FLAGS = DL_NOFLAGS;
|
|
reply.DL_COUNT = 0;
|
|
|
|
/* Pending read and something in recv_list? */
|
|
if (!STAILQ_EMPTY(&recv_list) && rx_pending) {
|
|
dst = pending_rx_msg.m_source;
|
|
reply.DL_COUNT = virtio_net_cpy_to_user(&pending_rx_msg);
|
|
reply.DL_FLAGS |= DL_PACK_RECV;
|
|
rx_pending = 0;
|
|
}
|
|
|
|
if (!STAILQ_EMPTY(&free_list) && tx_pending) {
|
|
dst = pending_tx_msg.m_source;
|
|
virtio_net_cpy_from_user(&pending_tx_msg);
|
|
reply.DL_FLAGS |= DL_PACK_SEND;
|
|
tx_pending = 0;
|
|
}
|
|
|
|
/* Only reply if a pending request was handled */
|
|
if (reply.DL_FLAGS != DL_NOFLAGS)
|
|
if ((r = send(dst, &reply)) != OK)
|
|
panic("%s: send to %d failed (%d)", name, dst, r);
|
|
}
|
|
|
|
static void
|
|
virtio_net_fetch_iovec(iovec_s_t *iov, message *m)
|
|
{
|
|
int r;
|
|
r = sys_safecopyfrom(m->m_source, m->DL_GRANT, 0, (vir_bytes)iov,
|
|
m->DL_COUNT * sizeof(iov[0]));
|
|
|
|
if (r != OK)
|
|
panic("%s: iovec fail for %d (%d)", name, m->m_source, r);
|
|
}
|
|
|
|
static int
|
|
virtio_net_cpy_to_user(message *m)
|
|
{
|
|
/* Hmm, this looks so similar to cpy_from_user... TODO */
|
|
int i, r, size, ivsz;
|
|
int left = MAX_PACK_SIZE; /* Try copying the whole packet */
|
|
int bytes = 0;
|
|
iovec_s_t iovec[NR_IOREQS];
|
|
struct packet *p;
|
|
|
|
/* This should only be called if recv_list has some entries */
|
|
assert(!STAILQ_EMPTY(&recv_list));
|
|
|
|
p = STAILQ_FIRST(&recv_list);
|
|
STAILQ_REMOVE_HEAD(&recv_list, next);
|
|
|
|
virtio_net_fetch_iovec(iovec, m);
|
|
|
|
for (i = 0; i < m->DL_COUNT && left > 0; i++) {
|
|
ivsz = iovec[i].iov_size;
|
|
size = left > ivsz ? ivsz : left;
|
|
r = sys_safecopyto(m->m_source, iovec[i].iov_grant, 0,
|
|
(vir_bytes) p->vdata + bytes, size);
|
|
|
|
if (r != OK)
|
|
panic("%s: copy to %d failed (%d)", name,
|
|
m->m_source,
|
|
r);
|
|
|
|
left -= size;
|
|
bytes += size;
|
|
}
|
|
|
|
if (left != 0)
|
|
dput(("Uhm... left=%d", left));
|
|
|
|
/* Clean the packet */
|
|
memset(p->vhdr, 0, sizeof(*p->vhdr));
|
|
memset(p->vdata, 0, MAX_PACK_SIZE);
|
|
STAILQ_INSERT_HEAD(&free_list, p, next);
|
|
|
|
return bytes;
|
|
}
|
|
|
|
static int
|
|
sys_easy_vsafecopy_from(endpoint_t src_proc, iovec_s_t *iov, int count,
|
|
vir_bytes dst, size_t max, size_t *copied)
|
|
{
|
|
int i, r;
|
|
size_t left = max;
|
|
vir_bytes cur_off = 0;
|
|
struct vscp_vec vv[NR_IOREQS];
|
|
|
|
for (i = 0; i < count && left > 0; i++) {
|
|
vv[i].v_from = src_proc;
|
|
vv[i].v_to = SELF;
|
|
vv[i].v_gid = iov[i].iov_grant;
|
|
vv[i].v_offset = 0;
|
|
vv[i].v_addr = dst + cur_off;
|
|
vv[i].v_bytes = iov[i].iov_size;
|
|
|
|
/* More data in iov than the buffer can hold, this should be
|
|
* manageable by the caller.
|
|
*/
|
|
if (left - vv[i].v_bytes > left) {
|
|
printf("sys_easy_vsafecopy_from: buf too small!\n");
|
|
return ENOMEM;
|
|
}
|
|
|
|
left -= iov[i].iov_size;
|
|
cur_off += iov[i].iov_size;
|
|
}
|
|
|
|
/* Now that we prepared the vscp_vec, we can call vsafecopy() */
|
|
if ((r = sys_vsafecopy(vv, count)) != OK)
|
|
printf("sys_vsafecopy: failed: (%d)\n", r);
|
|
|
|
if (copied)
|
|
*copied = cur_off;
|
|
|
|
return OK;
|
|
}
|
|
|
|
static int
|
|
virtio_net_cpy_from_user(message *m)
|
|
{
|
|
/* Put user bytes into a a free packet buffer and
|
|
* then forward this packet to the TX queue.
|
|
*/
|
|
int r;
|
|
iovec_s_t iovec[NR_IOREQS];
|
|
struct vumap_phys phys[2];
|
|
struct packet *p;
|
|
size_t bytes;
|
|
|
|
/* This should only be called if free_list has some entries */
|
|
assert(!STAILQ_EMPTY(&free_list));
|
|
|
|
p = STAILQ_FIRST(&free_list);
|
|
STAILQ_REMOVE_HEAD(&free_list, next);
|
|
|
|
virtio_net_fetch_iovec(iovec, m);
|
|
|
|
r = sys_easy_vsafecopy_from(m->m_source, iovec, m->DL_COUNT,
|
|
(vir_bytes)p->vdata, MAX_PACK_SIZE,
|
|
&bytes);
|
|
|
|
if (r != OK)
|
|
panic("%s: copy from %d failed", name, m->m_source);
|
|
|
|
|
|
phys[0].vp_addr = p->phdr;
|
|
assert(!(phys[0].vp_addr & 1));
|
|
phys[0].vp_size = sizeof(struct virtio_net_hdr);
|
|
phys[1].vp_addr = p->pdata;
|
|
assert(!(phys[1].vp_addr & 1));
|
|
phys[1].vp_size = bytes;
|
|
virtio_to_queue(net_dev, TX_Q, phys, 2, p);
|
|
return bytes;
|
|
}
|
|
|
|
static void
|
|
virtio_net_intr(message *m)
|
|
{
|
|
/* Check and clear interrupt flag */
|
|
if (virtio_had_irq(net_dev)) {
|
|
virtio_net_check_queues();
|
|
} else {
|
|
if (!spurious_interrupt)
|
|
dput(("Spurious interrupt"));
|
|
|
|
spurious_interrupt = 1;
|
|
}
|
|
|
|
virtio_net_check_pending();
|
|
|
|
virtio_irq_enable(net_dev);
|
|
}
|
|
|
|
static void
|
|
virtio_net_write(message *m)
|
|
{
|
|
int r;
|
|
message reply;
|
|
|
|
reply.m_type = DL_TASK_REPLY;
|
|
reply.DL_FLAGS = DL_NOFLAGS;
|
|
reply.DL_COUNT = 0;
|
|
|
|
|
|
if (!STAILQ_EMPTY(&free_list)) {
|
|
/* free_list contains at least one packet, use it */
|
|
reply.DL_COUNT = virtio_net_cpy_from_user(m);
|
|
reply.DL_FLAGS = DL_PACK_SEND;
|
|
} else {
|
|
pending_tx_msg = *m;
|
|
tx_pending = 1;
|
|
}
|
|
|
|
if ((r = send(m->m_source, &reply)) != OK)
|
|
panic("%s: send to %d failed (%d)", name, m->m_source, r);
|
|
}
|
|
|
|
static void
|
|
virtio_net_read(message *m)
|
|
{
|
|
int r;
|
|
message reply;
|
|
|
|
reply.m_type = DL_TASK_REPLY;
|
|
reply.DL_FLAGS = DL_NOFLAGS;
|
|
reply.DL_COUNT = 0;
|
|
|
|
if (!STAILQ_EMPTY(&recv_list)) {
|
|
/* recv_list contains at least one packet, copy it */
|
|
reply.DL_COUNT = virtio_net_cpy_to_user(m);
|
|
reply.DL_FLAGS = DL_PACK_RECV;
|
|
} else {
|
|
rx_pending = 1;
|
|
pending_rx_msg = *m;
|
|
}
|
|
|
|
if ((r = send(m->m_source, &reply)) != OK)
|
|
panic("%s: send to %d failed (%d)", name, m->m_source, r);
|
|
}
|
|
|
|
static void
|
|
virtio_net_conf(message *m)
|
|
{
|
|
/* TODO: Add the multicast, broadcast filtering etc. */
|
|
int i, r;
|
|
|
|
message reply;
|
|
|
|
/* If this is the first CONF message we see, fully initialize
|
|
* the device now.
|
|
*/
|
|
if (!started) {
|
|
started = 1;
|
|
virtio_device_ready(net_dev);
|
|
virtio_irq_enable(net_dev);
|
|
}
|
|
|
|
/* Prepare reply */
|
|
for (i = 0; i < sizeof(virtio_net_mac); i++)
|
|
((u8_t*)reply.DL_HWADDR)[i] = virtio_net_mac[i];
|
|
|
|
reply.m_type = DL_CONF_REPLY;
|
|
reply.DL_STAT = OK;
|
|
reply.DL_COUNT = 0;
|
|
|
|
if ((r = send(m->m_source, &reply)) != OK)
|
|
panic("%s: send to %d failed (%d)", name, m->m_source, r);
|
|
}
|
|
|
|
static void
|
|
virtio_net_getstat(message *m)
|
|
{
|
|
int r;
|
|
message reply;
|
|
|
|
reply.m_type = DL_STAT_REPLY;
|
|
reply.DL_STAT = OK;
|
|
reply.DL_COUNT = 0;
|
|
|
|
|
|
r = sys_safecopyto(m->m_source, m->DL_GRANT, 0,
|
|
(vir_bytes)&virtio_net_stats,
|
|
sizeof(virtio_net_stats));
|
|
|
|
if (r != OK)
|
|
panic("%s: copy to %d failed (%d)", name, m->m_source, r);
|
|
|
|
if ((r = send(m->m_source, &reply)) != OK)
|
|
panic("%s: send to %d failed (%d)", name, m->m_source, r);
|
|
}
|
|
|
|
static void
|
|
virtio_net_notify(message *m)
|
|
{
|
|
if (_ENDPOINT_P(m->m_source) == HARDWARE)
|
|
virtio_net_intr(m);
|
|
}
|
|
|
|
static void
|
|
virtio_net_msg(message *m)
|
|
{
|
|
switch (m->m_type) {
|
|
case DL_WRITEV_S:
|
|
virtio_net_write(m);
|
|
break;
|
|
case DL_READV_S:
|
|
virtio_net_read(m);
|
|
break;
|
|
case DL_CONF:
|
|
virtio_net_conf(m);
|
|
break;
|
|
case DL_GETSTAT_S:
|
|
virtio_net_getstat(m);
|
|
break;
|
|
default:
|
|
panic("%s: illegal message: %d", name, m->m_type);
|
|
}
|
|
}
|
|
|
|
static void
|
|
virtio_net_main_loop(void)
|
|
{
|
|
message m;
|
|
int ipc_status;
|
|
int r;
|
|
|
|
while (TRUE) {
|
|
|
|
virtio_net_refill_rx_queue();
|
|
|
|
if ((r = netdriver_receive(ANY, &m, &ipc_status)) != OK)
|
|
panic("%s: netdriver_receive failed: %d", name, r);
|
|
|
|
if (is_ipc_notify(ipc_status))
|
|
virtio_net_notify(&m);
|
|
else
|
|
virtio_net_msg(&m);
|
|
}
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
env_setargs(argc, argv);
|
|
sef_local_startup();
|
|
|
|
virtio_net_main_loop();
|
|
}
|
|
|
|
static void
|
|
sef_local_startup()
|
|
{
|
|
sef_setcb_init_fresh(sef_cb_init_fresh);
|
|
sef_setcb_init_lu(sef_cb_init_fresh);
|
|
sef_setcb_init_restart(sef_cb_init_fresh);
|
|
|
|
sef_setcb_lu_prepare(sef_cb_lu_prepare_always_ready);
|
|
sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid_workfree);
|
|
|
|
sef_setcb_signal_handler(sef_cb_signal_handler);
|
|
|
|
sef_startup();
|
|
}
|
|
|
|
static int
|
|
sef_cb_init_fresh(int type, sef_init_info_t *info)
|
|
{
|
|
long instance = 0;
|
|
env_parse("instance", "d", 0, &instance, 0, 255);
|
|
|
|
if (virtio_net_probe((int)instance) != OK)
|
|
panic("%s: No device found", name);
|
|
|
|
if (virtio_net_config() != OK)
|
|
panic("%s: No device found", name);
|
|
|
|
if (virtio_net_alloc_bufs() != OK)
|
|
panic("%s: Buffer allocation failed", name);
|
|
|
|
virtio_net_init_queues();
|
|
|
|
netdriver_announce();
|
|
|
|
return(OK);
|
|
}
|
|
|
|
static void
|
|
sef_cb_signal_handler(int signo)
|
|
{
|
|
if (signo != SIGTERM)
|
|
return;
|
|
|
|
dput(("Terminating"));
|
|
|
|
free_contig(data_vir, PACKET_BUF_SZ);
|
|
free_contig(hdrs_vir, BUF_PACKETS * sizeof(hdrs_vir[0]));
|
|
free(packets);
|
|
|
|
virtio_reset_device(net_dev);
|
|
virtio_free_queues(net_dev);
|
|
virtio_free_device(net_dev);
|
|
net_dev = NULL;
|
|
|
|
exit(1);
|
|
}
|