Commit 4475aeb8 authored by Sebastian Ott's avatar Sebastian Ott Committed by Martin Schwidefsky

s390/cio: fix premature wakeup during chp configure

We store requests for channel path configure operations in an array but
maintain an additional cfg_busy variable (indicating if we have requests
stored in said array). When 2 tasks request a channel path configure
operation cfg_busy could be set to 0 even if we still have unprocessed
requests. This would lead to the second task being woken up although its
request was not processed yet.

Fix that by getting rid of cfg_busy and use the chp_cfg_task array
in the wake up condition.
Signed-off-by: default avatarSebastian Ott <sebott@linux.vnet.ibm.com>
Reviewed-by: default avatarPeter Oberparleiter <oberpar@linux.vnet.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 837c5220
......@@ -38,7 +38,6 @@ enum cfg_task_t {
/* Map for pending configure tasks. */
static enum cfg_task_t chp_cfg_task[__MAX_CSSID + 1][__MAX_CHPID + 1];
static DEFINE_SPINLOCK(cfg_lock);
static int cfg_busy;
/* Map for channel-path status. */
static struct sclp_chp_info chp_info;
......@@ -666,6 +665,20 @@ static void cfg_set_task(struct chp_id chpid, enum cfg_task_t cfg)
chp_cfg_task[chpid.cssid][chpid.id] = cfg;
}
/* Fetch the first configure task. Set chpid accordingly. */
static enum cfg_task_t chp_cfg_fetch_task(struct chp_id *chpid)
{
enum cfg_task_t t = cfg_none;
chp_id_for_each(chpid) {
t = cfg_get_task(*chpid);
if (t != cfg_none)
break;
}
return t;
}
/* Perform one configure/deconfigure request. Reschedule work function until
* last request. */
static void cfg_func(struct work_struct *work)
......@@ -675,14 +688,7 @@ static void cfg_func(struct work_struct *work)
int rc;
spin_lock(&cfg_lock);
t = cfg_none;
chp_id_for_each(&chpid) {
t = cfg_get_task(chpid);
if (t != cfg_none) {
cfg_set_task(chpid, cfg_none);
break;
}
}
t = chp_cfg_fetch_task(&chpid);
spin_unlock(&cfg_lock);
switch (t) {
......@@ -709,12 +715,13 @@ static void cfg_func(struct work_struct *work)
case cfg_none:
/* Get updated information after last change. */
info_update();
spin_lock(&cfg_lock);
cfg_busy = 0;
spin_unlock(&cfg_lock);
wake_up_interruptible(&cfg_wait_queue);
return;
}
spin_lock(&cfg_lock);
if (t == cfg_get_task(chpid))
cfg_set_task(chpid, cfg_none);
spin_unlock(&cfg_lock);
schedule_work(&cfg_work);
}
......@@ -731,7 +738,6 @@ void chp_cfg_schedule(struct chp_id chpid, int configure)
configure);
spin_lock(&cfg_lock);
cfg_set_task(chpid, configure ? cfg_configure : cfg_deconfigure);
cfg_busy = 1;
spin_unlock(&cfg_lock);
schedule_work(&cfg_work);
}
......@@ -752,9 +758,21 @@ void chp_cfg_cancel_deconfigure(struct chp_id chpid)
spin_unlock(&cfg_lock);
}
static bool cfg_idle(void)
{
struct chp_id chpid;
enum cfg_task_t t;
spin_lock(&cfg_lock);
t = chp_cfg_fetch_task(&chpid);
spin_unlock(&cfg_lock);
return t == cfg_none;
}
static int cfg_wait_idle(void)
{
if (wait_event_interruptible(cfg_wait_queue, !cfg_busy))
if (wait_event_interruptible(cfg_wait_queue, cfg_idle()))
return -ERESTARTSYS;
return 0;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment