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: Sebastian Ott <sebott@linux.vnet.ibm.com>
Reviewed-by: Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
This commit is contained in:
Sebastian Ott 2016-07-19 10:53:35 +02:00 коммит произвёл Martin Schwidefsky
Родитель 837c522055
Коммит 4475aeb8b7
1 изменённых файлов: 32 добавлений и 14 удалений

Просмотреть файл

@ -38,7 +38,6 @@ enum cfg_task_t {
/* Map for pending configure tasks. */ /* Map for pending configure tasks. */
static enum cfg_task_t chp_cfg_task[__MAX_CSSID + 1][__MAX_CHPID + 1]; static enum cfg_task_t chp_cfg_task[__MAX_CSSID + 1][__MAX_CHPID + 1];
static DEFINE_SPINLOCK(cfg_lock); static DEFINE_SPINLOCK(cfg_lock);
static int cfg_busy;
/* Map for channel-path status. */ /* Map for channel-path status. */
static struct sclp_chp_info chp_info; 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; 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 /* Perform one configure/deconfigure request. Reschedule work function until
* last request. */ * last request. */
static void cfg_func(struct work_struct *work) static void cfg_func(struct work_struct *work)
@ -675,14 +688,7 @@ static void cfg_func(struct work_struct *work)
int rc; int rc;
spin_lock(&cfg_lock); spin_lock(&cfg_lock);
t = cfg_none; t = chp_cfg_fetch_task(&chpid);
chp_id_for_each(&chpid) {
t = cfg_get_task(chpid);
if (t != cfg_none) {
cfg_set_task(chpid, cfg_none);
break;
}
}
spin_unlock(&cfg_lock); spin_unlock(&cfg_lock);
switch (t) { switch (t) {
@ -709,12 +715,13 @@ static void cfg_func(struct work_struct *work)
case cfg_none: case cfg_none:
/* Get updated information after last change. */ /* Get updated information after last change. */
info_update(); info_update();
spin_lock(&cfg_lock);
cfg_busy = 0;
spin_unlock(&cfg_lock);
wake_up_interruptible(&cfg_wait_queue); wake_up_interruptible(&cfg_wait_queue);
return; 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); schedule_work(&cfg_work);
} }
@ -731,7 +738,6 @@ void chp_cfg_schedule(struct chp_id chpid, int configure)
configure); configure);
spin_lock(&cfg_lock); spin_lock(&cfg_lock);
cfg_set_task(chpid, configure ? cfg_configure : cfg_deconfigure); cfg_set_task(chpid, configure ? cfg_configure : cfg_deconfigure);
cfg_busy = 1;
spin_unlock(&cfg_lock); spin_unlock(&cfg_lock);
schedule_work(&cfg_work); schedule_work(&cfg_work);
} }
@ -752,9 +758,21 @@ void chp_cfg_cancel_deconfigure(struct chp_id chpid)
spin_unlock(&cfg_lock); 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) 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 -ERESTARTSYS;
return 0; return 0;
} }