1
0
Fork 0

Merge git://git.kernel.org/pub/scm/linux/kernel/git/rw/uml

Pull UML updates from Richard Weinberger:

 - a new and faster epoll based IRQ controller and NIC driver

 - misc fixes and janitorial updates

* git://git.kernel.org/pub/scm/linux/kernel/git/rw/uml:
  Fix vector raw inintialization logic
  Migrate vector timers to new timer API
  um: Compile with modern headers
  um: vector: Fix an error handling path in 'vector_parse()'
  um: vector: Fix a memory allocation check
  um: vector: fix missing unlock on error in vector_net_open()
  um: Add missing EXPORT for free_irq_by_fd()
  High Performance UML Vector Network Driver
  Epoll based IRQ controller
  um: Use POSIX ucontext_t instead of struct ucontext
  um: time: Use timespec64 for persistent clock
  um: Restore symbol versions for __memcpy and memcpy
hifive-unleashed-5.1
Linus Torvalds 2018-04-11 16:36:47 -07:00
commit 375479c386
23 changed files with 3417 additions and 347 deletions

View File

@ -109,6 +109,17 @@ config UML_NET_DAEMON
more than one without conflict. If you don't need UML networking,
say N.
config UML_NET_VECTOR
bool "Vector I/O high performance network devices"
depends on UML_NET
help
This User-Mode Linux network driver uses multi-message send
and receive functions. The host running the UML guest must have
a linux kernel version above 3.0 and a libc version > 2.13.
This driver provides tap, raw, gre and l2tpv3 network transports
with up to 4 times higher network throughput than the UML network
drivers.
config UML_NET_VDE
bool "VDE transport"
depends on UML_NET

View File

@ -9,6 +9,7 @@
slip-objs := slip_kern.o slip_user.o
slirp-objs := slirp_kern.o slirp_user.o
daemon-objs := daemon_kern.o daemon_user.o
vector-objs := vector_kern.o vector_user.o vector_transports.o
umcast-objs := umcast_kern.o umcast_user.o
net-objs := net_kern.o net_user.o
mconsole-objs := mconsole_kern.o mconsole_user.o
@ -43,6 +44,7 @@ obj-$(CONFIG_STDERR_CONSOLE) += stderr_console.o
obj-$(CONFIG_UML_NET_SLIP) += slip.o slip_common.o
obj-$(CONFIG_UML_NET_SLIRP) += slirp.o slip_common.o
obj-$(CONFIG_UML_NET_DAEMON) += daemon.o
obj-$(CONFIG_UML_NET_VECTOR) += vector.o
obj-$(CONFIG_UML_NET_VDE) += vde.o
obj-$(CONFIG_UML_NET_MCAST) += umcast.o
obj-$(CONFIG_UML_NET_PCAP) += pcap.o
@ -61,7 +63,7 @@ obj-$(CONFIG_BLK_DEV_COW_COMMON) += cow_user.o
obj-$(CONFIG_UML_RANDOM) += random.o
# pcap_user.o must be added explicitly.
USER_OBJS := fd.o null.o pty.o tty.o xterm.o slip_common.o pcap_user.o vde_user.o
USER_OBJS := fd.o null.o pty.o tty.o xterm.o slip_common.o pcap_user.o vde_user.o vector_user.o
CFLAGS_null.o = -DDEV_NULL=$(DEV_NULL_PATH)
include arch/um/scripts/Makefile.rules

View File

@ -171,56 +171,19 @@ int enable_chan(struct line *line)
return err;
}
/* Items are added in IRQ context, when free_irq can't be called, and
* removed in process context, when it can.
* This handles interrupt sources which disappear, and which need to
* be permanently disabled. This is discovered in IRQ context, but
* the freeing of the IRQ must be done later.
*/
static DEFINE_SPINLOCK(irqs_to_free_lock);
static LIST_HEAD(irqs_to_free);
void free_irqs(void)
{
struct chan *chan;
LIST_HEAD(list);
struct list_head *ele;
unsigned long flags;
spin_lock_irqsave(&irqs_to_free_lock, flags);
list_splice_init(&irqs_to_free, &list);
spin_unlock_irqrestore(&irqs_to_free_lock, flags);
list_for_each(ele, &list) {
chan = list_entry(ele, struct chan, free_list);
if (chan->input && chan->enabled)
um_free_irq(chan->line->driver->read_irq, chan);
if (chan->output && chan->enabled)
um_free_irq(chan->line->driver->write_irq, chan);
chan->enabled = 0;
}
}
static void close_one_chan(struct chan *chan, int delay_free_irq)
{
unsigned long flags;
if (!chan->opened)
return;
if (delay_free_irq) {
spin_lock_irqsave(&irqs_to_free_lock, flags);
list_add(&chan->free_list, &irqs_to_free);
spin_unlock_irqrestore(&irqs_to_free_lock, flags);
}
else {
if (chan->input && chan->enabled)
um_free_irq(chan->line->driver->read_irq, chan);
if (chan->output && chan->enabled)
um_free_irq(chan->line->driver->write_irq, chan);
chan->enabled = 0;
}
/* we can safely call free now - it will be marked
* as free and freed once the IRQ stopped processing
*/
if (chan->input && chan->enabled)
um_free_irq(chan->line->driver->read_irq, chan);
if (chan->output && chan->enabled)
um_free_irq(chan->line->driver->write_irq, chan);
chan->enabled = 0;
if (chan->ops->close != NULL)
(*chan->ops->close)(chan->fd, chan->data);

View File

@ -284,7 +284,7 @@ int line_setup_irq(int fd, int input, int output, struct line *line, void *data)
if (err)
return err;
if (output)
err = um_request_irq(driver->write_irq, fd, IRQ_WRITE,
err = um_request_irq(driver->write_irq, fd, IRQ_NONE,
line_write_interrupt, IRQF_SHARED,
driver->write_irq_name, data);
return err;

View File

@ -288,7 +288,7 @@ static void uml_net_user_timer_expire(struct timer_list *t)
#endif
}
static void setup_etheraddr(struct net_device *dev, char *str)
void uml_net_setup_etheraddr(struct net_device *dev, char *str)
{
unsigned char *addr = dev->dev_addr;
char *end;
@ -412,7 +412,7 @@ static void eth_configure(int n, void *init, char *mac,
*/
snprintf(dev->name, sizeof(dev->name), "eth%d", n);
setup_etheraddr(dev, mac);
uml_net_setup_etheraddr(dev, mac);
printk(KERN_INFO "Netdevice %d (%pM) : ", n, dev->dev_addr);

View File

@ -13,6 +13,7 @@
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <init.h>
#include <irq_kern.h>
#include <os.h>
@ -154,7 +155,14 @@ err_out_cleanup_hw:
/*
* rng_cleanup - shutdown RNG module
*/
static void __exit rng_cleanup (void)
static void cleanup(void)
{
free_irq_by_fd(random_fd);
os_close_file(random_fd);
}
static void __exit rng_cleanup(void)
{
os_close_file(random_fd);
misc_deregister (&rng_miscdev);
@ -162,6 +170,7 @@ static void __exit rng_cleanup (void)
module_init (rng_init);
module_exit (rng_cleanup);
__uml_exitcall(cleanup);
MODULE_DESCRIPTION("UML Host Random Number Generator (RNG) driver");
MODULE_LICENSE("GPL");

View File

@ -1587,11 +1587,11 @@ int io_thread(void *arg)
do {
res = os_write_file(kernel_fd, ((char *) io_req_buffer) + written, n);
if (res > 0) {
if (res >= 0) {
written += res;
} else {
if (res != -EAGAIN) {
printk("io_thread - read failed, fd = %d, "
printk("io_thread - write failed, fd = %d, "
"err = %d\n", kernel_fd, -n);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,130 @@
/*
* Copyright (C) 2002 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
* Licensed under the GPL
*/
#ifndef __UM_VECTOR_KERN_H
#define __UM_VECTOR_KERN_H
#include <linux/netdevice.h>
#include <linux/platform_device.h>
#include <linux/skbuff.h>
#include <linux/socket.h>
#include <linux/list.h>
#include <linux/ctype.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include "vector_user.h"
/* Queue structure specially adapted for multiple enqueue/dequeue
* in a mmsgrecv/mmsgsend context
*/
/* Dequeue method */
#define QUEUE_SENDMSG 0
#define QUEUE_SENDMMSG 1
#define VECTOR_RX 1
#define VECTOR_TX (1 << 1)
#define VECTOR_BPF (1 << 2)
#define VECTOR_QDISC_BYPASS (1 << 3)
#define ETH_MAX_PACKET 1500
#define ETH_HEADER_OTHER 32 /* just in case someone decides to go mad on QnQ */
struct vector_queue {
struct mmsghdr *mmsg_vector;
void **skbuff_vector;
/* backlink to device which owns us */
struct net_device *dev;
spinlock_t head_lock;
spinlock_t tail_lock;
int queue_depth, head, tail, max_depth, max_iov_frags;
short options;
};
struct vector_estats {
uint64_t rx_queue_max;
uint64_t rx_queue_running_average;
uint64_t tx_queue_max;
uint64_t tx_queue_running_average;
uint64_t rx_encaps_errors;
uint64_t tx_timeout_count;
uint64_t tx_restart_queue;
uint64_t tx_kicks;
uint64_t tx_flow_control_xon;
uint64_t tx_flow_control_xoff;
uint64_t rx_csum_offload_good;
uint64_t rx_csum_offload_errors;
uint64_t sg_ok;
uint64_t sg_linearized;
};
#define VERIFY_HEADER_NOK -1
#define VERIFY_HEADER_OK 0
#define VERIFY_CSUM_OK 1
struct vector_private {
struct list_head list;
spinlock_t lock;
struct net_device *dev;
int unit;
/* Timeout timer in TX */
struct timer_list tl;
/* Scheduled "remove device" work */
struct work_struct reset_tx;
struct vector_fds *fds;
struct vector_queue *rx_queue;
struct vector_queue *tx_queue;
int rx_irq;
int tx_irq;
struct arglist *parsed;
void *transport_data; /* transport specific params if needed */
int max_packet;
int req_size; /* different from max packet - used for TSO */
int headroom;
int options;
/* remote address if any - some transports will leave this as null */
int header_size;
int rx_header_size;
int coalesce;
void *header_rxbuffer;
void *header_txbuffer;
int (*form_header)(uint8_t *header,
struct sk_buff *skb, struct vector_private *vp);
int (*verify_header)(uint8_t *header,
struct sk_buff *skb, struct vector_private *vp);
spinlock_t stats_lock;
struct tasklet_struct tx_poll;
bool rexmit_scheduled;
bool opened;
bool in_write_poll;
/* ethtool stats */
struct vector_estats estats;
void *bpf;
char user[0];
};
extern int build_transport_data(struct vector_private *vp);
#endif

View File

@ -0,0 +1,458 @@
/*
* Copyright (C) 2017 - Cambridge Greys Limited
* Copyright (C) 2011 - 2014 Cisco Systems Inc
* Licensed under the GPL.
*/
#include <linux/etherdevice.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <asm/byteorder.h>
#include <uapi/linux/ip.h>
#include <uapi/linux/virtio_net.h>
#include <linux/virtio_net.h>
#include <linux/virtio_byteorder.h>
#include <linux/netdev_features.h>
#include "vector_user.h"
#include "vector_kern.h"
#define GOOD_LINEAR 512
#define GSO_ERROR "Incoming GSO frames and GRO disabled on the interface"
struct gre_minimal_header {
uint16_t header;
uint16_t arptype;
};
struct uml_gre_data {
uint32_t rx_key;
uint32_t tx_key;
uint32_t sequence;
bool ipv6;
bool has_sequence;
bool pin_sequence;
bool checksum;
bool key;
struct gre_minimal_header expected_header;
uint32_t checksum_offset;
uint32_t key_offset;
uint32_t sequence_offset;
};
struct uml_l2tpv3_data {
uint64_t rx_cookie;
uint64_t tx_cookie;
uint64_t rx_session;
uint64_t tx_session;
uint32_t counter;
bool udp;
bool ipv6;
bool has_counter;
bool pin_counter;
bool cookie;
bool cookie_is_64;
uint32_t cookie_offset;
uint32_t session_offset;
uint32_t counter_offset;
};
static int l2tpv3_form_header(uint8_t *header,
struct sk_buff *skb, struct vector_private *vp)
{
struct uml_l2tpv3_data *td = vp->transport_data;
uint32_t *counter;
if (td->udp)
*(uint32_t *) header = cpu_to_be32(L2TPV3_DATA_PACKET);
(*(uint32_t *) (header + td->session_offset)) = td->tx_session;
if (td->cookie) {
if (td->cookie_is_64)
(*(uint64_t *)(header + td->cookie_offset)) =
td->tx_cookie;
else
(*(uint32_t *)(header + td->cookie_offset)) =
td->tx_cookie;
}
if (td->has_counter) {
counter = (uint32_t *)(header + td->counter_offset);
if (td->pin_counter) {
*counter = 0;
} else {
td->counter++;
*counter = cpu_to_be32(td->counter);
}
}
return 0;
}
static int gre_form_header(uint8_t *header,
struct sk_buff *skb, struct vector_private *vp)
{
struct uml_gre_data *td = vp->transport_data;
uint32_t *sequence;
*((uint32_t *) header) = *((uint32_t *) &td->expected_header);
if (td->key)
(*(uint32_t *) (header + td->key_offset)) = td->tx_key;
if (td->has_sequence) {
sequence = (uint32_t *)(header + td->sequence_offset);
if (td->pin_sequence)
*sequence = 0;
else
*sequence = cpu_to_be32(++td->sequence);
}
return 0;
}
static int raw_form_header(uint8_t *header,
struct sk_buff *skb, struct vector_private *vp)
{
struct virtio_net_hdr *vheader = (struct virtio_net_hdr *) header;
virtio_net_hdr_from_skb(
skb,
vheader,
virtio_legacy_is_little_endian(),
false
);
return 0;
}
static int l2tpv3_verify_header(
uint8_t *header, struct sk_buff *skb, struct vector_private *vp)
{
struct uml_l2tpv3_data *td = vp->transport_data;
uint32_t *session;
uint64_t cookie;
if ((!td->udp) && (!td->ipv6))
header += sizeof(struct iphdr) /* fix for ipv4 raw */;
/* we do not do a strict check for "data" packets as per
* the RFC spec because the pure IP spec does not have
* that anyway.
*/
if (td->cookie) {
if (td->cookie_is_64)
cookie = *(uint64_t *)(header + td->cookie_offset);
else
cookie = *(uint32_t *)(header + td->cookie_offset);
if (cookie != td->rx_cookie) {
if (net_ratelimit())
netdev_err(vp->dev, "uml_l2tpv3: unknown cookie id");
return -1;
}
}
session = (uint32_t *) (header + td->session_offset);
if (*session != td->rx_session) {
if (net_ratelimit())
netdev_err(vp->dev, "uml_l2tpv3: session mismatch");
return -1;
}
return 0;
}
static int gre_verify_header(
uint8_t *header, struct sk_buff *skb, struct vector_private *vp)
{
uint32_t key;
struct uml_gre_data *td = vp->transport_data;
if (!td->ipv6)
header += sizeof(struct iphdr) /* fix for ipv4 raw */;
if (*((uint32_t *) header) != *((uint32_t *) &td->expected_header)) {
if (net_ratelimit())
netdev_err(vp->dev, "header type disagreement, expecting %0x, got %0x",
*((uint32_t *) &td->expected_header),
*((uint32_t *) header)
);
return -1;
}
if (td->key) {
key = (*(uint32_t *)(header + td->key_offset));
if (key != td->rx_key) {
if (net_ratelimit())
netdev_err(vp->dev, "unknown key id %0x, expecting %0x",
key, td->rx_key);
return -1;
}
}
return 0;
}
static int raw_verify_header(
uint8_t *header, struct sk_buff *skb, struct vector_private *vp)
{
struct virtio_net_hdr *vheader = (struct virtio_net_hdr *) header;
if ((vheader->gso_type != VIRTIO_NET_HDR_GSO_NONE) &&
(vp->req_size != 65536)) {
if (net_ratelimit())
netdev_err(
vp->dev,
GSO_ERROR
);
}
if ((vheader->flags & VIRTIO_NET_HDR_F_DATA_VALID) > 0)
return 1;
virtio_net_hdr_to_skb(skb, vheader, virtio_legacy_is_little_endian());
return 0;
}
static bool get_uint_param(
struct arglist *def, char *param, unsigned int *result)
{
char *arg = uml_vector_fetch_arg(def, param);
if (arg != NULL) {
if (kstrtoint(arg, 0, result) == 0)
return true;
}
return false;
}
static bool get_ulong_param(
struct arglist *def, char *param, unsigned long *result)
{
char *arg = uml_vector_fetch_arg(def, param);
if (arg != NULL) {
if (kstrtoul(arg, 0, result) == 0)
return true;
return true;
}
return false;
}
static int build_gre_transport_data(struct vector_private *vp)
{
struct uml_gre_data *td;
int temp_int;
int temp_rx;
int temp_tx;
vp->transport_data = kmalloc(sizeof(struct uml_gre_data), GFP_KERNEL);
if (vp->transport_data == NULL)
return -ENOMEM;
td = vp->transport_data;
td->sequence = 0;
td->expected_header.arptype = GRE_IRB;
td->expected_header.header = 0;
vp->form_header = &gre_form_header;
vp->verify_header = &gre_verify_header;
vp->header_size = 4;
td->key_offset = 4;
td->sequence_offset = 4;
td->checksum_offset = 4;
td->ipv6 = false;
if (get_uint_param(vp->parsed, "v6", &temp_int)) {
if (temp_int > 0)
td->ipv6 = true;
}
td->key = false;
if (get_uint_param(vp->parsed, "rx_key", &temp_rx)) {
if (get_uint_param(vp->parsed, "tx_key", &temp_tx)) {
td->key = true;
td->expected_header.header |= GRE_MODE_KEY;
td->rx_key = cpu_to_be32(temp_rx);
td->tx_key = cpu_to_be32(temp_tx);
vp->header_size += 4;
td->sequence_offset += 4;
} else {
return -EINVAL;
}
}
td->sequence = false;
if (get_uint_param(vp->parsed, "sequence", &temp_int)) {
if (temp_int > 0) {
vp->header_size += 4;
td->has_sequence = true;
td->expected_header.header |= GRE_MODE_SEQUENCE;
if (get_uint_param(
vp->parsed, "pin_sequence", &temp_int)) {
if (temp_int > 0)
td->pin_sequence = true;
}
}
}
vp->rx_header_size = vp->header_size;
if (!td->ipv6)
vp->rx_header_size += sizeof(struct iphdr);
return 0;
}
static int build_l2tpv3_transport_data(struct vector_private *vp)
{
struct uml_l2tpv3_data *td;
int temp_int, temp_rxs, temp_txs;
unsigned long temp_rx;
unsigned long temp_tx;
vp->transport_data = kmalloc(
sizeof(struct uml_l2tpv3_data), GFP_KERNEL);
if (vp->transport_data == NULL)
return -ENOMEM;
td = vp->transport_data;
vp->form_header = &l2tpv3_form_header;
vp->verify_header = &l2tpv3_verify_header;
td->counter = 0;
vp->header_size = 4;
td->session_offset = 0;
td->cookie_offset = 4;
td->counter_offset = 4;
td->ipv6 = false;
if (get_uint_param(vp->parsed, "v6", &temp_int)) {
if (temp_int > 0)
td->ipv6 = true;
}
if (get_uint_param(vp->parsed, "rx_session", &temp_rxs)) {
if (get_uint_param(vp->parsed, "tx_session", &temp_txs)) {
td->tx_session = cpu_to_be32(temp_txs);
td->rx_session = cpu_to_be32(temp_rxs);
} else {
return -EINVAL;
}
} else {
return -EINVAL;
}
td->cookie_is_64 = false;
if (get_uint_param(vp->parsed, "cookie64", &temp_int)) {
if (temp_int > 0)
td->cookie_is_64 = true;
}
td->cookie = false;
if (get_ulong_param(vp->parsed, "rx_cookie", &temp_rx)) {
if (get_ulong_param(vp->parsed, "tx_cookie", &temp_tx)) {
td->cookie = true;
if (td->cookie_is_64) {
td->rx_cookie = cpu_to_be64(temp_rx);
td->tx_cookie = cpu_to_be64(temp_tx);
vp->header_size += 8;
td->counter_offset += 8;
} else {
td->rx_cookie = cpu_to_be32(temp_rx);
td->tx_cookie = cpu_to_be32(temp_tx);
vp->header_size += 4;
td->counter_offset += 4;
}
} else {
return -EINVAL;
}
}
td->has_counter = false;
if (get_uint_param(vp->parsed, "counter", &temp_int)) {
if (temp_int > 0) {
td->has_counter = true;
vp->header_size += 4;
if (get_uint_param(
vp->parsed, "pin_counter", &temp_int)) {
if (temp_int > 0)
td->pin_counter = true;
}
}
}
if (get_uint_param(vp->parsed, "udp", &temp_int)) {
if (temp_int > 0) {
td->udp = true;
vp->header_size += 4;
td->counter_offset += 4;
td->session_offset += 4;
td->cookie_offset += 4;
}
}
vp->rx_header_size = vp->header_size;
if ((!td->ipv6) && (!td->udp))
vp->rx_header_size += sizeof(struct iphdr);
return 0;
}
static int build_raw_transport_data(struct vector_private *vp)
{
if (uml_raw_enable_vnet_headers(vp->fds->rx_fd)) {
if (!uml_raw_enable_vnet_headers(vp->fds->tx_fd))
return -1;
vp->form_header = &raw_form_header;
vp->verify_header = &raw_verify_header;
vp->header_size = sizeof(struct virtio_net_hdr);
vp->rx_header_size = sizeof(struct virtio_net_hdr);
vp->dev->hw_features |= (NETIF_F_TSO | NETIF_F_GRO);
vp->dev->features |=
(NETIF_F_RXCSUM | NETIF_F_HW_CSUM |
NETIF_F_TSO | NETIF_F_GRO);
netdev_info(
vp->dev,
"raw: using vnet headers for tso and tx/rx checksum"
);
}
return 0;
}
static int build_tap_transport_data(struct vector_private *vp)
{
if (uml_raw_enable_vnet_headers(vp->fds->rx_fd)) {
vp->form_header = &raw_form_header;
vp->verify_header = &raw_verify_header;
vp->header_size = sizeof(struct virtio_net_hdr);
vp->rx_header_size = sizeof(struct virtio_net_hdr);
vp->dev->hw_features |=
(NETIF_F_TSO | NETIF_F_GSO | NETIF_F_GRO);
vp->dev->features |=
(NETIF_F_RXCSUM | NETIF_F_HW_CSUM |
NETIF_F_TSO | NETIF_F_GSO | NETIF_F_GRO);
netdev_info(
vp->dev,
"tap/raw: using vnet headers for tso and tx/rx checksum"
);
} else {
return 0; /* do not try to enable tap too if raw failed */
}
if (uml_tap_enable_vnet_headers(vp->fds->tx_fd))
return 0;
return -1;
}
int build_transport_data(struct vector_private *vp)
{
char *transport = uml_vector_fetch_arg(vp->parsed, "transport");
if (strncmp(transport, TRANS_GRE, TRANS_GRE_LEN) == 0)
return build_gre_transport_data(vp);
if (strncmp(transport, TRANS_L2TPV3, TRANS_L2TPV3_LEN) == 0)
return build_l2tpv3_transport_data(vp);
if (strncmp(transport, TRANS_RAW, TRANS_RAW_LEN) == 0)
return build_raw_transport_data(vp);
if (strncmp(transport, TRANS_TAP, TRANS_TAP_LEN) == 0)
return build_tap_transport_data(vp);
return 0;
}

View File

@ -0,0 +1,590 @@
/*
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
* Licensed under the GPL
*/
#include <stdio.h>
#include <unistd.h>
#include <stdarg.h>
#include <errno.h>
#include <stddef.h>
#include <string.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <linux/if_tun.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/ethernet.h>
#include <netinet/ip.h>
#include <netinet/ether.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <linux/virtio_net.h>
#include <netdb.h>
#include <stdlib.h>
#include <os.h>
#include <um_malloc.h>
#include "vector_user.h"
#define ID_GRE 0
#define ID_L2TPV3 1
#define ID_MAX 1
#define TOKEN_IFNAME "ifname"
#define TRANS_RAW "raw"
#define TRANS_RAW_LEN strlen(TRANS_RAW)
#define VNET_HDR_FAIL "could not enable vnet headers on fd %d"
#define TUN_GET_F_FAIL "tapraw: TUNGETFEATURES failed: %s"
#define L2TPV3_BIND_FAIL "l2tpv3_open : could not bind socket err=%i"
#define BPF_ATTACH_FAIL "Failed to attach filter size %d to %d, err %d\n"
/* This is very ugly and brute force lookup, but it is done
* only once at initialization so not worth doing hashes or
* anything more intelligent
*/
char *uml_vector_fetch_arg(struct arglist *ifspec, char *token)
{
int i;
for (i = 0; i < ifspec->numargs; i++) {
if (strcmp(ifspec->tokens[i], token) == 0)
return ifspec->values[i];
}
return NULL;
}
struct arglist *uml_parse_vector_ifspec(char *arg)
{
struct arglist *result;
int pos, len;
bool parsing_token = true, next_starts = true;
if (arg == NULL)
return NULL;
result = uml_kmalloc(sizeof(struct arglist), UM_GFP_KERNEL);
if (result == NULL)
return NULL;
result->numargs = 0;
len = strlen(arg);
for (pos = 0; pos < len; pos++) {
if (next_starts) {
if (parsing_token) {
result->tokens[result->numargs] = arg + pos;
} else {
result->values[result->numargs] = arg + pos;
result->numargs++;
}
next_starts = false;
}
if (*(arg + pos) == '=') {
if (parsing_token)
parsing_token = false;
else
goto cleanup;
next_starts = true;
(*(arg + pos)) = '\0';
}
if (*(arg + pos) == ',') {
parsing_token = true;
next_starts = true;
(*(arg + pos)) = '\0';
}
}
return result;
cleanup:
printk(UM_KERN_ERR "vector_setup - Couldn't parse '%s'\n", arg);
kfree(result);
return NULL;
}
/*
* Socket/FD configuration functions. These return an structure
* of rx and tx descriptors to cover cases where these are not
* the same (f.e. read via raw socket and write via tap).
*/
#define PATH_NET_TUN "/dev/net/tun"
static struct vector_fds *user_init_tap_fds(struct arglist *ifspec)
{
struct ifreq ifr;
int fd = -1;
struct sockaddr_ll sock;
int err = -ENOMEM, offload;
char *iface;
struct vector_fds *result = NULL;
iface = uml_vector_fetch_arg(ifspec, TOKEN_IFNAME);
if (iface == NULL) {
printk(UM_KERN_ERR "uml_tap: failed to parse interface spec\n");
goto tap_cleanup;
}
result = uml_kmalloc(sizeof(struct vector_fds), UM_GFP_KERNEL);
if (result == NULL) {
printk(UM_KERN_ERR "uml_tap: failed to allocate file descriptors\n");
goto tap_cleanup;
}
result->rx_fd = -1;
result->tx_fd = -1;
result->remote_addr = NULL;
result->remote_addr_size = 0;
/* TAP */
fd = open(PATH_NET_TUN, O_RDWR);
if (fd < 0) {
printk(UM_KERN_ERR "uml_tap: failed to open tun device\n");
goto tap_cleanup;
}
result->tx_fd = fd;
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_VNET_HDR;
strncpy((char *)&ifr.ifr_name, iface, sizeof(ifr.ifr_name) - 1);
err = ioctl(fd, TUNSETIFF, (void *) &ifr);
if (err != 0) {
printk(UM_KERN_ERR "uml_tap: failed to select tap interface\n");
goto tap_cleanup;
}
offload = TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6;
ioctl(fd, TUNSETOFFLOAD, offload);
/* RAW */
fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (fd == -1) {
printk(UM_KERN_ERR
"uml_tap: failed to create socket: %i\n", -errno);
goto tap_cleanup;
}
result->rx_fd = fd;
memset(&ifr, 0, sizeof(ifr));
strncpy((char *)&ifr.ifr_name, iface, sizeof(ifr.ifr_name) - 1);
if (ioctl(fd, SIOCGIFINDEX, (void *) &ifr) < 0) {
printk(UM_KERN_ERR
"uml_tap: failed to set interface: %i\n", -errno);
goto tap_cleanup;
}
sock.sll_family = AF_PACKET;
sock.sll_protocol = htons(ETH_P_ALL);
sock.sll_ifindex = ifr.ifr_ifindex;
if (bind(fd,
(struct sockaddr *) &sock, sizeof(struct sockaddr_ll)) < 0) {
printk(UM_KERN_ERR
"user_init_tap: failed to bind raw pair, err %d\n",
-errno);
goto tap_cleanup;
}
return result;
tap_cleanup:
printk(UM_KERN_ERR "user_init_tap: init failed, error %d", err);
if (result != NULL) {
if (result->rx_fd >= 0)
os_close_file(result->rx_fd);
if (result->tx_fd >= 0)
os_close_file(result->tx_fd);
kfree(result);
}
return NULL;
}
static struct vector_fds *user_init_raw_fds(struct arglist *ifspec)
{
struct ifreq ifr;
int rxfd = -1, txfd = -1;
struct sockaddr_ll sock;
int err = -ENOMEM;
char *iface;
struct vector_fds *result = NULL;
iface = uml_vector_fetch_arg(ifspec, TOKEN_IFNAME);
if (iface == NULL)
goto cleanup;
rxfd = socket(AF_PACKET, SOCK_RAW, ETH_P_ALL);
if (rxfd == -1) {
err = -errno;
goto cleanup;
}
txfd = socket(AF_PACKET, SOCK_RAW, 0); /* Turn off RX on this fd */
if (txfd == -1) {
err = -errno;
goto cleanup;
}
memset(&ifr, 0, sizeof(ifr));
strncpy((char *)&ifr.ifr_name, iface, sizeof(ifr.ifr_name) - 1);
if (ioctl(rxfd, SIOCGIFINDEX, (void *) &ifr) < 0) {
err = -errno;
goto cleanup;
}
sock.sll_family = AF_PACKET;
sock.sll_protocol = htons(ETH_P_ALL);
sock.sll_ifindex = ifr.ifr_ifindex;
if (bind(rxfd,
(struct sockaddr *) &sock, sizeof(struct sockaddr_ll)) < 0) {
err = -errno;
goto cleanup;
}
sock.sll_family = AF_PACKET;
sock.sll_protocol = htons(ETH_P_IP);
sock.sll_ifindex = ifr.ifr_ifindex;
if (bind(txfd,
(struct sockaddr *) &sock, sizeof(struct sockaddr_ll)) < 0) {
err = -errno;
goto cleanup;
}
result = uml_kmalloc(sizeof(struct vector_fds), UM_GFP_KERNEL);
if (result != NULL) {
result->rx_fd = rxfd;
result->tx_fd = txfd;
result->remote_addr = NULL;
result->remote_addr_size = 0;
}
return result;
cleanup:
printk(UM_KERN_ERR "user_init_raw: init failed, error %d", err);
if (rxfd >= 0)
os_close_file(rxfd);
if (txfd >= 0)
os_close_file(txfd);
if (result != NULL)
kfree(result);
return NULL;
}
bool uml_raw_enable_qdisc_bypass(int fd)
{
int optval = 1;
if (setsockopt(fd,
SOL_PACKET, PACKET_QDISC_BYPASS,
&optval, sizeof(optval)) != 0) {
return false;
}
return true;
}
bool uml_raw_enable_vnet_headers(int fd)
{
int optval = 1;
if (setsockopt(fd,
SOL_PACKET, PACKET_VNET_HDR,
&optval, sizeof(optval)) != 0) {
printk(UM_KERN_INFO VNET_HDR_FAIL, fd);
return false;
}
return true;
}
bool uml_tap_enable_vnet_headers(int fd)
{
unsigned int features;
int len = sizeof(struct virtio_net_hdr);
if (ioctl(fd, TUNGETFEATURES, &features) == -1) {
printk(UM_KERN_INFO TUN_GET_F_FAIL, strerror(errno));
return false;
}
if ((features & IFF_VNET_HDR) == 0) {
printk(UM_KERN_INFO "tapraw: No VNET HEADER support");
return false;
}
ioctl(fd, TUNSETVNETHDRSZ, &len);
return true;
}
static struct vector_fds *user_init_socket_fds(struct arglist *ifspec, int id)
{
int err = -ENOMEM;
int fd = -1, gairet;
struct addrinfo srchints;
struct addrinfo dsthints;
bool v6, udp;
char *value;
char *src, *dst, *srcport, *dstport;
struct addrinfo *gairesult = NULL;
struct vector_fds *result = NULL;
value = uml_vector_fetch_arg(ifspec, "v6");
v6 = false;
udp = false;
if (value != NULL) {
if (strtol((const char *) value, NULL, 10) > 0)
v6 = true;
}
value = uml_vector_fetch_arg(ifspec, "udp");
if (value != NULL) {
if (strtol((const char *) value, NULL, 10) > 0)
udp = true;
}
src = uml_vector_fetch_arg(ifspec, "src");
dst = uml_vector_fetch_arg(ifspec, "dst");
srcport = uml_vector_fetch_arg(ifspec, "srcport");
dstport = uml_vector_fetch_arg(ifspec, "dstport");
memset(&dsthints, 0, sizeof(dsthints));
if (v6)
dsthints.ai_family = AF_INET6;
else
dsthints.ai_family = AF_INET;
switch (id) {
case ID_GRE:
dsthints.ai_socktype = SOCK_RAW;
dsthints.ai_protocol = IPPROTO_GRE;
break;
case ID_L2TPV3:
if (udp) {
dsthints.ai_socktype = SOCK_DGRAM;
dsthints.ai_protocol = 0;
} else {
dsthints.ai_socktype = SOCK_RAW;
dsthints.ai_protocol = IPPROTO_L2TP;
}
break;
default:
printk(KERN_ERR "Unsupported socket type\n");
return NULL;
}
memcpy(&srchints, &dsthints, sizeof(struct addrinfo));
gairet = getaddrinfo(src, srcport, &dsthints, &gairesult);
if ((gairet != 0) || (gairesult == NULL)) {
printk(UM_KERN_ERR
"socket_open : could not resolve src, error = %s",
gai_strerror(gairet)
);
return NULL;
}
fd = socket(gairesult->ai_family,
gairesult->ai_socktype, gairesult->ai_protocol);
if (fd == -1) {
printk(UM_KERN_ERR
"socket_open : could not open socket, error = %d",
-errno
);
goto cleanup;
}
if (bind(fd,
(struct sockaddr *) gairesult->ai_addr,
gairesult->ai_addrlen)) {
printk(UM_KERN_ERR L2TPV3_BIND_FAIL, errno);
goto cleanup;
}
if (gairesult != NULL)
freeaddrinfo(gairesult);
gairesult = NULL;
gairet = getaddrinfo(dst, dstport, &dsthints, &gairesult);
if ((gairet != 0) || (gairesult == NULL)) {
printk(UM_KERN_ERR
"socket_open : could not resolve dst, error = %s",
gai_strerror(gairet)
);
return NULL;
}
result = uml_kmalloc(sizeof(struct vector_fds), UM_GFP_KERNEL);
if (result != NULL) {
result->rx_fd = fd;
result->tx_fd = fd;
result->remote_addr = uml_kmalloc(
gairesult->ai_addrlen, UM_GFP_KERNEL);
if (result->remote_addr == NULL)
goto cleanup;
result->remote_addr_size = gairesult->ai_addrlen;
memcpy(
result->remote_addr,
gairesult->ai_addr,
gairesult->ai_addrlen
);
}
freeaddrinfo(gairesult);
return result;
cleanup:
if (gairesult != NULL)
freeaddrinfo(gairesult);
printk(UM_KERN_ERR "user_init_socket: init failed, error %d", err);
if (fd >= 0)
os_close_file(fd);
if (result != NULL) {
if (result->remote_addr != NULL)
kfree(result->remote_addr);
kfree(result);
}
return NULL;
}
struct vector_fds *uml_vector_user_open(
int unit,
struct arglist *parsed
)
{
char *transport;
if (parsed == NULL) {
printk(UM_KERN_ERR "no parsed config for unit %d\n", unit);
return NULL;
}
transport = uml_vector_fetch_arg(parsed, "transport");
if (transport == NULL) {
printk(UM_KERN_ERR "missing transport for unit %d\n", unit);
return NULL;
}
if (strncmp(transport, TRANS_RAW, TRANS_RAW_LEN) == 0)
return user_init_raw_fds(parsed);
if (strncmp(transport, TRANS_TAP, TRANS_TAP_LEN) == 0)
return user_init_tap_fds(parsed);
if (strncmp(transport, TRANS_GRE, TRANS_GRE_LEN) == 0)
return user_init_socket_fds(parsed, ID_GRE);
if (strncmp(transport, TRANS_L2TPV3, TRANS_L2TPV3_LEN) == 0)
return user_init_socket_fds(parsed, ID_L2TPV3);
return NULL;
}
int uml_vector_sendmsg(int fd, void *hdr, int flags)
{
int n;
CATCH_EINTR(n = sendmsg(fd, (struct msghdr *) hdr, flags));
if ((n < 0) && (errno == EAGAIN))
return 0;
if (n >= 0)
return n;
else
return -errno;
}
int uml_vector_recvmsg(int fd, void *hdr, int flags)
{
int n;
CATCH_EINTR(n = recvmsg(fd, (struct msghdr *) hdr, flags));
if ((n < 0) && (errno == EAGAIN))
return 0;
if (n >= 0)
return n;
else
return -errno;
}
int uml_vector_writev(int fd, void *hdr, int iovcount)
{
int n;
CATCH_EINTR(n = writev(fd, (struct iovec *) hdr, iovcount));
if ((n < 0) && (errno == EAGAIN))
return 0;
if (n >= 0)
return n;
else
return -errno;
}
int uml_vector_sendmmsg(
int fd,
void *msgvec,
unsigned int vlen,
unsigned int flags)
{
int n;
CATCH_EINTR(n = sendmmsg(fd, (struct mmsghdr *) msgvec, vlen, flags));
if ((n < 0) && (errno == EAGAIN))
return 0;
if (n >= 0)
return n;
else
return -errno;
}
int uml_vector_recvmmsg(
int fd,
void *msgvec,
unsigned int vlen,
unsigned int flags)
{
int n;
CATCH_EINTR(
n = recvmmsg(fd, (struct mmsghdr *) msgvec, vlen, flags, 0));
if ((n < 0) && (errno == EAGAIN))
return 0;
if (n >= 0)
return n;
else
return -errno;
}
int uml_vector_attach_bpf(int fd, void *bpf, int bpf_len)
{
int err = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, bpf, bpf_len);
if (err < 0)
printk(KERN_ERR BPF_ATTACH_FAIL, bpf_len, fd, -errno);
return err;
}
#define DEFAULT_BPF_LEN 6
void *uml_vector_default_bpf(int fd, void *mac)
{
struct sock_filter *bpf;
uint32_t *mac1 = (uint32_t *)(mac + 2);
uint16_t *mac2 = (uint16_t *) mac;
struct sock_fprog bpf_prog = {
.len = 6,
.filter = NULL,
};
bpf = uml_kmalloc(
sizeof(struct sock_filter) * DEFAULT_BPF_LEN, UM_GFP_KERNEL);
if (bpf != NULL) {
bpf_prog.filter = bpf;
/* ld [8] */
bpf[0] = (struct sock_filter){ 0x20, 0, 0, 0x00000008 };
/* jeq #0xMAC[2-6] jt 2 jf 5*/
bpf[1] = (struct sock_filter){ 0x15, 0, 3, ntohl(*mac1)};
/* ldh [6] */
bpf[2] = (struct sock_filter){ 0x28, 0, 0, 0x00000006 };
/* jeq #0xMAC[0-1] jt 4 jf 5 */
bpf[3] = (struct sock_filter){ 0x15, 0, 1, ntohs(*mac2)};
/* ret #0 */
bpf[4] = (struct sock_filter){ 0x6, 0, 0, 0x00000000 };
/* ret #0x40000 */
bpf[5] = (struct sock_filter){ 0x6, 0, 0, 0x00040000 };
if (uml_vector_attach_bpf(
fd, &bpf_prog, sizeof(struct sock_fprog)) < 0) {
kfree(bpf);
bpf = NULL;
}
}
return bpf;
}

View File

@ -0,0 +1,100 @@
/*
* Copyright (C) 2002 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
* Licensed under the GPL
*/
#ifndef __UM_VECTOR_USER_H
#define __UM_VECTOR_USER_H
#define MAXVARGS 20
#define TOKEN_IFNAME "ifname"
#define TRANS_RAW "raw"
#define TRANS_RAW_LEN strlen(TRANS_RAW)
#define TRANS_TAP "tap"
#define TRANS_TAP_LEN strlen(TRANS_TAP)
#define TRANS_GRE "gre"
#define TRANS_GRE_LEN strlen(TRANS_RAW)
#define TRANS_L2TPV3 "l2tpv3"
#define TRANS_L2TPV3_LEN strlen(TRANS_L2TPV3)
#ifndef IPPROTO_GRE
#define IPPROTO_GRE 0x2F
#endif
#define GRE_MODE_CHECKSUM cpu_to_be16(8 << 12) /* checksum */
#define GRE_MODE_RESERVED cpu_to_be16(4 << 12) /* unused */
#define GRE_MODE_KEY cpu_to_be16(2 << 12) /* KEY present */
#define GRE_MODE_SEQUENCE cpu_to_be16(1 << 12) /* sequence */
#define GRE_IRB cpu_to_be16(0x6558)
#define L2TPV3_DATA_PACKET 0x30000
/* IANA-assigned IP protocol ID for L2TPv3 */
#ifndef IPPROTO_L2TP
#define IPPROTO_L2TP 0x73
#endif
struct arglist {
int numargs;
char *tokens[MAXVARGS];
char *values[MAXVARGS];
};
/* Separating read and write FDs allows us to have different
* rx and tx method. Example - read tap via raw socket using
* recvmmsg, write using legacy tap write calls
*/
struct vector_fds {
int rx_fd;
int tx_fd;
void *remote_addr;
int remote_addr_size;
};
#define VECTOR_READ 1
#define VECTOR_WRITE (1 < 1)
#define VECTOR_HEADERS (1 < 2)
extern struct arglist *uml_parse_vector_ifspec(char *arg);
extern struct vector_fds *uml_vector_user_open(
int unit,
struct arglist *parsed
);
extern char *uml_vector_fetch_arg(
struct arglist *ifspec,
char *token
);
extern int uml_vector_recvmsg(int fd, void *hdr, int flags);
extern int uml_vector_sendmsg(int fd, void *hdr, int flags);
extern int uml_vector_writev(int fd, void *hdr, int iovcount);
extern int uml_vector_sendmmsg(
int fd, void *msgvec,
unsigned int vlen,
unsigned int flags
);
extern int uml_vector_recvmmsg(
int fd,
void *msgvec,
unsigned int vlen,
unsigned int flags
);
extern void *uml_vector_default_bpf(int fd, void *mac);
extern int uml_vector_attach_bpf(int fd, void *bpf, int bpf_len);
extern bool uml_raw_enable_qdisc_bypass(int fd);
extern bool uml_raw_enable_vnet_headers(int fd);
extern bool uml_tap_enable_vnet_headers(int fd);
#endif

View File

@ -0,0 +1 @@
#include <asm-generic/asm-prototypes.h>

View File

@ -18,7 +18,19 @@
#define XTERM_IRQ 13
#define RANDOM_IRQ 14
#ifdef CONFIG_UML_NET_VECTOR
#define VECTOR_BASE_IRQ 15
#define VECTOR_IRQ_SPACE 8
#define LAST_IRQ (VECTOR_IRQ_SPACE + VECTOR_BASE_IRQ)
#else
#define LAST_IRQ RANDOM_IRQ
#endif
#define NR_IRQS (LAST_IRQ + 1)
#endif

View File

@ -7,6 +7,7 @@
#define __IRQ_USER_H__
#include <sysdep/ptrace.h>
#include <stdbool.h>
struct irq_fd {
struct irq_fd *next;
@ -15,10 +16,17 @@ struct irq_fd {
int type;
int irq;
int events;
int current_events;
bool active;
bool pending;
bool purge;
};
enum { IRQ_READ, IRQ_WRITE };
#define IRQ_READ 0
#define IRQ_WRITE 1
#define IRQ_NONE 2
#define MAX_IRQ_TYPE (IRQ_NONE + 1)
struct siginfo;
extern void sigio_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs);

View File

@ -65,5 +65,7 @@ extern int tap_setup_common(char *str, char *type, char **dev_name,
char **mac_out, char **gate_addr);
extern void register_transport(struct transport *new);
extern unsigned short eth_protocol(struct sk_buff *skb);
extern void uml_net_setup_etheraddr(struct net_device *dev, char *str);
#endif

View File

@ -290,15 +290,16 @@ extern void halt_skas(void);
extern void reboot_skas(void);
/* irq.c */
extern int os_waiting_for_events(struct irq_fd *active_fds);
extern int os_create_pollfd(int fd, int events, void *tmp_pfd, int size_tmpfds);
extern void os_free_irq_by_cb(int (*test)(struct irq_fd *, void *), void *arg,
struct irq_fd *active_fds, struct irq_fd ***last_irq_ptr2);
extern void os_free_irq_later(struct irq_fd *active_fds,
int irq, void *dev_id);
extern int os_get_pollfd(int i);
extern void os_set_pollfd(int i, int fd);
extern int os_waiting_for_events_epoll(void);
extern void *os_epoll_get_data_pointer(int index);
extern int os_epoll_triggered(int index, int events);
extern int os_event_mask(int irq_type);
extern int os_setup_epoll(void);
extern int os_add_epoll_fd(int events, int fd, void *data);
extern int os_mod_epoll_fd(int events, int fd, void *data);
extern int os_del_epoll_fd(int fd);
extern void os_set_ioignore(void);
extern void os_close_epoll_fd(void);
/* sigio.c */
extern int add_sigio_fd(int fd);

View File

@ -1,4 +1,6 @@
/*
* Copyright (C) 2017 - Cambridge Greys Ltd
* Copyright (C) 2011 - 2014 Cisco Systems Inc
* Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
* Licensed under the GPL
* Derived (i.e. mostly copied) from arch/i386/kernel/irq.c:
@ -16,243 +18,362 @@
#include <as-layout.h>
#include <kern_util.h>
#include <os.h>
#include <irq_user.h>
/*
* This list is accessed under irq_lock, except in sigio_handler,
* where it is safe from being modified. IRQ handlers won't change it -
* if an IRQ source has vanished, it will be freed by free_irqs just
* before returning from sigio_handler. That will process a separate
* list of irqs to free, with its own locking, coming back here to
* remove list elements, taking the irq_lock to do so.
/* When epoll triggers we do not know why it did so
* we can also have different IRQs for read and write.
* This is why we keep a small irq_fd array for each fd -
* one entry per IRQ type
*/
static struct irq_fd *active_fds = NULL;
static struct irq_fd **last_irq_ptr = &active_fds;
extern void free_irqs(void);
struct irq_entry {
struct irq_entry *next;
int fd;
struct irq_fd *irq_array[MAX_IRQ_TYPE + 1];
};
void sigio_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs)
{
struct irq_fd *irq_fd;
int n;
while (1) {
n = os_waiting_for_events(active_fds);
if (n <= 0) {
if (n == -EINTR)
continue;
else break;
}
for (irq_fd = active_fds; irq_fd != NULL;
irq_fd = irq_fd->next) {
if (irq_fd->current_events != 0) {
irq_fd->current_events = 0;
do_IRQ(irq_fd->irq, regs);
}
}
}
free_irqs();
}
static struct irq_entry *active_fds;
static DEFINE_SPINLOCK(irq_lock);
static void irq_io_loop(struct irq_fd *irq, struct uml_pt_regs *regs)
{
/*
* irq->active guards against reentry
* irq->pending accumulates pending requests
* if pending is raised the irq_handler is re-run
* until pending is cleared
*/
if (irq->active) {
irq->active = false;
do {
irq->pending = false;
do_IRQ(irq->irq, regs);
} while (irq->pending && (!irq->purge));
if (!irq->purge)
irq->active = true;
} else {
irq->pending = true;
}
}
void sigio_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs)
{
struct irq_entry *irq_entry;
struct irq_fd *irq;
int n, i, j;
while (1) {
/* This is now lockless - epoll keeps back-referencesto the irqs
* which have trigger it so there is no need to walk the irq
* list and lock it every time. We avoid locking by turning off
* IO for a specific fd by executing os_del_epoll_fd(fd) before
* we do any changes to the actual data structures
*/
n = os_waiting_for_events_epoll();
if (n <= 0) {
if (n == -EINTR)
continue;
else
break;
}
for (i = 0; i < n ; i++) {
/* Epoll back reference is the entry with 3 irq_fd
* leaves - one for each irq type.
*/
irq_entry = (struct irq_entry *)
os_epoll_get_data_pointer(i);
for (j = 0; j < MAX_IRQ_TYPE ; j++) {
irq = irq_entry->irq_array[j];
if (irq == NULL)
continue;
if (os_epoll_triggered(i, irq->events) > 0)
irq_io_loop(irq, regs);
if (irq->purge) {
irq_entry->irq_array[j] = NULL;
kfree(irq);
}
}
}
}
}
static int assign_epoll_events_to_irq(struct irq_entry *irq_entry)
{
int i;
int events = 0;
struct irq_fd *irq;
for (i = 0; i < MAX_IRQ_TYPE ; i++) {
irq = irq_entry->irq_array[i];
if (irq != NULL)
events = irq->events | events;
}
if (events > 0) {
/* os_add_epoll will call os_mod_epoll if this already exists */
return os_add_epoll_fd(events, irq_entry->fd, irq_entry);
}
/* No events - delete */
return os_del_epoll_fd(irq_entry->fd);
}
static int activate_fd(int irq, int fd, int type, void *dev_id)
{
struct pollfd *tmp_pfd;
struct irq_fd *new_fd, *irq_fd;
struct irq_fd *new_fd;
struct irq_entry *irq_entry;
int i, err, events;
unsigned long flags;
int events, err, n;
err = os_set_fd_async(fd);
if (err < 0)
goto out;
err = -ENOMEM;
new_fd = kmalloc(sizeof(struct irq_fd), GFP_KERNEL);
if (new_fd == NULL)
goto out;
spin_lock_irqsave(&irq_lock, flags);
if (type == IRQ_READ)
events = UM_POLLIN | UM_POLLPRI;
else events = UM_POLLOUT;
*new_fd = ((struct irq_fd) { .next = NULL,
.id = dev_id,
.fd = fd,
.type = type,
.irq = irq,
.events = events,
.current_events = 0 } );
/* Check if we have an entry for this fd */
err = -EBUSY;
spin_lock_irqsave(&irq_lock, flags);
for (irq_fd = active_fds; irq_fd != NULL; irq_fd = irq_fd->next) {
if ((irq_fd->fd == fd) && (irq_fd->type == type)) {
printk(KERN_ERR "Registering fd %d twice\n", fd);
printk(KERN_ERR "Irqs : %d, %d\n", irq_fd->irq, irq);
printk(KERN_ERR "Ids : 0x%p, 0x%p\n", irq_fd->id,
dev_id);
for (irq_entry = active_fds;
irq_entry != NULL; irq_entry = irq_entry->next) {
if (irq_entry->fd == fd)
break;
}
if (irq_entry == NULL) {
/* This needs to be atomic as it may be called from an
* IRQ context.
*/
irq_entry = kmalloc(sizeof(struct irq_entry), GFP_ATOMIC);
if (irq_entry == NULL) {
printk(KERN_ERR
"Failed to allocate new IRQ entry\n");
goto out_unlock;
}
irq_entry->fd = fd;
for (i = 0; i < MAX_IRQ_TYPE; i++)
irq_entry->irq_array[i] = NULL;
irq_entry->next = active_fds;
active_fds = irq_entry;
}
if (type == IRQ_WRITE)
fd = -1;
tmp_pfd = NULL;
n = 0;
while (1) {
n = os_create_pollfd(fd, events, tmp_pfd, n);
if (n == 0)
break;
/*
* n > 0
* It means we couldn't put new pollfd to current pollfds
* and tmp_fds is NULL or too small for new pollfds array.
* Needed size is equal to n as minimum.
*
* Here we have to drop the lock in order to call
* kmalloc, which might sleep.
* If something else came in and changed the pollfds array
* so we will not be able to put new pollfd struct to pollfds
* then we free the buffer tmp_fds and try again.
*/
spin_unlock_irqrestore(&irq_lock, flags);
kfree(tmp_pfd);
tmp_pfd = kmalloc(n, GFP_KERNEL);
if (tmp_pfd == NULL)
goto out_kfree;
spin_lock_irqsave(&irq_lock, flags);
}
*last_irq_ptr = new_fd;
last_irq_ptr = &new_fd->next;
spin_unlock_irqrestore(&irq_lock, flags);
/*
* This calls activate_fd, so it has to be outside the critical
* section.
/* Check if we are trying to re-register an interrupt for a
* particular fd
*/
maybe_sigio_broken(fd, (type == IRQ_READ));
if (irq_entry->irq_array[type] != NULL) {
printk(KERN_ERR
"Trying to reregister IRQ %d FD %d TYPE %d ID %p\n",
irq, fd, type, dev_id
);
goto out_unlock;
} else {
/* New entry for this fd */
err = -ENOMEM;
new_fd = kmalloc(sizeof(struct irq_fd), GFP_ATOMIC);
if (new_fd == NULL)
goto out_unlock;
events = os_event_mask(type);
*new_fd = ((struct irq_fd) {
.id = dev_id,
.irq = irq,
.type = type,
.events = events,
.active = true,
.pending = false,
.purge = false
});
/* Turn off any IO on this fd - allows us to
* avoid locking the IRQ loop
*/
os_del_epoll_fd(irq_entry->fd);
irq_entry->irq_array[type] = new_fd;
}
/* Turn back IO on with the correct (new) IO event mask */
assign_epoll_events_to_irq(irq_entry);
spin_unlock_irqrestore(&irq_lock, flags);
maybe_sigio_broken(fd, (type != IRQ_NONE));
return 0;
out_unlock:
out_unlock:
spin_unlock_irqrestore(&irq_lock, flags);
out_kfree:
kfree(new_fd);
out:
out:
return err;
}
static void free_irq_by_cb(int (*test)(struct irq_fd *, void *), void *arg)
{
unsigned long flags;
/*
* Walk the IRQ list and dispose of any unused entries.
* Should be done under irq_lock.
*/
spin_lock_irqsave(&irq_lock, flags);
os_free_irq_by_cb(test, arg, active_fds, &last_irq_ptr);
spin_unlock_irqrestore(&irq_lock, flags);
static void garbage_collect_irq_entries(void)
{
int i;
bool reap;
struct irq_entry *walk;
struct irq_entry *previous = NULL;
struct irq_entry *to_free;
if (active_fds == NULL)
return;
walk = active_fds;
while (walk != NULL) {
reap = true;
for (i = 0; i < MAX_IRQ_TYPE ; i++) {
if (walk->irq_array[i] != NULL) {
reap = false;
break;
}
}
if (reap) {
if (previous == NULL)
active_fds = walk->next;
else
previous->next = walk->next;
to_free = walk;
} else {
to_free = NULL;
}
walk = walk->next;
if (to_free != NULL)
kfree(to_free);
}
}
struct irq_and_dev {
int irq;
void *dev;
};
/*
* Walk the IRQ list and get the descriptor for our FD
*/
static int same_irq_and_dev(struct irq_fd *irq, void *d)
static struct irq_entry *get_irq_entry_by_fd(int fd)
{
struct irq_and_dev *data = d;
struct irq_entry *walk = active_fds;
return ((irq->irq == data->irq) && (irq->id == data->dev));
while (walk != NULL) {
if (walk->fd == fd)
return walk;
walk = walk->next;
}
return NULL;
}
static void free_irq_by_irq_and_dev(unsigned int irq, void *dev)
{
struct irq_and_dev data = ((struct irq_and_dev) { .irq = irq,
.dev = dev });
free_irq_by_cb(same_irq_and_dev, &data);
}
/*
* Walk the IRQ list and dispose of an entry for a specific
* device, fd and number. Note - if sharing an IRQ for read
* and writefor the same FD it will be disposed in either case.
* If this behaviour is undesirable use different IRQ ids.
*/
static int same_fd(struct irq_fd *irq, void *fd)
#define IGNORE_IRQ 1
#define IGNORE_DEV (1<<1)
static void do_free_by_irq_and_dev(
struct irq_entry *irq_entry,
unsigned int irq,
void *dev,
int flags
)
{
return (irq->fd == *((int *)fd));
int i;
struct irq_fd *to_free;
for (i = 0; i < MAX_IRQ_TYPE ; i++) {
if (irq_entry->irq_array[i] != NULL) {
if (
((flags & IGNORE_IRQ) ||
(irq_entry->irq_array[i]->irq == irq)) &&
((flags & IGNORE_DEV) ||
(irq_entry->irq_array[i]->id == dev))
) {
/* Turn off any IO on this fd - allows us to
* avoid locking the IRQ loop
*/
os_del_epoll_fd(irq_entry->fd);
to_free = irq_entry->irq_array[i];
irq_entry->irq_array[i] = NULL;
assign_epoll_events_to_irq(irq_entry);
if (to_free->active)
to_free->purge = true;
else
kfree(to_free);
}
}
}
}
void free_irq_by_fd(int fd)
{
free_irq_by_cb(same_fd, &fd);
}
struct irq_entry *to_free;
unsigned long flags;
/* Must be called with irq_lock held */
static struct irq_fd *find_irq_by_fd(int fd, int irqnum, int *index_out)
spin_lock_irqsave(&irq_lock, flags);
to_free = get_irq_entry_by_fd(fd);
if (to_free != NULL) {
do_free_by_irq_and_dev(
to_free,
-1,
NULL,
IGNORE_IRQ | IGNORE_DEV
);
}
garbage_collect_irq_entries();
spin_unlock_irqrestore(&irq_lock, flags);
}
EXPORT_SYMBOL(free_irq_by_fd);
static void free_irq_by_irq_and_dev(unsigned int irq, void *dev)
{
struct irq_fd *irq;
int i = 0;
int fdi;
struct irq_entry *to_free;
unsigned long flags;
for (irq = active_fds; irq != NULL; irq = irq->next) {
if ((irq->fd == fd) && (irq->irq == irqnum))
break;
i++;
spin_lock_irqsave(&irq_lock, flags);
to_free = active_fds;
while (to_free != NULL) {
do_free_by_irq_and_dev(
to_free,
irq,
dev,
0
);
to_free = to_free->next;
}
if (irq == NULL) {
printk(KERN_ERR "find_irq_by_fd doesn't have descriptor %d\n",
fd);
goto out;
}
fdi = os_get_pollfd(i);
if ((fdi != -1) && (fdi != fd)) {
printk(KERN_ERR "find_irq_by_fd - mismatch between active_fds "
"and pollfds, fd %d vs %d, need %d\n", irq->fd,
fdi, fd);
irq = NULL;
goto out;
}
*index_out = i;
out:
return irq;
garbage_collect_irq_entries();
spin_unlock_irqrestore(&irq_lock, flags);
}
void reactivate_fd(int fd, int irqnum)
{
struct irq_fd *irq;
unsigned long flags;
int i;
spin_lock_irqsave(&irq_lock, flags);
irq = find_irq_by_fd(fd, irqnum, &i);
if (irq == NULL) {
spin_unlock_irqrestore(&irq_lock, flags);
return;
}
os_set_pollfd(i, irq->fd);
spin_unlock_irqrestore(&irq_lock, flags);
add_sigio_fd(fd);
/** NOP - we do auto-EOI now **/
}
void deactivate_fd(int fd, int irqnum)
{
struct irq_fd *irq;
struct irq_entry *to_free;
unsigned long flags;
int i;
os_del_epoll_fd(fd);
spin_lock_irqsave(&irq_lock, flags);
irq = find_irq_by_fd(fd, irqnum, &i);
if (irq == NULL) {
spin_unlock_irqrestore(&irq_lock, flags);
return;
to_free = get_irq_entry_by_fd(fd);
if (to_free != NULL) {
do_free_by_irq_and_dev(
to_free,
irqnum,
NULL,
IGNORE_DEV
);
}
os_set_pollfd(i, -1);
garbage_collect_irq_entries();
spin_unlock_irqrestore(&irq_lock, flags);
ignore_sigio_fd(fd);
}
EXPORT_SYMBOL(deactivate_fd);
@ -265,17 +386,28 @@ EXPORT_SYMBOL(deactivate_fd);
*/
int deactivate_all_fds(void)
{
struct irq_fd *irq;
int err;
unsigned long flags;
struct irq_entry *to_free;
for (irq = active_fds; irq != NULL; irq = irq->next) {
err = os_clear_fd_async(irq->fd);
if (err)
return err;
}
/* If there is a signal already queued, after unblocking ignore it */
spin_lock_irqsave(&irq_lock, flags);
/* Stop IO. The IRQ loop has no lock so this is our
* only way of making sure we are safe to dispose
* of all IRQ handlers
*/
os_set_ioignore();
to_free = active_fds;
while (to_free != NULL) {
do_free_by_irq_and_dev(
to_free,
-1,
NULL,
IGNORE_IRQ | IGNORE_DEV
);
to_free = to_free->next;
}
garbage_collect_irq_entries();
spin_unlock_irqrestore(&irq_lock, flags);
os_close_epoll_fd();
return 0;
}
@ -353,8 +485,11 @@ void __init init_IRQ(void)
irq_set_chip_and_handler(TIMER_IRQ, &SIGVTALRM_irq_type, handle_edge_irq);
for (i = 1; i < NR_IRQS; i++)
irq_set_chip_and_handler(i, &normal_irq_type, handle_edge_irq);
/* Initialize EPOLL Loop */
os_setup_epoll();
}
/*

View File

@ -121,12 +121,12 @@ static void __init um_timer_setup(void)
clockevents_register_device(&timer_clockevent);
}
void read_persistent_clock(struct timespec *ts)
void read_persistent_clock64(struct timespec64 *ts)
{
long long nsecs = os_persistent_clock_emulation();
set_normalized_timespec(ts, nsecs / NSEC_PER_SEC,
nsecs % NSEC_PER_SEC);
set_normalized_timespec64(ts, nsecs / NSEC_PER_SEC,
nsecs % NSEC_PER_SEC);
}
void __init time_init(void)

View File

@ -12,6 +12,7 @@
#include <sys/mount.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/un.h>
#include <sys/types.h>
#include <os.h>

View File

@ -1,135 +1,147 @@
/*
* Copyright (C) 2017 - Cambridge Greys Ltd
* Copyright (C) 2011 - 2014 Cisco Systems Inc
* Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
* Licensed under the GPL
*/
#include <stdlib.h>
#include <errno.h>
#include <poll.h>
#include <sys/epoll.h>
#include <signal.h>
#include <string.h>
#include <irq_user.h>
#include <os.h>
#include <um_malloc.h>
/*
* Locked by irq_lock in arch/um/kernel/irq.c. Changed by os_create_pollfd
* and os_free_irq_by_cb, which are called under irq_lock.
/* Epoll support */
static int epollfd = -1;
#define MAX_EPOLL_EVENTS 64
static struct epoll_event epoll_events[MAX_EPOLL_EVENTS];
/* Helper to return an Epoll data pointer from an epoll event structure.
* We need to keep this one on the userspace side to keep includes separate
*/
static struct pollfd *pollfds = NULL;
static int pollfds_num = 0;
static int pollfds_size = 0;
int os_waiting_for_events(struct irq_fd *active_fds)
void *os_epoll_get_data_pointer(int index)
{
struct irq_fd *irq_fd;
int i, n, err;
return epoll_events[index].data.ptr;
}
n = poll(pollfds, pollfds_num, 0);
/* Helper to compare events versus the events in the epoll structure.
* Same as above - needs to be on the userspace side
*/
int os_epoll_triggered(int index, int events)
{
return epoll_events[index].events & events;
}
/* Helper to set the event mask.
* The event mask is opaque to the kernel side, because it does not have
* access to the right includes/defines for EPOLL constants.
*/
int os_event_mask(int irq_type)
{
if (irq_type == IRQ_READ)
return EPOLLIN | EPOLLPRI;
if (irq_type == IRQ_WRITE)
return EPOLLOUT;
return 0;
}
/*
* Initial Epoll Setup
*/
int os_setup_epoll(void)
{
epollfd = epoll_create(MAX_EPOLL_EVENTS);
return epollfd;
}
/*
* Helper to run the actual epoll_wait
*/
int os_waiting_for_events_epoll(void)
{
int n, err;
n = epoll_wait(epollfd,
(struct epoll_event *) &epoll_events, MAX_EPOLL_EVENTS, 0);
if (n < 0) {
err = -errno;
if (errno != EINTR)
printk(UM_KERN_ERR "os_waiting_for_events:"
" poll returned %d, errno = %d\n", n, errno);
printk(
UM_KERN_ERR "os_waiting_for_events:"
" epoll returned %d, error = %s\n", n,
strerror(errno)
);
return err;
}
if (n == 0)
return 0;
irq_fd = active_fds;
for (i = 0; i < pollfds_num; i++) {
if (pollfds[i].revents != 0) {
irq_fd->current_events = pollfds[i].revents;
pollfds[i].fd = -1;
}
irq_fd = irq_fd->next;
}
return n;
}
int os_create_pollfd(int fd, int events, void *tmp_pfd, int size_tmpfds)
/*
* Helper to add a fd to epoll
*/
int os_add_epoll_fd(int events, int fd, void *data)
{
if (pollfds_num == pollfds_size) {
if (size_tmpfds <= pollfds_size * sizeof(pollfds[0])) {
/* return min size needed for new pollfds area */
return (pollfds_size + 1) * sizeof(pollfds[0]);
}
struct epoll_event event;
int result;
if (pollfds != NULL) {
memcpy(tmp_pfd, pollfds,
sizeof(pollfds[0]) * pollfds_size);
/* remove old pollfds */
kfree(pollfds);
}
pollfds = tmp_pfd;
pollfds_size++;
} else
kfree(tmp_pfd); /* remove not used tmp_pfd */
pollfds[pollfds_num] = ((struct pollfd) { .fd = fd,
.events = events,
.revents = 0 });
pollfds_num++;
return 0;
event.data.ptr = data;
event.events = events | EPOLLET;
result = epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
if ((result) && (errno == EEXIST))
result = os_mod_epoll_fd(events, fd, data);
if (result)
printk("epollctl add err fd %d, %s\n", fd, strerror(errno));
return result;
}
void os_free_irq_by_cb(int (*test)(struct irq_fd *, void *), void *arg,
struct irq_fd *active_fds, struct irq_fd ***last_irq_ptr2)
/*
* Helper to mod the fd event mask and/or data backreference
*/
int os_mod_epoll_fd(int events, int fd, void *data)
{
struct irq_fd **prev;
int i = 0;
struct epoll_event event;
int result;
prev = &active_fds;
while (*prev != NULL) {
if ((*test)(*prev, arg)) {
struct irq_fd *old_fd = *prev;
if ((pollfds[i].fd != -1) &&
(pollfds[i].fd != (*prev)->fd)) {
printk(UM_KERN_ERR "os_free_irq_by_cb - "
"mismatch between active_fds and "
"pollfds, fd %d vs %d\n",
(*prev)->fd, pollfds[i].fd);
goto out;
}
pollfds_num--;
/*
* This moves the *whole* array after pollfds[i]
* (though it doesn't spot as such)!
*/
memmove(&pollfds[i], &pollfds[i + 1],
(pollfds_num - i) * sizeof(pollfds[0]));
if (*last_irq_ptr2 == &old_fd->next)
*last_irq_ptr2 = prev;
*prev = (*prev)->next;
if (old_fd->type == IRQ_WRITE)
ignore_sigio_fd(old_fd->fd);
kfree(old_fd);
continue;
}
prev = &(*prev)->next;
i++;
}
out:
return;
event.data.ptr = data;
event.events = events;
result = epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &event);
if (result)
printk(UM_KERN_ERR
"epollctl mod err fd %d, %s\n", fd, strerror(errno));
return result;
}
int os_get_pollfd(int i)
/*
* Helper to delete the epoll fd
*/
int os_del_epoll_fd(int fd)
{
return pollfds[i].fd;
}
void os_set_pollfd(int i, int fd)
{
pollfds[i].fd = fd;
struct epoll_event event;
int result;
/* This is quiet as we use this as IO ON/OFF - so it is often
* invoked on a non-existent fd
*/
result = epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, &event);
return result;
}
void os_set_ioignore(void)
{
signal(SIGIO, SIG_IGN);
}
void os_close_epoll_fd(void)
{
/* Needed so we do not leak an fd when rebooting */
os_close_file(epollfd);
}

View File

@ -16,6 +16,7 @@
#include <os.h>
#include <sysdep/mcontext.h>
#include <um_malloc.h>
#include <sys/ucontext.h>
void (*sig_info[NSIG])(int, struct siginfo *, struct uml_pt_regs *) = {
[SIGTRAP] = relay_signal,
@ -159,7 +160,7 @@ static void (*handlers[_NSIG])(int sig, struct siginfo *si, mcontext_t *mc) = {
static void hard_handler(int sig, siginfo_t *si, void *p)
{
struct ucontext *uc = p;
ucontext_t *uc = p;
mcontext_t *mc = &uc->uc_mcontext;
unsigned long pending = 1UL << sig;

View File

@ -6,11 +6,12 @@
#include <sysdep/stub.h>
#include <sysdep/faultinfo.h>
#include <sysdep/mcontext.h>
#include <sys/ucontext.h>
void __attribute__ ((__section__ (".__syscall_stub")))
stub_segv_handler(int sig, siginfo_t *info, void *p)
{
struct ucontext *uc = p;
ucontext_t *uc = p;
GET_FAULTINFO_FROM_MC(*((struct faultinfo *) STUB_DATA),
&uc->uc_mcontext);