diff options
Diffstat (limited to 'drivers/s390/crypto/ap_bus.c')
| -rw-r--r-- | drivers/s390/crypto/ap_bus.c | 437 | 
1 files changed, 372 insertions, 65 deletions
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 8fd8c62455e..4038437ff03 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -1,11 +1,10 @@  /* - * linux/drivers/s390/crypto/ap_bus.c - * - * Copyright (C) 2006 IBM Corporation + * Copyright IBM Corp. 2006, 2012   * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>   *	      Martin Schwidefsky <schwidefsky@de.ibm.com>   *	      Ralph Wuerthner <rwuerthn@de.ibm.com>   *	      Felix Beck <felix.beck@de.ibm.com> + *	      Holger Dengler <hd@linux.vnet.ibm.com>   *   * Adjunct processor bus.   * @@ -27,6 +26,7 @@  #define KMSG_COMPONENT "ap"  #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt +#include <linux/kernel_stat.h>  #include <linux/module.h>  #include <linux/init.h>  #include <linux/delay.h> @@ -39,11 +39,11 @@  #include <linux/mutex.h>  #include <asm/reset.h>  #include <asm/airq.h> -#include <asm/atomic.h> -#include <asm/system.h> +#include <linux/atomic.h>  #include <asm/isc.h>  #include <linux/hrtimer.h>  #include <linux/ktime.h> +#include <asm/facility.h>  #include "ap_bus.h" @@ -58,32 +58,35 @@ static inline void ap_schedule_poll_timer(void);  static int __ap_poll_device(struct ap_device *ap_dev, unsigned long *flags);  static int ap_device_remove(struct device *dev);  static int ap_device_probe(struct device *dev); -static void ap_interrupt_handler(void *unused1, void *unused2); +static void ap_interrupt_handler(struct airq_struct *airq);  static void ap_reset(struct ap_device *ap_dev);  static void ap_config_timeout(unsigned long ptr);  static int ap_select_domain(void); +static void ap_query_configuration(void);  /*   * Module description.   */  MODULE_AUTHOR("IBM Corporation"); -MODULE_DESCRIPTION("Adjunct Processor Bus driver, " -		   "Copyright 2006 IBM Corporation"); +MODULE_DESCRIPTION("Adjunct Processor Bus driver, " \ +		   "Copyright IBM Corp. 2006, 2012");  MODULE_LICENSE("GPL"); +MODULE_ALIAS("z90crypt");  /*   * Module parameter   */  int ap_domain_index = -1;	/* Adjunct Processor Domain Index */ -module_param_named(domain, ap_domain_index, int, 0000); +module_param_named(domain, ap_domain_index, int, S_IRUSR|S_IRGRP);  MODULE_PARM_DESC(domain, "domain index for ap devices");  EXPORT_SYMBOL(ap_domain_index);  static int ap_thread_flag = 0; -module_param_named(poll_thread, ap_thread_flag, int, 0000); +module_param_named(poll_thread, ap_thread_flag, int, S_IRUSR|S_IRGRP);  MODULE_PARM_DESC(poll_thread, "Turn on/off poll thread, default is 0 (off).");  static struct device *ap_root_device = NULL; +static struct ap_config_info *ap_configuration;  static DEFINE_SPINLOCK(ap_device_list_lock);  static LIST_HEAD(ap_device_list); @@ -104,7 +107,6 @@ static DECLARE_WAIT_QUEUE_HEAD(ap_poll_wait);  static struct task_struct *ap_poll_kthread = NULL;  static DEFINE_MUTEX(ap_poll_thread_mutex);  static DEFINE_SPINLOCK(ap_poll_timer_lock); -static void *ap_interrupt_indicator;  static struct hrtimer ap_poll_timer;  /* In LPAR poll with 4kHz frequency. Poll every 250000 nanoseconds.   * If z/VM change to 1500000 nanoseconds to adjust to z/VM polling.*/ @@ -118,13 +120,21 @@ static int ap_suspend_flag;  static int user_set_domain = 0;  static struct bus_type ap_bus_type; +/* Adapter interrupt definitions */ +static int ap_airq_flag; + +static struct airq_struct ap_airq = { +	.handler = ap_interrupt_handler, +	.isc = AP_ISC, +}; +  /**   * ap_using_interrupts() - Returns non-zero if interrupt support is   * available.   */  static inline int ap_using_interrupts(void)  { -	return ap_interrupt_indicator != NULL; +	return ap_airq_flag;  }  /** @@ -154,8 +164,21 @@ static inline int ap_instructions_available(void)   */  static int ap_interrupts_available(void)  { -	return test_facility(1) && test_facility(2); +	return test_facility(2) && test_facility(65); +} + +/** + * ap_configuration_available(): Test if AP configuration + * information is available. + * + * Returns 1 if AP configuration information is available. + */ +#ifdef CONFIG_64BIT +static int ap_configuration_available(void) +{ +	return test_facility(2) && test_facility(12);  } +#endif  /**   * ap_test_queue(): Test adjunct processor queue. @@ -213,7 +236,7 @@ ap_queue_interruption_control(ap_qid_t qid, void *ind)  	register struct ap_queue_status reg1_out asm ("1");  	register void *reg2 asm ("2") = ind;  	asm volatile( -		".long 0xb2af0000"		/* PQAP(RAPQ) */ +		".long 0xb2af0000"		/* PQAP(AQIC) */  		: "+d" (reg0), "+d" (reg1_in), "=d" (reg1_out), "+d" (reg2)  		:  		: "cc" ); @@ -221,6 +244,96 @@ ap_queue_interruption_control(ap_qid_t qid, void *ind)  }  #endif +#ifdef CONFIG_64BIT +static inline struct ap_queue_status +__ap_query_functions(ap_qid_t qid, unsigned int *functions) +{ +	register unsigned long reg0 asm ("0") = 0UL | qid | (1UL << 23); +	register struct ap_queue_status reg1 asm ("1") = AP_QUEUE_STATUS_INVALID; +	register unsigned long reg2 asm ("2"); + +	asm volatile( +		".long 0xb2af0000\n"		/* PQAP(TAPQ) */ +		"0:\n" +		EX_TABLE(0b, 0b) +		: "+d" (reg0), "+d" (reg1), "=d" (reg2) +		: +		: "cc"); + +	*functions = (unsigned int)(reg2 >> 32); +	return reg1; +} +#endif + +#ifdef CONFIG_64BIT +static inline int __ap_query_configuration(struct ap_config_info *config) +{ +	register unsigned long reg0 asm ("0") = 0x04000000UL; +	register unsigned long reg1 asm ("1") = -EINVAL; +	register unsigned char *reg2 asm ("2") = (unsigned char *)config; + +	asm volatile( +		".long 0xb2af0000\n"		/* PQAP(QCI) */ +		"0: la    %1,0\n" +		"1:\n" +		EX_TABLE(0b, 1b) +		: "+d" (reg0), "+d" (reg1), "+d" (reg2) +		: +		: "cc"); + +	return reg1; +} +#endif + +/** + * ap_query_functions(): Query supported functions. + * @qid: The AP queue number + * @functions: Pointer to functions field. + * + * Returns + *   0	     on success. + *   -ENODEV  if queue not valid. + *   -EBUSY   if device busy. + *   -EINVAL  if query function is not supported + */ +static int ap_query_functions(ap_qid_t qid, unsigned int *functions) +{ +#ifdef CONFIG_64BIT +	struct ap_queue_status status; +	int i; +	status = __ap_query_functions(qid, functions); + +	for (i = 0; i < AP_MAX_RESET; i++) { +		if (ap_queue_status_invalid_test(&status)) +			return -ENODEV; + +		switch (status.response_code) { +		case AP_RESPONSE_NORMAL: +			return 0; +		case AP_RESPONSE_RESET_IN_PROGRESS: +		case AP_RESPONSE_BUSY: +			break; +		case AP_RESPONSE_Q_NOT_AVAIL: +		case AP_RESPONSE_DECONFIGURED: +		case AP_RESPONSE_CHECKSTOPPED: +		case AP_RESPONSE_INVALID_ADDRESS: +			return -ENODEV; +		case AP_RESPONSE_OTHERWISE_CHANGED: +			break; +		default: +			break; +		} +		if (i < AP_MAX_RESET - 1) { +			udelay(5); +			status = __ap_query_functions(qid, functions); +		} +	} +	return -EBUSY; +#else +	return -EINVAL; +#endif +} +  /**   * ap_queue_enable_interruption(): Enable interruption on an AP.   * @qid: The AP queue number @@ -247,6 +360,12 @@ static int ap_queue_enable_interruption(ap_qid_t qid, void *ind)  			break;  		case AP_RESPONSE_RESET_IN_PROGRESS:  		case AP_RESPONSE_BUSY: +			if (i < AP_MAX_RESET - 1) { +				udelay(5); +				status = ap_queue_interruption_control(qid, +								       ind); +				continue; +			}  			break;  		case AP_RESPONSE_Q_NOT_AVAIL:  		case AP_RESPONSE_DECONFIGURED: @@ -294,13 +413,13 @@ __ap_send(ap_qid_t qid, unsigned long long psmid, void *msg, size_t length,  	register unsigned long reg2 asm ("2") = (unsigned long) msg;  	register unsigned long reg3 asm ("3") = (unsigned long) length;  	register unsigned long reg4 asm ("4") = (unsigned int) (psmid >> 32); -	register unsigned long reg5 asm ("5") = (unsigned int) psmid; +	register unsigned long reg5 asm ("5") = psmid & 0xffffffff;  	if (special == 1)  		reg0 |= 0x400000UL;  	asm volatile ( -		"0: .long 0xb2ad0042\n"		/* DQAP */ +		"0: .long 0xb2ad0042\n"		/* NQAP */  		"   brc   2,0b"  		: "+d" (reg0), "=d" (reg1), "+d" (reg2), "+d" (reg3)  		: "d" (reg4), "d" (reg5), "m" (*(msgblock *) msg) @@ -359,7 +478,7 @@ __ap_recv(ap_qid_t qid, unsigned long long *psmid, void *msg, size_t length)  	asm volatile( -		"0: .long 0xb2ae0064\n" +		"0: .long 0xb2ae0064\n"		/* DQAP */  		"   brc   6,0b\n"  		: "+d" (reg0), "=d" (reg1), "+d" (reg2),  		"+d" (reg4), "+d" (reg5), "+d" (reg6), "+d" (reg7), @@ -472,12 +591,18 @@ static int ap_init_queue(ap_qid_t qid)  		if (rc != -ENODEV && rc != -EBUSY)  			break;  		if (i < AP_MAX_RESET - 1) { -			udelay(5); +			/* Time we are waiting until we give up (0.7sec * 90). +			 * Since the actual request (in progress) will not +			 * interrupted immediately for the reset command, +			 * we have to be patient. In worst case we have to +			 * wait 60sec + reset time (some msec). +			 */ +			schedule_timeout(AP_RESET_TIMEOUT);  			status = ap_test_queue(qid, &dummy, &dummy);  		}  	}  	if (rc == 0 && ap_using_interrupts()) { -		rc = ap_queue_enable_interruption(qid, ap_interrupt_indicator); +		rc = ap_queue_enable_interruption(qid, ap_airq.lsi_ptr);  		/* If interruption mode is supported by the machine,  		* but an AP can not be enabled for interruption then  		* the AP will be discarded.    */ @@ -562,6 +687,34 @@ static ssize_t ap_request_count_show(struct device *dev,  static DEVICE_ATTR(request_count, 0444, ap_request_count_show, NULL); +static ssize_t ap_requestq_count_show(struct device *dev, +				      struct device_attribute *attr, char *buf) +{ +	struct ap_device *ap_dev = to_ap_dev(dev); +	int rc; + +	spin_lock_bh(&ap_dev->lock); +	rc = snprintf(buf, PAGE_SIZE, "%d\n", ap_dev->requestq_count); +	spin_unlock_bh(&ap_dev->lock); +	return rc; +} + +static DEVICE_ATTR(requestq_count, 0444, ap_requestq_count_show, NULL); + +static ssize_t ap_pendingq_count_show(struct device *dev, +				      struct device_attribute *attr, char *buf) +{ +	struct ap_device *ap_dev = to_ap_dev(dev); +	int rc; + +	spin_lock_bh(&ap_dev->lock); +	rc = snprintf(buf, PAGE_SIZE, "%d\n", ap_dev->pendingq_count); +	spin_unlock_bh(&ap_dev->lock); +	return rc; +} + +static DEVICE_ATTR(pendingq_count, 0444, ap_pendingq_count_show, NULL); +  static ssize_t ap_modalias_show(struct device *dev,  				struct device_attribute *attr, char *buf)  { @@ -570,11 +723,23 @@ static ssize_t ap_modalias_show(struct device *dev,  static DEVICE_ATTR(modalias, 0444, ap_modalias_show, NULL); +static ssize_t ap_functions_show(struct device *dev, +				 struct device_attribute *attr, char *buf) +{ +	struct ap_device *ap_dev = to_ap_dev(dev); +	return snprintf(buf, PAGE_SIZE, "0x%08X\n", ap_dev->functions); +} + +static DEVICE_ATTR(ap_functions, 0444, ap_functions_show, NULL); +  static struct attribute *ap_dev_attrs[] = {  	&dev_attr_hwtype.attr,  	&dev_attr_depth.attr,  	&dev_attr_request_count.attr, +	&dev_attr_requestq_count.attr, +	&dev_attr_pendingq_count.attr,  	&dev_attr_modalias.attr, +	&dev_attr_ap_functions.attr,  	NULL  };  static struct attribute_group ap_dev_attr_group = { @@ -670,13 +835,23 @@ static int ap_bus_suspend(struct device *dev, pm_message_t state)  static int ap_bus_resume(struct device *dev)  { -	int rc = 0;  	struct ap_device *ap_dev = to_ap_dev(dev); +	int rc;  	if (ap_suspend_flag) {  		ap_suspend_flag = 0; -		if (!ap_interrupts_available()) -			ap_interrupt_indicator = NULL; +		if (ap_interrupts_available()) { +			if (!ap_using_interrupts()) { +				rc = register_adapter_interrupt(&ap_airq); +				ap_airq_flag = (rc == 0); +			} +		} else { +			if (ap_using_interrupts()) { +				unregister_adapter_interrupt(&ap_airq); +				ap_airq_flag = 0; +			} +		} +		ap_query_configuration();  		if (!user_set_domain) {  			ap_domain_index = -1;  			ap_select_domain(); @@ -696,7 +871,10 @@ static int ap_bus_resume(struct device *dev)  			tasklet_schedule(&ap_tasklet);  		if (ap_thread_flag)  			rc = ap_poll_thread_start(); -	} +		else +			rc = 0; +	} else +		rc = 0;  	if (AP_QID_QUEUE(ap_dev->qid) != ap_domain_index) {  		spin_lock_bh(&ap_dev->lock);  		ap_dev->qid = AP_MKQID(AP_QID_DEVICE(ap_dev->qid), @@ -723,10 +901,15 @@ static int ap_device_probe(struct device *dev)  	int rc;  	ap_dev->drv = ap_drv; + +	spin_lock_bh(&ap_device_list_lock); +	list_add(&ap_dev->list, &ap_device_list); +	spin_unlock_bh(&ap_device_list_lock); +  	rc = ap_drv->probe ? ap_drv->probe(ap_dev) : -ENODEV; -	if (!rc) { +	if (rc) {  		spin_lock_bh(&ap_device_list_lock); -		list_add(&ap_dev->list, &ap_device_list); +		list_del_init(&ap_dev->list);  		spin_unlock_bh(&ap_device_list_lock);  	}  	return rc; @@ -745,12 +928,12 @@ static void __ap_flush_queue(struct ap_device *ap_dev)  	list_for_each_entry_safe(ap_msg, next, &ap_dev->pendingq, list) {  		list_del_init(&ap_msg->list);  		ap_dev->pendingq_count--; -		ap_dev->drv->receive(ap_dev, ap_msg, ERR_PTR(-ENODEV)); +		ap_msg->receive(ap_dev, ap_msg, ERR_PTR(-ENODEV));  	}  	list_for_each_entry_safe(ap_msg, next, &ap_dev->requestq, list) {  		list_del_init(&ap_msg->list);  		ap_dev->requestq_count--; -		ap_dev->drv->receive(ap_dev, ap_msg, ERR_PTR(-ENODEV)); +		ap_msg->receive(ap_dev, ap_msg, ERR_PTR(-ENODEV));  	}  } @@ -800,6 +983,16 @@ void ap_driver_unregister(struct ap_driver *ap_drv)  }  EXPORT_SYMBOL(ap_driver_unregister); +void ap_bus_force_rescan(void) +{ +	/* reconfigure the AP bus rescan timer. */ +	mod_timer(&ap_config_timer, jiffies + ap_config_time * HZ); +	/* processing a asynchronous bus rescan */ +	queue_work(ap_work_queue, &ap_config_work); +	flush_work(&ap_config_work); +} +EXPORT_SYMBOL(ap_bus_force_rescan); +  /*   * AP bus attributes.   */ @@ -810,6 +1003,28 @@ static ssize_t ap_domain_show(struct bus_type *bus, char *buf)  static BUS_ATTR(ap_domain, 0444, ap_domain_show, NULL); +static ssize_t ap_control_domain_mask_show(struct bus_type *bus, char *buf) +{ +	if (ap_configuration != NULL) { /* QCI not supported */ +		if (test_facility(76)) { /* format 1 - 256 bit domain field */ +			return snprintf(buf, PAGE_SIZE, +				"0x%08x%08x%08x%08x%08x%08x%08x%08x\n", +			ap_configuration->adm[0], ap_configuration->adm[1], +			ap_configuration->adm[2], ap_configuration->adm[3], +			ap_configuration->adm[4], ap_configuration->adm[5], +			ap_configuration->adm[6], ap_configuration->adm[7]); +		} else { /* format 0 - 16 bit domain field */ +			return snprintf(buf, PAGE_SIZE, "%08x%08x\n", +			ap_configuration->adm[0], ap_configuration->adm[1]); +		  } +	} else { +		return snprintf(buf, PAGE_SIZE, "not supported\n"); +	  } +} + +static BUS_ATTR(ap_control_domain_mask, 0444, +		ap_control_domain_mask_show, NULL); +  static ssize_t ap_config_time_show(struct bus_type *bus, char *buf)  {  	return snprintf(buf, PAGE_SIZE, "%d\n", ap_config_time); @@ -895,6 +1110,7 @@ static BUS_ATTR(poll_timeout, 0644, poll_timeout_show, poll_timeout_store);  static struct bus_attribute *const ap_bus_attrs[] = {  	&bus_attr_ap_domain, +	&bus_attr_ap_control_domain_mask,  	&bus_attr_config_time,  	&bus_attr_poll_thread,  	&bus_attr_ap_interrupts, @@ -902,6 +1118,65 @@ static struct bus_attribute *const ap_bus_attrs[] = {  	NULL,  }; +static inline int ap_test_config(unsigned int *field, unsigned int nr) +{ +	if (nr > 0xFFu) +		return 0; +	return ap_test_bit((field + (nr >> 5)), (nr & 0x1f)); +} + +/* + * ap_test_config_card_id(): Test, whether an AP card ID is configured. + * @id AP card ID + * + * Returns 0 if the card is not configured + *	   1 if the card is configured or + *	     if the configuration information is not available + */ +static inline int ap_test_config_card_id(unsigned int id) +{ +	if (!ap_configuration) +		return 1; +	return ap_test_config(ap_configuration->apm, id); +} + +/* + * ap_test_config_domain(): Test, whether an AP usage domain is configured. + * @domain AP usage domain ID + * + * Returns 0 if the usage domain is not configured + *	   1 if the usage domain is configured or + *	     if the configuration information is not available + */ +static inline int ap_test_config_domain(unsigned int domain) +{ +	if (!ap_configuration) +		return 1; +	return ap_test_config(ap_configuration->aqm, domain); +} + +/** + * ap_query_configuration(): Query AP configuration information. + * + * Query information of installed cards and configured domains from AP. + */ +static void ap_query_configuration(void) +{ +#ifdef CONFIG_64BIT +	if (ap_configuration_available()) { +		if (!ap_configuration) +			ap_configuration = +				kzalloc(sizeof(struct ap_config_info), +					GFP_KERNEL); +		if (ap_configuration) +			__ap_query_configuration(ap_configuration); +	} else +		ap_configuration = NULL; +#else +	ap_configuration = NULL; +#endif +} +  /**   * ap_select_domain(): Select an AP domain.   * @@ -910,6 +1185,7 @@ static struct bus_attribute *const ap_bus_attrs[] = {  static int ap_select_domain(void)  {  	int queue_depth, device_type, count, max_count, best_domain; +	ap_qid_t qid;  	int rc, i, j;  	/* @@ -923,9 +1199,13 @@ static int ap_select_domain(void)  	best_domain = -1;  	max_count = 0;  	for (i = 0; i < AP_DOMAINS; i++) { +		if (!ap_test_config_domain(i)) +			continue;  		count = 0;  		for (j = 0; j < AP_DEVICES; j++) { -			ap_qid_t qid = AP_MKQID(j, i); +			if (!ap_test_config_card_id(j)) +				continue; +			qid = AP_MKQID(j, i);  			rc = ap_query_queue(qid, &queue_depth, &device_type);  			if (rc)  				continue; @@ -1040,8 +1320,9 @@ out:  	return rc;  } -static void ap_interrupt_handler(void *unused1, void *unused2) +static void ap_interrupt_handler(struct airq_struct *airq)  { +	inc_irq_stat(IRQIO_APB);  	tasklet_schedule(&ap_tasklet);  } @@ -1070,16 +1351,22 @@ static void ap_scan_bus(struct work_struct *unused)  	struct device *dev;  	ap_qid_t qid;  	int queue_depth, device_type; +	unsigned int device_functions;  	int rc, i; -	if (ap_select_domain() != 0) +	ap_query_configuration(); +	if (ap_select_domain() != 0) {  		return; +	}  	for (i = 0; i < AP_DEVICES; i++) {  		qid = AP_MKQID(i, ap_domain_index);  		dev = bus_find_device(&ap_bus_type, NULL,  				      (void *)(unsigned long)qid,  				      __ap_scan_bus); -		rc = ap_query_queue(qid, &queue_depth, &device_type); +		if (ap_test_config_card_id(i)) +			rc = ap_query_queue(qid, &queue_depth, &device_type); +		else +			rc = -ENODEV;  		if (dev) {  			if (rc == -EBUSY) {  				set_current_state(TASK_UNINTERRUPTIBLE); @@ -1118,10 +1405,23 @@ static void ap_scan_bus(struct work_struct *unused)  		INIT_LIST_HEAD(&ap_dev->list);  		setup_timer(&ap_dev->timeout, ap_request_timeout,  			    (unsigned long) ap_dev); -		if (device_type == 0) -			ap_probe_device_type(ap_dev); -		else +		switch (device_type) { +		case 0: +			/* device type probing for old cards */ +			if (ap_probe_device_type(ap_dev)) { +				kfree(ap_dev); +				continue; +			} +			break; +		default:  			ap_dev->device_type = device_type; +		} + +		rc = ap_query_functions(qid, &device_functions); +		if (!rc) +			ap_dev->functions = device_functions; +		else +			ap_dev->functions = 0u;  		ap_dev->device.bus = &ap_bus_type;  		ap_dev->device.parent = ap_root_device; @@ -1158,18 +1458,16 @@ ap_config_timeout(unsigned long ptr)  }  /** - * ap_schedule_poll_timer(): Schedule poll timer. + * __ap_schedule_poll_timer(): Schedule poll timer.   *   * Set up the timer to run the poll tasklet   */ -static inline void ap_schedule_poll_timer(void) +static inline void __ap_schedule_poll_timer(void)  {  	ktime_t hr_time;  	spin_lock_bh(&ap_poll_timer_lock); -	if (ap_using_interrupts() || ap_suspend_flag) -		goto out; -	if (hrtimer_is_queued(&ap_poll_timer)) +	if (hrtimer_is_queued(&ap_poll_timer) || ap_suspend_flag)  		goto out;  	if (ktime_to_ns(hrtimer_expires_remaining(&ap_poll_timer)) <= 0) {  		hr_time = ktime_set(0, poll_timeout); @@ -1181,6 +1479,18 @@ out:  }  /** + * ap_schedule_poll_timer(): Schedule poll timer. + * + * Set up the timer to run the poll tasklet + */ +static inline void ap_schedule_poll_timer(void) +{ +	if (ap_using_interrupts()) +		return; +	__ap_schedule_poll_timer(); +} + +/**   * ap_poll_read(): Receive pending reply messages from an AP device.   * @ap_dev: pointer to the AP device   * @flags: pointer to control flags, bit 2^0 is set if another poll is @@ -1206,7 +1516,7 @@ static int ap_poll_read(struct ap_device *ap_dev, unsigned long *flags)  				continue;  			list_del_init(&ap_msg->list);  			ap_dev->pendingq_count--; -			ap_dev->drv->receive(ap_dev, ap_msg, ap_dev->reply); +			ap_msg->receive(ap_dev, ap_msg, ap_dev->reply);  			break;  		}  		if (ap_dev->queue_count > 0) @@ -1261,8 +1571,9 @@ static int ap_poll_write(struct ap_device *ap_dev, unsigned long *flags)  			*flags |= 1;  		*flags |= 2;  		break; -	case AP_RESPONSE_Q_FULL:  	case AP_RESPONSE_RESET_IN_PROGRESS: +		__ap_schedule_poll_timer(); +	case AP_RESPONSE_Q_FULL:  		*flags |= 2;  		break;  	case AP_RESPONSE_MESSAGE_TOO_BIG: @@ -1326,10 +1637,10 @@ static int __ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_ms  			return -EBUSY;  		case AP_RESPONSE_REQ_FAC_NOT_INST:  		case AP_RESPONSE_MESSAGE_TOO_BIG: -			ap_dev->drv->receive(ap_dev, ap_msg, ERR_PTR(-EINVAL)); +			ap_msg->receive(ap_dev, ap_msg, ERR_PTR(-EINVAL));  			return -EINVAL;  		default:	/* Device is gone. */ -			ap_dev->drv->receive(ap_dev, ap_msg, ERR_PTR(-ENODEV)); +			ap_msg->receive(ap_dev, ap_msg, ERR_PTR(-ENODEV));  			return -ENODEV;  		}  	} else { @@ -1347,6 +1658,10 @@ void ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_msg)  	unsigned long flags;  	int rc; +	/* For asynchronous message handling a valid receive-callback +	 * is required. */ +	BUG_ON(!ap_msg->receive); +  	spin_lock_bh(&ap_dev->lock);  	if (!ap_dev->unregistered) {  		/* Make room on the queue by polling for finished requests. */ @@ -1358,7 +1673,7 @@ void ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_msg)  		if (rc == -ENODEV)  			ap_dev->unregistered = 1;  	} else { -		ap_dev->drv->receive(ap_dev, ap_msg, ERR_PTR(-ENODEV)); +		ap_msg->receive(ap_dev, ap_msg, ERR_PTR(-ENODEV));  		rc = -ENODEV;  	}  	spin_unlock_bh(&ap_dev->lock); @@ -1428,6 +1743,8 @@ static void ap_reset(struct ap_device *ap_dev)  	rc = ap_init_queue(ap_dev->qid);  	if (rc == -ENODEV)  		ap_dev->unregistered = 1; +	else +		__ap_schedule_poll_timer();  }  static int __ap_poll_device(struct ap_device *ap_dev, unsigned long *flags) @@ -1459,7 +1776,7 @@ static void ap_poll_all(unsigned long dummy)  	 * important that no requests on any AP get lost.  	 */  	if (ap_using_interrupts()) -		xchg((u8 *)ap_interrupt_indicator, 0); +		xchg(ap_airq.lsi_ptr, 0);  	do {  		flags = 0;  		spin_lock(&ap_device_list_lock); @@ -1491,7 +1808,7 @@ static int ap_poll_thread(void *data)  	int requests;  	struct ap_device *ap_dev; -	set_user_nice(current, 19); +	set_user_nice(current, MAX_NICE);  	while (1) {  		if (ap_suspend_flag)  			return 0; @@ -1532,7 +1849,7 @@ static int ap_poll_thread_start(void)  	mutex_lock(&ap_poll_thread_mutex);  	if (!ap_poll_kthread) {  		ap_poll_kthread = kthread_run(ap_poll_thread, NULL, "appoll"); -		rc = IS_ERR(ap_poll_kthread) ? PTR_ERR(ap_poll_kthread) : 0; +		rc = PTR_RET(ap_poll_kthread);  		if (rc)  			ap_poll_kthread = NULL;  	} @@ -1618,13 +1935,8 @@ int __init ap_module_init(void)  		return -ENODEV;  	}  	if (ap_interrupts_available()) { -		isc_register(AP_ISC); -		ap_interrupt_indicator = s390_register_adapter_interrupt( -			&ap_interrupt_handler, NULL, AP_ISC); -		if (IS_ERR(ap_interrupt_indicator)) { -			ap_interrupt_indicator = NULL; -			isc_unregister(AP_ISC); -		} +		rc = register_adapter_interrupt(&ap_airq); +		ap_airq_flag = (rc == 0);  	}  	register_reset_call(&ap_reset_call); @@ -1641,7 +1953,7 @@ int __init ap_module_init(void)  	/* Create /sys/devices/ap. */  	ap_root_device = root_device_register("ap"); -	rc = IS_ERR(ap_root_device) ? PTR_ERR(ap_root_device) : 0; +	rc = PTR_RET(ap_root_device);  	if (rc)  		goto out_bus; @@ -1651,6 +1963,7 @@ int __init ap_module_init(void)  		goto out_root;  	} +	ap_query_configuration();  	if (ap_select_domain() == 0)  		ap_scan_bus(NULL); @@ -1691,10 +2004,8 @@ out_bus:  	bus_unregister(&ap_bus_type);  out:  	unregister_reset_call(&ap_reset_call); -	if (ap_using_interrupts()) { -		s390_unregister_adapter_interrupt(ap_interrupt_indicator, AP_ISC); -		isc_unregister(AP_ISC); -	} +	if (ap_using_interrupts()) +		unregister_adapter_interrupt(&ap_airq);  	return rc;  } @@ -1730,13 +2041,9 @@ void ap_module_exit(void)  		bus_remove_file(&ap_bus_type, ap_bus_attrs[i]);  	bus_unregister(&ap_bus_type);  	unregister_reset_call(&ap_reset_call); -	if (ap_using_interrupts()) { -		s390_unregister_adapter_interrupt(ap_interrupt_indicator, AP_ISC); -		isc_unregister(AP_ISC); -	} +	if (ap_using_interrupts()) +		unregister_adapter_interrupt(&ap_airq);  } -#ifndef CONFIG_ZCRYPT_MONOLITHIC  module_init(ap_module_init);  module_exit(ap_module_exit); -#endif  | 
