[virtio_magma] Support magma_query_returns_buffer2

Change-Id: Icbd985d148c3ee3e2704904d7c0a102f692eea4b
diff --git a/drivers/virtio/virtio_magma.c b/drivers/virtio/virtio_magma.c
index 98b4344..ef3db43 100644
--- a/drivers/virtio/virtio_magma.c
+++ b/drivers/virtio/virtio_magma.c
@@ -651,7 +651,7 @@
 	max_map_size = PAGE_ALIGN(priv->buffer_size);
 
 	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)",
+		pr_warn("virtmagma: user tried to mmap with offset (%ld) and size (%ld) exceeding the buffer's size (%ld)",
 			vma->vm_pgoff * PAGE_SIZE, vm_size, max_map_size);
 		return -EINVAL;
 	}
@@ -1732,6 +1732,76 @@
 	return ret;
 }
 
+static int virtmagma_command_magma_query_returns_buffer2(
+	struct virtmagma_instance *instance,
+	struct virtmagma_virtio_command *command)
+{
+	struct virtmagma_buffer_fd_priv *priv;
+	struct virtio_magma_query_returns_buffer2_ctrl *request =
+		command->request_ptr;
+	struct virtio_magma_query_returns_buffer2_resp *response =
+		command->response_ptr;
+	uint64_t buffer_size;
+	uint32_t buffer_handle;
+	int ret;
+
+	/* Reallocate response buffer with additional space for buffer size;
+	   note that response_size is not modified, as we only want the response struct
+	   itself to be copied back to the user by our caller.
+	   Memory will be freed by caller.
+	*/
+	response = kzalloc(sizeof(*response) + sizeof(uint64_t), GFP_KERNEL);
+	if (!response)
+		return -ENOMEM;
+
+	command->response_ptr = response;
+
+	if (!COMMAND_OK(command, request, response))
+		return -EINVAL;
+
+	ret = vq_out_send_sync(instance->vi, command);
+	if (ret)
+		return ret;
+
+	ret = virtmagma_check_expected_response_type(request, response);
+	if (ret)
+		return ret;
+
+	if (response->result_return) {
+		pr_warn("virtmagma: magma_query_returns_buffer2 returned %d",
+			(int32_t)response->result_return);
+		return 0; /* the ioctl is still successful */
+	}
+
+	buffer_handle = response->handle_out;
+	buffer_size = *(uint64_t*)(response + 1);
+
+	priv = kzalloc(sizeof(struct virtmagma_buffer_fd_priv), GFP_KERNEL);
+	if (!priv) {
+		virtmagma_release_handle(instance->vi, buffer_handle);
+		return -ENOMEM;
+	}
+
+	mutex_init(&priv->mutex_lock);
+
+	priv->vi = instance->vi;
+	priv->buffer_handle = buffer_handle;
+	priv->buffer_size = buffer_size;
+
+	ret = anon_inode_getfd("[virtmagma]", &virtmagma_buffer_fd_fops, priv,
+			       O_RDWR);
+	if (ret < 0) {
+		pr_err("virtmagma: failed to create fd: %d", ret);
+		virtmagma_release_handle(instance->vi, buffer_handle);
+		kfree(priv);
+		return ret;
+	}
+
+	response->handle_out = ret;
+
+	return 0;
+}
+
 static int virtmagma_ioctl_handshake(struct file *filp, void __user *ptr)
 {
 	struct virtmagma_ioctl_args_handshake ioctl_args;
@@ -1878,6 +1948,9 @@
 		ret = virtmagma_command_magma_virt_get_image_info(instance,
 								    &command);
 		break;
+	case VIRTIO_MAGMA_CMD_QUERY_RETURNS_BUFFER2:
+		ret = virtmagma_command_magma_query_returns_buffer2(instance, &command);
+		break;
 	/* pass-through handlers */
 	case VIRTIO_MAGMA_CMD_QUERY2:
 	case VIRTIO_MAGMA_CMD_GET_ERROR: