162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Virtio vhost-user driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright(c) 2019 Intel Corporation 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * This driver allows virtio devices to be used over a vhost-user socket. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Guest devices can be instantiated by kernel module or command line 1062306a36Sopenharmony_ci * parameters. One device will be created for each parameter. Syntax: 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * virtio_uml.device=<socket>:<virtio_id>[:<platform_id>] 1362306a36Sopenharmony_ci * where: 1462306a36Sopenharmony_ci * <socket> := vhost-user socket path to connect 1562306a36Sopenharmony_ci * <virtio_id> := virtio device id (as in virtio_ids.h) 1662306a36Sopenharmony_ci * <platform_id> := (optional) platform device id 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * example: 1962306a36Sopenharmony_ci * virtio_uml.device=/var/uml.socket:1 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * Based on Virtio MMIO driver by Pawel Moll, copyright 2011-2014, ARM Ltd. 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ci#include <linux/module.h> 2462306a36Sopenharmony_ci#include <linux/of.h> 2562306a36Sopenharmony_ci#include <linux/platform_device.h> 2662306a36Sopenharmony_ci#include <linux/slab.h> 2762306a36Sopenharmony_ci#include <linux/virtio.h> 2862306a36Sopenharmony_ci#include <linux/virtio_config.h> 2962306a36Sopenharmony_ci#include <linux/virtio_ring.h> 3062306a36Sopenharmony_ci#include <linux/time-internal.h> 3162306a36Sopenharmony_ci#include <linux/virtio-uml.h> 3262306a36Sopenharmony_ci#include <shared/as-layout.h> 3362306a36Sopenharmony_ci#include <irq_kern.h> 3462306a36Sopenharmony_ci#include <init.h> 3562306a36Sopenharmony_ci#include <os.h> 3662306a36Sopenharmony_ci#include "vhost_user.h" 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define MAX_SUPPORTED_QUEUE_SIZE 256 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#define to_virtio_uml_device(_vdev) \ 4162306a36Sopenharmony_ci container_of(_vdev, struct virtio_uml_device, vdev) 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistruct virtio_uml_platform_data { 4462306a36Sopenharmony_ci u32 virtio_device_id; 4562306a36Sopenharmony_ci const char *socket_path; 4662306a36Sopenharmony_ci struct work_struct conn_broken_wk; 4762306a36Sopenharmony_ci struct platform_device *pdev; 4862306a36Sopenharmony_ci}; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistruct virtio_uml_device { 5162306a36Sopenharmony_ci struct virtio_device vdev; 5262306a36Sopenharmony_ci struct platform_device *pdev; 5362306a36Sopenharmony_ci struct virtio_uml_platform_data *pdata; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci spinlock_t sock_lock; 5662306a36Sopenharmony_ci int sock, req_fd, irq; 5762306a36Sopenharmony_ci u64 features; 5862306a36Sopenharmony_ci u64 protocol_features; 5962306a36Sopenharmony_ci u8 status; 6062306a36Sopenharmony_ci u8 registered:1; 6162306a36Sopenharmony_ci u8 suspended:1; 6262306a36Sopenharmony_ci u8 no_vq_suspend:1; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci u8 config_changed_irq:1; 6562306a36Sopenharmony_ci uint64_t vq_irq_vq_map; 6662306a36Sopenharmony_ci int recv_rc; 6762306a36Sopenharmony_ci}; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistruct virtio_uml_vq_info { 7062306a36Sopenharmony_ci int kick_fd, call_fd; 7162306a36Sopenharmony_ci char name[32]; 7262306a36Sopenharmony_ci bool suspended; 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ciextern unsigned long long physmem_size, highmem; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci#define vu_err(vu_dev, ...) dev_err(&(vu_dev)->pdev->dev, ##__VA_ARGS__) 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* Vhost-user protocol */ 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic int full_sendmsg_fds(int fd, const void *buf, unsigned int len, 8262306a36Sopenharmony_ci const int *fds, unsigned int fds_num) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci int rc; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci do { 8762306a36Sopenharmony_ci rc = os_sendmsg_fds(fd, buf, len, fds, fds_num); 8862306a36Sopenharmony_ci if (rc > 0) { 8962306a36Sopenharmony_ci buf += rc; 9062306a36Sopenharmony_ci len -= rc; 9162306a36Sopenharmony_ci fds = NULL; 9262306a36Sopenharmony_ci fds_num = 0; 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci } while (len && (rc >= 0 || rc == -EINTR)); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (rc < 0) 9762306a36Sopenharmony_ci return rc; 9862306a36Sopenharmony_ci return 0; 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic int full_read(int fd, void *buf, int len, bool abortable) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci int rc; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (!len) 10662306a36Sopenharmony_ci return 0; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci do { 10962306a36Sopenharmony_ci rc = os_read_file(fd, buf, len); 11062306a36Sopenharmony_ci if (rc > 0) { 11162306a36Sopenharmony_ci buf += rc; 11262306a36Sopenharmony_ci len -= rc; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci } while (len && (rc > 0 || rc == -EINTR || (!abortable && rc == -EAGAIN))); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci if (rc < 0) 11762306a36Sopenharmony_ci return rc; 11862306a36Sopenharmony_ci if (rc == 0) 11962306a36Sopenharmony_ci return -ECONNRESET; 12062306a36Sopenharmony_ci return 0; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic int vhost_user_recv_header(int fd, struct vhost_user_msg *msg) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci return full_read(fd, msg, sizeof(msg->header), true); 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic int vhost_user_recv(struct virtio_uml_device *vu_dev, 12962306a36Sopenharmony_ci int fd, struct vhost_user_msg *msg, 13062306a36Sopenharmony_ci size_t max_payload_size, bool wait) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci size_t size; 13362306a36Sopenharmony_ci int rc; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci /* 13662306a36Sopenharmony_ci * In virtio time-travel mode, we're handling all the vhost-user 13762306a36Sopenharmony_ci * FDs by polling them whenever appropriate. However, we may get 13862306a36Sopenharmony_ci * into a situation where we're sending out an interrupt message 13962306a36Sopenharmony_ci * to a device (e.g. a net device) and need to handle a simulation 14062306a36Sopenharmony_ci * time message while doing so, e.g. one that tells us to update 14162306a36Sopenharmony_ci * our idea of how long we can run without scheduling. 14262306a36Sopenharmony_ci * 14362306a36Sopenharmony_ci * Thus, we need to not just read() from the given fd, but need 14462306a36Sopenharmony_ci * to also handle messages for the simulation time - this function 14562306a36Sopenharmony_ci * does that for us while waiting for the given fd to be readable. 14662306a36Sopenharmony_ci */ 14762306a36Sopenharmony_ci if (wait) 14862306a36Sopenharmony_ci time_travel_wait_readable(fd); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci rc = vhost_user_recv_header(fd, msg); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (rc) 15362306a36Sopenharmony_ci return rc; 15462306a36Sopenharmony_ci size = msg->header.size; 15562306a36Sopenharmony_ci if (size > max_payload_size) 15662306a36Sopenharmony_ci return -EPROTO; 15762306a36Sopenharmony_ci return full_read(fd, &msg->payload, size, false); 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic void vhost_user_check_reset(struct virtio_uml_device *vu_dev, 16162306a36Sopenharmony_ci int rc) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci struct virtio_uml_platform_data *pdata = vu_dev->pdata; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci if (rc != -ECONNRESET) 16662306a36Sopenharmony_ci return; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (!vu_dev->registered) 16962306a36Sopenharmony_ci return; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci vu_dev->registered = 0; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci schedule_work(&pdata->conn_broken_wk); 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic int vhost_user_recv_resp(struct virtio_uml_device *vu_dev, 17762306a36Sopenharmony_ci struct vhost_user_msg *msg, 17862306a36Sopenharmony_ci size_t max_payload_size) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci int rc = vhost_user_recv(vu_dev, vu_dev->sock, msg, 18162306a36Sopenharmony_ci max_payload_size, true); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci if (rc) { 18462306a36Sopenharmony_ci vhost_user_check_reset(vu_dev, rc); 18562306a36Sopenharmony_ci return rc; 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (msg->header.flags != (VHOST_USER_FLAG_REPLY | VHOST_USER_VERSION)) 18962306a36Sopenharmony_ci return -EPROTO; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci return 0; 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic int vhost_user_recv_u64(struct virtio_uml_device *vu_dev, 19562306a36Sopenharmony_ci u64 *value) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct vhost_user_msg msg; 19862306a36Sopenharmony_ci int rc = vhost_user_recv_resp(vu_dev, &msg, 19962306a36Sopenharmony_ci sizeof(msg.payload.integer)); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (rc) 20262306a36Sopenharmony_ci return rc; 20362306a36Sopenharmony_ci if (msg.header.size != sizeof(msg.payload.integer)) 20462306a36Sopenharmony_ci return -EPROTO; 20562306a36Sopenharmony_ci *value = msg.payload.integer; 20662306a36Sopenharmony_ci return 0; 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic int vhost_user_recv_req(struct virtio_uml_device *vu_dev, 21062306a36Sopenharmony_ci struct vhost_user_msg *msg, 21162306a36Sopenharmony_ci size_t max_payload_size) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci int rc = vhost_user_recv(vu_dev, vu_dev->req_fd, msg, 21462306a36Sopenharmony_ci max_payload_size, false); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (rc) 21762306a36Sopenharmony_ci return rc; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if ((msg->header.flags & ~VHOST_USER_FLAG_NEED_REPLY) != 22062306a36Sopenharmony_ci VHOST_USER_VERSION) 22162306a36Sopenharmony_ci return -EPROTO; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci return 0; 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic int vhost_user_send(struct virtio_uml_device *vu_dev, 22762306a36Sopenharmony_ci bool need_response, struct vhost_user_msg *msg, 22862306a36Sopenharmony_ci int *fds, size_t num_fds) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci size_t size = sizeof(msg->header) + msg->header.size; 23162306a36Sopenharmony_ci unsigned long flags; 23262306a36Sopenharmony_ci bool request_ack; 23362306a36Sopenharmony_ci int rc; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci msg->header.flags |= VHOST_USER_VERSION; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci /* 23862306a36Sopenharmony_ci * The need_response flag indicates that we already need a response, 23962306a36Sopenharmony_ci * e.g. to read the features. In these cases, don't request an ACK as 24062306a36Sopenharmony_ci * it is meaningless. Also request an ACK only if supported. 24162306a36Sopenharmony_ci */ 24262306a36Sopenharmony_ci request_ack = !need_response; 24362306a36Sopenharmony_ci if (!(vu_dev->protocol_features & 24462306a36Sopenharmony_ci BIT_ULL(VHOST_USER_PROTOCOL_F_REPLY_ACK))) 24562306a36Sopenharmony_ci request_ack = false; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (request_ack) 24862306a36Sopenharmony_ci msg->header.flags |= VHOST_USER_FLAG_NEED_REPLY; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci spin_lock_irqsave(&vu_dev->sock_lock, flags); 25162306a36Sopenharmony_ci rc = full_sendmsg_fds(vu_dev->sock, msg, size, fds, num_fds); 25262306a36Sopenharmony_ci if (rc < 0) 25362306a36Sopenharmony_ci goto out; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (request_ack) { 25662306a36Sopenharmony_ci uint64_t status; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci rc = vhost_user_recv_u64(vu_dev, &status); 25962306a36Sopenharmony_ci if (rc) 26062306a36Sopenharmony_ci goto out; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (status) { 26362306a36Sopenharmony_ci vu_err(vu_dev, "slave reports error: %llu\n", status); 26462306a36Sopenharmony_ci rc = -EIO; 26562306a36Sopenharmony_ci goto out; 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ciout: 27062306a36Sopenharmony_ci spin_unlock_irqrestore(&vu_dev->sock_lock, flags); 27162306a36Sopenharmony_ci return rc; 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic int vhost_user_send_no_payload(struct virtio_uml_device *vu_dev, 27562306a36Sopenharmony_ci bool need_response, u32 request) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci struct vhost_user_msg msg = { 27862306a36Sopenharmony_ci .header.request = request, 27962306a36Sopenharmony_ci }; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci return vhost_user_send(vu_dev, need_response, &msg, NULL, 0); 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic int vhost_user_send_no_payload_fd(struct virtio_uml_device *vu_dev, 28562306a36Sopenharmony_ci u32 request, int fd) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci struct vhost_user_msg msg = { 28862306a36Sopenharmony_ci .header.request = request, 28962306a36Sopenharmony_ci }; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci return vhost_user_send(vu_dev, false, &msg, &fd, 1); 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic int vhost_user_send_u64(struct virtio_uml_device *vu_dev, 29562306a36Sopenharmony_ci u32 request, u64 value) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci struct vhost_user_msg msg = { 29862306a36Sopenharmony_ci .header.request = request, 29962306a36Sopenharmony_ci .header.size = sizeof(msg.payload.integer), 30062306a36Sopenharmony_ci .payload.integer = value, 30162306a36Sopenharmony_ci }; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci return vhost_user_send(vu_dev, false, &msg, NULL, 0); 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cistatic int vhost_user_set_owner(struct virtio_uml_device *vu_dev) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci return vhost_user_send_no_payload(vu_dev, false, VHOST_USER_SET_OWNER); 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cistatic int vhost_user_get_features(struct virtio_uml_device *vu_dev, 31262306a36Sopenharmony_ci u64 *features) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci int rc = vhost_user_send_no_payload(vu_dev, true, 31562306a36Sopenharmony_ci VHOST_USER_GET_FEATURES); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci if (rc) 31862306a36Sopenharmony_ci return rc; 31962306a36Sopenharmony_ci return vhost_user_recv_u64(vu_dev, features); 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic int vhost_user_set_features(struct virtio_uml_device *vu_dev, 32362306a36Sopenharmony_ci u64 features) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci return vhost_user_send_u64(vu_dev, VHOST_USER_SET_FEATURES, features); 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic int vhost_user_get_protocol_features(struct virtio_uml_device *vu_dev, 32962306a36Sopenharmony_ci u64 *protocol_features) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci int rc = vhost_user_send_no_payload(vu_dev, true, 33262306a36Sopenharmony_ci VHOST_USER_GET_PROTOCOL_FEATURES); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (rc) 33562306a36Sopenharmony_ci return rc; 33662306a36Sopenharmony_ci return vhost_user_recv_u64(vu_dev, protocol_features); 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_cistatic int vhost_user_set_protocol_features(struct virtio_uml_device *vu_dev, 34062306a36Sopenharmony_ci u64 protocol_features) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci return vhost_user_send_u64(vu_dev, VHOST_USER_SET_PROTOCOL_FEATURES, 34362306a36Sopenharmony_ci protocol_features); 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic void vhost_user_reply(struct virtio_uml_device *vu_dev, 34762306a36Sopenharmony_ci struct vhost_user_msg *msg, int response) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci struct vhost_user_msg reply = { 35062306a36Sopenharmony_ci .payload.integer = response, 35162306a36Sopenharmony_ci }; 35262306a36Sopenharmony_ci size_t size = sizeof(reply.header) + sizeof(reply.payload.integer); 35362306a36Sopenharmony_ci int rc; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci reply.header = msg->header; 35662306a36Sopenharmony_ci reply.header.flags &= ~VHOST_USER_FLAG_NEED_REPLY; 35762306a36Sopenharmony_ci reply.header.flags |= VHOST_USER_FLAG_REPLY; 35862306a36Sopenharmony_ci reply.header.size = sizeof(reply.payload.integer); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci rc = full_sendmsg_fds(vu_dev->req_fd, &reply, size, NULL, 0); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if (rc) 36362306a36Sopenharmony_ci vu_err(vu_dev, 36462306a36Sopenharmony_ci "sending reply to slave request failed: %d (size %zu)\n", 36562306a36Sopenharmony_ci rc, size); 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_cistatic irqreturn_t vu_req_read_message(struct virtio_uml_device *vu_dev, 36962306a36Sopenharmony_ci struct time_travel_event *ev) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci struct virtqueue *vq; 37262306a36Sopenharmony_ci int response = 1; 37362306a36Sopenharmony_ci struct { 37462306a36Sopenharmony_ci struct vhost_user_msg msg; 37562306a36Sopenharmony_ci u8 extra_payload[512]; 37662306a36Sopenharmony_ci } msg; 37762306a36Sopenharmony_ci int rc; 37862306a36Sopenharmony_ci irqreturn_t irq_rc = IRQ_NONE; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci while (1) { 38162306a36Sopenharmony_ci rc = vhost_user_recv_req(vu_dev, &msg.msg, 38262306a36Sopenharmony_ci sizeof(msg.msg.payload) + 38362306a36Sopenharmony_ci sizeof(msg.extra_payload)); 38462306a36Sopenharmony_ci if (rc) 38562306a36Sopenharmony_ci break; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci switch (msg.msg.header.request) { 38862306a36Sopenharmony_ci case VHOST_USER_SLAVE_CONFIG_CHANGE_MSG: 38962306a36Sopenharmony_ci vu_dev->config_changed_irq = true; 39062306a36Sopenharmony_ci response = 0; 39162306a36Sopenharmony_ci break; 39262306a36Sopenharmony_ci case VHOST_USER_SLAVE_VRING_CALL: 39362306a36Sopenharmony_ci virtio_device_for_each_vq((&vu_dev->vdev), vq) { 39462306a36Sopenharmony_ci if (vq->index == msg.msg.payload.vring_state.index) { 39562306a36Sopenharmony_ci response = 0; 39662306a36Sopenharmony_ci vu_dev->vq_irq_vq_map |= BIT_ULL(vq->index); 39762306a36Sopenharmony_ci break; 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci break; 40162306a36Sopenharmony_ci case VHOST_USER_SLAVE_IOTLB_MSG: 40262306a36Sopenharmony_ci /* not supported - VIRTIO_F_ACCESS_PLATFORM */ 40362306a36Sopenharmony_ci case VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG: 40462306a36Sopenharmony_ci /* not supported - VHOST_USER_PROTOCOL_F_HOST_NOTIFIER */ 40562306a36Sopenharmony_ci default: 40662306a36Sopenharmony_ci vu_err(vu_dev, "unexpected slave request %d\n", 40762306a36Sopenharmony_ci msg.msg.header.request); 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci if (ev && !vu_dev->suspended) 41162306a36Sopenharmony_ci time_travel_add_irq_event(ev); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci if (msg.msg.header.flags & VHOST_USER_FLAG_NEED_REPLY) 41462306a36Sopenharmony_ci vhost_user_reply(vu_dev, &msg.msg, response); 41562306a36Sopenharmony_ci irq_rc = IRQ_HANDLED; 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci /* mask EAGAIN as we try non-blocking read until socket is empty */ 41862306a36Sopenharmony_ci vu_dev->recv_rc = (rc == -EAGAIN) ? 0 : rc; 41962306a36Sopenharmony_ci return irq_rc; 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_cistatic irqreturn_t vu_req_interrupt(int irq, void *data) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci struct virtio_uml_device *vu_dev = data; 42562306a36Sopenharmony_ci irqreturn_t ret = IRQ_HANDLED; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (!um_irq_timetravel_handler_used()) 42862306a36Sopenharmony_ci ret = vu_req_read_message(vu_dev, NULL); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci if (vu_dev->recv_rc) { 43162306a36Sopenharmony_ci vhost_user_check_reset(vu_dev, vu_dev->recv_rc); 43262306a36Sopenharmony_ci } else if (vu_dev->vq_irq_vq_map) { 43362306a36Sopenharmony_ci struct virtqueue *vq; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci virtio_device_for_each_vq((&vu_dev->vdev), vq) { 43662306a36Sopenharmony_ci if (vu_dev->vq_irq_vq_map & BIT_ULL(vq->index)) 43762306a36Sopenharmony_ci vring_interrupt(0 /* ignored */, vq); 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci vu_dev->vq_irq_vq_map = 0; 44062306a36Sopenharmony_ci } else if (vu_dev->config_changed_irq) { 44162306a36Sopenharmony_ci virtio_config_changed(&vu_dev->vdev); 44262306a36Sopenharmony_ci vu_dev->config_changed_irq = false; 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci return ret; 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic void vu_req_interrupt_comm_handler(int irq, int fd, void *data, 44962306a36Sopenharmony_ci struct time_travel_event *ev) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci vu_req_read_message(data, ev); 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic int vhost_user_init_slave_req(struct virtio_uml_device *vu_dev) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci int rc, req_fds[2]; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci /* Use a pipe for slave req fd, SIGIO is not supported for eventfd */ 45962306a36Sopenharmony_ci rc = os_pipe(req_fds, true, true); 46062306a36Sopenharmony_ci if (rc < 0) 46162306a36Sopenharmony_ci return rc; 46262306a36Sopenharmony_ci vu_dev->req_fd = req_fds[0]; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci rc = um_request_irq_tt(UM_IRQ_ALLOC, vu_dev->req_fd, IRQ_READ, 46562306a36Sopenharmony_ci vu_req_interrupt, IRQF_SHARED, 46662306a36Sopenharmony_ci vu_dev->pdev->name, vu_dev, 46762306a36Sopenharmony_ci vu_req_interrupt_comm_handler); 46862306a36Sopenharmony_ci if (rc < 0) 46962306a36Sopenharmony_ci goto err_close; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci vu_dev->irq = rc; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci rc = vhost_user_send_no_payload_fd(vu_dev, VHOST_USER_SET_SLAVE_REQ_FD, 47462306a36Sopenharmony_ci req_fds[1]); 47562306a36Sopenharmony_ci if (rc) 47662306a36Sopenharmony_ci goto err_free_irq; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci goto out; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_cierr_free_irq: 48162306a36Sopenharmony_ci um_free_irq(vu_dev->irq, vu_dev); 48262306a36Sopenharmony_cierr_close: 48362306a36Sopenharmony_ci os_close_file(req_fds[0]); 48462306a36Sopenharmony_ciout: 48562306a36Sopenharmony_ci /* Close unused write end of request fds */ 48662306a36Sopenharmony_ci os_close_file(req_fds[1]); 48762306a36Sopenharmony_ci return rc; 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_cistatic int vhost_user_init(struct virtio_uml_device *vu_dev) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci int rc = vhost_user_set_owner(vu_dev); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci if (rc) 49562306a36Sopenharmony_ci return rc; 49662306a36Sopenharmony_ci rc = vhost_user_get_features(vu_dev, &vu_dev->features); 49762306a36Sopenharmony_ci if (rc) 49862306a36Sopenharmony_ci return rc; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci if (vu_dev->features & BIT_ULL(VHOST_USER_F_PROTOCOL_FEATURES)) { 50162306a36Sopenharmony_ci rc = vhost_user_get_protocol_features(vu_dev, 50262306a36Sopenharmony_ci &vu_dev->protocol_features); 50362306a36Sopenharmony_ci if (rc) 50462306a36Sopenharmony_ci return rc; 50562306a36Sopenharmony_ci vu_dev->protocol_features &= VHOST_USER_SUPPORTED_PROTOCOL_F; 50662306a36Sopenharmony_ci rc = vhost_user_set_protocol_features(vu_dev, 50762306a36Sopenharmony_ci vu_dev->protocol_features); 50862306a36Sopenharmony_ci if (rc) 50962306a36Sopenharmony_ci return rc; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci if (vu_dev->protocol_features & 51362306a36Sopenharmony_ci BIT_ULL(VHOST_USER_PROTOCOL_F_SLAVE_REQ)) { 51462306a36Sopenharmony_ci rc = vhost_user_init_slave_req(vu_dev); 51562306a36Sopenharmony_ci if (rc) 51662306a36Sopenharmony_ci return rc; 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci return 0; 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_cistatic void vhost_user_get_config(struct virtio_uml_device *vu_dev, 52362306a36Sopenharmony_ci u32 offset, void *buf, u32 len) 52462306a36Sopenharmony_ci{ 52562306a36Sopenharmony_ci u32 cfg_size = offset + len; 52662306a36Sopenharmony_ci struct vhost_user_msg *msg; 52762306a36Sopenharmony_ci size_t payload_size = sizeof(msg->payload.config) + cfg_size; 52862306a36Sopenharmony_ci size_t msg_size = sizeof(msg->header) + payload_size; 52962306a36Sopenharmony_ci int rc; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci if (!(vu_dev->protocol_features & 53262306a36Sopenharmony_ci BIT_ULL(VHOST_USER_PROTOCOL_F_CONFIG))) 53362306a36Sopenharmony_ci return; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci msg = kzalloc(msg_size, GFP_KERNEL); 53662306a36Sopenharmony_ci if (!msg) 53762306a36Sopenharmony_ci return; 53862306a36Sopenharmony_ci msg->header.request = VHOST_USER_GET_CONFIG; 53962306a36Sopenharmony_ci msg->header.size = payload_size; 54062306a36Sopenharmony_ci msg->payload.config.offset = 0; 54162306a36Sopenharmony_ci msg->payload.config.size = cfg_size; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci rc = vhost_user_send(vu_dev, true, msg, NULL, 0); 54462306a36Sopenharmony_ci if (rc) { 54562306a36Sopenharmony_ci vu_err(vu_dev, "sending VHOST_USER_GET_CONFIG failed: %d\n", 54662306a36Sopenharmony_ci rc); 54762306a36Sopenharmony_ci goto free; 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci rc = vhost_user_recv_resp(vu_dev, msg, msg_size); 55162306a36Sopenharmony_ci if (rc) { 55262306a36Sopenharmony_ci vu_err(vu_dev, 55362306a36Sopenharmony_ci "receiving VHOST_USER_GET_CONFIG response failed: %d\n", 55462306a36Sopenharmony_ci rc); 55562306a36Sopenharmony_ci goto free; 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci if (msg->header.size != payload_size || 55962306a36Sopenharmony_ci msg->payload.config.size != cfg_size) { 56062306a36Sopenharmony_ci rc = -EPROTO; 56162306a36Sopenharmony_ci vu_err(vu_dev, 56262306a36Sopenharmony_ci "Invalid VHOST_USER_GET_CONFIG sizes (payload %d expected %zu, config %u expected %u)\n", 56362306a36Sopenharmony_ci msg->header.size, payload_size, 56462306a36Sopenharmony_ci msg->payload.config.size, cfg_size); 56562306a36Sopenharmony_ci goto free; 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci memcpy(buf, msg->payload.config.payload + offset, len); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_cifree: 57062306a36Sopenharmony_ci kfree(msg); 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_cistatic void vhost_user_set_config(struct virtio_uml_device *vu_dev, 57462306a36Sopenharmony_ci u32 offset, const void *buf, u32 len) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci struct vhost_user_msg *msg; 57762306a36Sopenharmony_ci size_t payload_size = sizeof(msg->payload.config) + len; 57862306a36Sopenharmony_ci size_t msg_size = sizeof(msg->header) + payload_size; 57962306a36Sopenharmony_ci int rc; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci if (!(vu_dev->protocol_features & 58262306a36Sopenharmony_ci BIT_ULL(VHOST_USER_PROTOCOL_F_CONFIG))) 58362306a36Sopenharmony_ci return; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci msg = kzalloc(msg_size, GFP_KERNEL); 58662306a36Sopenharmony_ci if (!msg) 58762306a36Sopenharmony_ci return; 58862306a36Sopenharmony_ci msg->header.request = VHOST_USER_SET_CONFIG; 58962306a36Sopenharmony_ci msg->header.size = payload_size; 59062306a36Sopenharmony_ci msg->payload.config.offset = offset; 59162306a36Sopenharmony_ci msg->payload.config.size = len; 59262306a36Sopenharmony_ci memcpy(msg->payload.config.payload, buf, len); 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci rc = vhost_user_send(vu_dev, false, msg, NULL, 0); 59562306a36Sopenharmony_ci if (rc) 59662306a36Sopenharmony_ci vu_err(vu_dev, "sending VHOST_USER_SET_CONFIG failed: %d\n", 59762306a36Sopenharmony_ci rc); 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci kfree(msg); 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_cistatic int vhost_user_init_mem_region(u64 addr, u64 size, int *fd_out, 60362306a36Sopenharmony_ci struct vhost_user_mem_region *region_out) 60462306a36Sopenharmony_ci{ 60562306a36Sopenharmony_ci unsigned long long mem_offset; 60662306a36Sopenharmony_ci int rc = phys_mapping(addr, &mem_offset); 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci if (WARN(rc < 0, "phys_mapping of 0x%llx returned %d\n", addr, rc)) 60962306a36Sopenharmony_ci return -EFAULT; 61062306a36Sopenharmony_ci *fd_out = rc; 61162306a36Sopenharmony_ci region_out->guest_addr = addr; 61262306a36Sopenharmony_ci region_out->user_addr = addr; 61362306a36Sopenharmony_ci region_out->size = size; 61462306a36Sopenharmony_ci region_out->mmap_offset = mem_offset; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci /* Ensure mapping is valid for the entire region */ 61762306a36Sopenharmony_ci rc = phys_mapping(addr + size - 1, &mem_offset); 61862306a36Sopenharmony_ci if (WARN(rc != *fd_out, "phys_mapping of 0x%llx failed: %d != %d\n", 61962306a36Sopenharmony_ci addr + size - 1, rc, *fd_out)) 62062306a36Sopenharmony_ci return -EFAULT; 62162306a36Sopenharmony_ci return 0; 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_cistatic int vhost_user_set_mem_table(struct virtio_uml_device *vu_dev) 62562306a36Sopenharmony_ci{ 62662306a36Sopenharmony_ci struct vhost_user_msg msg = { 62762306a36Sopenharmony_ci .header.request = VHOST_USER_SET_MEM_TABLE, 62862306a36Sopenharmony_ci .header.size = sizeof(msg.payload.mem_regions), 62962306a36Sopenharmony_ci .payload.mem_regions.num = 1, 63062306a36Sopenharmony_ci }; 63162306a36Sopenharmony_ci unsigned long reserved = uml_reserved - uml_physmem; 63262306a36Sopenharmony_ci int fds[2]; 63362306a36Sopenharmony_ci int rc; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci /* 63662306a36Sopenharmony_ci * This is a bit tricky, see also the comment with setup_physmem(). 63762306a36Sopenharmony_ci * 63862306a36Sopenharmony_ci * Essentially, setup_physmem() uses a file to mmap() our physmem, 63962306a36Sopenharmony_ci * but the code and data we *already* have is omitted. To us, this 64062306a36Sopenharmony_ci * is no difference, since they both become part of our address 64162306a36Sopenharmony_ci * space and memory consumption. To somebody looking in from the 64262306a36Sopenharmony_ci * outside, however, it is different because the part of our memory 64362306a36Sopenharmony_ci * consumption that's already part of the binary (code/data) is not 64462306a36Sopenharmony_ci * mapped from the file, so it's not visible to another mmap from 64562306a36Sopenharmony_ci * the file descriptor. 64662306a36Sopenharmony_ci * 64762306a36Sopenharmony_ci * Thus, don't advertise this space to the vhost-user slave. This 64862306a36Sopenharmony_ci * means that the slave will likely abort or similar when we give 64962306a36Sopenharmony_ci * it an address from the hidden range, since it's not marked as 65062306a36Sopenharmony_ci * a valid address, but at least that way we detect the issue and 65162306a36Sopenharmony_ci * don't just have the slave read an all-zeroes buffer from the 65262306a36Sopenharmony_ci * shared memory file, or write something there that we can never 65362306a36Sopenharmony_ci * see (depending on the direction of the virtqueue traffic.) 65462306a36Sopenharmony_ci * 65562306a36Sopenharmony_ci * Since we usually don't want to use .text for virtio buffers, 65662306a36Sopenharmony_ci * this effectively means that you cannot use 65762306a36Sopenharmony_ci * 1) global variables, which are in the .bss and not in the shm 65862306a36Sopenharmony_ci * file-backed memory 65962306a36Sopenharmony_ci * 2) the stack in some processes, depending on where they have 66062306a36Sopenharmony_ci * their stack (or maybe only no interrupt stack?) 66162306a36Sopenharmony_ci * 66262306a36Sopenharmony_ci * The stack is already not typically valid for DMA, so this isn't 66362306a36Sopenharmony_ci * much of a restriction, but global variables might be encountered. 66462306a36Sopenharmony_ci * 66562306a36Sopenharmony_ci * It might be possible to fix it by copying around the data that's 66662306a36Sopenharmony_ci * between bss_start and where we map the file now, but it's not 66762306a36Sopenharmony_ci * something that you typically encounter with virtio drivers, so 66862306a36Sopenharmony_ci * it didn't seem worthwhile. 66962306a36Sopenharmony_ci */ 67062306a36Sopenharmony_ci rc = vhost_user_init_mem_region(reserved, physmem_size - reserved, 67162306a36Sopenharmony_ci &fds[0], 67262306a36Sopenharmony_ci &msg.payload.mem_regions.regions[0]); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci if (rc < 0) 67562306a36Sopenharmony_ci return rc; 67662306a36Sopenharmony_ci if (highmem) { 67762306a36Sopenharmony_ci msg.payload.mem_regions.num++; 67862306a36Sopenharmony_ci rc = vhost_user_init_mem_region(__pa(end_iomem), highmem, 67962306a36Sopenharmony_ci &fds[1], &msg.payload.mem_regions.regions[1]); 68062306a36Sopenharmony_ci if (rc < 0) 68162306a36Sopenharmony_ci return rc; 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci return vhost_user_send(vu_dev, false, &msg, fds, 68562306a36Sopenharmony_ci msg.payload.mem_regions.num); 68662306a36Sopenharmony_ci} 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_cistatic int vhost_user_set_vring_state(struct virtio_uml_device *vu_dev, 68962306a36Sopenharmony_ci u32 request, u32 index, u32 num) 69062306a36Sopenharmony_ci{ 69162306a36Sopenharmony_ci struct vhost_user_msg msg = { 69262306a36Sopenharmony_ci .header.request = request, 69362306a36Sopenharmony_ci .header.size = sizeof(msg.payload.vring_state), 69462306a36Sopenharmony_ci .payload.vring_state.index = index, 69562306a36Sopenharmony_ci .payload.vring_state.num = num, 69662306a36Sopenharmony_ci }; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci return vhost_user_send(vu_dev, false, &msg, NULL, 0); 69962306a36Sopenharmony_ci} 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_cistatic int vhost_user_set_vring_num(struct virtio_uml_device *vu_dev, 70262306a36Sopenharmony_ci u32 index, u32 num) 70362306a36Sopenharmony_ci{ 70462306a36Sopenharmony_ci return vhost_user_set_vring_state(vu_dev, VHOST_USER_SET_VRING_NUM, 70562306a36Sopenharmony_ci index, num); 70662306a36Sopenharmony_ci} 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_cistatic int vhost_user_set_vring_base(struct virtio_uml_device *vu_dev, 70962306a36Sopenharmony_ci u32 index, u32 offset) 71062306a36Sopenharmony_ci{ 71162306a36Sopenharmony_ci return vhost_user_set_vring_state(vu_dev, VHOST_USER_SET_VRING_BASE, 71262306a36Sopenharmony_ci index, offset); 71362306a36Sopenharmony_ci} 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_cistatic int vhost_user_set_vring_addr(struct virtio_uml_device *vu_dev, 71662306a36Sopenharmony_ci u32 index, u64 desc, u64 used, u64 avail, 71762306a36Sopenharmony_ci u64 log) 71862306a36Sopenharmony_ci{ 71962306a36Sopenharmony_ci struct vhost_user_msg msg = { 72062306a36Sopenharmony_ci .header.request = VHOST_USER_SET_VRING_ADDR, 72162306a36Sopenharmony_ci .header.size = sizeof(msg.payload.vring_addr), 72262306a36Sopenharmony_ci .payload.vring_addr.index = index, 72362306a36Sopenharmony_ci .payload.vring_addr.desc = desc, 72462306a36Sopenharmony_ci .payload.vring_addr.used = used, 72562306a36Sopenharmony_ci .payload.vring_addr.avail = avail, 72662306a36Sopenharmony_ci .payload.vring_addr.log = log, 72762306a36Sopenharmony_ci }; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci return vhost_user_send(vu_dev, false, &msg, NULL, 0); 73062306a36Sopenharmony_ci} 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_cistatic int vhost_user_set_vring_fd(struct virtio_uml_device *vu_dev, 73362306a36Sopenharmony_ci u32 request, int index, int fd) 73462306a36Sopenharmony_ci{ 73562306a36Sopenharmony_ci struct vhost_user_msg msg = { 73662306a36Sopenharmony_ci .header.request = request, 73762306a36Sopenharmony_ci .header.size = sizeof(msg.payload.integer), 73862306a36Sopenharmony_ci .payload.integer = index, 73962306a36Sopenharmony_ci }; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci if (index & ~VHOST_USER_VRING_INDEX_MASK) 74262306a36Sopenharmony_ci return -EINVAL; 74362306a36Sopenharmony_ci if (fd < 0) { 74462306a36Sopenharmony_ci msg.payload.integer |= VHOST_USER_VRING_POLL_MASK; 74562306a36Sopenharmony_ci return vhost_user_send(vu_dev, false, &msg, NULL, 0); 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci return vhost_user_send(vu_dev, false, &msg, &fd, 1); 74862306a36Sopenharmony_ci} 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_cistatic int vhost_user_set_vring_call(struct virtio_uml_device *vu_dev, 75162306a36Sopenharmony_ci int index, int fd) 75262306a36Sopenharmony_ci{ 75362306a36Sopenharmony_ci return vhost_user_set_vring_fd(vu_dev, VHOST_USER_SET_VRING_CALL, 75462306a36Sopenharmony_ci index, fd); 75562306a36Sopenharmony_ci} 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_cistatic int vhost_user_set_vring_kick(struct virtio_uml_device *vu_dev, 75862306a36Sopenharmony_ci int index, int fd) 75962306a36Sopenharmony_ci{ 76062306a36Sopenharmony_ci return vhost_user_set_vring_fd(vu_dev, VHOST_USER_SET_VRING_KICK, 76162306a36Sopenharmony_ci index, fd); 76262306a36Sopenharmony_ci} 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_cistatic int vhost_user_set_vring_enable(struct virtio_uml_device *vu_dev, 76562306a36Sopenharmony_ci u32 index, bool enable) 76662306a36Sopenharmony_ci{ 76762306a36Sopenharmony_ci if (!(vu_dev->features & BIT_ULL(VHOST_USER_F_PROTOCOL_FEATURES))) 76862306a36Sopenharmony_ci return 0; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci return vhost_user_set_vring_state(vu_dev, VHOST_USER_SET_VRING_ENABLE, 77162306a36Sopenharmony_ci index, enable); 77262306a36Sopenharmony_ci} 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci/* Virtio interface */ 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_cistatic bool vu_notify(struct virtqueue *vq) 77862306a36Sopenharmony_ci{ 77962306a36Sopenharmony_ci struct virtio_uml_vq_info *info = vq->priv; 78062306a36Sopenharmony_ci const uint64_t n = 1; 78162306a36Sopenharmony_ci int rc; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci if (info->suspended) 78462306a36Sopenharmony_ci return true; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci time_travel_propagate_time(); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci if (info->kick_fd < 0) { 78962306a36Sopenharmony_ci struct virtio_uml_device *vu_dev; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci vu_dev = to_virtio_uml_device(vq->vdev); 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci return vhost_user_set_vring_state(vu_dev, VHOST_USER_VRING_KICK, 79462306a36Sopenharmony_ci vq->index, 0) == 0; 79562306a36Sopenharmony_ci } 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci do { 79862306a36Sopenharmony_ci rc = os_write_file(info->kick_fd, &n, sizeof(n)); 79962306a36Sopenharmony_ci } while (rc == -EINTR); 80062306a36Sopenharmony_ci return !WARN(rc != sizeof(n), "write returned %d\n", rc); 80162306a36Sopenharmony_ci} 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_cistatic irqreturn_t vu_interrupt(int irq, void *opaque) 80462306a36Sopenharmony_ci{ 80562306a36Sopenharmony_ci struct virtqueue *vq = opaque; 80662306a36Sopenharmony_ci struct virtio_uml_vq_info *info = vq->priv; 80762306a36Sopenharmony_ci uint64_t n; 80862306a36Sopenharmony_ci int rc; 80962306a36Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci do { 81262306a36Sopenharmony_ci rc = os_read_file(info->call_fd, &n, sizeof(n)); 81362306a36Sopenharmony_ci if (rc == sizeof(n)) 81462306a36Sopenharmony_ci ret |= vring_interrupt(irq, vq); 81562306a36Sopenharmony_ci } while (rc == sizeof(n) || rc == -EINTR); 81662306a36Sopenharmony_ci WARN(rc != -EAGAIN, "read returned %d\n", rc); 81762306a36Sopenharmony_ci return ret; 81862306a36Sopenharmony_ci} 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_cistatic void vu_get(struct virtio_device *vdev, unsigned offset, 82262306a36Sopenharmony_ci void *buf, unsigned len) 82362306a36Sopenharmony_ci{ 82462306a36Sopenharmony_ci struct virtio_uml_device *vu_dev = to_virtio_uml_device(vdev); 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci vhost_user_get_config(vu_dev, offset, buf, len); 82762306a36Sopenharmony_ci} 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_cistatic void vu_set(struct virtio_device *vdev, unsigned offset, 83062306a36Sopenharmony_ci const void *buf, unsigned len) 83162306a36Sopenharmony_ci{ 83262306a36Sopenharmony_ci struct virtio_uml_device *vu_dev = to_virtio_uml_device(vdev); 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci vhost_user_set_config(vu_dev, offset, buf, len); 83562306a36Sopenharmony_ci} 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_cistatic u8 vu_get_status(struct virtio_device *vdev) 83862306a36Sopenharmony_ci{ 83962306a36Sopenharmony_ci struct virtio_uml_device *vu_dev = to_virtio_uml_device(vdev); 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci return vu_dev->status; 84262306a36Sopenharmony_ci} 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_cistatic void vu_set_status(struct virtio_device *vdev, u8 status) 84562306a36Sopenharmony_ci{ 84662306a36Sopenharmony_ci struct virtio_uml_device *vu_dev = to_virtio_uml_device(vdev); 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci vu_dev->status = status; 84962306a36Sopenharmony_ci} 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_cistatic void vu_reset(struct virtio_device *vdev) 85262306a36Sopenharmony_ci{ 85362306a36Sopenharmony_ci struct virtio_uml_device *vu_dev = to_virtio_uml_device(vdev); 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci vu_dev->status = 0; 85662306a36Sopenharmony_ci} 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_cistatic void vu_del_vq(struct virtqueue *vq) 85962306a36Sopenharmony_ci{ 86062306a36Sopenharmony_ci struct virtio_uml_vq_info *info = vq->priv; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci if (info->call_fd >= 0) { 86362306a36Sopenharmony_ci struct virtio_uml_device *vu_dev; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci vu_dev = to_virtio_uml_device(vq->vdev); 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci um_free_irq(vu_dev->irq, vq); 86862306a36Sopenharmony_ci os_close_file(info->call_fd); 86962306a36Sopenharmony_ci } 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci if (info->kick_fd >= 0) 87262306a36Sopenharmony_ci os_close_file(info->kick_fd); 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci vring_del_virtqueue(vq); 87562306a36Sopenharmony_ci kfree(info); 87662306a36Sopenharmony_ci} 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_cistatic void vu_del_vqs(struct virtio_device *vdev) 87962306a36Sopenharmony_ci{ 88062306a36Sopenharmony_ci struct virtio_uml_device *vu_dev = to_virtio_uml_device(vdev); 88162306a36Sopenharmony_ci struct virtqueue *vq, *n; 88262306a36Sopenharmony_ci u64 features; 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci /* Note: reverse order as a workaround to a decoding bug in snabb */ 88562306a36Sopenharmony_ci list_for_each_entry_reverse(vq, &vdev->vqs, list) 88662306a36Sopenharmony_ci WARN_ON(vhost_user_set_vring_enable(vu_dev, vq->index, false)); 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci /* Ensure previous messages have been processed */ 88962306a36Sopenharmony_ci WARN_ON(vhost_user_get_features(vu_dev, &features)); 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci list_for_each_entry_safe(vq, n, &vdev->vqs, list) 89262306a36Sopenharmony_ci vu_del_vq(vq); 89362306a36Sopenharmony_ci} 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_cistatic int vu_setup_vq_call_fd(struct virtio_uml_device *vu_dev, 89662306a36Sopenharmony_ci struct virtqueue *vq) 89762306a36Sopenharmony_ci{ 89862306a36Sopenharmony_ci struct virtio_uml_vq_info *info = vq->priv; 89962306a36Sopenharmony_ci int call_fds[2]; 90062306a36Sopenharmony_ci int rc; 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci /* no call FD needed/desired in this case */ 90362306a36Sopenharmony_ci if (vu_dev->protocol_features & 90462306a36Sopenharmony_ci BIT_ULL(VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS) && 90562306a36Sopenharmony_ci vu_dev->protocol_features & 90662306a36Sopenharmony_ci BIT_ULL(VHOST_USER_PROTOCOL_F_SLAVE_REQ)) { 90762306a36Sopenharmony_ci info->call_fd = -1; 90862306a36Sopenharmony_ci return 0; 90962306a36Sopenharmony_ci } 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci /* Use a pipe for call fd, since SIGIO is not supported for eventfd */ 91262306a36Sopenharmony_ci rc = os_pipe(call_fds, true, true); 91362306a36Sopenharmony_ci if (rc < 0) 91462306a36Sopenharmony_ci return rc; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci info->call_fd = call_fds[0]; 91762306a36Sopenharmony_ci rc = um_request_irq(vu_dev->irq, info->call_fd, IRQ_READ, 91862306a36Sopenharmony_ci vu_interrupt, IRQF_SHARED, info->name, vq); 91962306a36Sopenharmony_ci if (rc < 0) 92062306a36Sopenharmony_ci goto close_both; 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci rc = vhost_user_set_vring_call(vu_dev, vq->index, call_fds[1]); 92362306a36Sopenharmony_ci if (rc) 92462306a36Sopenharmony_ci goto release_irq; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci goto out; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_cirelease_irq: 92962306a36Sopenharmony_ci um_free_irq(vu_dev->irq, vq); 93062306a36Sopenharmony_ciclose_both: 93162306a36Sopenharmony_ci os_close_file(call_fds[0]); 93262306a36Sopenharmony_ciout: 93362306a36Sopenharmony_ci /* Close (unused) write end of call fds */ 93462306a36Sopenharmony_ci os_close_file(call_fds[1]); 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci return rc; 93762306a36Sopenharmony_ci} 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_cistatic struct virtqueue *vu_setup_vq(struct virtio_device *vdev, 94062306a36Sopenharmony_ci unsigned index, vq_callback_t *callback, 94162306a36Sopenharmony_ci const char *name, bool ctx) 94262306a36Sopenharmony_ci{ 94362306a36Sopenharmony_ci struct virtio_uml_device *vu_dev = to_virtio_uml_device(vdev); 94462306a36Sopenharmony_ci struct platform_device *pdev = vu_dev->pdev; 94562306a36Sopenharmony_ci struct virtio_uml_vq_info *info; 94662306a36Sopenharmony_ci struct virtqueue *vq; 94762306a36Sopenharmony_ci int num = MAX_SUPPORTED_QUEUE_SIZE; 94862306a36Sopenharmony_ci int rc; 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci info = kzalloc(sizeof(*info), GFP_KERNEL); 95162306a36Sopenharmony_ci if (!info) { 95262306a36Sopenharmony_ci rc = -ENOMEM; 95362306a36Sopenharmony_ci goto error_kzalloc; 95462306a36Sopenharmony_ci } 95562306a36Sopenharmony_ci snprintf(info->name, sizeof(info->name), "%s.%d-%s", pdev->name, 95662306a36Sopenharmony_ci pdev->id, name); 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci vq = vring_create_virtqueue(index, num, PAGE_SIZE, vdev, true, true, 95962306a36Sopenharmony_ci ctx, vu_notify, callback, info->name); 96062306a36Sopenharmony_ci if (!vq) { 96162306a36Sopenharmony_ci rc = -ENOMEM; 96262306a36Sopenharmony_ci goto error_create; 96362306a36Sopenharmony_ci } 96462306a36Sopenharmony_ci vq->priv = info; 96562306a36Sopenharmony_ci vq->num_max = num; 96662306a36Sopenharmony_ci num = virtqueue_get_vring_size(vq); 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci if (vu_dev->protocol_features & 96962306a36Sopenharmony_ci BIT_ULL(VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS)) { 97062306a36Sopenharmony_ci info->kick_fd = -1; 97162306a36Sopenharmony_ci } else { 97262306a36Sopenharmony_ci rc = os_eventfd(0, 0); 97362306a36Sopenharmony_ci if (rc < 0) 97462306a36Sopenharmony_ci goto error_kick; 97562306a36Sopenharmony_ci info->kick_fd = rc; 97662306a36Sopenharmony_ci } 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci rc = vu_setup_vq_call_fd(vu_dev, vq); 97962306a36Sopenharmony_ci if (rc) 98062306a36Sopenharmony_ci goto error_call; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci rc = vhost_user_set_vring_num(vu_dev, index, num); 98362306a36Sopenharmony_ci if (rc) 98462306a36Sopenharmony_ci goto error_setup; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci rc = vhost_user_set_vring_base(vu_dev, index, 0); 98762306a36Sopenharmony_ci if (rc) 98862306a36Sopenharmony_ci goto error_setup; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci rc = vhost_user_set_vring_addr(vu_dev, index, 99162306a36Sopenharmony_ci virtqueue_get_desc_addr(vq), 99262306a36Sopenharmony_ci virtqueue_get_used_addr(vq), 99362306a36Sopenharmony_ci virtqueue_get_avail_addr(vq), 99462306a36Sopenharmony_ci (u64) -1); 99562306a36Sopenharmony_ci if (rc) 99662306a36Sopenharmony_ci goto error_setup; 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci return vq; 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_cierror_setup: 100162306a36Sopenharmony_ci if (info->call_fd >= 0) { 100262306a36Sopenharmony_ci um_free_irq(vu_dev->irq, vq); 100362306a36Sopenharmony_ci os_close_file(info->call_fd); 100462306a36Sopenharmony_ci } 100562306a36Sopenharmony_cierror_call: 100662306a36Sopenharmony_ci if (info->kick_fd >= 0) 100762306a36Sopenharmony_ci os_close_file(info->kick_fd); 100862306a36Sopenharmony_cierror_kick: 100962306a36Sopenharmony_ci vring_del_virtqueue(vq); 101062306a36Sopenharmony_cierror_create: 101162306a36Sopenharmony_ci kfree(info); 101262306a36Sopenharmony_cierror_kzalloc: 101362306a36Sopenharmony_ci return ERR_PTR(rc); 101462306a36Sopenharmony_ci} 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_cistatic int vu_find_vqs(struct virtio_device *vdev, unsigned nvqs, 101762306a36Sopenharmony_ci struct virtqueue *vqs[], vq_callback_t *callbacks[], 101862306a36Sopenharmony_ci const char * const names[], const bool *ctx, 101962306a36Sopenharmony_ci struct irq_affinity *desc) 102062306a36Sopenharmony_ci{ 102162306a36Sopenharmony_ci struct virtio_uml_device *vu_dev = to_virtio_uml_device(vdev); 102262306a36Sopenharmony_ci int i, queue_idx = 0, rc; 102362306a36Sopenharmony_ci struct virtqueue *vq; 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci /* not supported for now */ 102662306a36Sopenharmony_ci if (WARN_ON(nvqs > 64)) 102762306a36Sopenharmony_ci return -EINVAL; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci rc = vhost_user_set_mem_table(vu_dev); 103062306a36Sopenharmony_ci if (rc) 103162306a36Sopenharmony_ci return rc; 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci for (i = 0; i < nvqs; ++i) { 103462306a36Sopenharmony_ci if (!names[i]) { 103562306a36Sopenharmony_ci vqs[i] = NULL; 103662306a36Sopenharmony_ci continue; 103762306a36Sopenharmony_ci } 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci vqs[i] = vu_setup_vq(vdev, queue_idx++, callbacks[i], names[i], 104062306a36Sopenharmony_ci ctx ? ctx[i] : false); 104162306a36Sopenharmony_ci if (IS_ERR(vqs[i])) { 104262306a36Sopenharmony_ci rc = PTR_ERR(vqs[i]); 104362306a36Sopenharmony_ci goto error_setup; 104462306a36Sopenharmony_ci } 104562306a36Sopenharmony_ci } 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci list_for_each_entry(vq, &vdev->vqs, list) { 104862306a36Sopenharmony_ci struct virtio_uml_vq_info *info = vq->priv; 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci if (info->kick_fd >= 0) { 105162306a36Sopenharmony_ci rc = vhost_user_set_vring_kick(vu_dev, vq->index, 105262306a36Sopenharmony_ci info->kick_fd); 105362306a36Sopenharmony_ci if (rc) 105462306a36Sopenharmony_ci goto error_setup; 105562306a36Sopenharmony_ci } 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci rc = vhost_user_set_vring_enable(vu_dev, vq->index, true); 105862306a36Sopenharmony_ci if (rc) 105962306a36Sopenharmony_ci goto error_setup; 106062306a36Sopenharmony_ci } 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci return 0; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_cierror_setup: 106562306a36Sopenharmony_ci vu_del_vqs(vdev); 106662306a36Sopenharmony_ci return rc; 106762306a36Sopenharmony_ci} 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_cistatic u64 vu_get_features(struct virtio_device *vdev) 107062306a36Sopenharmony_ci{ 107162306a36Sopenharmony_ci struct virtio_uml_device *vu_dev = to_virtio_uml_device(vdev); 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci return vu_dev->features; 107462306a36Sopenharmony_ci} 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_cistatic int vu_finalize_features(struct virtio_device *vdev) 107762306a36Sopenharmony_ci{ 107862306a36Sopenharmony_ci struct virtio_uml_device *vu_dev = to_virtio_uml_device(vdev); 107962306a36Sopenharmony_ci u64 supported = vdev->features & VHOST_USER_SUPPORTED_F; 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci vring_transport_features(vdev); 108262306a36Sopenharmony_ci vu_dev->features = vdev->features | supported; 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci return vhost_user_set_features(vu_dev, vu_dev->features); 108562306a36Sopenharmony_ci} 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_cistatic const char *vu_bus_name(struct virtio_device *vdev) 108862306a36Sopenharmony_ci{ 108962306a36Sopenharmony_ci struct virtio_uml_device *vu_dev = to_virtio_uml_device(vdev); 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci return vu_dev->pdev->name; 109262306a36Sopenharmony_ci} 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_cistatic const struct virtio_config_ops virtio_uml_config_ops = { 109562306a36Sopenharmony_ci .get = vu_get, 109662306a36Sopenharmony_ci .set = vu_set, 109762306a36Sopenharmony_ci .get_status = vu_get_status, 109862306a36Sopenharmony_ci .set_status = vu_set_status, 109962306a36Sopenharmony_ci .reset = vu_reset, 110062306a36Sopenharmony_ci .find_vqs = vu_find_vqs, 110162306a36Sopenharmony_ci .del_vqs = vu_del_vqs, 110262306a36Sopenharmony_ci .get_features = vu_get_features, 110362306a36Sopenharmony_ci .finalize_features = vu_finalize_features, 110462306a36Sopenharmony_ci .bus_name = vu_bus_name, 110562306a36Sopenharmony_ci}; 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_cistatic void virtio_uml_release_dev(struct device *d) 110862306a36Sopenharmony_ci{ 110962306a36Sopenharmony_ci struct virtio_device *vdev = 111062306a36Sopenharmony_ci container_of(d, struct virtio_device, dev); 111162306a36Sopenharmony_ci struct virtio_uml_device *vu_dev = to_virtio_uml_device(vdev); 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci time_travel_propagate_time(); 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci /* might not have been opened due to not negotiating the feature */ 111662306a36Sopenharmony_ci if (vu_dev->req_fd >= 0) { 111762306a36Sopenharmony_ci um_free_irq(vu_dev->irq, vu_dev); 111862306a36Sopenharmony_ci os_close_file(vu_dev->req_fd); 111962306a36Sopenharmony_ci } 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci os_close_file(vu_dev->sock); 112262306a36Sopenharmony_ci kfree(vu_dev); 112362306a36Sopenharmony_ci} 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_civoid virtio_uml_set_no_vq_suspend(struct virtio_device *vdev, 112662306a36Sopenharmony_ci bool no_vq_suspend) 112762306a36Sopenharmony_ci{ 112862306a36Sopenharmony_ci struct virtio_uml_device *vu_dev = to_virtio_uml_device(vdev); 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci if (WARN_ON(vdev->config != &virtio_uml_config_ops)) 113162306a36Sopenharmony_ci return; 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci vu_dev->no_vq_suspend = no_vq_suspend; 113462306a36Sopenharmony_ci dev_info(&vdev->dev, "%sabled VQ suspend\n", 113562306a36Sopenharmony_ci no_vq_suspend ? "dis" : "en"); 113662306a36Sopenharmony_ci} 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_cistatic void vu_of_conn_broken(struct work_struct *wk) 113962306a36Sopenharmony_ci{ 114062306a36Sopenharmony_ci struct virtio_uml_platform_data *pdata; 114162306a36Sopenharmony_ci struct virtio_uml_device *vu_dev; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci pdata = container_of(wk, struct virtio_uml_platform_data, conn_broken_wk); 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci vu_dev = platform_get_drvdata(pdata->pdev); 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci virtio_break_device(&vu_dev->vdev); 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci /* 115062306a36Sopenharmony_ci * We can't remove the device from the devicetree so the only thing we 115162306a36Sopenharmony_ci * can do is warn. 115262306a36Sopenharmony_ci */ 115362306a36Sopenharmony_ci WARN_ON(1); 115462306a36Sopenharmony_ci} 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci/* Platform device */ 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_cistatic struct virtio_uml_platform_data * 115962306a36Sopenharmony_civirtio_uml_create_pdata(struct platform_device *pdev) 116062306a36Sopenharmony_ci{ 116162306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 116262306a36Sopenharmony_ci struct virtio_uml_platform_data *pdata; 116362306a36Sopenharmony_ci int ret; 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci if (!np) 116662306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); 116962306a36Sopenharmony_ci if (!pdata) 117062306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci INIT_WORK(&pdata->conn_broken_wk, vu_of_conn_broken); 117362306a36Sopenharmony_ci pdata->pdev = pdev; 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci ret = of_property_read_string(np, "socket-path", &pdata->socket_path); 117662306a36Sopenharmony_ci if (ret) 117762306a36Sopenharmony_ci return ERR_PTR(ret); 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci ret = of_property_read_u32(np, "virtio-device-id", 118062306a36Sopenharmony_ci &pdata->virtio_device_id); 118162306a36Sopenharmony_ci if (ret) 118262306a36Sopenharmony_ci return ERR_PTR(ret); 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci return pdata; 118562306a36Sopenharmony_ci} 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_cistatic int virtio_uml_probe(struct platform_device *pdev) 118862306a36Sopenharmony_ci{ 118962306a36Sopenharmony_ci struct virtio_uml_platform_data *pdata = pdev->dev.platform_data; 119062306a36Sopenharmony_ci struct virtio_uml_device *vu_dev; 119162306a36Sopenharmony_ci int rc; 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci if (!pdata) { 119462306a36Sopenharmony_ci pdata = virtio_uml_create_pdata(pdev); 119562306a36Sopenharmony_ci if (IS_ERR(pdata)) 119662306a36Sopenharmony_ci return PTR_ERR(pdata); 119762306a36Sopenharmony_ci } 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci vu_dev = kzalloc(sizeof(*vu_dev), GFP_KERNEL); 120062306a36Sopenharmony_ci if (!vu_dev) 120162306a36Sopenharmony_ci return -ENOMEM; 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci vu_dev->pdata = pdata; 120462306a36Sopenharmony_ci vu_dev->vdev.dev.parent = &pdev->dev; 120562306a36Sopenharmony_ci vu_dev->vdev.dev.release = virtio_uml_release_dev; 120662306a36Sopenharmony_ci vu_dev->vdev.config = &virtio_uml_config_ops; 120762306a36Sopenharmony_ci vu_dev->vdev.id.device = pdata->virtio_device_id; 120862306a36Sopenharmony_ci vu_dev->vdev.id.vendor = VIRTIO_DEV_ANY_ID; 120962306a36Sopenharmony_ci vu_dev->pdev = pdev; 121062306a36Sopenharmony_ci vu_dev->req_fd = -1; 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci time_travel_propagate_time(); 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci do { 121562306a36Sopenharmony_ci rc = os_connect_socket(pdata->socket_path); 121662306a36Sopenharmony_ci } while (rc == -EINTR); 121762306a36Sopenharmony_ci if (rc < 0) 121862306a36Sopenharmony_ci goto error_free; 121962306a36Sopenharmony_ci vu_dev->sock = rc; 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci spin_lock_init(&vu_dev->sock_lock); 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci rc = vhost_user_init(vu_dev); 122462306a36Sopenharmony_ci if (rc) 122562306a36Sopenharmony_ci goto error_init; 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci platform_set_drvdata(pdev, vu_dev); 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci device_set_wakeup_capable(&vu_dev->vdev.dev, true); 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci rc = register_virtio_device(&vu_dev->vdev); 123262306a36Sopenharmony_ci if (rc) 123362306a36Sopenharmony_ci put_device(&vu_dev->vdev.dev); 123462306a36Sopenharmony_ci vu_dev->registered = 1; 123562306a36Sopenharmony_ci return rc; 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_cierror_init: 123862306a36Sopenharmony_ci os_close_file(vu_dev->sock); 123962306a36Sopenharmony_cierror_free: 124062306a36Sopenharmony_ci kfree(vu_dev); 124162306a36Sopenharmony_ci return rc; 124262306a36Sopenharmony_ci} 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_cistatic int virtio_uml_remove(struct platform_device *pdev) 124562306a36Sopenharmony_ci{ 124662306a36Sopenharmony_ci struct virtio_uml_device *vu_dev = platform_get_drvdata(pdev); 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci unregister_virtio_device(&vu_dev->vdev); 124962306a36Sopenharmony_ci return 0; 125062306a36Sopenharmony_ci} 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci/* Command line device list */ 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_cistatic void vu_cmdline_release_dev(struct device *d) 125562306a36Sopenharmony_ci{ 125662306a36Sopenharmony_ci} 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_cistatic struct device vu_cmdline_parent = { 125962306a36Sopenharmony_ci .init_name = "virtio-uml-cmdline", 126062306a36Sopenharmony_ci .release = vu_cmdline_release_dev, 126162306a36Sopenharmony_ci}; 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_cistatic bool vu_cmdline_parent_registered; 126462306a36Sopenharmony_cistatic int vu_cmdline_id; 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_cistatic int vu_unregister_cmdline_device(struct device *dev, void *data) 126762306a36Sopenharmony_ci{ 126862306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 126962306a36Sopenharmony_ci struct virtio_uml_platform_data *pdata = pdev->dev.platform_data; 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci kfree(pdata->socket_path); 127262306a36Sopenharmony_ci platform_device_unregister(pdev); 127362306a36Sopenharmony_ci return 0; 127462306a36Sopenharmony_ci} 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_cistatic void vu_conn_broken(struct work_struct *wk) 127762306a36Sopenharmony_ci{ 127862306a36Sopenharmony_ci struct virtio_uml_platform_data *pdata; 127962306a36Sopenharmony_ci struct virtio_uml_device *vu_dev; 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci pdata = container_of(wk, struct virtio_uml_platform_data, conn_broken_wk); 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci vu_dev = platform_get_drvdata(pdata->pdev); 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci virtio_break_device(&vu_dev->vdev); 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci vu_unregister_cmdline_device(&pdata->pdev->dev, NULL); 128862306a36Sopenharmony_ci} 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_cistatic int vu_cmdline_set(const char *device, const struct kernel_param *kp) 129162306a36Sopenharmony_ci{ 129262306a36Sopenharmony_ci const char *ids = strchr(device, ':'); 129362306a36Sopenharmony_ci unsigned int virtio_device_id; 129462306a36Sopenharmony_ci int processed, consumed, err; 129562306a36Sopenharmony_ci char *socket_path; 129662306a36Sopenharmony_ci struct virtio_uml_platform_data pdata, *ppdata; 129762306a36Sopenharmony_ci struct platform_device *pdev; 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci if (!ids || ids == device) 130062306a36Sopenharmony_ci return -EINVAL; 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci processed = sscanf(ids, ":%u%n:%d%n", 130362306a36Sopenharmony_ci &virtio_device_id, &consumed, 130462306a36Sopenharmony_ci &vu_cmdline_id, &consumed); 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci if (processed < 1 || ids[consumed]) 130762306a36Sopenharmony_ci return -EINVAL; 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci if (!vu_cmdline_parent_registered) { 131062306a36Sopenharmony_ci err = device_register(&vu_cmdline_parent); 131162306a36Sopenharmony_ci if (err) { 131262306a36Sopenharmony_ci pr_err("Failed to register parent device!\n"); 131362306a36Sopenharmony_ci put_device(&vu_cmdline_parent); 131462306a36Sopenharmony_ci return err; 131562306a36Sopenharmony_ci } 131662306a36Sopenharmony_ci vu_cmdline_parent_registered = true; 131762306a36Sopenharmony_ci } 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci socket_path = kmemdup_nul(device, ids - device, GFP_KERNEL); 132062306a36Sopenharmony_ci if (!socket_path) 132162306a36Sopenharmony_ci return -ENOMEM; 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci pdata.virtio_device_id = (u32) virtio_device_id; 132462306a36Sopenharmony_ci pdata.socket_path = socket_path; 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_ci pr_info("Registering device virtio-uml.%d id=%d at %s\n", 132762306a36Sopenharmony_ci vu_cmdline_id, virtio_device_id, socket_path); 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci pdev = platform_device_register_data(&vu_cmdline_parent, "virtio-uml", 133062306a36Sopenharmony_ci vu_cmdline_id++, &pdata, 133162306a36Sopenharmony_ci sizeof(pdata)); 133262306a36Sopenharmony_ci err = PTR_ERR_OR_ZERO(pdev); 133362306a36Sopenharmony_ci if (err) 133462306a36Sopenharmony_ci goto free; 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci ppdata = pdev->dev.platform_data; 133762306a36Sopenharmony_ci ppdata->pdev = pdev; 133862306a36Sopenharmony_ci INIT_WORK(&ppdata->conn_broken_wk, vu_conn_broken); 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci return 0; 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_cifree: 134362306a36Sopenharmony_ci kfree(socket_path); 134462306a36Sopenharmony_ci return err; 134562306a36Sopenharmony_ci} 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_cistatic int vu_cmdline_get_device(struct device *dev, void *data) 134862306a36Sopenharmony_ci{ 134962306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 135062306a36Sopenharmony_ci struct virtio_uml_platform_data *pdata = pdev->dev.platform_data; 135162306a36Sopenharmony_ci char *buffer = data; 135262306a36Sopenharmony_ci unsigned int len = strlen(buffer); 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_ci snprintf(buffer + len, PAGE_SIZE - len, "%s:%d:%d\n", 135562306a36Sopenharmony_ci pdata->socket_path, pdata->virtio_device_id, pdev->id); 135662306a36Sopenharmony_ci return 0; 135762306a36Sopenharmony_ci} 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_cistatic int vu_cmdline_get(char *buffer, const struct kernel_param *kp) 136062306a36Sopenharmony_ci{ 136162306a36Sopenharmony_ci buffer[0] = '\0'; 136262306a36Sopenharmony_ci if (vu_cmdline_parent_registered) 136362306a36Sopenharmony_ci device_for_each_child(&vu_cmdline_parent, buffer, 136462306a36Sopenharmony_ci vu_cmdline_get_device); 136562306a36Sopenharmony_ci return strlen(buffer) + 1; 136662306a36Sopenharmony_ci} 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_cistatic const struct kernel_param_ops vu_cmdline_param_ops = { 136962306a36Sopenharmony_ci .set = vu_cmdline_set, 137062306a36Sopenharmony_ci .get = vu_cmdline_get, 137162306a36Sopenharmony_ci}; 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_cidevice_param_cb(device, &vu_cmdline_param_ops, NULL, S_IRUSR); 137462306a36Sopenharmony_ci__uml_help(vu_cmdline_param_ops, 137562306a36Sopenharmony_ci"virtio_uml.device=<socket>:<virtio_id>[:<platform_id>]\n" 137662306a36Sopenharmony_ci" Configure a virtio device over a vhost-user socket.\n" 137762306a36Sopenharmony_ci" See virtio_ids.h for a list of possible virtio device id values.\n" 137862306a36Sopenharmony_ci" Optionally use a specific platform_device id.\n\n" 137962306a36Sopenharmony_ci); 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_cistatic void vu_unregister_cmdline_devices(void) 138362306a36Sopenharmony_ci{ 138462306a36Sopenharmony_ci if (vu_cmdline_parent_registered) { 138562306a36Sopenharmony_ci device_for_each_child(&vu_cmdline_parent, NULL, 138662306a36Sopenharmony_ci vu_unregister_cmdline_device); 138762306a36Sopenharmony_ci device_unregister(&vu_cmdline_parent); 138862306a36Sopenharmony_ci vu_cmdline_parent_registered = false; 138962306a36Sopenharmony_ci } 139062306a36Sopenharmony_ci} 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_ci/* Platform driver */ 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_cistatic const struct of_device_id virtio_uml_match[] = { 139562306a36Sopenharmony_ci { .compatible = "virtio,uml", }, 139662306a36Sopenharmony_ci { } 139762306a36Sopenharmony_ci}; 139862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, virtio_uml_match); 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_cistatic int virtio_uml_suspend(struct platform_device *pdev, pm_message_t state) 140162306a36Sopenharmony_ci{ 140262306a36Sopenharmony_ci struct virtio_uml_device *vu_dev = platform_get_drvdata(pdev); 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci if (!vu_dev->no_vq_suspend) { 140562306a36Sopenharmony_ci struct virtqueue *vq; 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci virtio_device_for_each_vq((&vu_dev->vdev), vq) { 140862306a36Sopenharmony_ci struct virtio_uml_vq_info *info = vq->priv; 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci info->suspended = true; 141162306a36Sopenharmony_ci vhost_user_set_vring_enable(vu_dev, vq->index, false); 141262306a36Sopenharmony_ci } 141362306a36Sopenharmony_ci } 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci if (!device_may_wakeup(&vu_dev->vdev.dev)) { 141662306a36Sopenharmony_ci vu_dev->suspended = true; 141762306a36Sopenharmony_ci return 0; 141862306a36Sopenharmony_ci } 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci return irq_set_irq_wake(vu_dev->irq, 1); 142162306a36Sopenharmony_ci} 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_cistatic int virtio_uml_resume(struct platform_device *pdev) 142462306a36Sopenharmony_ci{ 142562306a36Sopenharmony_ci struct virtio_uml_device *vu_dev = platform_get_drvdata(pdev); 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci if (!vu_dev->no_vq_suspend) { 142862306a36Sopenharmony_ci struct virtqueue *vq; 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci virtio_device_for_each_vq((&vu_dev->vdev), vq) { 143162306a36Sopenharmony_ci struct virtio_uml_vq_info *info = vq->priv; 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_ci info->suspended = false; 143462306a36Sopenharmony_ci vhost_user_set_vring_enable(vu_dev, vq->index, true); 143562306a36Sopenharmony_ci } 143662306a36Sopenharmony_ci } 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci vu_dev->suspended = false; 143962306a36Sopenharmony_ci 144062306a36Sopenharmony_ci if (!device_may_wakeup(&vu_dev->vdev.dev)) 144162306a36Sopenharmony_ci return 0; 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ci return irq_set_irq_wake(vu_dev->irq, 0); 144462306a36Sopenharmony_ci} 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_cistatic struct platform_driver virtio_uml_driver = { 144762306a36Sopenharmony_ci .probe = virtio_uml_probe, 144862306a36Sopenharmony_ci .remove = virtio_uml_remove, 144962306a36Sopenharmony_ci .driver = { 145062306a36Sopenharmony_ci .name = "virtio-uml", 145162306a36Sopenharmony_ci .of_match_table = virtio_uml_match, 145262306a36Sopenharmony_ci }, 145362306a36Sopenharmony_ci .suspend = virtio_uml_suspend, 145462306a36Sopenharmony_ci .resume = virtio_uml_resume, 145562306a36Sopenharmony_ci}; 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_cistatic int __init virtio_uml_init(void) 145862306a36Sopenharmony_ci{ 145962306a36Sopenharmony_ci return platform_driver_register(&virtio_uml_driver); 146062306a36Sopenharmony_ci} 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_cistatic void __exit virtio_uml_exit(void) 146362306a36Sopenharmony_ci{ 146462306a36Sopenharmony_ci platform_driver_unregister(&virtio_uml_driver); 146562306a36Sopenharmony_ci vu_unregister_cmdline_devices(); 146662306a36Sopenharmony_ci} 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_cimodule_init(virtio_uml_init); 146962306a36Sopenharmony_cimodule_exit(virtio_uml_exit); 147062306a36Sopenharmony_ci__uml_exitcall(virtio_uml_exit); 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ciMODULE_DESCRIPTION("UML driver for vhost-user virtio devices"); 147362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1474