summaryrefslogtreecommitdiffstats
path: root/drivers/s390/cio
diff options
context:
space:
mode:
authorHeiko Carstens <heiko.carstens@de.ibm.com>2009-03-26 15:24:09 +0100
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2009-03-26 15:24:13 +0100
commit98c1c6825247c71e3d8a9a5439ba21fce7563014 (patch)
tree6e2311aff5eefba2aaad2f09b1c11b7b2b0eceae /drivers/s390/cio
parente74fe0cec92439115630b51195444b89b910800a (diff)
[S390] cio/crw: add/fix locking
The crw_unregister_handler uses xchg + synchronize_sched when unregistering a crw_handler. This doesn't protect crw_collect_info to potentially jump to NULL since it has unlocked code like this: if (crw_handlers[i]) crw_handlers[i](NULL, NULL, 1); So add a mutex which protects the crw handler array for changes. Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390/cio')
-rw-r--r--drivers/s390/cio/crw.c32
1 files changed, 23 insertions, 9 deletions
diff --git a/drivers/s390/cio/crw.c b/drivers/s390/cio/crw.c
index 508f88f6420..d157665d0e7 100644
--- a/drivers/s390/cio/crw.c
+++ b/drivers/s390/cio/crw.c
@@ -9,11 +9,13 @@
*/
#include <linux/semaphore.h>
+#include <linux/mutex.h>
#include <linux/kthread.h>
#include <linux/init.h>
#include <asm/crw.h>
static struct semaphore crw_semaphore;
+static DEFINE_MUTEX(crw_handler_mutex);
static crw_handler_t crw_handlers[NR_RSCS];
/**
@@ -25,11 +27,17 @@ static crw_handler_t crw_handlers[NR_RSCS];
*/
int crw_register_handler(int rsc, crw_handler_t handler)
{
+ int rc = 0;
+
if ((rsc < 0) || (rsc >= NR_RSCS))
return -EINVAL;
- if (!cmpxchg(&crw_handlers[rsc], NULL, handler))
- return 0;
- return -EBUSY;
+ mutex_lock(&crw_handler_mutex);
+ if (crw_handlers[rsc])
+ rc = -EBUSY;
+ else
+ crw_handlers[rsc] = handler;
+ mutex_unlock(&crw_handler_mutex);
+ return rc;
}
/**
@@ -40,8 +48,9 @@ void crw_unregister_handler(int rsc)
{
if ((rsc < 0) || (rsc >= NR_RSCS))
return;
- xchg(&crw_handlers[rsc], NULL);
- synchronize_sched();
+ mutex_lock(&crw_handler_mutex);
+ crw_handlers[rsc] = NULL;
+ mutex_unlock(&crw_handler_mutex);
}
/*
@@ -58,6 +67,8 @@ repeat:
ignore = down_interruptible(&crw_semaphore);
chain = 0;
while (1) {
+ crw_handler_t handler;
+
if (unlikely(chain > 1)) {
struct crw tmp_crw;
@@ -90,10 +101,12 @@ repeat:
int i;
pr_debug("%s: crw overflow detected!\n", __func__);
+ mutex_lock(&crw_handler_mutex);
for (i = 0; i < NR_RSCS; i++) {
if (crw_handlers[i])
crw_handlers[i](NULL, NULL, 1);
}
+ mutex_unlock(&crw_handler_mutex);
chain = 0;
continue;
}
@@ -101,10 +114,11 @@ repeat:
chain++;
continue;
}
- if (crw_handlers[crw[chain].rsc])
- crw_handlers[crw[chain].rsc](&crw[0],
- chain ? &crw[1] : NULL,
- 0);
+ mutex_lock(&crw_handler_mutex);
+ handler = crw_handlers[crw[chain].rsc];
+ if (handler)
+ handler(&crw[0], chain ? &crw[1] : NULL, 0);
+ mutex_unlock(&crw_handler_mutex);
/* chain is always 0 or 1 here. */
chain = crw[chain].chn ? chain + 1 : 0;
}