// SPDX-License-Identifier: GPL-2.0-or-later
/*
* (c) 2017 Stefano Stabellini <stefano@aporeto.com>
*/
#include <linux/module.h>
#include <linux/net.h>
#include <linux/socket.h>
#include <net/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>
#include "pvcalls-front.h"
#define PVCALLS_INVALID_ID UINT_MAX
#define PVCALLS_RING_ORDER XENBUS_MAX_RING_GRANT_ORDER
#define PVCALLS_NR_RSP_PER_RING __CONST_RING_SIZE(xen_pvcalls, XEN_PAGE_SIZE)
#define PVCALLS_FRONT_MAX_SPIN 5000
static struct proto pvcalls_proto = {
.name = "PVCalls",
.owner = THIS_MODULE,
.obj_size = sizeof(struct sock),
};
struct pvcalls_bedata {
struct xen_pvcalls_front_ring ring;
grant_ref_t ref;
int irq;
struct list_head socket_mappings;
spinlock_t socket_lock;
wait_queue_head_t inflight_req;
struct xen_pvcalls_response rsp[PVCALLS_NR_RSP_PER_RING];
};
/* Only one front/back connection supported. */
static struct xenbus_device *pvcalls_front_dev;
static atomic_t pvcalls_refcount;
/* first increment refcount, then proceed */
#define pvcalls_enter() { \
atomic_inc(&pvcalls_refcount); \
}
/* first complete other operations, then decrement refcount */
#define pvcalls_exit() { \
atomic_dec(&pvcalls_refcount); \
}
struct sock_mapping {
bool active_socket;
struct list_head list;
struct socket *sock;
atomic_t refcount;
union {
struct {
int irq;
grant_ref_t ref;
struct pvcalls_data_intf *ring;
struct pvcalls_data data;
struct mutex in_mutex;
struct mutex out_mutex;
wait_queue_head_t inflight_conn_req;
} active;
struct {
/*
* Socket status, needs to be 64-bit aligned due to the
* test_and_* functions which have this requirement on arm64.
*/
#define PVCALLS_STATUS_UNINITALIZED 0
#define PVCALLS_STATUS_BIND 1
#define PVCALLS_STATUS_LISTEN 2
uint8_t status __attribute__((aligned(8)));
/*
* Internal state-machine flags.
* Only one accept operation can be inflight for a socket.
* Only one poll operation can be inflight for a given socket.
* flags needs to be 64-bit aligned due to the test_and_*
* functions which have this requirement on arm64.
*/
#define PVCALLS_FLAG_ACCEPT_INFLIGHT 0
#define PVCALLS_FLAG_POLL_INFLIGHT 1
#define PVCALLS_FLAG_POLL_RET 2
uint8_t flags __attribute__((aligned(8)));
uint32_t inflight_req_id;
struct sock_mapping *accept_map;
wait_queue_head_t inflight_accept_req;
} passive;
};
};
static inline struct sock_mapping *pvcalls_enter_sock(struct socket *sock)
{
struct sock_mapping *map;
if (!pvcalls_front_dev ||
dev_get_drvdata(&pvcalls_front_dev->dev) == NULL)
return ERR_PTR(-ENOTCONN);
map = (struct sock_mapping *)sock->sk->sk_send_head;
if (map == NULL)
return ERR_PTR(-ENOTSOCK);
pvcalls_enter();
atomic_inc(&map->refcount);
return map;
}
static inline void pvcalls_exit_sock(struct socket *sock)
{
struct sock_mapping *map;
map = (struct sock_mapping *)sock->sk->sk_send_head;
atomic_dec(&map->refcount);
pvcalls_exit();
}
static inline int get_request(struct pvcalls_bedata *bedata, int *req_id)
{
*req_id = bedata->ring.req_prod_pvt & (RING_SIZE