diff options
author | Cornelia Huck <cornelia.huck@de.ibm.com> | 2013-01-07 14:51:51 (GMT) |
---|---|---|
committer | Marcelo Tosatti <mtosatti@redhat.com> | 2013-01-09 20:40:18 (GMT) |
commit | 73fa21ea4fc662a2e8e85f84c4ca3fcb55fa4da2 (patch) | |
tree | 8bc6c22fd9d2e3db42b95b4aede95dfde8bc8a75 /drivers/s390/kvm | |
parent | fb864fbc72fd4e2175fb64072fe9134d3a3ab89a (diff) | |
download | linux-fsl-qoriq-73fa21ea4fc662a2e8e85f84c4ca3fcb55fa4da2.tar.xz |
KVM: s390: Dynamic allocation of virtio-ccw I/O data.
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>
Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
Diffstat (limited to 'drivers/s390/kvm')
-rw-r--r-- | drivers/s390/kvm/virtio_ccw.c | 280 |
1 files changed, 174 insertions, 106 deletions
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; |