Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add raw socket (AF_PACKET) backend for virtio-net host #171

Merged
2 commits merged into from
Jul 6, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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