Spdk/lib/vfio_user/host/vfio_user.c
Jim Harris 488570ebd4 Replace most BSD 3-clause license text with SPDX identifier.
Many open source projects have moved to using SPDX identifiers
to specify license information, reducing the amount of
boilerplate code in every source file.  This patch replaces
the bulk of SPDK .c, .cpp and Makefiles with the BSD-3-Clause
identifier.

Almost all of these files share the exact same license text,
and this patch only modifies the files that contain the
most common license text.  There can be slight variations
because the third clause contains company names - most say
"Intel Corporation", but there are instances for Nvidia,
Samsung, Eideticom and even "the copyright holder".

Used a bash script to automate replacement of the license text
with SPDX identifier which is checked into scripts/spdx.sh.

Signed-off-by: Jim Harris <james.r.harris@intel.com>
Change-Id: Iaa88ab5e92ea471691dc298cfe41ebfb5d169780
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/12904
Community-CI: Broadcom CI <spdk-ci.pdl@broadcom.com>
Community-CI: Mellanox Build Bot
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Aleksey Marchuk <alexeymar@nvidia.com>
Reviewed-by: Changpeng Liu <changpeng.liu@intel.com>
Reviewed-by: Dong Yi <dongx.yi@intel.com>
Reviewed-by: Konrad Sztyber <konrad.sztyber@intel.com>
Reviewed-by: Paul Luse <paul.e.luse@intel.com>
Reviewed-by: <qun.wan@intel.com>
2022-06-09 07:35:12 +00:00

380 lines
9.3 KiB
C

/* SPDX-License-Identifier: BSD-3-Clause
* Copyright (c) Intel Corporation.
* All rights reserved.
*/
/*
* vfio-user client socket messages.
*/
#include "spdk/stdinc.h"
#include "spdk/queue.h"
#include "spdk/util.h"
#include "spdk/log.h"
#include "spdk/vfio_user_spec.h"
#include "vfio_user_internal.h"
struct vfio_user_request {
struct vfio_user_header hdr;
#define VFIO_USER_MAX_PAYLOAD_SIZE (4096)
uint8_t payload[VFIO_USER_MAX_PAYLOAD_SIZE];
int fds[VFIO_MAXIMUM_SPARSE_MMAP_REGIONS];
int fd_num;
};
#ifdef DEBUG
static const char *vfio_user_message_str[VFIO_USER_MAX] = {
[VFIO_USER_VERSION] = "VFIO_USER_VERSION",
[VFIO_USER_DMA_MAP] = "VFIO_USER_DMA_MAP",
[VFIO_USER_DMA_UNMAP] = "VFIO_USER_DMA_UNMAP",
[VFIO_USER_DEVICE_GET_INFO] = "VFIO_USER_DEVICE_GET_INFO",
[VFIO_USER_DEVICE_GET_REGION_INFO] = "VFIO_USER_DEVICE_GET_REGION_INFO",
[VFIO_USER_DEVICE_GET_IRQ_INFO] = "VFIO_USER_DEVICE_GET_IRQ_INFO",
[VFIO_USER_DEVICE_SET_IRQS] = "VFIO_USER_DEVICE_SET_IRQS",
[VFIO_USER_REGION_READ] = "VFIO_USER_REGION_READ",
[VFIO_USER_REGION_WRITE] = "VFIO_USER_REGION_WRITE",
[VFIO_USER_DMA_READ] = "VFIO_USER_DMA_READ",
[VFIO_USER_DMA_WRITE] = "VFIO_USER_DMA_WRITE",
[VFIO_USER_DEVICE_RESET] = "VFIO_USER_DEVICE_RESET",
};
#endif
static int
vfio_user_write(int fd, void *buf, int len, int *fds, int num_fds)
{
int r;
struct msghdr msgh;
struct iovec iov;
size_t fd_size = num_fds * sizeof(int);
char control[CMSG_SPACE(VFIO_MAXIMUM_SPARSE_MMAP_REGIONS * sizeof(int))];
struct cmsghdr *cmsg;
memset(&msgh, 0, sizeof(msgh));
memset(control, 0, sizeof(control));
iov.iov_base = (uint8_t *)buf;
iov.iov_len = len;
msgh.msg_iov = &iov;
msgh.msg_iovlen = 1;
assert(num_fds <= VFIO_MAXIMUM_SPARSE_MMAP_REGIONS);
if (fds && num_fds) {
msgh.msg_control = control;
msgh.msg_controllen = CMSG_SPACE(fd_size);
cmsg = CMSG_FIRSTHDR(&msgh);
assert(cmsg != NULL);
cmsg->cmsg_len = CMSG_LEN(fd_size);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
memcpy(CMSG_DATA(cmsg), fds, fd_size);
} else {
msgh.msg_control = NULL;
msgh.msg_controllen = 0;
}
do {
r = sendmsg(fd, &msgh, MSG_NOSIGNAL);
} while (r < 0 && errno == EINTR);
if (r == -1) {
return -errno;
}
return 0;
}
static int
read_fd_message(int sockfd, char *buf, int buflen, int *fds, int *fd_num)
{
struct iovec iov;
struct msghdr msgh;
char control[CMSG_SPACE(VFIO_MAXIMUM_SPARSE_MMAP_REGIONS * sizeof(int))];
struct cmsghdr *cmsg;
int got_fds = 0;
int ret;
memset(&msgh, 0, sizeof(msgh));
iov.iov_base = buf;
iov.iov_len = buflen;
msgh.msg_iov = &iov;
msgh.msg_iovlen = 1;
msgh.msg_control = control;
msgh.msg_controllen = sizeof(control);
ret = recvmsg(sockfd, &msgh, 0);
if (ret <= 0) {
return ret;
}
if (msgh.msg_flags & (MSG_TRUNC | MSG_CTRUNC)) {
return -ENOTSUP;
}
for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL;
cmsg = CMSG_NXTHDR(&msgh, cmsg)) {
if ((cmsg->cmsg_level == SOL_SOCKET) &&
(cmsg->cmsg_type == SCM_RIGHTS)) {
got_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
*fd_num = got_fds;
assert(got_fds <= VFIO_MAXIMUM_SPARSE_MMAP_REGIONS);
memcpy(fds, CMSG_DATA(cmsg), got_fds * sizeof(int));
break;
}
}
return ret;
}
static int
vfio_user_read(int fd, struct vfio_user_request *req)
{
int ret;
size_t sz_payload;
ret = read_fd_message(fd, (char *)req, sizeof(struct vfio_user_header), req->fds, &req->fd_num);
if (ret <= 0) {
return ret;
}
if (req->hdr.flags.error) {
SPDK_ERRLOG("Command %u return failure\n", req->hdr.cmd);
errno = req->hdr.error_no;
return -EFAULT;
}
if (req->hdr.msg_size > sizeof(struct vfio_user_header)) {
sz_payload = req->hdr.msg_size - sizeof(struct vfio_user_header);
ret = read(fd, req->payload, sz_payload);
if (ret <= 0) {
return ret;
}
}
return 0;
}
static int
vfio_user_dev_send_request(struct vfio_device *dev, enum vfio_user_command command,
void *arg, size_t arg_len, size_t buf_len, int *fds, int max_fds)
{
struct vfio_user_request req = {};
size_t sz_payload;
int ret;
bool fds_write = false;
if (arg_len > VFIO_USER_MAX_PAYLOAD_SIZE) {
SPDK_ERRLOG("Oversized argument length, command %u\n", command);
return -EINVAL;
}
req.hdr.cmd = command;
req.hdr.msg_size = sizeof(struct vfio_user_header) + arg_len;
memcpy(req.payload, arg, arg_len);
if (command == VFIO_USER_DMA_MAP || command == VFIO_USER_DMA_UNMAP) {
fds_write = true;
}
SPDK_DEBUGLOG(vfio_user, "[I] Command %s, msg size %u, fds %p, max_fds %d\n",
vfio_user_message_str[command], req.hdr.msg_size, fds, max_fds);
if (fds_write && fds) {
ret = vfio_user_write(dev->fd, (void *)&req, req.hdr.msg_size, fds, max_fds);
} else {
ret = vfio_user_write(dev->fd, (void *)&req, req.hdr.msg_size, NULL, 0);
}
if (ret) {
return ret;
}
/* a reply is mandatory */
memset(&req, 0, sizeof(req));
ret = vfio_user_read(dev->fd, &req);
if (ret) {
return ret;
}
SPDK_DEBUGLOG(vfio_user, "[I] Command %s response, msg size %u\n",
vfio_user_message_str[req.hdr.cmd], req.hdr.msg_size);
assert(req.hdr.flags.type == VFIO_USER_MESSAGE_REPLY);
sz_payload = req.hdr.msg_size - sizeof(struct vfio_user_header);
if (!sz_payload) {
return 0;
}
if (!fds_write) {
if (sz_payload > buf_len) {
SPDK_ERRLOG("Payload size error sz %zd, buf_len %zd\n", sz_payload, buf_len);
return -EIO;
}
memcpy(arg, req.payload, sz_payload);
/* VFIO_USER_DEVICE_GET_REGION_INFO may contains BAR fd */
if (fds && req.fd_num) {
assert(req.fd_num < max_fds);
memcpy(fds, req.fds, sizeof(int) * req.fd_num);
}
}
return 0;
}
static int
vfio_user_check_version(struct vfio_device *dev)
{
int ret;
struct vfio_user_request req = {};
struct vfio_user_version *version = (struct vfio_user_version *)req.payload;
version->major = VFIO_USER_MAJOR_VER;
version->minor = VFIO_USER_MINOR_VER;
ret = vfio_user_dev_send_request(dev, VFIO_USER_VERSION, req.payload,
sizeof(struct vfio_user_version), sizeof(req.payload), NULL, 0);
if (ret < 0) {
return ret;
}
SPDK_DEBUGLOG(vfio_user, "%s Negotiate version %u.%u\n", vfio_user_message_str[VFIO_USER_VERSION],
version->major, version->minor);
return 0;
}
int
vfio_user_get_dev_region_info(struct vfio_device *dev, struct vfio_region_info *region_info,
size_t buf_len, int *fds, int num_fds)
{
assert(buf_len > sizeof(struct vfio_region_info));
region_info->argsz = buf_len - sizeof(struct vfio_region_info);
return vfio_user_dev_send_request(dev, VFIO_USER_DEVICE_GET_REGION_INFO,
region_info, region_info->argsz, buf_len, fds, num_fds);
}
int
vfio_user_get_dev_info(struct vfio_device *dev, struct vfio_user_device_info *dev_info,
size_t buf_len)
{
dev_info->argsz = sizeof(struct vfio_user_device_info);
return vfio_user_dev_send_request(dev, VFIO_USER_DEVICE_GET_INFO,
dev_info, dev_info->argsz, buf_len, NULL, 0);
}
int
vfio_user_dev_dma_map_unmap(struct vfio_device *dev, struct vfio_memory_region *mr, bool map)
{
struct vfio_user_dma_map dma_map = { 0 };
struct vfio_user_dma_unmap dma_unmap = { 0 };
if (map) {
dma_map.argsz = sizeof(struct vfio_user_dma_map);
dma_map.addr = mr->iova;
dma_map.size = mr->size;
dma_map.offset = mr->offset;
dma_map.flags = VFIO_USER_F_DMA_REGION_READ | VFIO_USER_F_DMA_REGION_WRITE;
return vfio_user_dev_send_request(dev, VFIO_USER_DMA_MAP,
&dma_map, sizeof(dma_map), sizeof(dma_map), &mr->fd, 1);
} else {
dma_unmap.argsz = sizeof(struct vfio_user_dma_unmap);
dma_unmap.addr = mr->iova;
dma_unmap.size = mr->size;
return vfio_user_dev_send_request(dev, VFIO_USER_DMA_UNMAP,
&dma_unmap, sizeof(dma_unmap), sizeof(dma_unmap), &mr->fd, 1);
}
}
int
vfio_user_dev_mmio_access(struct vfio_device *dev, uint32_t index, uint64_t offset,
size_t len, void *buf, bool is_write)
{
struct vfio_user_region_access *access;
size_t arg_len;
int ret;
arg_len = sizeof(*access) + len;
access = calloc(1, arg_len);
if (!access) {
return -ENOMEM;
}
access->offset = offset;
access->region = index;
access->count = len;
if (is_write) {
memcpy(access->data, buf, len);
ret = vfio_user_dev_send_request(dev, VFIO_USER_REGION_WRITE,
access, arg_len, arg_len, NULL, 0);
} else {
ret = vfio_user_dev_send_request(dev, VFIO_USER_REGION_READ,
access, sizeof(*access), arg_len, NULL, 0);
}
if (ret) {
free(access);
return ret;
}
if (!is_write) {
memcpy(buf, (void *)access->data, len);
}
free(access);
return 0;
}
int
vfio_user_dev_setup(struct vfio_device *dev)
{
int fd;
int flag;
struct sockaddr_un un;
ssize_t rc;
fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd < 0) {
SPDK_ERRLOG("socket() error\n");
return -errno;
}
flag = fcntl(fd, F_GETFD);
if (fcntl(fd, F_SETFD, flag | FD_CLOEXEC) < 0) {
SPDK_ERRLOG("fcntl failed\n");
}
memset(&un, 0, sizeof(un));
un.sun_family = AF_UNIX;
rc = snprintf(un.sun_path, sizeof(un.sun_path), "%s", dev->path);
if (rc < 0 || (size_t)rc >= sizeof(un.sun_path)) {
SPDK_ERRLOG("socket path too long\n");
close(fd);
if (rc < 0) {
return -errno;
} else {
return -EINVAL;
}
}
if (connect(fd, (struct sockaddr *)&un, sizeof(un)) < 0) {
SPDK_ERRLOG("connect error\n");
close(fd);
return -errno;
}
dev->fd = fd;
if (vfio_user_check_version(dev)) {
SPDK_ERRLOG("Check VFIO_USER_VERSION message failed\n");
close(fd);
return -EFAULT;
}
return 0;
}
SPDK_LOG_REGISTER_COMPONENT(vfio_user)