s390/ap: configuration information exploitation

Query AP configuration information. Improve performance of AP bus
scans by skipping AP device probing, if the AP deviec is not
configured.

Reviewed-by: Ingo Tuchscherer <ingo.tuchscherer@linux.vnet.ibm.com>
Signed-off-by: Holger Dengler <hd@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
This commit is contained in:
Holger Dengler 2012-08-28 16:41:50 +02:00 committed by Martin Schwidefsky
parent 48a8ca03f8
commit 7501455051
2 changed files with 128 additions and 15 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright IBM Corp. 2006 * Copyright IBM Corp. 2006, 2012
* Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
* Martin Schwidefsky <schwidefsky@de.ibm.com> * Martin Schwidefsky <schwidefsky@de.ibm.com>
* Ralph Wuerthner <rwuerthn@de.ibm.com> * Ralph Wuerthner <rwuerthn@de.ibm.com>
@ -62,13 +62,14 @@ static void ap_interrupt_handler(void *unused1, void *unused2);
static void ap_reset(struct ap_device *ap_dev); static void ap_reset(struct ap_device *ap_dev);
static void ap_config_timeout(unsigned long ptr); static void ap_config_timeout(unsigned long ptr);
static int ap_select_domain(void); static int ap_select_domain(void);
static void ap_query_configuration(void);
/* /*
* Module description. * Module description.
*/ */
MODULE_AUTHOR("IBM Corporation"); MODULE_AUTHOR("IBM Corporation");
MODULE_DESCRIPTION("Adjunct Processor Bus driver, " MODULE_DESCRIPTION("Adjunct Processor Bus driver, " \
"Copyright IBM Corp. 2006"); "Copyright IBM Corp. 2006, 2012");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
/* /*
@ -84,6 +85,7 @@ module_param_named(poll_thread, ap_thread_flag, int, 0000);
MODULE_PARM_DESC(poll_thread, "Turn on/off poll thread, default is 0 (off)."); MODULE_PARM_DESC(poll_thread, "Turn on/off poll thread, default is 0 (off).");
static struct device *ap_root_device = NULL; static struct device *ap_root_device = NULL;
static struct ap_config_info *ap_configuration;
static DEFINE_SPINLOCK(ap_device_list_lock); static DEFINE_SPINLOCK(ap_device_list_lock);
static LIST_HEAD(ap_device_list); static LIST_HEAD(ap_device_list);
@ -157,6 +159,17 @@ static int ap_interrupts_available(void)
return test_facility(2) && test_facility(65); 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.
*/
static int ap_configuration_available(void)
{
return test_facility(2) && test_facility(12);
}
/** /**
* ap_test_queue(): Test adjunct processor queue. * ap_test_queue(): Test adjunct processor queue.
* @qid: The AP queue number * @qid: The AP queue number
@ -242,6 +255,26 @@ __ap_query_functions(ap_qid_t qid, unsigned int *functions)
} }
#endif #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. * ap_query_functions(): Query supported functions.
* @qid: The AP queue number * @qid: The AP queue number
@ -305,8 +338,8 @@ int ap_4096_commands_available(ap_qid_t qid)
if (ap_query_functions(qid, &functions)) if (ap_query_functions(qid, &functions))
return 0; return 0;
return test_ap_facility(functions, 1) && return ap_test_bit(&functions, 1) &&
test_ap_facility(functions, 2); ap_test_bit(&functions, 2);
} }
EXPORT_SYMBOL(ap_4096_commands_available); EXPORT_SYMBOL(ap_4096_commands_available);
@ -772,6 +805,7 @@ static int ap_bus_resume(struct device *dev)
ap_suspend_flag = 0; ap_suspend_flag = 0;
if (!ap_interrupts_available()) if (!ap_interrupts_available())
ap_interrupt_indicator = NULL; ap_interrupt_indicator = NULL;
ap_query_configuration();
if (!user_set_domain) { if (!user_set_domain) {
ap_domain_index = -1; ap_domain_index = -1;
ap_select_domain(); ap_select_domain();
@ -997,6 +1031,65 @@ static struct bus_attribute *const ap_bus_attrs[] = {
NULL, 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. * ap_select_domain(): Select an AP domain.
* *
@ -1005,6 +1098,7 @@ static struct bus_attribute *const ap_bus_attrs[] = {
static int ap_select_domain(void) static int ap_select_domain(void)
{ {
int queue_depth, device_type, count, max_count, best_domain; int queue_depth, device_type, count, max_count, best_domain;
ap_qid_t qid;
int rc, i, j; int rc, i, j;
/* /*
@ -1018,9 +1112,13 @@ static int ap_select_domain(void)
best_domain = -1; best_domain = -1;
max_count = 0; max_count = 0;
for (i = 0; i < AP_DOMAINS; i++) { for (i = 0; i < AP_DOMAINS; i++) {
if (!ap_test_config_domain(i))
continue;
count = 0; count = 0;
for (j = 0; j < AP_DEVICES; j++) { 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); rc = ap_query_queue(qid, &queue_depth, &device_type);
if (rc) if (rc)
continue; continue;
@ -1169,6 +1267,7 @@ static void ap_scan_bus(struct work_struct *unused)
unsigned int device_functions; unsigned int device_functions;
int rc, i; int rc, i;
ap_query_configuration();
if (ap_select_domain() != 0) if (ap_select_domain() != 0)
return; return;
for (i = 0; i < AP_DEVICES; i++) { for (i = 0; i < AP_DEVICES; i++) {
@ -1176,7 +1275,10 @@ static void ap_scan_bus(struct work_struct *unused)
dev = bus_find_device(&ap_bus_type, NULL, dev = bus_find_device(&ap_bus_type, NULL,
(void *)(unsigned long)qid, (void *)(unsigned long)qid,
__ap_scan_bus); __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 (dev) {
if (rc == -EBUSY) { if (rc == -EBUSY) {
set_current_state(TASK_UNINTERRUPTIBLE); set_current_state(TASK_UNINTERRUPTIBLE);
@ -1227,9 +1329,9 @@ static void ap_scan_bus(struct work_struct *unused)
kfree(ap_dev); kfree(ap_dev);
continue; continue;
} }
if (test_ap_facility(device_functions, 3)) if (ap_test_bit(&device_functions, 3))
ap_dev->device_type = AP_DEVICE_TYPE_CEX3C; ap_dev->device_type = AP_DEVICE_TYPE_CEX3C;
else if (test_ap_facility(device_functions, 4)) else if (ap_test_bit(&device_functions, 4))
ap_dev->device_type = AP_DEVICE_TYPE_CEX3A; ap_dev->device_type = AP_DEVICE_TYPE_CEX3A;
else { else {
kfree(ap_dev); kfree(ap_dev);
@ -1785,6 +1887,7 @@ int __init ap_module_init(void)
goto out_root; goto out_root;
} }
ap_query_configuration();
if (ap_select_domain() == 0) if (ap_select_domain() == 0)
ap_scan_bus(NULL); ap_scan_bus(NULL);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright IBM Corp. 2006 * Copyright IBM Corp. 2006, 2012
* Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
* Martin Schwidefsky <schwidefsky@de.ibm.com> * Martin Schwidefsky <schwidefsky@de.ibm.com>
* Ralph Wuerthner <rwuerthn@de.ibm.com> * Ralph Wuerthner <rwuerthn@de.ibm.com>
@ -83,13 +83,12 @@ int ap_queue_status_invalid_test(struct ap_queue_status *status)
return !(memcmp(status, &invalid, sizeof(struct ap_queue_status))); return !(memcmp(status, &invalid, sizeof(struct ap_queue_status)));
} }
#define MAX_AP_FACILITY 31 #define AP_MAX_BITS 31
static inline int ap_test_bit(unsigned int *ptr, unsigned int nr)
static inline int test_ap_facility(unsigned int function, unsigned int nr)
{ {
if (nr > MAX_AP_FACILITY) if (nr > AP_MAX_BITS)
return 0; return 0;
return function & (unsigned int)(0x80000000 >> nr); return (*ptr & (0x80000000u >> nr)) != 0;
} }
#define AP_RESPONSE_NORMAL 0x00 #define AP_RESPONSE_NORMAL 0x00
@ -183,6 +182,17 @@ struct ap_message {
struct ap_message *); struct ap_message *);
}; };
struct ap_config_info {
unsigned int special_command:1;
unsigned int ap_extended:1;
unsigned char reserved1:6;
unsigned char reserved2[15];
unsigned int apm[8]; /* AP ID mask */
unsigned int aqm[8]; /* AP queue mask */
unsigned int adm[8]; /* AP domain mask */
unsigned char reserved4[16];
} __packed;
#define AP_DEVICE(dt) \ #define AP_DEVICE(dt) \
.dev_type=(dt), \ .dev_type=(dt), \
.match_flags=AP_DEVICE_ID_MATCH_DEVICE_TYPE, .match_flags=AP_DEVICE_ID_MATCH_DEVICE_TYPE,