/*
* (c) 2017 Stefano Stabellini <stefano@aporeto.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/inet.h>
#include <linux/kthread.h>
#include <linux/list.h>
#include <linux/radix-tree.h>
#include <linux/module.h>
#include <linux/semaphore.h>
#include <linux/wait.h>
#include <net/sock.h>
#include <net/inet_common.h>
#include <net/inet_connection_sock.h>
#include <net/request_sock.h>
#include <xen/events.h>
#include <xen/grant_table.h>
#include <xen/xen.h>
#include <xen/xenbus.h>
#include <xen/interface/io/pvcalls.h>
#define PVCALLS_VERSIONS "1"
#define MAX_RING_ORDER XENBUS_MAX_RING_GRANT_ORDER
struct pvcalls_back_global {
struct list_head frontends;
struct semaphore frontends_lock;
} pvcalls_back_global;
/*
* Per-frontend data structure. It contains pointers to the command
* ring, its event channel, a list of active sockets and a tree of
* passive sockets.
*/
struct pvcalls_fedata {
struct list_head list;
struct xenbus_device *dev;
struct xen_pvcalls_sring *sring;
struct xen_pvcalls_back_ring ring;
int irq;
struct list_head socket_mappings;
struct radix_tree_root socketpass_mappings;
struct semaphore socket_lock;
};
struct pvcalls_ioworker {
struct work_struct register_work;
struct workqueue_struct *wq;
};
struct sock_mapping {
struct list_head list;
struct pvcalls_fedata *fedata;
struct sockpass_mapping *sockpass;
struct socket *sock;
uint64_t id;
grant_ref_t ref;
struct pvcalls_data_intf *ring;
void *bytes;
struct pvcalls_data data;
uint32_t ring_order;
int irq;
atomic_t read;
atomic_t write;
atomic_t io;
atomic_t release;
void (*saved_data_ready)(struct sock *sk);
struct pvcalls_ioworker ioworker;
};
struct sockpass_mapping {
struct list_head list;
struct pvcalls_fedata *fedata;
struct socket *sock;
uint64_t id;
struct xen_pvcalls_request reqcopy;
spinlock_t copy_lock;
struct workqueue_struct *wq;
struct work_struct register_work;
void (*saved_data_ready)(struct sock *sk);
};
static irqreturn_t pvcalls_back_conn_event(int irq, void *sock_map);
static int pvcalls_back_release_active(struct xenbus_device *dev,
struct pvcalls_fedata *fedata,
struct sock_mapping *map);
static void pvcalls_conn_back_read(void *opaque)
{
struct sock_mapping *map = (struct sock_mapping *)opaque;
struct msghdr msg;
struct kvec vec[2];
RING_IDX cons, prod, size, wanted, array_size, masked_prod, masked_cons;
int32_t error;
struct pvcalls_data_intf *intf = map->ring;
struct pvcalls_data *data = &map->data;
unsigned long flags;
int ret;
array_size = XEN_FLEX_RING_SIZE(map->ring_order);
cons = intf->in_cons;
prod = intf->in_prod;
error = intf->in_error;
/* read the indexes first, then deal with the data */
virt_mb();
if (error)
return;
size = pvcalls_queued(prod, cons, array_size);
if (size >= array_size)
return;
spin_lock_irqsave(&map->sock->sk->sk_receive_queue.lock, flags);
if (skb_queue_empty(&map->sock->sk->sk_receive_queue)) {
atomic_set