diff options
Diffstat (limited to 'drivers/lguest/lguest_device.c')
| -rw-r--r-- | drivers/lguest/lguest_device.c | 70 | 
1 files changed, 47 insertions, 23 deletions
diff --git a/drivers/lguest/lguest_device.c b/drivers/lguest/lguest_device.c index 69c84a1d88e..d0a1d8a45c8 100644 --- a/drivers/lguest/lguest_device.c +++ b/drivers/lguest/lguest_device.c @@ -15,6 +15,7 @@  #include <linux/interrupt.h>  #include <linux/virtio_ring.h>  #include <linux/err.h> +#include <linux/export.h>  #include <linux/slab.h>  #include <asm/io.h>  #include <asm/paravirt.h> @@ -109,6 +110,17 @@ static u32 lg_get_features(struct virtio_device *vdev)  }  /* + * To notify on reset or feature finalization, we (ab)use the NOTIFY + * hypercall, with the descriptor address of the device. + */ +static void status_notify(struct virtio_device *vdev) +{ +	unsigned long offset = (void *)to_lgdev(vdev)->desc - lguest_devices; + +	hcall(LHCALL_NOTIFY, (max_pfn << PAGE_SHIFT) + offset, 0, 0, 0); +} + +/*   * The virtio core takes the features the Host offers, and copies the ones   * supported by the driver into the vdev->features array.  Once that's all   * sorted out, this routine is called so we can tell the Host which features we @@ -135,6 +147,9 @@ static void lg_finalize_features(struct virtio_device *vdev)  		if (test_bit(i, vdev->features))  			out_features[i / 8] |= (1 << (i % 8));  	} + +	/* Tell Host we've finished with this device's feature negotiation */ +	status_notify(vdev);  }  /* Once they've found a field, getting a copy of it is easy. */ @@ -168,28 +183,21 @@ static u8 lg_get_status(struct virtio_device *vdev)  	return to_lgdev(vdev)->desc->status;  } -/* - * To notify on status updates, we (ab)use the NOTIFY hypercall, with the - * descriptor address of the device.  A zero status means "reset". - */ -static void set_status(struct virtio_device *vdev, u8 status) -{ -	unsigned long offset = (void *)to_lgdev(vdev)->desc - lguest_devices; - -	/* We set the status. */ -	to_lgdev(vdev)->desc->status = status; -	hcall(LHCALL_NOTIFY, (max_pfn << PAGE_SHIFT) + offset, 0, 0, 0); -} -  static void lg_set_status(struct virtio_device *vdev, u8 status)  {  	BUG_ON(!status); -	set_status(vdev, status); +	to_lgdev(vdev)->desc->status = status; + +	/* Tell Host immediately if we failed. */ +	if (status & VIRTIO_CONFIG_S_FAILED) +		status_notify(vdev);  }  static void lg_reset(struct virtio_device *vdev)  { -	set_status(vdev, 0); +	/* 0 status means "reset" */ +	to_lgdev(vdev)->desc->status = 0; +	status_notify(vdev);  }  /* @@ -221,7 +229,7 @@ struct lguest_vq_info {   * make a hypercall.  We hand the physical address of the virtqueue so the Host   * knows which virtqueue we're talking about.   */ -static void lg_notify(struct virtqueue *vq) +static bool lg_notify(struct virtqueue *vq)  {  	/*  	 * We store our virtqueue information in the "priv" pointer of the @@ -230,10 +238,11 @@ static void lg_notify(struct virtqueue *vq)  	struct lguest_vq_info *lvq = vq->priv;  	hcall(LHCALL_NOTIFY, lvq->config.pfn << PAGE_SHIFT, 0, 0, 0); +	return true;  }  /* An extern declaration inside a C file is bad form.  Don't do it. */ -extern void lguest_setup_irq(unsigned int irq); +extern int lguest_setup_irq(unsigned int irq);  /*   * This routine finds the Nth virtqueue described in the configuration of @@ -255,6 +264,9 @@ static struct virtqueue *lg_find_vq(struct virtio_device *vdev,  	struct virtqueue *vq;  	int err; +	if (!name) +		return NULL; +  	/* We must have this many virtqueues. */  	if (index >= ldev->desc->num_vq)  		return ERR_PTR(-ENOENT); @@ -284,17 +296,21 @@ static struct virtqueue *lg_find_vq(struct virtio_device *vdev,  	/*  	 * OK, tell virtio_ring.c to set up a virtqueue now we know its size -	 * and we've got a pointer to its pages. +	 * and we've got a pointer to its pages.  Note that we set weak_barriers +	 * to 'true': the host just a(nother) SMP CPU, so we only need inter-cpu +	 * barriers.  	 */ -	vq = vring_new_virtqueue(lvq->config.num, LGUEST_VRING_ALIGN, -				 vdev, lvq->pages, lg_notify, callback, name); +	vq = vring_new_virtqueue(index, lvq->config.num, LGUEST_VRING_ALIGN, vdev, +				 true, lvq->pages, lg_notify, callback, name);  	if (!vq) {  		err = -ENOMEM;  		goto unmap;  	}  	/* Make sure the interrupt is allocated. */ -	lguest_setup_irq(lvq->config.irq); +	err = lguest_setup_irq(lvq->config.irq); +	if (err) +		goto destroy_vring;  	/*  	 * Tell the interrupt for this virtqueue to go to the virtio_ring @@ -307,7 +323,7 @@ static struct virtqueue *lg_find_vq(struct virtio_device *vdev,  	err = request_irq(lvq->config.irq, vring_interrupt, IRQF_SHARED,  			  dev_name(&vdev->dev), vq);  	if (err) -		goto destroy_vring; +		goto free_desc;  	/*  	 * Last of all we hook up our 'struct lguest_vq_info" to the @@ -316,6 +332,8 @@ static struct virtqueue *lg_find_vq(struct virtio_device *vdev,  	vq->priv = lvq;  	return vq; +free_desc: +	irq_free_desc(lvq->config.irq);  destroy_vring:  	vring_del_virtqueue(vq);  unmap: @@ -373,8 +391,13 @@ error:  	return PTR_ERR(vqs[i]);  } +static const char *lg_bus_name(struct virtio_device *vdev) +{ +	return ""; +} +  /* The ops structure which hooks everything together. */ -static struct virtio_config_ops lguest_config_ops = { +static const struct virtio_config_ops lguest_config_ops = {  	.get_features = lg_get_features,  	.finalize_features = lg_finalize_features,  	.get = lg_get, @@ -384,6 +407,7 @@ static struct virtio_config_ops lguest_config_ops = {  	.reset = lg_reset,  	.find_vqs = lg_find_vqs,  	.del_vqs = lg_del_vqs, +	.bus_name = lg_bus_name,  };  /*  | 
