[virtio-magma] Add magma_read_notification_channel2

Change-Id: I07edfa0beefd32dd31dfa8f8524baea43c42027b
diff --git a/drivers/virtio/virtio_magma.c b/drivers/virtio/virtio_magma.c
index efa3e08..e8f4f45 100644
--- a/drivers/virtio/virtio_magma.c
+++ b/drivers/virtio/virtio_magma.c
@@ -1187,6 +1187,54 @@
 			    response->buffer_size_out);
 }
 
+static int virtmagma_command_magma_read_notification_channel2(
+	struct virtmagma_instance *instance,
+	struct virtmagma_virtio_command *command)
+{
+	int ret;
+	struct virtio_magma_read_notification_channel2_ctrl *request =
+		command->request_ptr;
+	struct virtio_magma_read_notification_channel2_resp *response =
+		command->response_ptr;
+
+	if (!COMMAND_OK(command, request, response))
+		return -EINVAL;
+
+	/* reallocate response buffer with additional space for notification data.
+	   note that the size is not modified, as we only want the response struct
+	   itself to be copied back to the user by our caller */
+
+	command->response_ptr = response =
+		kzalloc(sizeof(*response) + NOTIFICATION_MAX_BYTES, GFP_KERNEL);
+	if (!command->response_ptr)
+		return -ENOMEM;
+
+	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 writing to the buffer */
+	if (response->result_return) {
+		pr_warn("virtmagma: magma_read_notification_channel2 returned %d",
+			(int32_t)response->result_return);
+		return 0; /* the ioctl is still successful */
+	}
+
+	if (response->buffer_size_out > request->buffer_size) {
+		pr_err("virtmagma: magma_read_notification_channel2 returned buffer_size_out (%lld) larger than buffer_size (%lld)",
+		       response->buffer_size_out, request->buffer_size);
+		return -EIO;
+	}
+
+	return copy_to_user((void *)request->buffer,
+			    (char *)command->response_ptr + sizeof(*response),
+			    response->buffer_size_out);
+}
+
 #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);
@@ -1400,6 +1448,10 @@
 		ret = virtmagma_command_magma_read_notification_channel(
 			instance, &command);
 		break;
+	case VIRTIO_MAGMA_CMD_READ_NOTIFICATION_CHANNEL2:
+		ret = virtmagma_command_magma_read_notification_channel2(
+			instance, &command);
+		break;
 	case VIRTIO_MAGMA_CMD_EXPORT:
 		ret = virtmagma_command_magma_export(instance, &command);
 		break;
diff --git a/include/uapi/linux/virtio_magma.h b/include/uapi/linux/virtio_magma.h
index aaf5e0e..3430b83 100644
--- a/include/uapi/linux/virtio_magma.h
+++ b/include/uapi/linux/virtio_magma.h
@@ -70,6 +70,7 @@
 	VIRTIO_MAGMA_CMD_BUFFER_RANGE_OP = 0x1041,
 	VIRTIO_MAGMA_CMD_BUFFER_GET_INFO = 0x1042,
 	VIRTIO_MAGMA_CMD_GET_BUFFER_HANDLE = 0x1043,
+	VIRTIO_MAGMA_CMD_READ_NOTIFICATION_CHANNEL2 = 0x1046,
 	VIRTIO_MAGMA_CMD_INTERNAL_MAP = 0x1044,
 	VIRTIO_MAGMA_CMD_INTERNAL_UNMAP = 0x1045,
 	/* magma success responses
@@ -123,6 +124,7 @@
 	VIRTIO_MAGMA_RESP_BUFFER_RANGE_OP = 0x2041,
 	VIRTIO_MAGMA_RESP_BUFFER_GET_INFO = 0x2042,
 	VIRTIO_MAGMA_RESP_GET_BUFFER_HANDLE = 0x2043,
+	VIRTIO_MAGMA_RESP_READ_NOTIFICATION_CHANNEL2 = 0x2046,
 	VIRTIO_MAGMA_RESP_INTERNAL_MAP = 0x2044,
 	VIRTIO_MAGMA_RESP_INTERNAL_UNMAP = 0x2045,
 	/* magma error responses
@@ -235,6 +237,8 @@
 		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_CMD_READ_NOTIFICATION_CHANNEL2: return "VIRTIO_MAGMA_CMD_READ_NOTIFICATION_CHANNEL2";
+		case VIRTIO_MAGMA_RESP_READ_NOTIFICATION_CHANNEL2: return "VIRTIO_MAGMA_RESP_READ_NOTIFICATION_CHANNEL2";
 		case VIRTIO_MAGMA_CMD_INTERNAL_MAP: return "VIRTIO_MAGMA_CMD_INTERNAL_MAP";
 		case VIRTIO_MAGMA_RESP_INTERNAL_MAP: return "VIRTIO_MAGMA_RESP_INTERNAL_MAP";
 		case VIRTIO_MAGMA_CMD_INTERNAL_UNMAP: return "VIRTIO_MAGMA_CMD_INTERNAL_UNMAP";
@@ -300,6 +304,7 @@
 		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;
+		case VIRTIO_MAGMA_CMD_READ_NOTIFICATION_CHANNEL2: return VIRTIO_MAGMA_RESP_READ_NOTIFICATION_CHANNEL2;
 		case VIRTIO_MAGMA_CMD_INTERNAL_MAP: return VIRTIO_MAGMA_RESP_INTERNAL_MAP;
 		case VIRTIO_MAGMA_CMD_INTERNAL_UNMAP: return VIRTIO_MAGMA_RESP_INTERNAL_UNMAP;
 		default: return VIRTIO_MAGMA_RESP_ERR_INVALID_COMMAND;
@@ -869,6 +874,20 @@
 	__le64 result_return;
 } __attribute((packed));
 
+struct virtio_magma_read_notification_channel2_ctrl {
+	struct virtio_magma_ctrl_hdr hdr;
+	__le64 connection;
+	__le64 buffer;
+	__le64 buffer_size;
+} __attribute((packed));
+
+struct virtio_magma_read_notification_channel2_resp {
+	struct virtio_magma_ctrl_hdr hdr;
+	__le64 buffer_size_out;
+	__le64 more_data_out;
+	__le64 result_return;
+} __attribute((packed));
+
 struct virtio_magma_internal_map_ctrl {
 	struct virtio_magma_ctrl_hdr hdr;
 	__le64 connection;