Skip to content

Commit

Permalink
Merge pull request #171 from libos-nuse/feature-virtio-rawsock
Browse files Browse the repository at this point in the history
add raw socket (AF_PACKET) backend for virtio-net host
  • Loading branch information
Octavian Purdila authored Jul 6, 2016
2 parents 6dacb1e + a28b355 commit 7881bd3
Show file tree
Hide file tree
Showing 11 changed files with 408 additions and 253 deletions.
8 changes: 8 additions & 0 deletions tools/lkl/include/lkl.h
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,14 @@ struct lkl_netdev *lkl_netdev_dpdk_create(const char *ifname);
*/
struct lkl_netdev *lkl_netdev_vde_create(const char *switch_path);

/**
* lkl_netdev_raw_create - create raw socket net_device for the virtio net
* backend
*
* @ifname - interface name for the snoop device.
*/
struct lkl_netdev *lkl_netdev_raw_create(const char *ifname);

/*
* lkl_register_dbg_handler- register a signal handler that loads a debug lib.
*
Expand Down
2 changes: 2 additions & 0 deletions tools/lkl/lib/Build
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ lkl-y += virtio.o
lkl-y += dbg.o
lkl-y += dbg_handler.o
lkl-$(CONFIG_AUTO_LKL_POSIX_HOST) += virtio_net.o
lkl-$(CONFIG_AUTO_LKL_POSIX_HOST) += virtio_net_linux_fdnet.o
lkl-$(CONFIG_AUTO_LKL_POSIX_HOST) += virtio_net_tap.o
lkl-$(CONFIG_AUTO_LKL_POSIX_HOST) += virtio_net_raw.o
lkl-$(CONFIG_AUTO_LKL_VIRTIO_NET_DPDK) += virtio_net_dpdk.o
lkl-$(CONFIG_AUTO_LKL_VIRTIO_NET_VDE) += virtio_net_vde.o
2 changes: 1 addition & 1 deletion tools/lkl/lib/hijack/hijack.c
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ HOST_CALL(socket);
int socket(int domain, int type, int protocol)
{
CHECK_HOST_CALL(socket);
if (domain == AF_UNIX)
if (domain == AF_UNIX || domain == PF_PACKET)
return host_socket(domain, type, protocol);

return lkl_call(__lkl__NR_socket, 3, domain, type, protocol);
Expand Down
10 changes: 6 additions & 4 deletions tools/lkl/lib/hijack/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
#include <lkl_host.h>

#include "xlate.h"
#include "../virtio_net_tap.h"
#include "../virtio_net_linux_fdnet.h"

#define __USE_GNU
#include <dlfcn.h>
Expand Down Expand Up @@ -170,11 +170,11 @@ static void mount_cmds_exec(char *_cmds, int (*callback)(char*))
free(cmds);
}

void fixup_netdev_tap_ops(void)
void fixup_netdev_linux_fdnet_ops(void)
{
/* It's okay if this is NULL, because then netdev close will
* fall back onto an uncloseable implementation. */
lkl_netdev_tap_ops.eventfd = dlsym(RTLD_NEXT, "eventfd");
lkl_netdev_linux_fdnet_ops.eventfd = dlsym(RTLD_NEXT, "eventfd");
}

void __attribute__((constructor(102)))
Expand All @@ -197,7 +197,7 @@ hijack_init(void)
char *arp_entries = getenv("LKL_HIJACK_NET_ARP");

/* Must be run before lkl_netdev_tap_create */
fixup_netdev_tap_ops();
fixup_netdev_linux_fdnet_ops();

if (tap) {
fprintf(stderr,
Expand All @@ -214,6 +214,8 @@ hijack_init(void)
nd = lkl_netdev_dpdk_create(ifparams);
else if (strcmp(iftype, "vde") == 0)
nd = lkl_netdev_vde_create(ifparams);
else if (strcmp(iftype, "raw") == 0)
nd = lkl_netdev_raw_create(ifparams);
}

if (nd) {
Expand Down
251 changes: 251 additions & 0 deletions tools/lkl/lib/virtio_net_linux_fdnet.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
/*
* Linux File descripter based virtual network interface feature for LKL
* Copyright (c) 2015,2016 Ryo Nakamura, Hajime Tazaki
*
* Author: Ryo Nakamura <upa@wide.ad.jp>
* Hajime Tazaki <thehajime@gmail.com>
* Octavian Purdila <octavian.purdila@intel.com>
*
* Current implementation is linux-specific.
*/

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/eventfd.h>

#include "virtio.h"
#include "virtio_net_linux_fdnet.h"

struct lkl_netdev_linux_fdnet_ops lkl_netdev_linux_fdnet_ops = {
/*
* /dev/net/tun is Linux specific so we know our host is some
* flavor of Linux, but this allows graceful support if we're
* on a kernel that's < 2.6.22.
*/
#ifdef __NR_eventfd
/* This sigature was recently (9/2014) changed in glibc. */
.eventfd = (int (*)(unsigned int, int))eventfd,
#endif /* __NR_eventfd */
};

static int linux_fdnet_net_tx(struct lkl_netdev *nd, void *data, int len)
{
int ret;
struct lkl_netdev_linux_fdnet *nd_fdnet =
container_of(nd, struct lkl_netdev_linux_fdnet, dev);

do {
ret = write(nd_fdnet->fd, data, len);
} while (ret == -1 && errno == EINVAL);
if (ret > 0)
return 0;
if (ret < 0 && errno != EAGAIN)
perror("write to Linux fd netdev fails");

return -1;
}

static int linux_fdnet_net_rx(struct lkl_netdev *nd, void *data, int *len)
{
int ret;
struct lkl_netdev_linux_fdnet *nd_fdnet =
container_of(nd, struct lkl_netdev_linux_fdnet, dev);

do {
ret = read(nd_fdnet->fd, data, *len);
} while (ret == -1 && errno == EINVAL);
if (ret > 0) {
*len = ret;
return 0;
}
if (ret < 0 && errno != EAGAIN)
perror("read from fdnet device fails");

return -1;
}

static int linux_fdnet_net_poll(struct lkl_netdev *nd, int events)
{
struct lkl_netdev_linux_fdnet *nd_fdnet =
container_of(nd, struct lkl_netdev_linux_fdnet, dev);
int epoll_fd = -1;
struct epoll_event ev[2];
int ret;
const int is_rx = events & LKL_DEV_NET_POLL_RX;
const int is_tx = events & LKL_DEV_NET_POLL_TX;
int i;
int ret_ev = 0;
unsigned int event;

if (is_rx && is_tx) {
fprintf(stderr, "both LKL_DEV_NET_POLL_RX and "
"LKL_DEV_NET_POLL_TX are set\n");
lkl_host_ops.panic();
return -1;
}
if (!is_rx && !is_tx) {
fprintf(stderr, "Neither LKL_DEV_NET_POLL_RX nor"
" LKL_DEV_NET_POLL_TX are set.\n");
lkl_host_ops.panic();
return -1;
}

if (is_rx)
epoll_fd = nd_fdnet->epoll_rx_fd;
else if (is_tx)
epoll_fd = nd_fdnet->epoll_tx_fd;

do {
ret = epoll_wait(epoll_fd, ev, 2, -1);
} while (ret == -1 && errno == EINTR);
if (ret < 0) {
perror("epoll_wait");
return -1;
}

for (i = 0; i < ret; ++i) {
if (ev[i].data.fd == nd_fdnet->eventfd)
return -1;
if (ev[i].data.fd == nd_fdnet->fd) {
event = ev[i].events;
if (event & (EPOLLIN | EPOLLPRI))
ret_ev = LKL_DEV_NET_POLL_RX;
else if (event & EPOLLOUT)
ret_ev = LKL_DEV_NET_POLL_TX;
else
return -1;
}
}
return ret_ev;
}

static int linux_fdnet_net_close(struct lkl_netdev *nd)
{
long buf = 1;
struct lkl_netdev_linux_fdnet *nd_fdnet =
container_of(nd, struct lkl_netdev_linux_fdnet, dev);

if (nd_fdnet->eventfd == -1) {
/* No eventfd support. */
return 0;
}

if (write(nd_fdnet->eventfd, &buf, sizeof(buf)) < 0) {
perror("linux-fdnet: failed to close fd");
/* This should never happen. */
return -1;
}

/* The order that we join in doesn't matter. */
if (lkl_host_ops.thread_join(nd->rx_tid) ||
lkl_host_ops.thread_join(nd->tx_tid))
return -1;

/* nor does the order that we close */
if (close(nd_fdnet->fd) || close(nd_fdnet->eventfd) ||
close(nd_fdnet->epoll_rx_fd) || close(nd_fdnet->epoll_tx_fd)) {
perror("linux-fdnet net_close fd");
return -1;
}

return 0;
}

struct lkl_dev_net_ops linux_fdnet_net_ops = {
.tx = linux_fdnet_net_tx,
.rx = linux_fdnet_net_rx,
.poll = linux_fdnet_net_poll,
.close = linux_fdnet_net_close,
};

static int add_to_epoll(int epoll_fd, int fd, unsigned int events)
{
struct epoll_event ev;
int ret;

memset(&ev, 0, sizeof(ev));
ev.events = events;
ev.data.fd = fd;
ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev);
if (ret) {
perror("EPOLL_CTL_ADD fails");
return -1;
}
return 0;
}

static int create_epoll_fd(int fd, unsigned int events)
{
int ret = epoll_create1(0);

if (ret < 0) {
perror("epoll_create1");
return -1;
}
if (add_to_epoll(ret, fd, events)) {
close(ret);
return -1;
}
return ret;
}


struct lkl_netdev_linux_fdnet *lkl_register_netdev_linux_fdnet(int fd)
{
struct lkl_netdev_linux_fdnet *nd;

nd = (struct lkl_netdev_linux_fdnet *)
malloc(sizeof(struct lkl_netdev_linux_fdnet));
if (!nd) {
fprintf(stderr, "fdnet: failed to allocate memory\n");
/* TODO: propagate the error state, maybe use errno for that? */
return NULL;
}

nd->fd = fd;
/* Making them edge-triggered to save CPU. */
nd->epoll_rx_fd = create_epoll_fd(nd->fd, EPOLLIN | EPOLLPRI | EPOLLET);
nd->epoll_tx_fd = create_epoll_fd(nd->fd, EPOLLOUT | EPOLLET);
if (nd->epoll_rx_fd < 0 || nd->epoll_tx_fd < 0) {
if (nd->epoll_rx_fd >= 0)
close(nd->epoll_rx_fd);
if (nd->epoll_tx_fd >= 0)
close(nd->epoll_tx_fd);
lkl_unregister_netdev_linux_fdnet(nd);
return NULL;
}

if (lkl_netdev_linux_fdnet_ops.eventfd) {
/* eventfd is supported by the host, all is well */
nd->eventfd = lkl_netdev_linux_fdnet_ops.eventfd(
0, EFD_NONBLOCK | EFD_SEMAPHORE);

if (nd->eventfd < 0) {
perror("fdnet: create eventfd");
lkl_unregister_netdev_linux_fdnet(nd);
return NULL;
}
if (add_to_epoll(nd->epoll_rx_fd, nd->eventfd, EPOLLIN) ||
add_to_epoll(nd->epoll_tx_fd, nd->eventfd, EPOLLIN)) {
lkl_unregister_netdev_linux_fdnet(nd);
return NULL;
}
} else {
/* no host eventfd support */
nd->eventfd = -1;
}

nd->dev.ops = &linux_fdnet_net_ops;
return nd;
}

void lkl_unregister_netdev_linux_fdnet(struct lkl_netdev_linux_fdnet *nd)
{
close(nd->eventfd);
close(nd->epoll_rx_fd);
close(nd->epoll_tx_fd);
free(nd);
}
41 changes: 41 additions & 0 deletions tools/lkl/lib/virtio_net_linux_fdnet.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#ifndef _VIRTIO_NET_LINUX_FDNET_H
#define _VIRTIO_NET_LINUX_FDNET_H

struct lkl_netdev_linux_fdnet {
struct lkl_netdev dev;
/* file-descriptor based device */
int fd;
/* Needed to initiate shutdown */
int eventfd;
/* epoll fds for rx and tx */
int epoll_rx_fd;
int epoll_tx_fd;
};

extern struct lkl_netdev_linux_fdnet_ops {
/*
* We need this so that we can "unhijack" this function in
* case we decided to hijack it.
*/
int (*eventfd)(unsigned int initval, int flags);
} lkl_netdev_linux_fdnet_ops;

/**
* lkl_register_netdev_linux_fdnet - register a file descriptor-based network
* device as a NIC
*
* @fd - a POSIX file descriptor number for input/output
* @returns a struct lkl_netdev_linux_fdnet entry for virtio-net
*/
struct lkl_netdev_linux_fdnet *lkl_register_netdev_linux_fdnet(int fd);


/**
* lkl_unregister_netdev_linux_fdnet - unregister a file descriptor-based
* network device as a NIC
*
* @nd - a struct lkl_netdev_linux_fdnet entry to be unregistered
*/
void lkl_unregister_netdev_linux_fdnet(struct lkl_netdev_linux_fdnet *nd);

#endif /* _VIRTIO_NET_LINUX_FDNET_H*/
Loading

0 comments on commit 7881bd3

Please sign in to comment.