diff options
author | Peter Oberparleiter <peter.oberparleiter@de.ibm.com> | 2008-01-26 08:10:44 -0500 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2008-01-26 08:11:00 -0500 |
commit | 4e8e56c6713398f417317d449f50c08bf2756c66 (patch) | |
tree | 61c87e58f13faa93de725120cedb2540f058ae32 /drivers/s390 | |
parent | cd6b4f27b9bb2a6a5ec82b96b87c85421257be6c (diff) |
[S390] cio: Extend adapter interrupt interface.
From: Cornelia Huck <cornelia.huck@de.ibm.com>
Change the adapter interrupt interface in order to allow multiple
adapter interrupt handlers to be registered. Indicators are now
allocated by cio instead of the device driver.
The qdio parts have been
Acked-by: Ursula Braun <ubraun@linux.vnet.ibm.com>
Signed-off-by: Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390')
-rw-r--r-- | drivers/s390/cio/airq.c | 171 | ||||
-rw-r--r-- | drivers/s390/cio/airq.h | 10 | ||||
-rw-r--r-- | drivers/s390/cio/cio.c | 2 | ||||
-rw-r--r-- | drivers/s390/cio/cio.h | 1 | ||||
-rw-r--r-- | drivers/s390/cio/qdio.c | 35 |
5 files changed, 134 insertions, 85 deletions
diff --git a/drivers/s390/cio/airq.c b/drivers/s390/cio/airq.c index 5287631fbfc8..b7a07a866291 100644 --- a/drivers/s390/cio/airq.c +++ b/drivers/s390/cio/airq.c | |||
@@ -1,12 +1,12 @@ | |||
1 | /* | 1 | /* |
2 | * drivers/s390/cio/airq.c | 2 | * drivers/s390/cio/airq.c |
3 | * S/390 common I/O routines -- support for adapter interruptions | 3 | * Support for adapter interruptions |
4 | * | 4 | * |
5 | * Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH, | 5 | * Copyright IBM Corp. 1999,2007 |
6 | * IBM Corporation | 6 | * Author(s): Ingo Adlung <adlung@de.ibm.com> |
7 | * Author(s): Ingo Adlung (adlung@de.ibm.com) | 7 | * Cornelia Huck <cornelia.huck@de.ibm.com> |
8 | * Cornelia Huck (cornelia.huck@de.ibm.com) | 8 | * Arnd Bergmann <arndb@de.ibm.com> |
9 | * Arnd Bergmann (arndb@de.ibm.com) | 9 | * Peter Oberparleiter <peter.oberparleiter@de.ibm.com> |
10 | */ | 10 | */ |
11 | 11 | ||
12 | #include <linux/init.h> | 12 | #include <linux/init.h> |
@@ -14,72 +14,131 @@ | |||
14 | #include <linux/slab.h> | 14 | #include <linux/slab.h> |
15 | #include <linux/rcupdate.h> | 15 | #include <linux/rcupdate.h> |
16 | 16 | ||
17 | #include <asm/airq.h> | ||
18 | |||
19 | #include "cio.h" | ||
17 | #include "cio_debug.h" | 20 | #include "cio_debug.h" |
18 | #include "airq.h" | ||
19 | 21 | ||
20 | static adapter_int_handler_t adapter_handler; | 22 | #define NR_AIRQS 32 |
23 | #define NR_AIRQS_PER_WORD sizeof(unsigned long) | ||
24 | #define NR_AIRQ_WORDS (NR_AIRQS / NR_AIRQS_PER_WORD) | ||
21 | 25 | ||
22 | /* | 26 | union indicator_t { |
23 | * register for adapter interrupts | 27 | unsigned long word[NR_AIRQ_WORDS]; |
24 | * | 28 | unsigned char byte[NR_AIRQS]; |
25 | * With HiperSockets the zSeries architecture provides for | 29 | } __attribute__((packed)); |
26 | * means of adapter interrups, pseudo I/O interrupts that are | ||
27 | * not tied to an I/O subchannel, but to an adapter. However, | ||
28 | * it doesn't disclose the info how to enable/disable them, but | ||
29 | * to recognize them only. Perhaps we should consider them | ||
30 | * being shared interrupts, and thus build a linked list | ||
31 | * of adapter handlers ... to be evaluated ... | ||
32 | */ | ||
33 | int | ||
34 | s390_register_adapter_interrupt (adapter_int_handler_t handler) | ||
35 | { | ||
36 | int ret; | ||
37 | char dbf_txt[15]; | ||
38 | 30 | ||
39 | CIO_TRACE_EVENT (4, "rgaint"); | 31 | struct airq_t { |
32 | adapter_int_handler_t handler; | ||
33 | void *drv_data; | ||
34 | }; | ||
40 | 35 | ||
41 | if (handler == NULL) | 36 | static union indicator_t indicators; |
42 | ret = -EINVAL; | 37 | static struct airq_t *airqs[NR_AIRQS]; |
43 | else | ||
44 | ret = (cmpxchg(&adapter_handler, NULL, handler) ? -EBUSY : 0); | ||
45 | if (!ret) | ||
46 | synchronize_sched(); /* Allow interrupts to complete. */ | ||
47 | 38 | ||
48 | sprintf (dbf_txt, "ret:%d", ret); | 39 | static int register_airq(struct airq_t *airq) |
49 | CIO_TRACE_EVENT (4, dbf_txt); | 40 | { |
41 | int i; | ||
50 | 42 | ||
51 | return ret; | 43 | for (i = 0; i < NR_AIRQS; i++) |
44 | if (!cmpxchg(&airqs[i], NULL, airq)) | ||
45 | return i; | ||
46 | return -ENOMEM; | ||
52 | } | 47 | } |
53 | 48 | ||
54 | int | 49 | /** |
55 | s390_unregister_adapter_interrupt (adapter_int_handler_t handler) | 50 | * s390_register_adapter_interrupt() - register adapter interrupt handler |
51 | * @handler: adapter handler to be registered | ||
52 | * @drv_data: driver data passed with each call to the handler | ||
53 | * | ||
54 | * Returns: | ||
55 | * Pointer to the indicator to be used on success | ||
56 | * ERR_PTR() if registration failed | ||
57 | */ | ||
58 | void *s390_register_adapter_interrupt(adapter_int_handler_t handler, | ||
59 | void *drv_data) | ||
56 | { | 60 | { |
61 | struct airq_t *airq; | ||
62 | char dbf_txt[16]; | ||
57 | int ret; | 63 | int ret; |
58 | char dbf_txt[15]; | ||
59 | 64 | ||
60 | CIO_TRACE_EVENT (4, "urgaint"); | 65 | airq = kmalloc(sizeof(struct airq_t), GFP_KERNEL); |
61 | 66 | if (!airq) { | |
62 | if (handler == NULL) | 67 | ret = -ENOMEM; |
63 | ret = -EINVAL; | 68 | goto out; |
64 | else { | ||
65 | adapter_handler = NULL; | ||
66 | synchronize_sched(); /* Allow interrupts to complete. */ | ||
67 | ret = 0; | ||
68 | } | 69 | } |
69 | sprintf (dbf_txt, "ret:%d", ret); | 70 | airq->handler = handler; |
70 | CIO_TRACE_EVENT (4, dbf_txt); | 71 | airq->drv_data = drv_data; |
71 | 72 | ret = register_airq(airq); | |
72 | return ret; | 73 | if (ret < 0) |
74 | kfree(airq); | ||
75 | out: | ||
76 | snprintf(dbf_txt, sizeof(dbf_txt), "rairq:%d", ret); | ||
77 | CIO_TRACE_EVENT(4, dbf_txt); | ||
78 | if (ret < 0) | ||
79 | return ERR_PTR(ret); | ||
80 | else | ||
81 | return &indicators.byte[ret]; | ||
73 | } | 82 | } |
83 | EXPORT_SYMBOL(s390_register_adapter_interrupt); | ||
74 | 84 | ||
75 | void | 85 | /** |
76 | do_adapter_IO (void) | 86 | * s390_unregister_adapter_interrupt - unregister adapter interrupt handler |
87 | * @ind: indicator for which the handler is to be unregistered | ||
88 | */ | ||
89 | void s390_unregister_adapter_interrupt(void *ind) | ||
77 | { | 90 | { |
78 | CIO_TRACE_EVENT (6, "doaio"); | 91 | struct airq_t *airq; |
92 | char dbf_txt[16]; | ||
93 | int i; | ||
79 | 94 | ||
80 | if (adapter_handler) | 95 | i = (int) ((addr_t) ind) - ((addr_t) &indicators.byte[0]); |
81 | (*adapter_handler) (); | 96 | snprintf(dbf_txt, sizeof(dbf_txt), "urairq:%d", i); |
97 | CIO_TRACE_EVENT(4, dbf_txt); | ||
98 | indicators.byte[i] = 0; | ||
99 | airq = xchg(&airqs[i], NULL); | ||
100 | /* | ||
101 | * Allow interrupts to complete. This will ensure that the airq handle | ||
102 | * is no longer referenced by any interrupt handler. | ||
103 | */ | ||
104 | synchronize_sched(); | ||
105 | kfree(airq); | ||
82 | } | 106 | } |
107 | EXPORT_SYMBOL(s390_unregister_adapter_interrupt); | ||
108 | |||
109 | #define INDICATOR_MASK (0xffUL << ((NR_AIRQS_PER_WORD - 1) * 8)) | ||
83 | 110 | ||
84 | EXPORT_SYMBOL (s390_register_adapter_interrupt); | 111 | void do_adapter_IO(void) |
85 | EXPORT_SYMBOL (s390_unregister_adapter_interrupt); | 112 | { |
113 | int w; | ||
114 | int i; | ||
115 | unsigned long word; | ||
116 | struct airq_t *airq; | ||
117 | |||
118 | /* | ||
119 | * Access indicator array in word-sized chunks to minimize storage | ||
120 | * fetch operations. | ||
121 | */ | ||
122 | for (w = 0; w < NR_AIRQ_WORDS; w++) { | ||
123 | word = indicators.word[w]; | ||
124 | i = w * NR_AIRQS_PER_WORD; | ||
125 | /* | ||
126 | * Check bytes within word for active indicators. | ||
127 | */ | ||
128 | while (word) { | ||
129 | if (word & INDICATOR_MASK) { | ||
130 | airq = airqs[i]; | ||
131 | if (likely(airq)) | ||
132 | airq->handler(&indicators.byte[i], | ||
133 | airq->drv_data); | ||
134 | else | ||
135 | /* | ||
136 | * Reset ill-behaved indicator. | ||
137 | */ | ||
138 | indicators.byte[i] = 0; | ||
139 | } | ||
140 | word <<= 8; | ||
141 | i++; | ||
142 | } | ||
143 | } | ||
144 | } | ||
diff --git a/drivers/s390/cio/airq.h b/drivers/s390/cio/airq.h deleted file mode 100644 index 7d6be3fdcd66..000000000000 --- a/drivers/s390/cio/airq.h +++ /dev/null | |||
@@ -1,10 +0,0 @@ | |||
1 | #ifndef S390_AINTERRUPT_H | ||
2 | #define S390_AINTERRUPT_H | ||
3 | |||
4 | typedef int (*adapter_int_handler_t)(void); | ||
5 | |||
6 | extern int s390_register_adapter_interrupt(adapter_int_handler_t handler); | ||
7 | extern int s390_unregister_adapter_interrupt(adapter_int_handler_t handler); | ||
8 | extern void do_adapter_IO (void); | ||
9 | |||
10 | #endif | ||
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index d0bcebde3fa2..89ced3408138 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c | |||
@@ -23,7 +23,7 @@ | |||
23 | #include <asm/reset.h> | 23 | #include <asm/reset.h> |
24 | #include <asm/ipl.h> | 24 | #include <asm/ipl.h> |
25 | #include <asm/chpid.h> | 25 | #include <asm/chpid.h> |
26 | #include "airq.h" | 26 | #include <asm/airq.h> |
27 | #include "cio.h" | 27 | #include "cio.h" |
28 | #include "css.h" | 28 | #include "css.h" |
29 | #include "chsc.h" | 29 | #include "chsc.h" |
diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h index a6ef218defbe..d1483d65049c 100644 --- a/drivers/s390/cio/cio.h +++ b/drivers/s390/cio/cio.h | |||
@@ -99,6 +99,7 @@ extern int cio_get_options (struct subchannel *); | |||
99 | extern int cio_modify (struct subchannel *); | 99 | extern int cio_modify (struct subchannel *); |
100 | 100 | ||
101 | int cio_create_sch_lock(struct subchannel *); | 101 | int cio_create_sch_lock(struct subchannel *); |
102 | void do_adapter_IO(void); | ||
102 | 103 | ||
103 | /* Use with care. */ | 104 | /* Use with care. */ |
104 | #ifdef CONFIG_CCW_CONSOLE | 105 | #ifdef CONFIG_CCW_CONSOLE |
diff --git a/drivers/s390/cio/qdio.c b/drivers/s390/cio/qdio.c index 40a3208c7cf3..7a353db8564f 100644 --- a/drivers/s390/cio/qdio.c +++ b/drivers/s390/cio/qdio.c | |||
@@ -48,11 +48,11 @@ | |||
48 | #include <asm/debug.h> | 48 | #include <asm/debug.h> |
49 | #include <asm/s390_rdev.h> | 49 | #include <asm/s390_rdev.h> |
50 | #include <asm/qdio.h> | 50 | #include <asm/qdio.h> |
51 | #include <asm/airq.h> | ||
51 | 52 | ||
52 | #include "cio.h" | 53 | #include "cio.h" |
53 | #include "css.h" | 54 | #include "css.h" |
54 | #include "device.h" | 55 | #include "device.h" |
55 | #include "airq.h" | ||
56 | #include "qdio.h" | 56 | #include "qdio.h" |
57 | #include "ioasm.h" | 57 | #include "ioasm.h" |
58 | #include "chsc.h" | 58 | #include "chsc.h" |
@@ -96,7 +96,7 @@ static debug_info_t *qdio_dbf_slsb_in; | |||
96 | static volatile struct qdio_q *tiq_list=NULL; /* volatile as it could change | 96 | static volatile struct qdio_q *tiq_list=NULL; /* volatile as it could change |
97 | during a while loop */ | 97 | during a while loop */ |
98 | static DEFINE_SPINLOCK(ttiq_list_lock); | 98 | static DEFINE_SPINLOCK(ttiq_list_lock); |
99 | static int register_thinint_result; | 99 | static void *tiqdio_ind; |
100 | static void tiqdio_tl(unsigned long); | 100 | static void tiqdio_tl(unsigned long); |
101 | static DECLARE_TASKLET(tiqdio_tasklet,tiqdio_tl,0); | 101 | static DECLARE_TASKLET(tiqdio_tasklet,tiqdio_tl,0); |
102 | 102 | ||
@@ -399,7 +399,7 @@ qdio_get_indicator(void) | |||
399 | { | 399 | { |
400 | int i; | 400 | int i; |
401 | 401 | ||
402 | for (i=1;i<INDICATORS_PER_CACHELINE;i++) | 402 | for (i = 0; i < INDICATORS_PER_CACHELINE; i++) |
403 | if (!indicator_used[i]) { | 403 | if (!indicator_used[i]) { |
404 | indicator_used[i]=1; | 404 | indicator_used[i]=1; |
405 | return indicators+i; | 405 | return indicators+i; |
@@ -1911,8 +1911,7 @@ qdio_fill_thresholds(struct qdio_irq *irq_ptr, | |||
1911 | } | 1911 | } |
1912 | } | 1912 | } |
1913 | 1913 | ||
1914 | static int | 1914 | static void tiqdio_thinint_handler(void *ind, void *drv_data) |
1915 | tiqdio_thinint_handler(void) | ||
1916 | { | 1915 | { |
1917 | QDIO_DBF_TEXT4(0,trace,"thin_int"); | 1916 | QDIO_DBF_TEXT4(0,trace,"thin_int"); |
1918 | 1917 | ||
@@ -1925,7 +1924,6 @@ tiqdio_thinint_handler(void) | |||
1925 | tiqdio_clear_global_summary(); | 1924 | tiqdio_clear_global_summary(); |
1926 | 1925 | ||
1927 | tiqdio_inbound_checks(); | 1926 | tiqdio_inbound_checks(); |
1928 | return 0; | ||
1929 | } | 1927 | } |
1930 | 1928 | ||
1931 | static void | 1929 | static void |
@@ -2445,7 +2443,7 @@ tiqdio_set_subchannel_ind(struct qdio_irq *irq_ptr, int reset_to_zero) | |||
2445 | real_addr_dev_st_chg_ind=0; | 2443 | real_addr_dev_st_chg_ind=0; |
2446 | } else { | 2444 | } else { |
2447 | real_addr_local_summary_bit= | 2445 | real_addr_local_summary_bit= |
2448 | virt_to_phys((volatile void *)indicators); | 2446 | virt_to_phys((volatile void *)tiqdio_ind); |
2449 | real_addr_dev_st_chg_ind= | 2447 | real_addr_dev_st_chg_ind= |
2450 | virt_to_phys((volatile void *)irq_ptr->dev_st_chg_ind); | 2448 | virt_to_phys((volatile void *)irq_ptr->dev_st_chg_ind); |
2451 | } | 2449 | } |
@@ -3740,23 +3738,25 @@ static void | |||
3740 | tiqdio_register_thinints(void) | 3738 | tiqdio_register_thinints(void) |
3741 | { | 3739 | { |
3742 | char dbf_text[20]; | 3740 | char dbf_text[20]; |
3743 | register_thinint_result= | 3741 | |
3744 | s390_register_adapter_interrupt(&tiqdio_thinint_handler); | 3742 | tiqdio_ind = |
3745 | if (register_thinint_result) { | 3743 | s390_register_adapter_interrupt(&tiqdio_thinint_handler, NULL); |
3746 | sprintf(dbf_text,"regthn%x",(register_thinint_result&0xff)); | 3744 | if (IS_ERR(tiqdio_ind)) { |
3745 | sprintf(dbf_text, "regthn%lx", PTR_ERR(tiqdio_ind)); | ||
3747 | QDIO_DBF_TEXT0(0,setup,dbf_text); | 3746 | QDIO_DBF_TEXT0(0,setup,dbf_text); |
3748 | QDIO_PRINT_ERR("failed to register adapter handler " \ | 3747 | QDIO_PRINT_ERR("failed to register adapter handler " \ |
3749 | "(rc=%i).\nAdapter interrupts might " \ | 3748 | "(rc=%li).\nAdapter interrupts might " \ |
3750 | "not work. Continuing.\n", | 3749 | "not work. Continuing.\n", |
3751 | register_thinint_result); | 3750 | PTR_ERR(tiqdio_ind)); |
3751 | tiqdio_ind = NULL; | ||
3752 | } | 3752 | } |
3753 | } | 3753 | } |
3754 | 3754 | ||
3755 | static void | 3755 | static void |
3756 | tiqdio_unregister_thinints(void) | 3756 | tiqdio_unregister_thinints(void) |
3757 | { | 3757 | { |
3758 | if (!register_thinint_result) | 3758 | if (tiqdio_ind) |
3759 | s390_unregister_adapter_interrupt(&tiqdio_thinint_handler); | 3759 | s390_unregister_adapter_interrupt(tiqdio_ind); |
3760 | } | 3760 | } |
3761 | 3761 | ||
3762 | static int | 3762 | static int |
@@ -3768,8 +3768,8 @@ qdio_get_qdio_memory(void) | |||
3768 | for (i=1;i<INDICATORS_PER_CACHELINE;i++) | 3768 | for (i=1;i<INDICATORS_PER_CACHELINE;i++) |
3769 | indicator_used[i]=0; | 3769 | indicator_used[i]=0; |
3770 | indicators = kzalloc(sizeof(__u32)*(INDICATORS_PER_CACHELINE), | 3770 | indicators = kzalloc(sizeof(__u32)*(INDICATORS_PER_CACHELINE), |
3771 | GFP_KERNEL); | 3771 | GFP_KERNEL); |
3772 | if (!indicators) | 3772 | if (!indicators) |
3773 | return -ENOMEM; | 3773 | return -ENOMEM; |
3774 | return 0; | 3774 | return 0; |
3775 | } | 3775 | } |
@@ -3780,7 +3780,6 @@ qdio_release_qdio_memory(void) | |||
3780 | kfree(indicators); | 3780 | kfree(indicators); |
3781 | } | 3781 | } |
3782 | 3782 | ||
3783 | |||
3784 | static void | 3783 | static void |
3785 | qdio_unregister_dbf_views(void) | 3784 | qdio_unregister_dbf_views(void) |
3786 | { | 3785 | { |