Skip to content

Commit

Permalink
[S390] cio: reduce cpu utilization during device scan
Browse files Browse the repository at this point in the history
Minimize calls to cpu intensive function get_subchannel_by_schid()
by introducing function for_each_subchannel_staged() which
temporarily caches the information about registered subchannels
in a bitmap.

Signed-off-by: Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
  • Loading branch information
Peter Oberparleiter authored and Martin Schwidefsky committed Jan 26, 2008
1 parent 4beee64 commit e82a156
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 84 deletions.
98 changes: 32 additions & 66 deletions drivers/s390/cio/chsc.c
Original file line number Diff line number Diff line change
Expand Up @@ -136,17 +136,13 @@ static void terminate_internal_io(struct subchannel *sch)
sch->driver->termination(sch);
}

static int
s390_subchannel_remove_chpid(struct device *dev, void *data)
static int s390_subchannel_remove_chpid(struct subchannel *sch, void *data)
{
int j;
int mask;
struct subchannel *sch;
struct chp_id *chpid;
struct chp_id *chpid = data;
struct schib schib;

sch = to_subchannel(dev);
chpid = data;
for (j = 0; j < 8; j++) {
mask = 0x80 >> j;
if ((sch->schib.pmcw.pim & mask) &&
Expand Down Expand Up @@ -202,12 +198,10 @@ void chsc_chp_offline(struct chp_id chpid)

if (chp_get_status(chpid) <= 0)
return;
bus_for_each_dev(&css_bus_type, NULL, &chpid,
s390_subchannel_remove_chpid);
for_each_subchannel_staged(s390_subchannel_remove_chpid, NULL, &chpid);
}

static int
s390_process_res_acc_new_sch(struct subchannel_id schid)
static int s390_process_res_acc_new_sch(struct subchannel_id schid, void *data)
{
struct schib schib;
/*
Expand Down Expand Up @@ -253,18 +247,10 @@ static int get_res_chpid_mask(struct chsc_ssd_info *ssd,
return 0;
}

static int
__s390_process_res_acc(struct subchannel_id schid, void *data)
static int __s390_process_res_acc(struct subchannel *sch, void *data)
{
int chp_mask, old_lpm;
struct res_acc_data *res_data;
struct subchannel *sch;

res_data = data;
sch = get_subchannel_by_schid(schid);
if (!sch)
/* Check if a subchannel is newly available. */
return s390_process_res_acc_new_sch(schid);
struct res_acc_data *res_data = data;

spin_lock_irq(sch->lock);
chp_mask = get_res_chpid_mask(&sch->ssd_info, res_data);
Expand All @@ -283,7 +269,7 @@ __s390_process_res_acc(struct subchannel_id schid, void *data)
sch->driver->verify(sch);
out:
spin_unlock_irq(sch->lock);
put_device(&sch->dev);

return 0;
}

Expand All @@ -306,7 +292,8 @@ static void s390_process_res_acc (struct res_acc_data *res_data)
* The more information we have (info), the less scanning
* will we have to do.
*/
for_each_subchannel(__s390_process_res_acc, res_data);
for_each_subchannel_staged(__s390_process_res_acc,
s390_process_res_acc_new_sch, res_data);
}

static int
Expand Down Expand Up @@ -500,8 +487,7 @@ void chsc_process_crw(void)
} while (sei_area->flags & 0x80);
}

static int
__chp_add_new_sch(struct subchannel_id schid)
static int __chp_add_new_sch(struct subchannel_id schid, void *data)
{
struct schib schib;

Expand All @@ -515,35 +501,27 @@ __chp_add_new_sch(struct subchannel_id schid)
}


static int
__chp_add(struct subchannel_id schid, void *data)
static int __chp_add(struct subchannel *sch, void *data)
{
int i, mask;
struct chp_id *chpid;
struct subchannel *sch;

chpid = data;
sch = get_subchannel_by_schid(schid);
if (!sch)
/* Check if the subchannel is now available. */
return __chp_add_new_sch(schid);
struct chp_id *chpid = data;

spin_lock_irq(sch->lock);
for (i=0; i<8; i++) {
mask = 0x80 >> i;
if ((sch->schib.pmcw.pim & mask) &&
(sch->schib.pmcw.chpid[i] == chpid->id)) {
if (stsch(sch->schid, &sch->schib) != 0) {
/* Endgame. */
spin_unlock_irq(sch->lock);
return -ENXIO;
}
(sch->schib.pmcw.chpid[i] == chpid->id))
break;
}
}
if (i==8) {
spin_unlock_irq(sch->lock);
return 0;
}
if (stsch(sch->schid, &sch->schib)) {
spin_unlock_irq(sch->lock);
css_schedule_eval(sch->schid);
return 0;
}
sch->lpm = ((sch->schib.pmcw.pim &
sch->schib.pmcw.pam &
sch->schib.pmcw.pom)
Expand All @@ -553,7 +531,7 @@ __chp_add(struct subchannel_id schid, void *data)
sch->driver->verify(sch);

spin_unlock_irq(sch->lock);
put_device(&sch->dev);

return 0;
}

Expand All @@ -565,7 +543,8 @@ void chsc_chp_online(struct chp_id chpid)
CIO_TRACE_EVENT(2, dbf_txt);

if (chp_get_status(chpid) != 0)
for_each_subchannel(__chp_add, &chpid);
for_each_subchannel_staged(__chp_add, __chp_add_new_sch,
&chpid);
}

static void __s390_subchannel_vary_chpid(struct subchannel *sch,
Expand Down Expand Up @@ -616,25 +595,17 @@ static void __s390_subchannel_vary_chpid(struct subchannel *sch,
spin_unlock_irqrestore(sch->lock, flags);
}

static int s390_subchannel_vary_chpid_off(struct device *dev, void *data)
static int s390_subchannel_vary_chpid_off(struct subchannel *sch, void *data)
{
struct subchannel *sch;
struct chp_id *chpid;

sch = to_subchannel(dev);
chpid = data;
struct chp_id *chpid = data;

__s390_subchannel_vary_chpid(sch, *chpid, 0);
return 0;
}

static int s390_subchannel_vary_chpid_on(struct device *dev, void *data)
static int s390_subchannel_vary_chpid_on(struct subchannel *sch, void *data)
{
struct subchannel *sch;
struct chp_id *chpid;

sch = to_subchannel(dev);
chpid = data;
struct chp_id *chpid = data;

__s390_subchannel_vary_chpid(sch, *chpid, 1);
return 0;
Expand All @@ -644,13 +615,7 @@ static int
__s390_vary_chpid_on(struct subchannel_id schid, void *data)
{
struct schib schib;
struct subchannel *sch;

sch = get_subchannel_by_schid(schid);
if (sch) {
put_device(&sch->dev);
return 0;
}
if (stsch_err(schid, &schib))
/* We're through */
return -ENXIO;
Expand All @@ -670,12 +635,13 @@ int chsc_chp_vary(struct chp_id chpid, int on)
* Redo PathVerification on the devices the chpid connects to
*/

bus_for_each_dev(&css_bus_type, NULL, &chpid, on ?
s390_subchannel_vary_chpid_on :
s390_subchannel_vary_chpid_off);
if (on)
/* Scan for new devices on varied on path. */
for_each_subchannel(__s390_vary_chpid_on, NULL);
for_each_subchannel_staged(s390_subchannel_vary_chpid_on,
__s390_vary_chpid_on, &chpid);
else
for_each_subchannel_staged(s390_subchannel_vary_chpid_off,
NULL, &chpid);

return 0;
}

Expand Down
120 changes: 102 additions & 18 deletions drivers/s390/cio/css.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,62 @@ for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *data)
return ret;
}

struct cb_data {
void *data;
struct idset *set;
int (*fn_known_sch)(struct subchannel *, void *);
int (*fn_unknown_sch)(struct subchannel_id, void *);
};

static int call_fn_known_sch(struct device *dev, void *data)
{
struct subchannel *sch = to_subchannel(dev);
struct cb_data *cb = data;
int rc = 0;

idset_sch_del(cb->set, sch->schid);
if (cb->fn_known_sch)
rc = cb->fn_known_sch(sch, cb->data);
return rc;
}

static int call_fn_unknown_sch(struct subchannel_id schid, void *data)
{
struct cb_data *cb = data;
int rc = 0;

if (idset_sch_contains(cb->set, schid))
rc = cb->fn_unknown_sch(schid, cb->data);
return rc;
}

int for_each_subchannel_staged(int (*fn_known)(struct subchannel *, void *),
int (*fn_unknown)(struct subchannel_id,
void *), void *data)
{
struct cb_data cb;
int rc;

cb.set = idset_sch_new();
if (!cb.set)
return -ENOMEM;
idset_fill(cb.set);
cb.data = data;
cb.fn_known_sch = fn_known;
cb.fn_unknown_sch = fn_unknown;
/* Process registered subchannels. */
rc = bus_for_each_dev(&css_bus_type, NULL, &cb, call_fn_known_sch);
if (rc)
goto out;
/* Process unregistered subchannels. */
if (fn_unknown)
rc = for_each_subchannel(call_fn_unknown_sch, &cb);
out:
idset_free(cb.set);

return rc;
}

static struct subchannel *
css_alloc_subchannel(struct subchannel_id schid)
{
Expand Down Expand Up @@ -402,20 +458,56 @@ static int __init slow_subchannel_init(void)
return 0;
}

static void css_slow_path_func(struct work_struct *unused)
static int slow_eval_known_fn(struct subchannel *sch, void *data)
{
struct subchannel_id schid;
int eval;
int rc;

CIO_TRACE_EVENT(4, "slowpath");
spin_lock_irq(&slow_subchannel_lock);
init_subchannel_id(&schid);
while (idset_sch_get_first(slow_subchannel_set, &schid)) {
idset_sch_del(slow_subchannel_set, schid);
spin_unlock_irq(&slow_subchannel_lock);
css_evaluate_subchannel(schid, 1);
spin_lock_irq(&slow_subchannel_lock);
eval = idset_sch_contains(slow_subchannel_set, sch->schid);
idset_sch_del(slow_subchannel_set, sch->schid);
spin_unlock_irq(&slow_subchannel_lock);
if (eval) {
rc = css_evaluate_known_subchannel(sch, 1);
if (rc == -EAGAIN)
css_schedule_eval(sch->schid);
}
return 0;
}

static int slow_eval_unknown_fn(struct subchannel_id schid, void *data)
{
int eval;
int rc = 0;

spin_lock_irq(&slow_subchannel_lock);
eval = idset_sch_contains(slow_subchannel_set, schid);
idset_sch_del(slow_subchannel_set, schid);
spin_unlock_irq(&slow_subchannel_lock);
if (eval) {
rc = css_evaluate_new_subchannel(schid, 1);
switch (rc) {
case -EAGAIN:
css_schedule_eval(schid);
rc = 0;
break;
case -ENXIO:
case -ENOMEM:
case -EIO:
/* These should abort looping */
break;
default:
rc = 0;
}
}
return rc;
}

static void css_slow_path_func(struct work_struct *unused)
{
CIO_TRACE_EVENT(4, "slowpath");
for_each_subchannel_staged(slow_eval_known_fn, slow_eval_unknown_fn,
NULL);
}

static DECLARE_WORK(slow_path_work, css_slow_path_func);
Expand Down Expand Up @@ -444,21 +536,13 @@ void css_schedule_eval_all(void)
/* Reprobe subchannel if unregistered. */
static int reprobe_subchannel(struct subchannel_id schid, void *data)
{
struct subchannel *sch;
int ret;

CIO_MSG_EVENT(6, "cio: reprobe 0.%x.%04x\n",
schid.ssid, schid.sch_no);
if (need_reprobe)
return -EAGAIN;

sch = get_subchannel_by_schid(schid);
if (sch) {
/* Already known. */
put_device(&sch->dev);
return 0;
}

ret = css_probe_device(schid);
switch (ret) {
case 0:
Expand Down Expand Up @@ -486,7 +570,7 @@ static void reprobe_all(struct work_struct *unused)
/* Make sure initial subchannel scan is done. */
wait_event(ccw_device_init_wq,
atomic_read(&ccw_device_init_count) == 0);
ret = for_each_subchannel(reprobe_subchannel, NULL);
ret = for_each_subchannel_staged(NULL, reprobe_subchannel, NULL);

CIO_MSG_EVENT(2, "reprobe done (rc=%d, need_reprobe=%d)\n", ret,
need_reprobe);
Expand Down
3 changes: 3 additions & 0 deletions drivers/s390/cio/css.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ extern void css_driver_unregister(struct css_driver *);
extern void css_sch_device_unregister(struct subchannel *);
extern struct subchannel * get_subchannel_by_schid(struct subchannel_id);
extern int css_init_done;
int for_each_subchannel_staged(int (*fn_known)(struct subchannel *, void *),
int (*fn_unknown)(struct subchannel_id,
void *), void *data);
extern int for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *);
extern void css_process_crw(int, int);
extern void css_reiterate_subchannels(void);
Expand Down

0 comments on commit e82a156

Please sign in to comment.