Commit 390935ac authored by Peter Oberparleiter's avatar Peter Oberparleiter Committed by Martin Schwidefsky

[S390] cio: introduce subchannel todos

Ensure that current and future users of sch->work do not overwrite
each other by introducing a single mechanism for delayed subchannel
work.
Signed-off-by: default avatarPeter Oberparleiter <peter.oberparleiter@de.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 5d6e6b6f
...@@ -68,6 +68,11 @@ struct schib { ...@@ -68,6 +68,11 @@ struct schib {
__u8 mda[4]; /* model dependent area */ __u8 mda[4]; /* model dependent area */
} __attribute__ ((packed,aligned(4))); } __attribute__ ((packed,aligned(4)));
enum sch_todo {
SCH_TODO_NOTHING,
SCH_TODO_UNREG,
};
/* subchannel data structure used by I/O subroutines */ /* subchannel data structure used by I/O subroutines */
struct subchannel { struct subchannel {
struct subchannel_id schid; struct subchannel_id schid;
...@@ -95,7 +100,8 @@ struct subchannel { ...@@ -95,7 +100,8 @@ struct subchannel {
struct device dev; /* entry in device tree */ struct device dev; /* entry in device tree */
struct css_driver *driver; struct css_driver *driver;
void *private; /* private per subchannel type data */ void *private; /* private per subchannel type data */
struct work_struct work; enum sch_todo todo;
struct work_struct todo_work;
struct schib_config config; struct schib_config config;
} __attribute__ ((aligned(8))); } __attribute__ ((aligned(8)));
......
...@@ -133,6 +133,8 @@ int for_each_subchannel_staged(int (*fn_known)(struct subchannel *, void *), ...@@ -133,6 +133,8 @@ int for_each_subchannel_staged(int (*fn_known)(struct subchannel *, void *),
return rc; return rc;
} }
static void css_sch_todo(struct work_struct *work);
static struct subchannel * static struct subchannel *
css_alloc_subchannel(struct subchannel_id schid) css_alloc_subchannel(struct subchannel_id schid)
{ {
...@@ -147,6 +149,7 @@ css_alloc_subchannel(struct subchannel_id schid) ...@@ -147,6 +149,7 @@ css_alloc_subchannel(struct subchannel_id schid)
kfree(sch); kfree(sch);
return ERR_PTR(ret); return ERR_PTR(ret);
} }
INIT_WORK(&sch->todo_work, css_sch_todo);
return sch; return sch;
} }
...@@ -190,6 +193,51 @@ void css_sch_device_unregister(struct subchannel *sch) ...@@ -190,6 +193,51 @@ void css_sch_device_unregister(struct subchannel *sch)
} }
EXPORT_SYMBOL_GPL(css_sch_device_unregister); EXPORT_SYMBOL_GPL(css_sch_device_unregister);
static void css_sch_todo(struct work_struct *work)
{
struct subchannel *sch;
enum sch_todo todo;
sch = container_of(work, struct subchannel, todo_work);
/* Find out todo. */
spin_lock_irq(sch->lock);
todo = sch->todo;
CIO_MSG_EVENT(4, "sch_todo: sch=0.%x.%04x, todo=%d\n", sch->schid.ssid,
sch->schid.sch_no, todo);
sch->todo = SCH_TODO_NOTHING;
spin_unlock_irq(sch->lock);
/* Perform todo. */
if (todo == SCH_TODO_UNREG)
css_sch_device_unregister(sch);
/* Release workqueue ref. */
put_device(&sch->dev);
}
/**
* css_sched_sch_todo - schedule a subchannel operation
* @sch: subchannel
* @todo: todo
*
* Schedule the operation identified by @todo to be performed on the slow path
* workqueue. Do nothing if another operation with higher priority is already
* scheduled. Needs to be called with subchannel lock held.
*/
void css_sched_sch_todo(struct subchannel *sch, enum sch_todo todo)
{
CIO_MSG_EVENT(4, "sch_todo: sched sch=0.%x.%04x todo=%d\n",
sch->schid.ssid, sch->schid.sch_no, todo);
if (sch->todo >= todo)
return;
/* Get workqueue ref. */
if (!get_device(&sch->dev))
return;
sch->todo = todo;
if (!queue_work(slow_path_wq, &sch->todo_work)) {
/* Already queued, release workqueue ref. */
put_device(&sch->dev);
}
}
static void ssd_from_pmcw(struct chsc_ssd_info *ssd, struct pmcw *pmcw) static void ssd_from_pmcw(struct chsc_ssd_info *ssd, struct pmcw *pmcw)
{ {
int i; int i;
......
...@@ -11,6 +11,8 @@ ...@@ -11,6 +11,8 @@
#include <asm/chpid.h> #include <asm/chpid.h>
#include <asm/schid.h> #include <asm/schid.h>
#include "cio.h"
/* /*
* path grouping stuff * path grouping stuff
*/ */
...@@ -151,4 +153,5 @@ int css_sch_is_valid(struct schib *); ...@@ -151,4 +153,5 @@ int css_sch_is_valid(struct schib *);
extern struct workqueue_struct *slow_path_wq; extern struct workqueue_struct *slow_path_wq;
void css_wait_for_slow_path(void); void css_wait_for_slow_path(void);
void css_sched_sch_todo(struct subchannel *sch, enum sch_todo todo);
#endif #endif
...@@ -1051,23 +1051,6 @@ static void io_subchannel_init_fields(struct subchannel *sch) ...@@ -1051,23 +1051,6 @@ static void io_subchannel_init_fields(struct subchannel *sch)
io_subchannel_init_config(sch); io_subchannel_init_config(sch);
} }
static void io_subchannel_do_unreg(struct work_struct *work)
{
struct subchannel *sch;
sch = container_of(work, struct subchannel, work);
css_sch_device_unregister(sch);
put_device(&sch->dev);
}
/* Schedule unregister if we have no cdev. */
static void io_subchannel_schedule_removal(struct subchannel *sch)
{
get_device(&sch->dev);
INIT_WORK(&sch->work, io_subchannel_do_unreg);
queue_work(slow_path_wq, &sch->work);
}
/* /*
* Note: We always return 0 so that we bind to the device even on error. * Note: We always return 0 so that we bind to the device even on error.
* This is needed so that our remove function is called on unregister. * This is needed so that our remove function is called on unregister.
...@@ -1124,7 +1107,9 @@ static int io_subchannel_probe(struct subchannel *sch) ...@@ -1124,7 +1107,9 @@ static int io_subchannel_probe(struct subchannel *sch)
return 0; return 0;
out_schedule: out_schedule:
io_subchannel_schedule_removal(sch); spin_lock_irq(sch->lock);
css_sched_sch_todo(sch, SCH_TODO_UNREG);
spin_unlock_irq(sch->lock);
return 0; return 0;
} }
...@@ -1469,6 +1454,8 @@ static int io_subchannel_sch_event(struct subchannel *sch, int process) ...@@ -1469,6 +1454,8 @@ static int io_subchannel_sch_event(struct subchannel *sch, int process)
spin_lock_irqsave(sch->lock, flags); spin_lock_irqsave(sch->lock, flags);
if (!device_is_registered(&sch->dev)) if (!device_is_registered(&sch->dev))
goto out_unlock; goto out_unlock;
if (work_pending(&sch->todo_work))
goto out_unlock;
action = sch_get_action(sch); action = sch_get_action(sch);
CIO_MSG_EVENT(2, "event: sch 0.%x.%04x, process=%d, action=%d\n", CIO_MSG_EVENT(2, "event: sch 0.%x.%04x, process=%d, action=%d\n",
sch->schid.ssid, sch->schid.sch_no, process, sch->schid.ssid, sch->schid.sch_no, process,
......
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