Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a7e8ddd8 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull sparc update from David Miller:
 "Not a lot of stuff this time around, mostly bug fixing:

   - Fix alignment of 32-bit crosscall datastructure on Leon, from
     Andreas Larsson.

   - Several fixes to the virtual disk driver on sparc64 by Dwight
     Engen, including handling resets of the service domain properly"

* git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc:
  sunvdc: reconnect ldc after vds service domain restarts
  sparc/ldc: create separate ldc_unbind from ldc_free
  vio: create routines for inc,dec vio dring indexes
  sunvdc: fix module unload/reload
  sparc32, leon: Align ccall_info to prevent unaligned traps on crosscall
parents ad8f723a 76e74bbe
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -61,6 +61,7 @@ void ldc_free(struct ldc_channel *lp);

/* Register TX and RX queues of the link with the hypervisor.  */
int ldc_bind(struct ldc_channel *lp);
void ldc_unbind(struct ldc_channel *lp);

/* For non-RAW protocols we need to complete a handshake before
 * communication can proceed.  ldc_connect() does that, if the
+15 −0
Original line number Diff line number Diff line
@@ -300,6 +300,21 @@ static inline u32 vio_dring_avail(struct vio_dring_state *dr,
		((dr->prod - dr->cons) & (ring_size - 1)) - 1);
}

static inline u32 vio_dring_next(struct vio_dring_state *dr, u32 index)
{
	if (++index == dr->num_entries)
		index = 0;
	return index;
}

static inline u32 vio_dring_prev(struct vio_dring_state *dr, u32 index)
{
	if (index == 0)
		return dr->num_entries - 1;
	else
		return index - 1;
}

#define VIO_MAX_TYPE_LEN	32
#define VIO_MAX_COMPAT_LEN	64

+9 −3
Original line number Diff line number Diff line
@@ -1222,11 +1222,12 @@ struct ldc_channel *ldc_alloc(unsigned long id,
}
EXPORT_SYMBOL(ldc_alloc);

void ldc_free(struct ldc_channel *lp)
void ldc_unbind(struct ldc_channel *lp)
{
	if (lp->flags & LDC_FLAG_REGISTERED_IRQS) {
		free_irq(lp->cfg.rx_irq, lp);
		free_irq(lp->cfg.tx_irq, lp);
		lp->flags &= ~LDC_FLAG_REGISTERED_IRQS;
	}

	if (lp->flags & LDC_FLAG_REGISTERED_QUEUES) {
@@ -1240,10 +1241,15 @@ void ldc_free(struct ldc_channel *lp)
		lp->flags &= ~LDC_FLAG_ALLOCED_QUEUES;
	}

	hlist_del(&lp->list);
	ldc_set_state(lp, LDC_STATE_INIT);
}
EXPORT_SYMBOL(ldc_unbind);

void ldc_free(struct ldc_channel *lp)
{
	ldc_unbind(lp);
	hlist_del(&lp->list);
	kfree(lp->mssbuf);

	ldc_iommu_release(lp);

	kfree(lp);
+1 −1
Original line number Diff line number Diff line
@@ -368,7 +368,7 @@ static struct smp_funcall {
	unsigned long arg5;
	unsigned long processors_in[NR_CPUS];	/* Set when ipi entered. */
	unsigned long processors_out[NR_CPUS];	/* Set when ipi exited. */
} ccall_info;
} ccall_info __attribute__((aligned(8)));

static DEFINE_SPINLOCK(cross_call_lock);

+197 −25
Original line number Diff line number Diff line
@@ -23,8 +23,8 @@

#define DRV_MODULE_NAME		"sunvdc"
#define PFX DRV_MODULE_NAME	": "
#define DRV_MODULE_VERSION	"1.1"
#define DRV_MODULE_RELDATE	"February 13, 2013"
#define DRV_MODULE_VERSION	"1.2"
#define DRV_MODULE_RELDATE	"November 24, 2014"

static char version[] =
	DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
@@ -40,6 +40,8 @@ MODULE_VERSION(DRV_MODULE_VERSION);
#define WAITING_FOR_GEN_CMD	0x04
#define WAITING_FOR_ANY		-1

static struct workqueue_struct *sunvdc_wq;

struct vdc_req_entry {
	struct request		*req;
};
@@ -60,6 +62,10 @@ struct vdc_port {
	u64			max_xfer_size;
	u32			vdisk_block_size;

	u64			ldc_timeout;
	struct timer_list	ldc_reset_timer;
	struct work_struct	ldc_reset_work;

	/* The server fills these in for us in the disk attribute
	 * ACK packet.
	 */
@@ -71,6 +77,10 @@ struct vdc_port {
	char			disk_name[32];
};

static void vdc_ldc_reset(struct vdc_port *port);
static void vdc_ldc_reset_work(struct work_struct *work);
static void vdc_ldc_reset_timer(unsigned long _arg);

static inline struct vdc_port *to_vdc_port(struct vio_driver_state *vio)
{
	return container_of(vio, struct vdc_port, vio);
@@ -150,6 +160,21 @@ static const struct block_device_operations vdc_fops = {
	.ioctl		= vdc_ioctl,
};

static void vdc_blk_queue_start(struct vdc_port *port)
{
	struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_TX_RING];

	/* restart blk queue when ring is half emptied. also called after
	 * handshake completes, so check for initial handshake before we've
	 * allocated a disk.
	 */
	if (port->disk && blk_queue_stopped(port->disk->queue) &&
	    vdc_tx_dring_avail(dr) * 100 / VDC_TX_RING_SIZE >= 50) {
		blk_start_queue(port->disk->queue);
	}

}

static void vdc_finish(struct vio_driver_state *vio, int err, int waiting_for)
{
	if (vio->cmp &&
@@ -163,7 +188,11 @@ static void vdc_finish(struct vio_driver_state *vio, int err, int waiting_for)

static void vdc_handshake_complete(struct vio_driver_state *vio)
{
	struct vdc_port *port = to_vdc_port(vio);

	del_timer(&port->ldc_reset_timer);
	vdc_finish(vio, 0, WAITING_FOR_LINK_UP);
	vdc_blk_queue_start(port);
}

static int vdc_handle_unknown(struct vdc_port *port, void *arg)
@@ -269,7 +298,7 @@ static void vdc_end_one(struct vdc_port *port, struct vio_dring_state *dr,

	ldc_unmap(port->vio.lp, desc->cookies, desc->ncookies);
	desc->hdr.state = VIO_DESC_FREE;
	dr->cons = (index + 1) & (VDC_TX_RING_SIZE - 1);
	dr->cons = vio_dring_next(dr, index);

	req = rqe->req;
	if (req == NULL) {
@@ -281,10 +310,7 @@ static void vdc_end_one(struct vdc_port *port, struct vio_dring_state *dr,

	__blk_end_request(req, (desc->status ? -EIO : 0), desc->size);

	/* restart blk queue when ring is half emptied */
	if (blk_queue_stopped(port->disk->queue) &&
	    vdc_tx_dring_avail(dr) * 100 / VDC_TX_RING_SIZE >= 50)
		blk_start_queue(port->disk->queue);
	vdc_blk_queue_start(port);
}

static int vdc_ack(struct vdc_port *port, void *msgbuf)
@@ -317,17 +343,20 @@ static void vdc_event(void *arg, int event)

	spin_lock_irqsave(&vio->lock, flags);

	if (unlikely(event == LDC_EVENT_RESET ||
		     event == LDC_EVENT_UP)) {
	if (unlikely(event == LDC_EVENT_RESET)) {
		vio_link_state_change(vio, event);
		spin_unlock_irqrestore(&vio->lock, flags);
		return;
		queue_work(sunvdc_wq, &port->ldc_reset_work);
		goto out;
	}

	if (unlikely(event == LDC_EVENT_UP)) {
		vio_link_state_change(vio, event);
		goto out;
	}

	if (unlikely(event != LDC_EVENT_DATA_READY)) {
		printk(KERN_WARNING PFX "Unexpected LDC event %d\n", event);
		spin_unlock_irqrestore(&vio->lock, flags);
		return;
		pr_warn(PFX "Unexpected LDC event %d\n", event);
		goto out;
	}

	err = 0;
@@ -371,6 +400,7 @@ static void vdc_event(void *arg, int event)
	}
	if (err < 0)
		vdc_finish(&port->vio, err, WAITING_FOR_ANY);
out:
	spin_unlock_irqrestore(&vio->lock, flags);
}

@@ -403,6 +433,8 @@ static int __vdc_tx_trigger(struct vdc_port *port)
			delay = 128;
	} while (err == -EAGAIN);

	if (err == -ENOTCONN)
		vdc_ldc_reset(port);
	return err;
}

@@ -472,7 +504,7 @@ static int __send_request(struct request *req)
		printk(KERN_ERR PFX "vdc_tx_trigger() failure, err=%d\n", err);
	} else {
		port->req_id++;
		dr->prod = (dr->prod + 1) & (VDC_TX_RING_SIZE - 1);
		dr->prod = vio_dring_next(dr, dr->prod);
	}

	return err;
@@ -626,7 +658,7 @@ static int generic_request(struct vdc_port *port, u8 op, void *buf, int len)
	err = __vdc_tx_trigger(port);
	if (err >= 0) {
		port->req_id++;
		dr->prod = (dr->prod + 1) & (VDC_TX_RING_SIZE - 1);
		dr->prod = vio_dring_next(dr, dr->prod);
		spin_unlock_irqrestore(&port->vio.lock, flags);

		wait_for_completion(&comp.com);
@@ -690,12 +722,9 @@ static void vdc_free_tx_ring(struct vdc_port *port)
	}
}

static int probe_disk(struct vdc_port *port)
static int vdc_port_up(struct vdc_port *port)
{
	struct vio_completion comp;
	struct request_queue *q;
	struct gendisk *g;
	int err;

	init_completion(&comp.com);
	comp.err = 0;
@@ -703,10 +732,27 @@ static int probe_disk(struct vdc_port *port)
	port->vio.cmp = &comp;

	vio_port_up(&port->vio);

	wait_for_completion(&comp.com);
	if (comp.err)
	return comp.err;
}

static void vdc_port_down(struct vdc_port *port)
{
	ldc_disconnect(port->vio.lp);
	ldc_unbind(port->vio.lp);
	vdc_free_tx_ring(port);
	vio_ldc_free(&port->vio);
}

static int probe_disk(struct vdc_port *port)
{
	struct request_queue *q;
	struct gendisk *g;
	int err;

	err = vdc_port_up(port);
	if (err)
		return err;

	if (vdc_version_supported(port, 1, 1)) {
		/* vdisk_size should be set during the handshake, if it wasn't
@@ -819,6 +865,7 @@ static int vdc_port_probe(struct vio_dev *vdev, const struct vio_device_id *id)
	struct mdesc_handle *hp;
	struct vdc_port *port;
	int err;
	const u64 *ldc_timeout;

	print_version();

@@ -848,6 +895,16 @@ static int vdc_port_probe(struct vio_dev *vdev, const struct vio_device_id *id)
			 VDCBLK_NAME "%c", 'a' + ((int)vdev->dev_no % 26));
	port->vdisk_size = -1;

	/* Actual wall time may be double due to do_generic_file_read() doing
	 * a readahead I/O first, and once that fails it will try to read a
	 * single page.
	 */
	ldc_timeout = mdesc_get_property(hp, vdev->mp, "vdc-timeout", NULL);
	port->ldc_timeout = ldc_timeout ? *ldc_timeout : 0;
	setup_timer(&port->ldc_reset_timer, vdc_ldc_reset_timer,
		    (unsigned long)port);
	INIT_WORK(&port->ldc_reset_work, vdc_ldc_reset_work);

	err = vio_driver_init(&port->vio, vdev, VDEV_DISK,
			      vdc_versions, ARRAY_SIZE(vdc_versions),
			      &vdc_vio_ops, port->disk_name);
@@ -896,8 +953,21 @@ static int vdc_port_remove(struct vio_dev *vdev)
	struct vdc_port *port = dev_get_drvdata(&vdev->dev);

	if (port) {
		unsigned long flags;

		spin_lock_irqsave(&port->vio.lock, flags);
		blk_stop_queue(port->disk->queue);
		spin_unlock_irqrestore(&port->vio.lock, flags);

		flush_work(&port->ldc_reset_work);
		del_timer_sync(&port->ldc_reset_timer);
		del_timer_sync(&port->vio.timer);

		del_gendisk(port->disk);
		blk_cleanup_queue(port->disk->queue);
		put_disk(port->disk);
		port->disk = NULL;

		vdc_free_tx_ring(port);
		vio_ldc_free(&port->vio);

@@ -908,6 +978,102 @@ static int vdc_port_remove(struct vio_dev *vdev)
	return 0;
}

static void vdc_requeue_inflight(struct vdc_port *port)
{
	struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_TX_RING];
	u32 idx;

	for (idx = dr->cons; idx != dr->prod; idx = vio_dring_next(dr, idx)) {
		struct vio_disk_desc *desc = vio_dring_entry(dr, idx);
		struct vdc_req_entry *rqe = &port->rq_arr[idx];
		struct request *req;

		ldc_unmap(port->vio.lp, desc->cookies, desc->ncookies);
		desc->hdr.state = VIO_DESC_FREE;
		dr->cons = vio_dring_next(dr, idx);

		req = rqe->req;
		if (req == NULL) {
			vdc_end_special(port, desc);
			continue;
		}

		rqe->req = NULL;
		blk_requeue_request(port->disk->queue, req);
	}
}

static void vdc_queue_drain(struct vdc_port *port)
{
	struct request *req;

	while ((req = blk_fetch_request(port->disk->queue)) != NULL)
		__blk_end_request_all(req, -EIO);
}

static void vdc_ldc_reset_timer(unsigned long _arg)
{
	struct vdc_port *port = (struct vdc_port *) _arg;
	struct vio_driver_state *vio = &port->vio;
	unsigned long flags;

	spin_lock_irqsave(&vio->lock, flags);
	if (!(port->vio.hs_state & VIO_HS_COMPLETE)) {
		pr_warn(PFX "%s ldc down %llu seconds, draining queue\n",
			port->disk_name, port->ldc_timeout);
		vdc_queue_drain(port);
		vdc_blk_queue_start(port);
	}
	spin_unlock_irqrestore(&vio->lock, flags);
}

static void vdc_ldc_reset_work(struct work_struct *work)
{
	struct vdc_port *port;
	struct vio_driver_state *vio;
	unsigned long flags;

	port = container_of(work, struct vdc_port, ldc_reset_work);
	vio = &port->vio;

	spin_lock_irqsave(&vio->lock, flags);
	vdc_ldc_reset(port);
	spin_unlock_irqrestore(&vio->lock, flags);
}

static void vdc_ldc_reset(struct vdc_port *port)
{
	int err;

	assert_spin_locked(&port->vio.lock);

	pr_warn(PFX "%s ldc link reset\n", port->disk_name);
	blk_stop_queue(port->disk->queue);
	vdc_requeue_inflight(port);
	vdc_port_down(port);

	err = vio_ldc_alloc(&port->vio, &vdc_ldc_cfg, port);
	if (err) {
		pr_err(PFX "%s vio_ldc_alloc:%d\n", port->disk_name, err);
		return;
	}

	err = vdc_alloc_tx_ring(port);
	if (err) {
		pr_err(PFX "%s vio_alloc_tx_ring:%d\n", port->disk_name, err);
		goto err_free_ldc;
	}

	if (port->ldc_timeout)
		mod_timer(&port->ldc_reset_timer,
			  round_jiffies(jiffies + HZ * port->ldc_timeout));
	mod_timer(&port->vio.timer, round_jiffies(jiffies + HZ));
	return;

err_free_ldc:
	vio_ldc_free(&port->vio);
}

static const struct vio_device_id vdc_port_match[] = {
	{
		.type = "vdc-port",
@@ -927,9 +1093,13 @@ static int __init vdc_init(void)
{
	int err;

	sunvdc_wq = alloc_workqueue("sunvdc", 0, 0);
	if (!sunvdc_wq)
		return -ENOMEM;

	err = register_blkdev(0, VDCBLK_NAME);
	if (err < 0)
		goto out_err;
		goto out_free_wq;

	vdc_major = err;

@@ -943,7 +1113,8 @@ static int __init vdc_init(void)
	unregister_blkdev(vdc_major, VDCBLK_NAME);
	vdc_major = 0;

out_err:
out_free_wq:
	destroy_workqueue(sunvdc_wq);
	return err;
}

@@ -951,6 +1122,7 @@ static void __exit vdc_exit(void)
{
	vio_unregister_driver(&vdc_port_driver);
	unregister_blkdev(vdc_major, VDCBLK_NAME);
	destroy_workqueue(sunvdc_wq);
}

module_init(vdc_init);
Loading