diff options
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/block/sunvdc.c | 222 | ||||
| -rw-r--r-- | drivers/net/ethernet/sun/sunvnet.c | 30 | 
2 files changed, 204 insertions, 48 deletions
diff --git a/drivers/block/sunvdc.c b/drivers/block/sunvdc.c index 0ebadf93b6c5..4b911ed96ea3 100644 --- a/drivers/block/sunvdc.c +++ b/drivers/block/sunvdc.c @@ -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 = ∁  	vio_port_up(&port->vio); -  	wait_for_completion(&comp.com); -	if (comp.err) -		return 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 @@ out_unregister_blkdev:  	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); diff --git a/drivers/net/ethernet/sun/sunvnet.c b/drivers/net/ethernet/sun/sunvnet.c index 90c86cd3be14..45c408ef67d0 100644 --- a/drivers/net/ethernet/sun/sunvnet.c +++ b/drivers/net/ethernet/sun/sunvnet.c @@ -466,23 +466,6 @@ static int vnet_send_ack(struct vnet_port *port, struct vio_dring_state *dr,  	return err;  } -static u32 next_idx(u32 idx, struct vio_dring_state *dr) -{ -	if (++idx == dr->num_entries) -		idx = 0; -	return idx; -} - -static u32 prev_idx(u32 idx, struct vio_dring_state *dr) -{ -	if (idx == 0) -		idx = dr->num_entries - 1; -	else -		idx--; - -	return idx; -} -  static struct vio_net_desc *get_rx_desc(struct vnet_port *port,  					struct vio_dring_state *dr,  					u32 index) @@ -556,7 +539,8 @@ static int vnet_walk_rx(struct vnet_port *port, struct vio_dring_state *dr,  	int ack_start = -1, ack_end = -1;  	bool send_ack = true; -	end = (end == (u32) -1) ? prev_idx(start, dr) : next_idx(end, dr); +	end = (end == (u32) -1) ? vio_dring_prev(dr, start) +				: vio_dring_next(dr, end);  	viodbg(DATA, "vnet_walk_rx start[%08x] end[%08x]\n", start, end); @@ -570,7 +554,7 @@ static int vnet_walk_rx(struct vnet_port *port, struct vio_dring_state *dr,  		if (ack_start == -1)  			ack_start = start;  		ack_end = start; -		start = next_idx(start, dr); +		start = vio_dring_next(dr, start);  		if (ack && start != end) {  			err = vnet_send_ack(port, dr, ack_start, ack_end,  					    VIO_DRING_ACTIVE); @@ -584,7 +568,7 @@ static int vnet_walk_rx(struct vnet_port *port, struct vio_dring_state *dr,  		}  	}  	if (unlikely(ack_start == -1)) -		ack_start = ack_end = prev_idx(start, dr); +		ack_start = ack_end = vio_dring_prev(dr, start);  	if (send_ack) {  		port->napi_resume = false;  		return vnet_send_ack(port, dr, ack_start, ack_end, @@ -633,7 +617,7 @@ static int idx_is_pending(struct vio_dring_state *dr, u32 end)  			found = 1;  			break;  		} -		idx = next_idx(idx, dr); +		idx = vio_dring_next(dr, idx);  	}  	return found;  } @@ -663,7 +647,7 @@ static int vnet_ack(struct vnet_port *port, void *msgbuf)  	/* sync for race conditions with vnet_start_xmit() and tell xmit it  	 * is time to send a trigger.  	 */ -	dr->cons = next_idx(end, dr); +	dr->cons = vio_dring_next(dr, end);  	desc = vio_dring_entry(dr, dr->cons);  	if (desc->hdr.state == VIO_DESC_READY && !port->start_cons) {  		/* vnet_start_xmit() just populated this dring but missed @@ -784,7 +768,7 @@ ldc_ctrl:  			pkt->tag.stype = VIO_SUBTYPE_INFO;  			pkt->tag.stype_env = VIO_DRING_DATA;  			pkt->seq = dr->rcv_nxt; -			pkt->start_idx = next_idx(port->napi_stop_idx, dr); +			pkt->start_idx = vio_dring_next(dr, port->napi_stop_idx);  			pkt->end_idx = -1;  			goto napi_resume;  		}  | 

