Skip to content

Commit

Permalink
[S390] cio: Rework css driver.
Browse files Browse the repository at this point in the history
Rework the css driver methods to provide sane callbacks for
subchannels of all types.

As a bonus, this cleans up and simplyfies the machine check
handling for I/O subchannels a lot.

Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
  • Loading branch information
Cornelia Huck authored and Heiko Carstens committed Jul 14, 2008
1 parent 7e9db9e commit c820de3
Show file tree
Hide file tree
Showing 9 changed files with 381 additions and 510 deletions.
20 changes: 20 additions & 0 deletions drivers/s390/cio/chp.c
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,26 @@ void chp_process_crw(int id, int status)
chsc_chp_offline(chpid);
}

int chp_ssd_get_mask(struct chsc_ssd_info *ssd, struct res_acc_data *data)
{
int i;
int mask;

for (i = 0; i < 8; i++) {
mask = 0x80 >> i;
if (!(ssd->path_mask & mask))
continue;
if (!chp_id_is_equal(&ssd->chpid[i], &data->chpid))
continue;
if ((ssd->fla_valid_mask & mask) &&
((ssd->fla[i] & data->fla_mask) != data->fla))
continue;
return mask;
}
return 0;
}
EXPORT_SYMBOL_GPL(chp_ssd_get_mask);

static inline int info_bit_num(struct chp_id id)
{
return id.id + id.cssid * (__MAX_CHPID + 1);
Expand Down
13 changes: 12 additions & 1 deletion drivers/s390/cio/chp.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,17 @@
#define CHP_STATUS_RESERVED 2
#define CHP_STATUS_NOT_RECOGNIZED 3

#define CHP_ONLINE 0
#define CHP_OFFLINE 1
#define CHP_VARY_ON 2
#define CHP_VARY_OFF 3

struct res_acc_data {
struct chp_id chpid;
u32 fla_mask;
u16 fla;
};

static inline int chp_test_bit(u8 *bitmap, int num)
{
int byte = num >> 3;
Expand Down Expand Up @@ -50,5 +61,5 @@ int chp_new(struct chp_id chpid);
void chp_cfg_schedule(struct chp_id chpid, int configure);
void chp_cfg_cancel_deconfigure(struct chp_id chpid);
int chp_info_get_status(struct chp_id chpid);

int chp_ssd_get_mask(struct chsc_ssd_info *, struct res_acc_data *);
#endif /* S390_CHP_H */
218 changes: 16 additions & 202 deletions drivers/s390/cio/chsc.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
* drivers/s390/cio/chsc.c
* S/390 common I/O routines -- channel subsystem call
*
* Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation
* Copyright IBM Corp. 1999,2008
* Author(s): Ingo Adlung (adlung@de.ibm.com)
* Cornelia Huck (cornelia.huck@de.ibm.com)
* Arnd Bergmann (arndb@de.ibm.com)
Expand Down Expand Up @@ -127,77 +126,12 @@ int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd)
return ret;
}

static int check_for_io_on_path(struct subchannel *sch, int mask)
{
int cc;

cc = stsch(sch->schid, &sch->schib);
if (cc)
return 0;
if (sch->schib.scsw.actl && sch->schib.pmcw.lpum == mask)
return 1;
return 0;
}

static void terminate_internal_io(struct subchannel *sch)
{
if (cio_clear(sch)) {
/* Recheck device in case clear failed. */
sch->lpm = 0;
if (device_trigger_verify(sch) != 0)
css_schedule_eval(sch->schid);
return;
}
/* Request retry of internal operation. */
device_set_intretry(sch);
/* Call handler. */
if (sch->driver && sch->driver->termination)
sch->driver->termination(sch);
}

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

for (j = 0; j < 8; j++) {
mask = 0x80 >> j;
if ((sch->schib.pmcw.pim & mask) &&
(sch->schib.pmcw.chpid[j] == chpid->id))
break;
}
if (j >= 8)
return 0;

spin_lock_irq(sch->lock);

stsch(sch->schid, &schib);
if (!css_sch_is_valid(&schib))
goto out_unreg;
memcpy(&sch->schib, &schib, sizeof(struct schib));
/* Check for single path devices. */
if (sch->schib.pmcw.pim == 0x80)
goto out_unreg;

if (check_for_io_on_path(sch, mask)) {
if (device_is_online(sch))
device_kill_io(sch);
else {
terminate_internal_io(sch);
/* Re-start path verification. */
if (sch->driver && sch->driver->verify)
sch->driver->verify(sch);
}
} else {
/* trigger path verification. */
if (sch->driver && sch->driver->verify)
sch->driver->verify(sch);
else if (sch->lpm == mask)
if (sch->driver && sch->driver->chp_event)
if (sch->driver->chp_event(sch, data, CHP_OFFLINE) != 0)
goto out_unreg;
}

spin_unlock_irq(sch->lock);
return 0;

Expand Down Expand Up @@ -242,53 +176,11 @@ static int s390_process_res_acc_new_sch(struct subchannel_id schid, void *data)
return 0;
}

struct res_acc_data {
struct chp_id chpid;
u32 fla_mask;
u16 fla;
};

static int get_res_chpid_mask(struct chsc_ssd_info *ssd,
struct res_acc_data *data)
{
int i;
int mask;

for (i = 0; i < 8; i++) {
mask = 0x80 >> i;
if (!(ssd->path_mask & mask))
continue;
if (!chp_id_is_equal(&ssd->chpid[i], &data->chpid))
continue;
if ((ssd->fla_valid_mask & mask) &&
((ssd->fla[i] & data->fla_mask) != data->fla))
continue;
return mask;
}
return 0;
}

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

spin_lock_irq(sch->lock);
chp_mask = get_res_chpid_mask(&sch->ssd_info, res_data);
if (chp_mask == 0)
goto out;
if (stsch(sch->schid, &sch->schib))
goto out;
old_lpm = sch->lpm;
sch->lpm = ((sch->schib.pmcw.pim &
sch->schib.pmcw.pam &
sch->schib.pmcw.pom)
| chp_mask) & sch->opm;
if (!old_lpm && sch->lpm)
device_trigger_reprobe(sch);
else if (sch->driver && sch->driver->verify)
sch->driver->verify(sch);
out:
if (sch->driver && sch->driver->chp_event)
sch->driver->chp_event(sch, data, CHP_ONLINE);
spin_unlock_irq(sch->lock);

return 0;
Expand Down Expand Up @@ -509,114 +401,36 @@ void chsc_process_crw(void)
} while (sei_area->flags & 0x80);
}

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

if (stsch_err(schid, &schib))
/* We're through */
return -ENXIO;

/* Put it on the slow path. */
css_schedule_eval(schid);
return 0;
}


static int __chp_add(struct subchannel *sch, void *data)
{
int i, mask;
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))
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)
| mask) & sch->opm;

if (sch->driver && sch->driver->verify)
sch->driver->verify(sch);

spin_unlock_irq(sch->lock);

return 0;
}

void chsc_chp_online(struct chp_id chpid)
{
char dbf_txt[15];
struct res_acc_data res_data;

sprintf(dbf_txt, "cadd%x.%02x", chpid.cssid, chpid.id);
CIO_TRACE_EVENT(2, dbf_txt);

if (chp_get_status(chpid) != 0) {
memset(&res_data, 0, sizeof(struct res_acc_data));
res_data.chpid = chpid;
/* Wait until previous actions have settled. */
css_wait_for_slow_path();
for_each_subchannel_staged(__chp_add, __chp_add_new_sch,
&chpid);
for_each_subchannel_staged(__s390_process_res_acc, NULL,
&res_data);
}
}

static void __s390_subchannel_vary_chpid(struct subchannel *sch,
struct chp_id chpid, int on)
{
int chp, old_lpm;
int mask;
unsigned long flags;
struct res_acc_data res_data;

memset(&res_data, 0, sizeof(struct res_acc_data));
res_data.chpid = chpid;
spin_lock_irqsave(sch->lock, flags);
old_lpm = sch->lpm;
for (chp = 0; chp < 8; chp++) {
mask = 0x80 >> chp;
if (!(sch->ssd_info.path_mask & mask))
continue;
if (!chp_id_is_equal(&sch->ssd_info.chpid[chp], &chpid))
continue;

if (on) {
sch->opm |= mask;
sch->lpm |= mask;
if (!old_lpm)
device_trigger_reprobe(sch);
else if (sch->driver && sch->driver->verify)
sch->driver->verify(sch);
break;
}
sch->opm &= ~mask;
sch->lpm &= ~mask;
if (check_for_io_on_path(sch, mask)) {
if (device_is_online(sch))
/* Path verification is done after killing. */
device_kill_io(sch);
else {
/* Kill and retry internal I/O. */
terminate_internal_io(sch);
/* Re-start path verification. */
if (sch->driver && sch->driver->verify)
sch->driver->verify(sch);
}
} else if (!sch->lpm) {
if (device_trigger_verify(sch) != 0)
css_schedule_eval(sch->schid);
} else if (sch->driver && sch->driver->verify)
sch->driver->verify(sch);
break;
}
if (sch->driver && sch->driver->chp_event)
sch->driver->chp_event(sch, &res_data,
on ? CHP_VARY_ON : CHP_VARY_OFF);
spin_unlock_irqrestore(sch->lock, flags);
}

Expand Down
1 change: 1 addition & 0 deletions drivers/s390/cio/cio.c
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,7 @@ int cio_validate_subchannel(struct subchannel *sch, struct subchannel_id schid)
}
/* Copy subchannel type from path management control word. */
sch->st = sch->schib.pmcw.st;

switch (sch->st) {
case SUBCHANNEL_TYPE_IO:
err = cio_validate_io_subchannel(sch);
Expand Down
Loading

0 comments on commit c820de3

Please sign in to comment.