[virtio_magma][virtio_wl] Support for magma_import

Change-Id: I72b488d6302bfd65f375a9aec2d9bcd5ba4ec725
diff --git a/drivers/virtio/virtio_magma.c b/drivers/virtio/virtio_magma.c
index 7f49cb9..dc655cd 100644
--- a/drivers/virtio/virtio_magma.c
+++ b/drivers/virtio/virtio_magma.c
@@ -1508,8 +1508,11 @@
 #if IS_ENABLED(CONFIG_VIRTIO_WL)
 /* use the implementation in the virtio_wl module */
 extern int virtwl_create_fd_for_vfd(void *filp_private_data, uint32_t vfd_id);
+extern int virtwl_get_vfd_from_fd(void *filp_private_data, int fd,
+				  uint32_t *vfd_id);
 #else
 #define virtwl_create_fd_for_vfd(a, b) (-ENODEV)
+#define virtwl_get_vfd_from_fd(a, b, c) (-ENODEV)
 #endif
 
 static int
@@ -1554,6 +1557,77 @@
 	return 0;
 }
 
+static int
+	virtmagma_command_magma_import(struct virtmagma_instance *instance,
+				       struct virtmagma_virtio_command *command)
+{
+	int ret;
+	struct virtmagma_connection *connection;
+	struct virtio_magma_import_ctrl *request = command->request_ptr;
+	struct virtio_magma_import_resp *response = command->response_ptr;
+	uint32_t vfd;
+	uint64_t buffer_size;
+
+	if (!instance->wayland_device_private_data)
+		return -ENODEV;
+
+	if (!COMMAND_OK(command, request, response))
+		return -EINVAL;
+
+	connection = get_connection(instance, request->connection);
+	if (!connection)
+		return -EINVAL;
+
+	{
+		int fd = request->buffer_handle;
+		ret = virtwl_get_vfd_from_fd(
+			instance->wayland_device_private_data, fd, &vfd);
+		if (ret < 0) {
+			pr_err("virtmagma: failed to get vfd from fd %u", fd);
+			return ret;
+		}
+	}
+
+	request->buffer_handle = vfd;
+
+	ret = vq_out_send_sync(instance->vi, command);
+	if (ret)
+		return ret;
+
+	ret = virtmagma_check_expected_response_type(request, response);
+	if (ret)
+		return ret;
+
+	/* pass on magma errors without creating a vfd */
+	if (response->result_return) {
+		pr_warn("virtmagma: magma_import returned %d",
+			(int32_t)response->result_return);
+		return 0; /* the ioctl is still successful */
+	}
+
+	ret = get_buffer_size(instance, response->buffer_out, &buffer_size);
+	if (ret)
+		return ret;
+
+	{
+		struct virtmagma_connection_object *object =
+			kzalloc(sizeof(*object), GFP_KERNEL);
+		if (!object)
+			return -ENOMEM;
+
+		object->parent_connection = connection;
+		object->host_value = response->buffer_out;
+		object->type = MAGMA_BUFFER;
+		object->buffer.size_requested = buffer_size;
+		object->buffer.size_allocated = buffer_size;
+
+		hash_add(connection->objects, &object->node,
+			 object->host_value);
+	}
+
+	return 0;
+}
+
 static int virtmagma_command_magma_get_buffer_handle(
 	struct virtmagma_instance *instance,
 	struct virtmagma_virtio_command *command)
@@ -1733,6 +1807,9 @@
 	case VIRTIO_MAGMA_CMD_EXPORT:
 		ret = virtmagma_command_magma_export(instance, &command);
 		break;
+	case VIRTIO_MAGMA_CMD_IMPORT:
+		ret = virtmagma_command_magma_import(instance, &command);
+		break;
 	case VIRTIO_MAGMA_CMD_GET_BUFFER_HANDLE:
 		ret = virtmagma_command_magma_get_buffer_handle(instance,
 								&command);
diff --git a/drivers/virtio/virtio_wl.c b/drivers/virtio/virtio_wl.c
index 3ee6225..5fdf9b7c 100644
--- a/drivers/virtio/virtio_wl.c
+++ b/drivers/virtio/virtio_wl.c
@@ -946,6 +946,21 @@
 				virtwl_vfd_file_flags(vfd) | O_CLOEXEC);
 }
 
+int virtwl_get_vfd_from_fd(void *filp_private_data, int fd, uint32_t* vfd_id_out) {
+	struct fd f = fdget(fd);
+
+	if (!f.file)
+		return -ENOENT;
+
+	{
+		struct virtwl_vfd *vfd = f.file->private_data;
+		*vfd_id_out = vfd->id;
+	}
+
+	fdput(f);
+	return 0;
+}
+
 static int virtwl_open(struct inode *inodep, struct file *filp)
 {
 	struct virtwl_info *vi = container_of(inodep->i_cdev,