diff options
Diffstat (limited to 'drivers/s390')
| -rw-r--r-- | drivers/s390/block/scm_blk.c | 76 | ||||
| -rw-r--r-- | drivers/s390/block/scm_blk.h | 2 | ||||
| -rw-r--r-- | drivers/s390/block/scm_drv.c | 23 | ||||
| -rw-r--r-- | drivers/s390/char/sclp_cmd.c | 2 | ||||
| -rw-r--r-- | drivers/s390/char/tty3270.c | 16 | ||||
| -rw-r--r-- | drivers/s390/cio/chsc.c | 17 | ||||
| -rw-r--r-- | drivers/s390/cio/chsc.h | 2 | ||||
| -rw-r--r-- | drivers/s390/cio/scm.c | 18 | ||||
| -rw-r--r-- | drivers/s390/net/qeth_core.h | 1 | ||||
| -rw-r--r-- | drivers/s390/net/qeth_core_main.c | 45 | ||||
| -rw-r--r-- | drivers/s390/net/qeth_l3_main.c | 23 | ||||
| -rw-r--r-- | drivers/s390/net/qeth_l3_sys.c | 2 | 
12 files changed, 189 insertions, 38 deletions
diff --git a/drivers/s390/block/scm_blk.c b/drivers/s390/block/scm_blk.c index 9978ad4433c..e9b9c839283 100644 --- a/drivers/s390/block/scm_blk.c +++ b/drivers/s390/block/scm_blk.c @@ -135,6 +135,11 @@ static const struct block_device_operations scm_blk_devops = {  	.release = scm_release,  }; +static bool scm_permit_request(struct scm_blk_dev *bdev, struct request *req) +{ +	return rq_data_dir(req) != WRITE || bdev->state != SCM_WR_PROHIBIT; +} +  static void scm_request_prepare(struct scm_request *scmrq)  {  	struct scm_blk_dev *bdev = scmrq->bdev; @@ -195,14 +200,18 @@ void scm_request_requeue(struct scm_request *scmrq)  	scm_release_cluster(scmrq);  	blk_requeue_request(bdev->rq, scmrq->request); +	atomic_dec(&bdev->queued_reqs);  	scm_request_done(scmrq);  	scm_ensure_queue_restart(bdev);  }  void scm_request_finish(struct scm_request *scmrq)  { +	struct scm_blk_dev *bdev = scmrq->bdev; +  	scm_release_cluster(scmrq);  	blk_end_request_all(scmrq->request, scmrq->error); +	atomic_dec(&bdev->queued_reqs);  	scm_request_done(scmrq);  } @@ -218,6 +227,10 @@ static void scm_blk_request(struct request_queue *rq)  		if (req->cmd_type != REQ_TYPE_FS)  			continue; +		if (!scm_permit_request(bdev, req)) { +			scm_ensure_queue_restart(bdev); +			return; +		}  		scmrq = scm_request_fetch();  		if (!scmrq) {  			SCM_LOG(5, "no request"); @@ -231,11 +244,13 @@ static void scm_blk_request(struct request_queue *rq)  			return;  		}  		if (scm_need_cluster_request(scmrq)) { +			atomic_inc(&bdev->queued_reqs);  			blk_start_request(req);  			scm_initiate_cluster_request(scmrq);  			return;  		}  		scm_request_prepare(scmrq); +		atomic_inc(&bdev->queued_reqs);  		blk_start_request(req);  		ret = scm_start_aob(scmrq->aob); @@ -244,7 +259,6 @@ static void scm_blk_request(struct request_queue *rq)  			scm_request_requeue(scmrq);  			return;  		} -		atomic_inc(&bdev->queued_reqs);  	}  } @@ -280,6 +294,38 @@ void scm_blk_irq(struct scm_device *scmdev, void *data, int error)  	tasklet_hi_schedule(&bdev->tasklet);  } +static void scm_blk_handle_error(struct scm_request *scmrq) +{ +	struct scm_blk_dev *bdev = scmrq->bdev; +	unsigned long flags; + +	if (scmrq->error != -EIO) +		goto restart; + +	/* For -EIO the response block is valid. */ +	switch (scmrq->aob->response.eqc) { +	case EQC_WR_PROHIBIT: +		spin_lock_irqsave(&bdev->lock, flags); +		if (bdev->state != SCM_WR_PROHIBIT) +			pr_info("%lx: Write access to the SCM increment is suspended\n", +				(unsigned long) bdev->scmdev->address); +		bdev->state = SCM_WR_PROHIBIT; +		spin_unlock_irqrestore(&bdev->lock, flags); +		goto requeue; +	default: +		break; +	} + +restart: +	if (!scm_start_aob(scmrq->aob)) +		return; + +requeue: +	spin_lock_irqsave(&bdev->rq_lock, flags); +	scm_request_requeue(scmrq); +	spin_unlock_irqrestore(&bdev->rq_lock, flags); +} +  static void scm_blk_tasklet(struct scm_blk_dev *bdev)  {  	struct scm_request *scmrq; @@ -293,11 +339,8 @@ static void scm_blk_tasklet(struct scm_blk_dev *bdev)  		spin_unlock_irqrestore(&bdev->lock, flags);  		if (scmrq->error && scmrq->retries-- > 0) { -			if (scm_start_aob(scmrq->aob)) { -				spin_lock_irqsave(&bdev->rq_lock, flags); -				scm_request_requeue(scmrq); -				spin_unlock_irqrestore(&bdev->rq_lock, flags); -			} +			scm_blk_handle_error(scmrq); +  			/* Request restarted or requeued, handle next. */  			spin_lock_irqsave(&bdev->lock, flags);  			continue; @@ -310,7 +353,6 @@ static void scm_blk_tasklet(struct scm_blk_dev *bdev)  		}  		scm_request_finish(scmrq); -		atomic_dec(&bdev->queued_reqs);  		spin_lock_irqsave(&bdev->lock, flags);  	}  	spin_unlock_irqrestore(&bdev->lock, flags); @@ -332,6 +374,7 @@ int scm_blk_dev_setup(struct scm_blk_dev *bdev, struct scm_device *scmdev)  	}  	bdev->scmdev = scmdev; +	bdev->state = SCM_OPER;  	spin_lock_init(&bdev->rq_lock);  	spin_lock_init(&bdev->lock);  	INIT_LIST_HEAD(&bdev->finished_requests); @@ -396,6 +439,18 @@ void scm_blk_dev_cleanup(struct scm_blk_dev *bdev)  	put_disk(bdev->gendisk);  } +void scm_blk_set_available(struct scm_blk_dev *bdev) +{ +	unsigned long flags; + +	spin_lock_irqsave(&bdev->lock, flags); +	if (bdev->state == SCM_WR_PROHIBIT) +		pr_info("%lx: Write access to the SCM increment is restored\n", +			(unsigned long) bdev->scmdev->address); +	bdev->state = SCM_OPER; +	spin_unlock_irqrestore(&bdev->lock, flags); +} +  static int __init scm_blk_init(void)  {  	int ret = -EINVAL; @@ -408,12 +463,15 @@ static int __init scm_blk_init(void)  		goto out;  	scm_major = ret; -	if (scm_alloc_rqs(nr_requests)) +	ret = scm_alloc_rqs(nr_requests); +	if (ret)  		goto out_unreg;  	scm_debug = debug_register("scm_log", 16, 1, 16); -	if (!scm_debug) +	if (!scm_debug) { +		ret = -ENOMEM;  		goto out_free; +	}  	debug_register_view(scm_debug, &debug_hex_ascii_view);  	debug_set_level(scm_debug, 2); diff --git a/drivers/s390/block/scm_blk.h b/drivers/s390/block/scm_blk.h index 3c1ccf49464..8b387b32fd6 100644 --- a/drivers/s390/block/scm_blk.h +++ b/drivers/s390/block/scm_blk.h @@ -21,6 +21,7 @@ struct scm_blk_dev {  	spinlock_t rq_lock;	/* guard the request queue */  	spinlock_t lock;	/* guard the rest of the blockdev */  	atomic_t queued_reqs; +	enum {SCM_OPER, SCM_WR_PROHIBIT} state;  	struct list_head finished_requests;  #ifdef CONFIG_SCM_BLOCK_CLUSTER_WRITE  	struct list_head cluster_list; @@ -48,6 +49,7 @@ struct scm_request {  int scm_blk_dev_setup(struct scm_blk_dev *, struct scm_device *);  void scm_blk_dev_cleanup(struct scm_blk_dev *); +void scm_blk_set_available(struct scm_blk_dev *);  void scm_blk_irq(struct scm_device *, void *, int);  void scm_request_finish(struct scm_request *); diff --git a/drivers/s390/block/scm_drv.c b/drivers/s390/block/scm_drv.c index 9fa0a908607..c98cf52d78d 100644 --- a/drivers/s390/block/scm_drv.c +++ b/drivers/s390/block/scm_drv.c @@ -13,12 +13,23 @@  #include <asm/eadm.h>  #include "scm_blk.h" -static void notify(struct scm_device *scmdev) +static void scm_notify(struct scm_device *scmdev, enum scm_event event)  { -	pr_info("%lu: The capabilities of the SCM increment changed\n", -		(unsigned long) scmdev->address); -	SCM_LOG(2, "State changed"); -	SCM_LOG_STATE(2, scmdev); +	struct scm_blk_dev *bdev = dev_get_drvdata(&scmdev->dev); + +	switch (event) { +	case SCM_CHANGE: +		pr_info("%lx: The capabilities of the SCM increment changed\n", +			(unsigned long) scmdev->address); +		SCM_LOG(2, "State changed"); +		SCM_LOG_STATE(2, scmdev); +		break; +	case SCM_AVAIL: +		SCM_LOG(2, "Increment available"); +		SCM_LOG_STATE(2, scmdev); +		scm_blk_set_available(bdev); +		break; +	}  }  static int scm_probe(struct scm_device *scmdev) @@ -64,7 +75,7 @@ static struct scm_driver scm_drv = {  		.name = "scm_block",  		.owner = THIS_MODULE,  	}, -	.notify = notify, +	.notify = scm_notify,  	.probe = scm_probe,  	.remove = scm_remove,  	.handler = scm_blk_irq, diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c index 30a2255389e..cd798386b62 100644 --- a/drivers/s390/char/sclp_cmd.c +++ b/drivers/s390/char/sclp_cmd.c @@ -627,6 +627,8 @@ static int __init sclp_detect_standby_memory(void)  	struct read_storage_sccb *sccb;  	int i, id, assigned, rc; +	if (OLDMEM_BASE) /* No standby memory in kdump mode */ +		return 0;  	if (!early_read_info_sccb_valid)  		return 0;  	if ((sclp_facilities & 0xe00000000000ULL) != 0xe00000000000ULL) diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c index b907dba2402..cee69dac3e1 100644 --- a/drivers/s390/char/tty3270.c +++ b/drivers/s390/char/tty3270.c @@ -915,7 +915,7 @@ static int tty3270_install(struct tty_driver *driver, struct tty_struct *tty)  	int i, rc;  	/* Check if the tty3270 is already there. */ -	view = raw3270_find_view(&tty3270_fn, tty->index); +	view = raw3270_find_view(&tty3270_fn, tty->index + RAW3270_FIRSTMINOR);  	if (!IS_ERR(view)) {  		tp = container_of(view, struct tty3270, view);  		tty->driver_data = tp; @@ -927,15 +927,16 @@ static int tty3270_install(struct tty_driver *driver, struct tty_struct *tty)  		tp->inattr = TF_INPUT;  		return tty_port_install(&tp->port, driver, tty);  	} -	if (tty3270_max_index < tty->index) -		tty3270_max_index = tty->index; +	if (tty3270_max_index < tty->index + 1) +		tty3270_max_index = tty->index + 1;  	/* Allocate tty3270 structure on first open. */  	tp = tty3270_alloc_view();  	if (IS_ERR(tp))  		return PTR_ERR(tp); -	rc = raw3270_add_view(&tp->view, &tty3270_fn, tty->index); +	rc = raw3270_add_view(&tp->view, &tty3270_fn, +			      tty->index + RAW3270_FIRSTMINOR);  	if (rc) {  		tty3270_free_view(tp);  		return rc; @@ -1846,12 +1847,12 @@ static const struct tty_operations tty3270_ops = {  void tty3270_create_cb(int minor)  { -	tty_register_device(tty3270_driver, minor, NULL); +	tty_register_device(tty3270_driver, minor - RAW3270_FIRSTMINOR, NULL);  }  void tty3270_destroy_cb(int minor)  { -	tty_unregister_device(tty3270_driver, minor); +	tty_unregister_device(tty3270_driver, minor - RAW3270_FIRSTMINOR);  }  struct raw3270_notifier tty3270_notifier = @@ -1884,7 +1885,8 @@ static int __init tty3270_init(void)  	driver->driver_name = "tty3270";  	driver->name = "3270/tty";  	driver->major = IBM_TTY3270_MAJOR; -	driver->minor_start = 0; +	driver->minor_start = RAW3270_FIRSTMINOR; +	driver->name_base = RAW3270_FIRSTMINOR;  	driver->type = TTY_DRIVER_TYPE_SYSTEM;  	driver->subtype = SYSTEM_TYPE_TTY;  	driver->init_termios = tty_std_termios; diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 31ceef1beb8..e16c553f655 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -433,6 +433,20 @@ static void chsc_process_sei_scm_change(struct chsc_sei_nt0_area *sei_area)  			      " failed (rc=%d).\n", ret);  } +static void chsc_process_sei_scm_avail(struct chsc_sei_nt0_area *sei_area) +{ +	int ret; + +	CIO_CRW_EVENT(4, "chsc: scm available information\n"); +	if (sei_area->rs != 7) +		return; + +	ret = scm_process_availability_information(); +	if (ret) +		CIO_CRW_EVENT(0, "chsc: process availability information" +			      " failed (rc=%d).\n", ret); +} +  static void chsc_process_sei_nt2(struct chsc_sei_nt2_area *sei_area)  {  	switch (sei_area->cc) { @@ -468,6 +482,9 @@ static void chsc_process_sei_nt0(struct chsc_sei_nt0_area *sei_area)  	case 12: /* scm change notification */  		chsc_process_sei_scm_change(sei_area);  		break; +	case 14: /* scm available notification */ +		chsc_process_sei_scm_avail(sei_area); +		break;  	default: /* other stuff */  		CIO_CRW_EVENT(2, "chsc: sei nt0 unhandled cc=%d\n",  			      sei_area->cc); diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h index 227e05f674b..349d5fc4719 100644 --- a/drivers/s390/cio/chsc.h +++ b/drivers/s390/cio/chsc.h @@ -156,8 +156,10 @@ int chsc_scm_info(struct chsc_scm_info *scm_area, u64 token);  #ifdef CONFIG_SCM_BUS  int scm_update_information(void); +int scm_process_availability_information(void);  #else /* CONFIG_SCM_BUS */  static inline int scm_update_information(void) { return 0; } +static inline int scm_process_availability_information(void) { return 0; }  #endif /* CONFIG_SCM_BUS */ diff --git a/drivers/s390/cio/scm.c b/drivers/s390/cio/scm.c index bcf20f3aa51..46ec25632e8 100644 --- a/drivers/s390/cio/scm.c +++ b/drivers/s390/cio/scm.c @@ -211,7 +211,7 @@ static void scmdev_update(struct scm_device *scmdev, struct sale *sale)  		goto out;  	scmdrv = to_scm_drv(scmdev->dev.driver);  	if (changed && scmdrv->notify) -		scmdrv->notify(scmdev); +		scmdrv->notify(scmdev, SCM_CHANGE);  out:  	device_unlock(&scmdev->dev);  	if (changed) @@ -297,6 +297,22 @@ int scm_update_information(void)  	return ret;  } +static int scm_dev_avail(struct device *dev, void *unused) +{ +	struct scm_driver *scmdrv = to_scm_drv(dev->driver); +	struct scm_device *scmdev = to_scm_dev(dev); + +	if (dev->driver && scmdrv->notify) +		scmdrv->notify(scmdev, SCM_AVAIL); + +	return 0; +} + +int scm_process_availability_information(void) +{ +	return bus_for_each_dev(&scm_bus_type, NULL, NULL, scm_dev_avail); +} +  static int __init scm_init(void)  {  	int ret; diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index d87961d4c0d..8c0622399fc 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -916,6 +916,7 @@ int qeth_send_control_data(struct qeth_card *, int, struct qeth_cmd_buffer *,  	void *reply_param);  int qeth_get_priority_queue(struct qeth_card *, struct sk_buff *, int, int);  int qeth_get_elements_no(struct qeth_card *, void *, struct sk_buff *, int); +int qeth_get_elements_for_frags(struct sk_buff *);  int qeth_do_send_packet_fast(struct qeth_card *, struct qeth_qdio_out_q *,  			struct sk_buff *, struct qeth_hdr *, int, int, int);  int qeth_do_send_packet(struct qeth_card *, struct qeth_qdio_out_q *, diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 0d8cdff8181..0d73a999983 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -3679,6 +3679,25 @@ int qeth_get_priority_queue(struct qeth_card *card, struct sk_buff *skb,  }  EXPORT_SYMBOL_GPL(qeth_get_priority_queue); +int qeth_get_elements_for_frags(struct sk_buff *skb) +{ +	int cnt, length, e, elements = 0; +	struct skb_frag_struct *frag; +	char *data; + +	for (cnt = 0; cnt < skb_shinfo(skb)->nr_frags; cnt++) { +		frag = &skb_shinfo(skb)->frags[cnt]; +		data = (char *)page_to_phys(skb_frag_page(frag)) + +			frag->page_offset; +		length = frag->size; +		e = PFN_UP((unsigned long)data + length - 1) - +			PFN_DOWN((unsigned long)data); +		elements += e; +	} +	return elements; +} +EXPORT_SYMBOL_GPL(qeth_get_elements_for_frags); +  int qeth_get_elements_no(struct qeth_card *card, void *hdr,  		     struct sk_buff *skb, int elems)  { @@ -3686,7 +3705,8 @@ int qeth_get_elements_no(struct qeth_card *card, void *hdr,  	int elements_needed = PFN_UP((unsigned long)skb->data + dlen - 1) -  		PFN_DOWN((unsigned long)skb->data); -	elements_needed += skb_shinfo(skb)->nr_frags; +	elements_needed += qeth_get_elements_for_frags(skb); +  	if ((elements_needed + elems) > QETH_MAX_BUFFER_ELEMENTS(card)) {  		QETH_DBF_MESSAGE(2, "Invalid size of IP packet "  			"(Number=%d / Length=%d). Discarded.\n", @@ -3771,12 +3791,23 @@ static inline void __qeth_fill_buffer(struct sk_buff *skb,  	for (cnt = 0; cnt < skb_shinfo(skb)->nr_frags; cnt++) {  		frag = &skb_shinfo(skb)->frags[cnt]; -		buffer->element[element].addr = (char *) -			page_to_phys(skb_frag_page(frag)) -			+ frag->page_offset; -		buffer->element[element].length = frag->size; -		buffer->element[element].eflags = SBAL_EFLAGS_MIDDLE_FRAG; -		element++; +		data = (char *)page_to_phys(skb_frag_page(frag)) + +			frag->page_offset; +		length = frag->size; +		while (length > 0) { +			length_here = PAGE_SIZE - +				((unsigned long) data % PAGE_SIZE); +			if (length < length_here) +				length_here = length; + +			buffer->element[element].addr = data; +			buffer->element[element].length = length_here; +			buffer->element[element].eflags = +				SBAL_EFLAGS_MIDDLE_FRAG; +			length -= length_here; +			data += length_here; +			element++; +		}  	}  	if (buffer->element[element - 1].eflags) diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 091ca0efa1c..8710337dab3 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -623,7 +623,7 @@ static int qeth_l3_send_setrouting(struct qeth_card *card,  	return rc;  } -static void qeth_l3_correct_routing_type(struct qeth_card *card, +static int qeth_l3_correct_routing_type(struct qeth_card *card,  		enum qeth_routing_types *type, enum qeth_prot_versions prot)  {  	if (card->info.type == QETH_CARD_TYPE_IQD) { @@ -632,7 +632,7 @@ static void qeth_l3_correct_routing_type(struct qeth_card *card,  		case PRIMARY_CONNECTOR:  		case SECONDARY_CONNECTOR:  		case MULTICAST_ROUTER: -			return; +			return 0;  		default:  			goto out_inval;  		} @@ -641,17 +641,18 @@ static void qeth_l3_correct_routing_type(struct qeth_card *card,  		case NO_ROUTER:  		case PRIMARY_ROUTER:  		case SECONDARY_ROUTER: -			return; +			return 0;  		case MULTICAST_ROUTER:  			if (qeth_is_ipafunc_supported(card, prot,  						      IPA_OSA_MC_ROUTER)) -				return; +				return 0;  		default:  			goto out_inval;  		}  	}  out_inval:  	*type = NO_ROUTER; +	return -EINVAL;  }  int qeth_l3_setrouting_v4(struct qeth_card *card) @@ -660,8 +661,10 @@ int qeth_l3_setrouting_v4(struct qeth_card *card)  	QETH_CARD_TEXT(card, 3, "setrtg4"); -	qeth_l3_correct_routing_type(card, &card->options.route4.type, +	rc = qeth_l3_correct_routing_type(card, &card->options.route4.type,  				  QETH_PROT_IPV4); +	if (rc) +		return rc;  	rc = qeth_l3_send_setrouting(card, card->options.route4.type,  				  QETH_PROT_IPV4); @@ -683,8 +686,10 @@ int qeth_l3_setrouting_v6(struct qeth_card *card)  	if (!qeth_is_supported(card, IPA_IPV6))  		return 0; -	qeth_l3_correct_routing_type(card, &card->options.route6.type, +	rc = qeth_l3_correct_routing_type(card, &card->options.route6.type,  				  QETH_PROT_IPV6); +	if (rc) +		return rc;  	rc = qeth_l3_send_setrouting(card, card->options.route6.type,  				  QETH_PROT_IPV6); @@ -2898,7 +2903,9 @@ static inline int qeth_l3_tso_elements(struct sk_buff *skb)  		tcp_hdr(skb)->doff * 4;  	int tcpd_len = skb->len - (tcpd - (unsigned long)skb->data);  	int elements = PFN_UP(tcpd + tcpd_len - 1) - PFN_DOWN(tcpd); -	elements += skb_shinfo(skb)->nr_frags; + +	elements += qeth_get_elements_for_frags(skb); +  	return elements;  } @@ -3348,7 +3355,6 @@ static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode)  		rc = -ENODEV;  		goto out_remove;  	} -	qeth_trace_features(card);  	if (!card->dev && qeth_l3_setup_netdev(card)) {  		rc = -ENODEV; @@ -3425,6 +3431,7 @@ contin:  		qeth_l3_set_multicast_list(card->dev);  		rtnl_unlock();  	} +	qeth_trace_features(card);  	/* let user_space know that device is online */  	kobject_uevent(&gdev->dev.kobj, KOBJ_CHANGE);  	mutex_unlock(&card->conf_mutex); diff --git a/drivers/s390/net/qeth_l3_sys.c b/drivers/s390/net/qeth_l3_sys.c index ebc37948626..e70af2406ff 100644 --- a/drivers/s390/net/qeth_l3_sys.c +++ b/drivers/s390/net/qeth_l3_sys.c @@ -87,6 +87,8 @@ static ssize_t qeth_l3_dev_route_store(struct qeth_card *card,  			rc = qeth_l3_setrouting_v6(card);  	}  out: +	if (rc) +		route->type = old_route_type;  	mutex_unlock(&card->conf_mutex);  	return rc ? rc : count;  }  |