diff mbox

[1/2] KVM: s390: Dynamic allocation of virtio-ccw I/O data.

Message ID 1357570312-32074-2-git-send-email-cornelia.huck@de.ibm.com (mailing list archive)
State New, archived
Headers show

Commit Message

Cornelia Huck Jan. 7, 2013, 2:51 p.m. UTC
Dynamically allocate any data structures like ccw used when
doing channel I/O. Otherwise, we'd need to add extra serialization
for the different callbacks using the same data structures.

Reported-by: Christian Borntraeger <borntraeger@de.ibm.com>
Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
---
 drivers/s390/kvm/virtio_ccw.c | 280 ++++++++++++++++++++++++++----------------
 1 file changed, 174 insertions(+), 106 deletions(-)
diff mbox

Patch

diff --git a/drivers/s390/kvm/virtio_ccw.c b/drivers/s390/kvm/virtio_ccw.c
index 1a5aff3..70419a7 100644
--- a/drivers/s390/kvm/virtio_ccw.c
+++ b/drivers/s390/kvm/virtio_ccw.c
@@ -46,11 +46,9 @@  struct vq_config_block {
 
 struct virtio_ccw_device {
 	struct virtio_device vdev;
-	__u8 status;
+	__u8 *status;
 	__u8 config[VIRTIO_CCW_CONFIG_SIZE];
 	struct ccw_device *cdev;
-	struct ccw1 *ccw;
-	__u32 area;
 	__u32 curr_io;
 	int err;
 	wait_queue_head_t wait_q;
@@ -127,14 +125,15 @@  static int doing_io(struct virtio_ccw_device *vcdev, __u32 flag)
 	return ret;
 }
 
-static int ccw_io_helper(struct virtio_ccw_device *vcdev, __u32 intparm)
+static int ccw_io_helper(struct virtio_ccw_device *vcdev,
+			 struct ccw1 *ccw, __u32 intparm)
 {
 	int ret;
 	unsigned long flags;
 	int flag = intparm & VIRTIO_CCW_INTPARM_MASK;
 
 	spin_lock_irqsave(get_ccwdev_lock(vcdev->cdev), flags);
-	ret = ccw_device_start(vcdev->cdev, vcdev->ccw, intparm, 0, 0);
+	ret = ccw_device_start(vcdev->cdev, ccw, intparm, 0, 0);
 	if (!ret)
 		vcdev->curr_io |= flag;
 	spin_unlock_irqrestore(get_ccwdev_lock(vcdev->cdev), flags);
@@ -167,18 +166,19 @@  static void virtio_ccw_kvm_notify(struct virtqueue *vq)
 	do_kvm_notify(schid, virtqueue_get_queue_index(vq));
 }
 
-static int virtio_ccw_read_vq_conf(struct virtio_ccw_device *vcdev, int index)
+static int virtio_ccw_read_vq_conf(struct virtio_ccw_device *vcdev,
+				   struct ccw1 *ccw, int index)
 {
 	vcdev->config_block->index = index;
-	vcdev->ccw->cmd_code = CCW_CMD_READ_VQ_CONF;
-	vcdev->ccw->flags = 0;
-	vcdev->ccw->count = sizeof(struct vq_config_block);
-	vcdev->ccw->cda = (__u32)(unsigned long)(vcdev->config_block);
-	ccw_io_helper(vcdev, VIRTIO_CCW_DOING_READ_VQ_CONF);
+	ccw->cmd_code = CCW_CMD_READ_VQ_CONF;
+	ccw->flags = 0;
+	ccw->count = sizeof(struct vq_config_block);
+	ccw->cda = (__u32)(unsigned long)(vcdev->config_block);
+	ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_READ_VQ_CONF);
 	return vcdev->config_block->num;
 }
 
-static void virtio_ccw_del_vq(struct virtqueue *vq)
+static void virtio_ccw_del_vq(struct virtqueue *vq, struct ccw1 *ccw)
 {
 	struct virtio_ccw_device *vcdev = to_vc_device(vq->vdev);
 	struct virtio_ccw_vq_info *info = vq->priv;
@@ -197,11 +197,12 @@  static void virtio_ccw_del_vq(struct virtqueue *vq)
 	info->info_block->align = 0;
 	info->info_block->index = index;
 	info->info_block->num = 0;
-	vcdev->ccw->cmd_code = CCW_CMD_SET_VQ;
-	vcdev->ccw->flags = 0;
-	vcdev->ccw->count = sizeof(*info->info_block);
-	vcdev->ccw->cda = (__u32)(unsigned long)(info->info_block);
-	ret = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_SET_VQ | index);
+	ccw->cmd_code = CCW_CMD_SET_VQ;
+	ccw->flags = 0;
+	ccw->count = sizeof(*info->info_block);
+	ccw->cda = (__u32)(unsigned long)(info->info_block);
+	ret = ccw_io_helper(vcdev, ccw,
+			    VIRTIO_CCW_DOING_SET_VQ | index);
 	/*
 	 * -ENODEV isn't considered an error: The device is gone anyway.
 	 * This may happen on device detach.
@@ -220,14 +221,23 @@  static void virtio_ccw_del_vq(struct virtqueue *vq)
 static void virtio_ccw_del_vqs(struct virtio_device *vdev)
 {
 	struct virtqueue *vq, *n;
+	struct ccw1 *ccw;
+
+	ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL);
+	if (!ccw)
+		return;
+
 
 	list_for_each_entry_safe(vq, n, &vdev->vqs, list)
-		virtio_ccw_del_vq(vq);
+		virtio_ccw_del_vq(vq, ccw);
+
+	kfree(ccw);
 }
 
 static struct virtqueue *virtio_ccw_setup_vq(struct virtio_device *vdev,
 					     int i, vq_callback_t *callback,
-					     const char *name)
+					     const char *name,
+					     struct ccw1 *ccw)
 {
 	struct virtio_ccw_device *vcdev = to_vc_device(vdev);
 	int err;
@@ -250,7 +260,7 @@  static struct virtqueue *virtio_ccw_setup_vq(struct virtio_device *vdev,
 		err = -ENOMEM;
 		goto out_err;
 	}
-	info->num = virtio_ccw_read_vq_conf(vcdev, i);
+	info->num = virtio_ccw_read_vq_conf(vcdev, ccw, i);
 	size = PAGE_ALIGN(vring_size(info->num, KVM_VIRTIO_CCW_RING_ALIGN));
 	info->queue = alloc_pages_exact(size, GFP_KERNEL | __GFP_ZERO);
 	if (info->queue == NULL) {
@@ -277,11 +287,11 @@  static struct virtqueue *virtio_ccw_setup_vq(struct virtio_device *vdev,
 	info->info_block->align = KVM_VIRTIO_CCW_RING_ALIGN;
 	info->info_block->index = i;
 	info->info_block->num = info->num;
-	vcdev->ccw->cmd_code = CCW_CMD_SET_VQ;
-	vcdev->ccw->flags = 0;
-	vcdev->ccw->count = sizeof(*info->info_block);
-	vcdev->ccw->cda = (__u32)(unsigned long)(info->info_block);
-	err = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_SET_VQ | i);
+	ccw->cmd_code = CCW_CMD_SET_VQ;
+	ccw->flags = 0;
+	ccw->count = sizeof(*info->info_block);
+	ccw->cda = (__u32)(unsigned long)(info->info_block);
+	err = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_SET_VQ | i);
 	if (err) {
 		dev_warn(&vcdev->cdev->dev, "SET_VQ failed\n");
 		free_pages_exact(info->queue, size);
@@ -312,9 +322,15 @@  static int virtio_ccw_find_vqs(struct virtio_device *vdev, unsigned nvqs,
 	struct virtio_ccw_device *vcdev = to_vc_device(vdev);
 	unsigned long *indicatorp = NULL;
 	int ret, i;
+	struct ccw1 *ccw;
+
+	ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL);
+	if (!ccw)
+		return -ENOMEM;
 
 	for (i = 0; i < nvqs; ++i) {
-		vqs[i] = virtio_ccw_setup_vq(vdev, i, callbacks[i], names[i]);
+		vqs[i] = virtio_ccw_setup_vq(vdev, i, callbacks[i], names[i],
+					     ccw);
 		if (IS_ERR(vqs[i])) {
 			ret = PTR_ERR(vqs[i]);
 			vqs[i] = NULL;
@@ -329,28 +345,30 @@  static int virtio_ccw_find_vqs(struct virtio_device *vdev, unsigned nvqs,
 	*indicatorp = (unsigned long) &vcdev->indicators;
 	/* Register queue indicators with host. */
 	vcdev->indicators = 0;
-	vcdev->ccw->cmd_code = CCW_CMD_SET_IND;
-	vcdev->ccw->flags = 0;
-	vcdev->ccw->count = sizeof(vcdev->indicators);
-	vcdev->ccw->cda = (__u32)(unsigned long) indicatorp;
-	ret = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_SET_IND);
+	ccw->cmd_code = CCW_CMD_SET_IND;
+	ccw->flags = 0;
+	ccw->count = sizeof(vcdev->indicators);
+	ccw->cda = (__u32)(unsigned long) indicatorp;
+	ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_SET_IND);
 	if (ret)
 		goto out;
 	/* Register indicators2 with host for config changes */
 	*indicatorp = (unsigned long) &vcdev->indicators2;
 	vcdev->indicators2 = 0;
-	vcdev->ccw->cmd_code = CCW_CMD_SET_CONF_IND;
-	vcdev->ccw->flags = 0;
-	vcdev->ccw->count = sizeof(vcdev->indicators2);
-	vcdev->ccw->cda = (__u32)(unsigned long) indicatorp;
-	ret = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_SET_CONF_IND);
+	ccw->cmd_code = CCW_CMD_SET_CONF_IND;
+	ccw->flags = 0;
+	ccw->count = sizeof(vcdev->indicators2);
+	ccw->cda = (__u32)(unsigned long) indicatorp;
+	ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_SET_CONF_IND);
 	if (ret)
 		goto out;
 
 	kfree(indicatorp);
+	kfree(ccw);
 	return 0;
 out:
 	kfree(indicatorp);
+	kfree(ccw);
 	virtio_ccw_del_vqs(vdev);
 	return ret;
 }
@@ -358,64 +376,95 @@  out:
 static void virtio_ccw_reset(struct virtio_device *vdev)
 {
 	struct virtio_ccw_device *vcdev = to_vc_device(vdev);
+	struct ccw1 *ccw;
+
+	ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL);
+	if (!ccw)
+		return;
 
 	/* Zero status bits. */
-	vcdev->status = 0;
+	*vcdev->status = 0;
 
 	/* Send a reset ccw on device. */
-	vcdev->ccw->cmd_code = CCW_CMD_VDEV_RESET;
-	vcdev->ccw->flags = 0;
-	vcdev->ccw->count = 0;
-	vcdev->ccw->cda = 0;
-	ccw_io_helper(vcdev, VIRTIO_CCW_DOING_RESET);
+	ccw->cmd_code = CCW_CMD_VDEV_RESET;
+	ccw->flags = 0;
+	ccw->count = 0;
+	ccw->cda = 0;
+	ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_RESET);
+	kfree(ccw);
 }
 
 static u32 virtio_ccw_get_features(struct virtio_device *vdev)
 {
 	struct virtio_ccw_device *vcdev = to_vc_device(vdev);
-	struct virtio_feature_desc features;
-	int ret;
+	struct virtio_feature_desc *features;
+	int ret, rc;
+	struct ccw1 *ccw;
 
+	ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL);
+	if (!ccw)
+		return 0;
+
+	features = kzalloc(sizeof(*features), GFP_DMA | GFP_KERNEL);
+	if (!features) {
+		rc = 0;
+		goto out_free;
+	}
 	/* Read the feature bits from the host. */
 	/* TODO: Features > 32 bits */
-	features.index = 0;
-	vcdev->ccw->cmd_code = CCW_CMD_READ_FEAT;
-	vcdev->ccw->flags = 0;
-	vcdev->ccw->count = sizeof(features);
-	vcdev->ccw->cda = vcdev->area;
-	ret = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_READ_FEAT);
-	if (ret)
-		return 0;
+	features->index = 0;
+	ccw->cmd_code = CCW_CMD_READ_FEAT;
+	ccw->flags = 0;
+	ccw->count = sizeof(*features);
+	ccw->cda = (__u32)(unsigned long)features;
+	ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_READ_FEAT);
+	if (ret) {
+		rc = 0;
+		goto out_free;
+	}
+
+	rc = le32_to_cpu(features->features);
 
-	memcpy(&features, (void *)(unsigned long)vcdev->area,
-	       sizeof(features));
-	return le32_to_cpu(features.features);
+out_free:
+	kfree(features);
+	kfree(ccw);
+	return rc;
 }
 
 static void virtio_ccw_finalize_features(struct virtio_device *vdev)
 {
 	struct virtio_ccw_device *vcdev = to_vc_device(vdev);
-	struct virtio_feature_desc features;
+	struct virtio_feature_desc *features;
 	int i;
+	struct ccw1 *ccw;
+
+	ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL);
+	if (!ccw)
+		return;
+
+	features = kzalloc(sizeof(*features), GFP_DMA | GFP_KERNEL);
+	if (!features)
+		goto out_free;
 
 	/* Give virtio_ring a chance to accept features. */
 	vring_transport_features(vdev);
 
-	for (i = 0; i < sizeof(*vdev->features) / sizeof(features.features);
+	for (i = 0; i < sizeof(*vdev->features) / sizeof(features->features);
 	     i++) {
 		int highbits = i % 2 ? 32 : 0;
-		features.index = i;
-		features.features = cpu_to_le32(vdev->features[i / 2]
-						>> highbits);
-		memcpy((void *)(unsigned long)vcdev->area, &features,
-		       sizeof(features));
+		features->index = i;
+		features->features = cpu_to_le32(vdev->features[i / 2]
+						 >> highbits);
 		/* Write the feature bits to the host. */
-		vcdev->ccw->cmd_code = CCW_CMD_WRITE_FEAT;
-		vcdev->ccw->flags = 0;
-		vcdev->ccw->count = sizeof(features);
-		vcdev->ccw->cda = vcdev->area;
-		ccw_io_helper(vcdev, VIRTIO_CCW_DOING_WRITE_FEAT);
+		ccw->cmd_code = CCW_CMD_WRITE_FEAT;
+		ccw->flags = 0;
+		ccw->count = sizeof(*features);
+		ccw->cda = (__u32)(unsigned long)features;
+		ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_WRITE_FEAT);
 	}
+out_free:
+	kfree(features);
+	kfree(ccw);
 }
 
 static void virtio_ccw_get_config(struct virtio_device *vdev,
@@ -423,19 +472,32 @@  static void virtio_ccw_get_config(struct virtio_device *vdev,
 {
 	struct virtio_ccw_device *vcdev = to_vc_device(vdev);
 	int ret;
+	struct ccw1 *ccw;
+	void *config_area;
+
+	ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL);
+	if (!ccw)
+		return;
+
+	config_area = kzalloc(VIRTIO_CCW_CONFIG_SIZE, GFP_DMA | GFP_KERNEL);
+	if (!config_area)
+		goto out_free;
 
 	/* Read the config area from the host. */
-	vcdev->ccw->cmd_code = CCW_CMD_READ_CONF;
-	vcdev->ccw->flags = 0;
-	vcdev->ccw->count = offset + len;
-	vcdev->ccw->cda = vcdev->area;
-	ret = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_READ_CONFIG);
+	ccw->cmd_code = CCW_CMD_READ_CONF;
+	ccw->flags = 0;
+	ccw->count = offset + len;
+	ccw->cda = (__u32)(unsigned long)config_area;
+	ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_READ_CONFIG);
 	if (ret)
-		return;
+		goto out_free;
 
-	memcpy(vcdev->config, (void *)(unsigned long)vcdev->area,
-	       sizeof(vcdev->config));
+	memcpy(vcdev->config, config_area, sizeof(vcdev->config));
 	memcpy(buf, &vcdev->config[offset], len);
+
+out_free:
+	kfree(config_area);
+	kfree(ccw);
 }
 
 static void virtio_ccw_set_config(struct virtio_device *vdev,
@@ -443,37 +505,55 @@  static void virtio_ccw_set_config(struct virtio_device *vdev,
 				  unsigned len)
 {
 	struct virtio_ccw_device *vcdev = to_vc_device(vdev);
+	struct ccw1 *ccw;
+	void *config_area;
+
+	ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL);
+	if (!ccw)
+		return;
+
+	config_area = kzalloc(VIRTIO_CCW_CONFIG_SIZE, GFP_DMA | GFP_KERNEL);
+	if (!config_area)
+		goto out_free;
 
 	memcpy(&vcdev->config[offset], buf, len);
 	/* Write the config area to the host. */
-	memcpy((void *)(unsigned long)vcdev->area, vcdev->config,
-	       sizeof(vcdev->config));
-	vcdev->ccw->cmd_code = CCW_CMD_WRITE_CONF;
-	vcdev->ccw->flags = 0;
-	vcdev->ccw->count = offset + len;
-	vcdev->ccw->cda = vcdev->area;
-	ccw_io_helper(vcdev, VIRTIO_CCW_DOING_WRITE_CONFIG);
+	memcpy(config_area, vcdev->config, sizeof(vcdev->config));
+	ccw->cmd_code = CCW_CMD_WRITE_CONF;
+	ccw->flags = 0;
+	ccw->count = offset + len;
+	ccw->cda = (__u32)(unsigned long)config_area;
+	ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_WRITE_CONFIG);
+
+out_free:
+	kfree(config_area);
+	kfree(ccw);
 }
 
 static u8 virtio_ccw_get_status(struct virtio_device *vdev)
 {
 	struct virtio_ccw_device *vcdev = to_vc_device(vdev);
 
-	return vcdev->status;
+	return *vcdev->status;
 }
 
 static void virtio_ccw_set_status(struct virtio_device *vdev, u8 status)
 {
 	struct virtio_ccw_device *vcdev = to_vc_device(vdev);
+	struct ccw1 *ccw;
+
+	ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL);
+	if (!ccw)
+		return;
 
 	/* Write the status to the host. */
-	vcdev->status = status;
-	memcpy((void *)(unsigned long)vcdev->area, &status, sizeof(status));
-	vcdev->ccw->cmd_code = CCW_CMD_WRITE_STATUS;
-	vcdev->ccw->flags = 0;
-	vcdev->ccw->count = sizeof(status);
-	vcdev->ccw->cda = vcdev->area;
-	ccw_io_helper(vcdev, VIRTIO_CCW_DOING_WRITE_STATUS);
+	*vcdev->status = status;
+	ccw->cmd_code = CCW_CMD_WRITE_STATUS;
+	ccw->flags = 0;
+	ccw->count = sizeof(status);
+	ccw->cda = (__u32)(unsigned long)vcdev->status;
+	ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_WRITE_STATUS);
+	kfree(ccw);
 }
 
 static struct virtio_config_ops virtio_ccw_config_ops = {
@@ -499,9 +579,8 @@  static void virtio_ccw_release_dev(struct device *_d)
 						 dev);
 	struct virtio_ccw_device *vcdev = to_vc_device(dev);
 
-	kfree((void *)(unsigned long)vcdev->area);
+	kfree(vcdev->status);
 	kfree(vcdev->config_block);
-	kfree(vcdev->ccw);
 	kfree(vcdev);
 }
 
@@ -657,9 +736,6 @@  static int virtio_ccw_offline(struct ccw_device *cdev)
 }
 
 
-/* Area needs to be big enough to fit status, features or configuration. */
-#define VIRTIO_AREA_SIZE VIRTIO_CCW_CONFIG_SIZE /* biggest possible use */
-
 static int virtio_ccw_online(struct ccw_device *cdev)
 {
 	int ret;
@@ -671,21 +747,14 @@  static int virtio_ccw_online(struct ccw_device *cdev)
 		ret = -ENOMEM;
 		goto out_free;
 	}
-	vcdev->area = (__u32)(unsigned long)kzalloc(VIRTIO_AREA_SIZE,
-						    GFP_DMA | GFP_KERNEL);
-	if (!vcdev->area) {
-		dev_warn(&cdev->dev, "Cound not get memory for virtio\n");
-		ret = -ENOMEM;
-		goto out_free;
-	}
 	vcdev->config_block = kzalloc(sizeof(*vcdev->config_block),
 				   GFP_DMA | GFP_KERNEL);
 	if (!vcdev->config_block) {
 		ret = -ENOMEM;
 		goto out_free;
 	}
-	vcdev->ccw = kzalloc(sizeof(*vcdev->ccw), GFP_DMA | GFP_KERNEL);
-	if (!vcdev->ccw) {
+	vcdev->status = kzalloc(sizeof(*vcdev->status), GFP_DMA | GFP_KERNEL);
+	if (!vcdev->status) {
 		ret = -ENOMEM;
 		goto out_free;
 	}
@@ -714,9 +783,8 @@  out_put:
 	return ret;
 out_free:
 	if (vcdev) {
-		kfree((void *)(unsigned long)vcdev->area);
+		kfree(vcdev->status);
 		kfree(vcdev->config_block);
-		kfree(vcdev->ccw);
 	}
 	kfree(vcdev);
 	return ret;