[virtio_magma] Support for magma_get_buffer_handle
Using magma_map/unmap for now; will be transitioned to
magma_internal_map/unmap in an upcoming change so the
former may be removed.
Bug:62444
Change-Id: Ia8c7f2bcdd1355208aa0db7a921fccda75270224
diff --git a/drivers/virtio/virtio_magma.c b/drivers/virtio/virtio_magma.c
index 58f5c31..d7e28c6 100644
--- a/drivers/virtio/virtio_magma.c
+++ b/drivers/virtio/virtio_magma.c
@@ -117,6 +117,14 @@ struct virtmagma_virtio_command {
size_t response_size;
};
+struct virtmagma_buffer_fd_priv {
+ struct virtmagma_instance *instance;
+ struct mutex mutex_lock;
+ u64 connection_id;
+ u64 buffer_id;
+ u64 phys_addr;
+};
+
static void virtmagma_cache_ctor(void *p)
{
memset(p, 0, MESSAGE_CACHE_OBJECT_SIZE);
@@ -529,10 +537,161 @@ static int virtmagma_mmfd_mmap(struct file *filp, struct vm_area_struct *vma)
return ret;
}
+static int virtmagma_buffer_fd_release(struct inode *inodep, struct file *filp)
+{
+ struct virtmagma_buffer_fd_priv *priv = filp->private_data;
+ struct virtmagma_instance *instance = priv->instance;
+ struct virtmagma_connection *connection = NULL;
+ struct virtmagma_connection_object *object = NULL;
+ struct virtio_magma_unmap_ctrl *request = NULL;
+ struct virtio_magma_unmap_resp *response = NULL;
+ int ret;
+
+ if (instance) {
+ connection = get_connection(instance, priv->connection_id);
+ }
+
+ if (connection) {
+ object = get_connection_object(connection, priv->buffer_id,
+ MAGMA_BUFFER);
+ }
+
+ if (connection && object && priv->phys_addr) {
+ request = kmem_cache_alloc(instance->msg_cache, GFP_KERNEL);
+ response = kmem_cache_alloc(instance->msg_cache, GFP_KERNEL);
+ }
+
+ if (request && response) {
+ struct virtmagma_virtio_command command = {
+ .request_ptr = request,
+ .request_size = sizeof(*request),
+ .response_ptr = response,
+ .response_size = sizeof(*response)
+ };
+
+ request->hdr.type = VIRTIO_MAGMA_CMD_UNMAP;
+ request->hdr.flags = 0;
+ request->connection = priv->connection_id;
+ request->buffer = priv->buffer_id;
+
+ ret = vq_out_send_sync(instance->vi, &command);
+ if (ret == 0) {
+ virtmagma_check_expected_response_type(request,
+ response);
+ }
+ }
+
+ if (request)
+ kmem_cache_free(instance->msg_cache, request);
+
+ if (response)
+ kmem_cache_free(instance->msg_cache, response);
+
+ mutex_destroy(&priv->mutex_lock);
+ kfree(priv);
+
+ return 0;
+}
+
+// Ensure the entire buffer is mapped in the host device; then
+// we create any number of whole or partial mappings in the guest
+// that point into the host mapped region.
+static int virtmagma_buffer_fd_mmap(struct file *filp,
+ struct vm_area_struct *vma)
+{
+ struct virtmagma_buffer_fd_priv *priv = filp->private_data;
+ struct virtmagma_instance *instance = priv->instance;
+ struct virtmagma_connection *connection;
+ struct virtmagma_connection_object *object;
+ struct virtio_magma_map_ctrl *request;
+ struct virtio_magma_map_resp *response;
+ unsigned long vm_size = vma->vm_end - vma->vm_start;
+ size_t max_map_size;
+ int ret = 0;
+
+ if (!instance)
+ return -ENODEV;
+
+ connection = get_connection(instance, priv->connection_id);
+ if (!connection)
+ return -EINVAL;
+
+ object = get_connection_object(connection, priv->buffer_id,
+ MAGMA_BUFFER);
+ if (!object)
+ return -EINVAL;
+
+ request = kmem_cache_alloc(instance->msg_cache, GFP_KERNEL);
+ if (!request)
+ return -ENOMEM;
+
+ response = kmem_cache_alloc(instance->msg_cache, GFP_KERNEL);
+ if (!response) {
+ kmem_cache_free(instance->msg_cache, request);
+ return -ENOMEM;
+ }
+
+ request->hdr.type = VIRTIO_MAGMA_CMD_MAP;
+ request->hdr.flags = 0;
+ request->connection = priv->connection_id;
+ request->buffer = priv->buffer_id;
+
+ mutex_lock(&priv->mutex_lock);
+ if (!priv->phys_addr) {
+ struct virtmagma_virtio_command command = {
+ .request_ptr = request,
+ .request_size = sizeof(*request),
+ .response_ptr = response,
+ .response_size = sizeof(*response)
+ };
+
+ ret = vq_out_send_sync(instance->vi, &command);
+ if (ret == 0) {
+ ret = virtmagma_check_expected_response_type(request,
+ response);
+ }
+
+ if (ret == 0) {
+ priv->phys_addr = response->addr_out;
+ }
+ }
+ mutex_unlock(&priv->mutex_lock);
+
+ kmem_cache_free(instance->msg_cache, request);
+ kmem_cache_free(instance->msg_cache, response);
+
+ if (ret)
+ return ret;
+
+ max_map_size = PAGE_ALIGN(object->buffer.size_allocated);
+
+ if (vma->vm_pgoff * PAGE_SIZE + vm_size > max_map_size) {
+ pr_warn("virtmagma: user tried to mmap with offset (%ld) and size (%ld) exceededing the buffer's size (%ld)",
+ vma->vm_pgoff * PAGE_SIZE, vm_size, max_map_size);
+ return -EINVAL;
+ }
+
+ ret = io_remap_pfn_range(vma, vma->vm_start,
+ priv->phys_addr / PAGE_SIZE + vma->vm_pgoff,
+ vm_size, vma->vm_page_prot);
+ if (ret)
+ return ret;
+
+ vma->vm_flags |= VM_PFNMAP | VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
+ vma->vm_private_data = priv;
+
+ return 0;
+}
+
static const struct file_operations virtmagma_mmfd_fops = {
.mmap = virtmagma_mmfd_mmap,
};
+static const struct file_operations virtmagma_buffer_fd_fops = {
+ .mmap = virtmagma_buffer_fd_mmap,
+ .release = virtmagma_buffer_fd_release,
+};
+
static int create_instance(struct virtmagma_info *vi,
struct virtmagma_instance **instance_out)
{
@@ -1176,6 +1335,54 @@ static int
return 0;
}
+static int virtmagma_command_magma_get_buffer_handle(
+ struct virtmagma_instance *instance,
+ struct virtmagma_virtio_command *command)
+{
+ struct virtmagma_connection *connection;
+ struct virtmagma_connection_object *object;
+ struct virtmagma_buffer_fd_priv *priv;
+ struct virtio_magma_get_buffer_handle_ctrl *request =
+ command->request_ptr;
+ struct virtio_magma_get_buffer_handle_resp *response =
+ command->response_ptr;
+ int ret;
+
+ connection = get_connection(instance, request->connection);
+ if (!connection)
+ return -EINVAL;
+
+ object = get_connection_object(connection, request->buffer,
+ MAGMA_BUFFER);
+ if (!object)
+ return -EINVAL;
+
+ priv = kzalloc(sizeof(struct virtmagma_buffer_fd_priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ mutex_init(&priv->mutex_lock);
+
+ // Simple reference; the client must ensure the fd is used only while
+ // the buffer remains valid.
+ priv->instance = instance;
+ priv->connection_id = request->connection;
+ priv->buffer_id = request->buffer;
+
+ ret = anon_inode_getfd("[virtmagma]", &virtmagma_buffer_fd_fops, priv,
+ O_RDWR);
+ if (ret < 0) {
+ pr_err("virtmagma: failed to create fd: %d", ret);
+ return ret;
+ }
+
+ response->hdr.type = VIRTIO_MAGMA_RESP_GET_BUFFER_HANDLE;
+ response->handle_out = ret;
+ response->result_return = 0;
+
+ return 0;
+}
+
static int virtmagma_ioctl_handshake(struct file *filp, void __user *ptr)
{
struct virtmagma_ioctl_args_handshake ioctl_args;
@@ -1313,6 +1520,10 @@ int virtmagma_ioctl_magma_command(struct file *filp, void __user *ptr)
case VIRTIO_MAGMA_CMD_EXPORT:
ret = virtmagma_command_magma_export(instance, &command);
break;
+ case VIRTIO_MAGMA_CMD_GET_BUFFER_HANDLE:
+ ret = virtmagma_command_magma_get_buffer_handle(instance,
+ &command);
+ break;
/* pass-through handlers */
case VIRTIO_MAGMA_CMD_QUERY2:
case VIRTIO_MAGMA_CMD_GET_ERROR:
diff --git a/include/uapi/linux/virtio_magma.h b/include/uapi/linux/virtio_magma.h
index afb67ee..6178400 100644
--- a/include/uapi/linux/virtio_magma.h
+++ b/include/uapi/linux/virtio_magma.h
@@ -78,6 +78,7 @@ enum virtio_magma_ctrl_type {
VIRTIO_MAGMA_CMD_BUFFER_SET_NAME = 0x1040,
VIRTIO_MAGMA_CMD_BUFFER_RANGE_OP = 0x1041,
VIRTIO_MAGMA_CMD_BUFFER_GET_INFO = 0x1042,
+ VIRTIO_MAGMA_CMD_GET_BUFFER_HANDLE = 0x1043,
/* magma success responses
*/
VIRTIO_MAGMA_RESP_RELEASE_CONNECTION = 0x2004,
@@ -137,6 +138,7 @@ enum virtio_magma_ctrl_type {
VIRTIO_MAGMA_RESP_BUFFER_SET_NAME = 0x2040,
VIRTIO_MAGMA_RESP_BUFFER_RANGE_OP = 0x2041,
VIRTIO_MAGMA_RESP_BUFFER_GET_INFO = 0x2042,
+ VIRTIO_MAGMA_RESP_GET_BUFFER_HANDLE = 0x2043,
/* magma error responses
*/
VIRTIO_MAGMA_RESP_ERR_UNIMPLEMENTED = 0x3001,
@@ -263,6 +265,8 @@ inline const char* virtio_magma_ctrl_type_string(enum virtio_magma_ctrl_type typ
case VIRTIO_MAGMA_RESP_BUFFER_RANGE_OP: return "VIRTIO_MAGMA_RESP_BUFFER_RANGE_OP";
case VIRTIO_MAGMA_CMD_BUFFER_GET_INFO: return "VIRTIO_MAGMA_CMD_BUFFER_GET_INFO";
case VIRTIO_MAGMA_RESP_BUFFER_GET_INFO: return "VIRTIO_MAGMA_RESP_BUFFER_GET_INFO";
+ case VIRTIO_MAGMA_CMD_GET_BUFFER_HANDLE: return "VIRTIO_MAGMA_CMD_GET_BUFFER_HANDLE";
+ case VIRTIO_MAGMA_RESP_GET_BUFFER_HANDLE: return "VIRTIO_MAGMA_RESP_GET_BUFFER_HANDLE";
case VIRTIO_MAGMA_RESP_ERR_UNIMPLEMENTED: return "VIRTIO_MAGMA_RESP_ERR_UNIMPLEMENTED";
case VIRTIO_MAGMA_RESP_ERR_INTERNAL: return "VIRTIO_MAGMA_RESP_ERR_INTERNAL";
case VIRTIO_MAGMA_RESP_ERR_HOST_DISCONNECTED: return "VIRTIO_MAGMA_RESP_ERR_HOST_DISCONNECTED";
@@ -332,6 +336,7 @@ inline enum virtio_magma_ctrl_type virtio_magma_expected_response_type(enum virt
case VIRTIO_MAGMA_CMD_BUFFER_SET_NAME: return VIRTIO_MAGMA_RESP_BUFFER_SET_NAME;
case VIRTIO_MAGMA_CMD_BUFFER_RANGE_OP: return VIRTIO_MAGMA_RESP_BUFFER_RANGE_OP;
case VIRTIO_MAGMA_CMD_BUFFER_GET_INFO: return VIRTIO_MAGMA_RESP_BUFFER_GET_INFO;
+ case VIRTIO_MAGMA_CMD_GET_BUFFER_HANDLE: return VIRTIO_MAGMA_RESP_GET_BUFFER_HANDLE;
default: return VIRTIO_MAGMA_RESP_ERR_INVALID_COMMAND;
}
}
@@ -998,5 +1003,17 @@ struct virtio_magma_buffer_get_info_resp {
__le64 result_return;
} __attribute((packed));
+struct virtio_magma_get_buffer_handle_ctrl {
+ struct virtio_magma_ctrl_hdr hdr;
+ __le64 connection;
+ __le64 buffer;
+} __attribute((packed));
+
+struct virtio_magma_get_buffer_handle_resp {
+ struct virtio_magma_ctrl_hdr hdr;
+ __le64 handle_out;
+ __le64 result_return;
+} __attribute((packed));
+
#endif /* _LINUX_VIRTIO_MAGMA_H
*/