aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390/cio
diff options
context:
space:
mode:
authorPeter Oberparleiter <peter.oberparleiter@de.ibm.com>2008-01-26 08:10:44 -0500
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2008-01-26 08:11:00 -0500
commit4e8e56c6713398f417317d449f50c08bf2756c66 (patch)
tree61c87e58f13faa93de725120cedb2540f058ae32 /drivers/s390/cio
parentcd6b4f27b9bb2a6a5ec82b96b87c85421257be6c (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/cio')
-rw-r--r--drivers/s390/cio/airq.c171
-rw-r--r--drivers/s390/cio/airq.h10
-rw-r--r--drivers/s390/cio/cio.c2
-rw-r--r--drivers/s390/cio/cio.h1
-rw-r--r--drivers/s390/cio/qdio.c35
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
20static 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/* 26union 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 */
33int
34s390_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"); 31struct airq_t {
32 adapter_int_handler_t handler;
33 void *drv_data;
34};
40 35
41 if (handler == NULL) 36static union indicator_t indicators;
42 ret = -EINVAL; 37static 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); 39static 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
54int 49/**
55s390_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 */
58void *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);
75out:
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}
83EXPORT_SYMBOL(s390_register_adapter_interrupt);
74 84
75void 85/**
76do_adapter_IO (void) 86 * s390_unregister_adapter_interrupt - unregister adapter interrupt handler
87 * @ind: indicator for which the handler is to be unregistered
88 */
89void 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}
107EXPORT_SYMBOL(s390_unregister_adapter_interrupt);
108
109#define INDICATOR_MASK (0xffUL << ((NR_AIRQS_PER_WORD - 1) * 8))
83 110
84EXPORT_SYMBOL (s390_register_adapter_interrupt); 111void do_adapter_IO(void)
85EXPORT_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
4typedef int (*adapter_int_handler_t)(void);
5
6extern int s390_register_adapter_interrupt(adapter_int_handler_t handler);
7extern int s390_unregister_adapter_interrupt(adapter_int_handler_t handler);
8extern 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 *);
99extern int cio_modify (struct subchannel *); 99extern int cio_modify (struct subchannel *);
100 100
101int cio_create_sch_lock(struct subchannel *); 101int cio_create_sch_lock(struct subchannel *);
102void 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;
96static volatile struct qdio_q *tiq_list=NULL; /* volatile as it could change 96static volatile struct qdio_q *tiq_list=NULL; /* volatile as it could change
97 during a while loop */ 97 during a while loop */
98static DEFINE_SPINLOCK(ttiq_list_lock); 98static DEFINE_SPINLOCK(ttiq_list_lock);
99static int register_thinint_result; 99static void *tiqdio_ind;
100static void tiqdio_tl(unsigned long); 100static void tiqdio_tl(unsigned long);
101static DECLARE_TASKLET(tiqdio_tasklet,tiqdio_tl,0); 101static 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
1914static int 1914static void tiqdio_thinint_handler(void *ind, void *drv_data)
1915tiqdio_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
1931static void 1929static 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
3740tiqdio_register_thinints(void) 3738tiqdio_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
3755static void 3755static void
3756tiqdio_unregister_thinints(void) 3756tiqdio_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
3762static int 3762static 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
3784static void 3783static void
3785qdio_unregister_dbf_views(void) 3784qdio_unregister_dbf_views(void)
3786{ 3785{