Messages in this thread Patch in this message | | | From | Martin Schwidefsky <> | Subject | [PATCH] s390 (3/16): common i/o layer update. | Date | Mon, 14 Apr 2003 19:48:16 +0200 |
| |
Common i/o layer fixes: - Fix for path no operational condition in cio_start. - Fix handling of user interruption parameter. - Add code to wait for devices in init_ccw_bus_type. - Move qdio states out of main cio state machine. - Reworked chsc data structures. - Add ccw_device_start_timeout. - Handle path verification required flag.
diffstat: cio/airq.c | 6 cio/airq.h | 4 cio/chsc.c | 338 ++++++++++++++++----------- cio/chsc.h | 83 ------ cio/cio.c | 29 -- cio/cio.h | 9 cio/css.c | 8 cio/css.h | 1 cio/device.c | 70 +++++ cio/device.h | 10 cio/device_fsm.c | 200 +++++++--------- cio/device_id.c | 10 cio/device_ops.c | 98 +++++--- cio/device_pgid.c | 27 +- cio/device_status.c | 4 cio/qdio.c | 635 ++++++++++++++++++++++++++++------------------------ cio/qdio.h | 110 +-------- cio/requestirq.c | 6 s390mach.c | 14 - 19 files changed, 859 insertions(+), 803 deletions(-)
diff -urN linux-2.5.67/drivers/s390/cio/airq.c linux-2.5.67-s390/drivers/s390/cio/airq.c --- linux-2.5.67/drivers/s390/cio/airq.c Mon Apr 7 19:32:18 2003 +++ linux-2.5.67-s390/drivers/s390/cio/airq.c Mon Apr 14 19:11:50 2003 @@ -2,7 +2,7 @@ * drivers/s390/cio/airq.c * S/390 common I/O routines -- support for adapter interruptions * - * $Revision: 1.10 $ + * $Revision: 1.11 $ * * Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH, * IBM Corporation @@ -87,14 +87,14 @@ } void -do_adapter_IO (__u32 intparm) +do_adapter_IO (void) { CIO_TRACE_EVENT (4, "doaio"); spin_lock (&adapter_lock); if (adapter_handler) - (*adapter_handler) (intparm); + (*adapter_handler) (); spin_unlock (&adapter_lock); diff -urN linux-2.5.67/drivers/s390/cio/airq.h linux-2.5.67-s390/drivers/s390/cio/airq.h --- linux-2.5.67/drivers/s390/cio/airq.h Mon Apr 7 19:32:23 2003 +++ linux-2.5.67-s390/drivers/s390/cio/airq.h Mon Apr 14 19:11:50 2003 @@ -1,10 +1,10 @@ #ifndef S390_AINTERRUPT_H #define S390_AINTERRUPT_H -typedef int (*adapter_int_handler_t)(__u32 intparm); +typedef int (*adapter_int_handler_t)(void); extern int s390_register_adapter_interrupt(adapter_int_handler_t handler); extern int s390_unregister_adapter_interrupt(adapter_int_handler_t handler); -extern void do_adapter_IO (__u32 intparm); +extern void do_adapter_IO (void); #endif diff -urN linux-2.5.67/drivers/s390/cio/chsc.c linux-2.5.67-s390/drivers/s390/cio/chsc.c --- linux-2.5.67/drivers/s390/cio/chsc.c Mon Apr 7 19:31:43 2003 +++ linux-2.5.67-s390/drivers/s390/cio/chsc.c Mon Apr 14 19:11:50 2003 @@ -1,7 +1,7 @@ /* * drivers/s390/cio/chsc.c * S/390 common I/O routines -- channel subsystem call - * $Revision: 1.57 $ + * $Revision: 1.67 $ * * Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH, * IBM Corporation @@ -76,53 +76,77 @@ chsc_get_sch_desc_irq(int irq) { int ccode, chpid, j; + int ret; - /* FIXME: chsc_area_sei cannot be on the stack since it needs to - * be page-aligned. Implement proper locking or dynamic - * allocation or prove that this function does not have to be - * reentrant! */ - static struct ssd_area chsc_area_ssd - __attribute__ ((aligned(PAGE_SIZE))); - - typeof (chsc_area_ssd.response_block) - *ssd_res = &chsc_area_ssd.response_block; - - chsc_area_ssd = (struct ssd_area) { - .request_block = { - .command_code1 = 0x0010, - .command_code2 = 0x0004, - .f_sch = irq, - .l_sch = irq, - } + struct { + struct chsc_header request; + u16 reserved1; + u16 f_sch; /* first subchannel */ + u16 reserved2; + u16 l_sch; /* last subchannel */ + u32 reserved3; + struct chsc_header response; + u32 reserved4; + u8 sch_valid : 1; + u8 dev_valid : 1; + u8 st : 3; /* subchannel type */ + u8 zeroes : 3; + u8 unit_addr; /* unit address */ + u16 devno; /* device number */ + u8 path_mask; + u8 fla_valid_mask; + u16 sch; /* subchannel */ + u8 chpid[8]; /* chpids 0-7 */ + u16 fla[8]; /* full link addresses 0-7 */ + } *ssd_area; + + ssd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!ssd_area) { + CIO_CRW_EVENT(0, "No memory for ssd area!\n"); + return -ENOMEM; + } + + ssd_area->request = (struct chsc_header) { + .length = 0x0010, + .code = 0x0004, }; - ccode = chsc(&chsc_area_ssd); + ssd_area->f_sch = irq; + ssd_area->l_sch = irq; + + ccode = chsc(ssd_area); if (ccode > 0) { pr_debug("chsc returned with ccode = %d\n", ccode); - if (ccode == 3) - return -ENODEV; - return -EBUSY; + ret = (ccode == 3) ? -ENODEV : -EBUSY; + goto out; } - switch (chsc_area_ssd.response_block.response_code) { + switch (ssd_area->response.code) { case 0x0001: /* everything ok */ + ret = 0; break; case 0x0002: CIO_CRW_EVENT(2, "Invalid command!\n"); case 0x0003: CIO_CRW_EVENT(2, "Error in chsc request block!\n"); - return -EINVAL; + ret = -EINVAL; + break; case 0x0004: CIO_CRW_EVENT(2, "Model does not provide ssd\n"); - return -EOPNOTSUPP; + ret = -EOPNOTSUPP; + break; default: CIO_CRW_EVENT(2, "Unknown CHSC response %d\n", - chsc_area_ssd.response_block.response_code); - return -EIO; + ssd_area->response.code); + ret = -EIO; + break; } + if (ret != 0) + goto out; + /* - * ssd_res->st stores the type of the detected + * ssd_area->st stores the type of the detected * subchannel, with the following definitions: * * 0: I/O subchannel: All fields have meaning @@ -135,43 +159,45 @@ * * Other types are currently undefined. */ - if (ssd_res->st > 3) { /* uhm, that looks strange... */ + if (ssd_area->st > 3) { /* uhm, that looks strange... */ CIO_CRW_EVENT(0, "Strange subchannel type %d" - " for sch %x\n", ssd_res->st, irq); + " for sch %x\n", ssd_area->st, irq); /* * There may have been a new subchannel type defined in the * time since this code was written; since we don't know which * fields have meaning and what to do with it we just jump out */ - return 0; + goto out; } else { - const char type[4][8] = {"I/O", "chsc", "message", "ADM"}; + const char *type[4] = {"I/O", "chsc", "message", "ADM"}; CIO_CRW_EVENT(6, "ssd: sch %x is %s subchannel\n", - irq, type[ssd_res->st]); + irq, type[ssd_area->st]); if (ioinfo[irq] == NULL) /* FIXME: we should do device rec. here... */ - return 0; + goto out; ioinfo[irq]->ssd_info.valid = 1; - ioinfo[irq]->ssd_info.type = ssd_res->st; + ioinfo[irq]->ssd_info.type = ssd_area->st; } - if (ssd_res->st == 0 || ssd_res->st == 2) { + if (ssd_area->st == 0 || ssd_area->st == 2) { for (j = 0; j < 8; j++) { - if (!((0x80 >> j) & ssd_res->path_mask & - ssd_res->fla_valid_mask)) + if (!((0x80 >> j) & ssd_area->path_mask & + ssd_area->fla_valid_mask)) continue; - chpid = ssd_res->chpid[j]; + chpid = ssd_area->chpid[j]; if (chpid && (!test_and_set_bit (chpid, chpids_known)) && (test_bit (chpid, chpids_logical))) set_bit (chpid, chpids); ioinfo[irq]->ssd_info.chpid[j] = chpid; - ioinfo[irq]->ssd_info.fla[j] = ssd_res->fla[j]; + ioinfo[irq]->ssd_info.fla[j] = ssd_area->fla[j]; } } - return 0; +out: + free_page ((unsigned long) ssd_area); + return ret; } static int @@ -216,6 +242,7 @@ s390_subchannel_remove_chpid(struct subchannel *sch, __u8 chpid) { int j; + int mask; for (j = 0; j < 8; j++) if (sch->schib.pmcw.chpid[j] == chpid) @@ -223,16 +250,68 @@ if (j >= 8) return; + mask = 0x80 >> j; spin_lock(&sch->lock); chsc_validate_chpids(sch); - /* just to be sure... */ - sch->lpm &= ~(0x80>>j); + stsch(sch->irq, &sch->schib); + if (sch->vpm == mask) { + dev_fsm_event(sch->dev.driver_data, DEV_EVENT_NOTOPER); + goto out_unlock; + } + if ((sch->schib.scsw.actl & (SCSW_ACTL_CLEAR_PEND | + SCSW_ACTL_HALT_PEND | + SCSW_ACTL_START_PEND | + SCSW_ACTL_RESUME_PEND)) && + (sch->schib.pmcw.lpum == mask)) { + int cc = cio_cancel(sch); + + if (cc == -ENODEV) { + dev_fsm_event(sch->dev.driver_data, DEV_EVENT_NOTOPER); + goto out_unlock; + } + + if (cc == -EINVAL) { + struct ccw_device *cdev; + + cc = cio_clear(sch); + if (cc == -ENODEV) { + dev_fsm_event(sch->dev.driver_data, + DEV_EVENT_NOTOPER); + goto out_unlock; + } + /* Call handler. */ + cdev = sch->dev.driver_data; + cdev->private->state = DEV_STATE_CLEAR_VERIFY; + if (cdev->handler) + cdev->handler(cdev, cdev->private->intparm, + ERR_PTR(-EIO)); + goto out_unlock; + } + } else if ((sch->schib.scsw.actl & SCSW_ACTL_DEVACT) && + (sch->schib.scsw.actl & SCSW_ACTL_SCHACT) && + (sch->schib.pmcw.lpum == mask)) { + struct ccw_device *cdev; + int cc; + + cc = cio_clear(sch); + if (cc == -ENODEV) { + dev_fsm_event(sch->dev.driver_data, DEV_EVENT_NOTOPER); + goto out_unlock; + } + /* Call handler. */ + cdev = sch->dev.driver_data; + cdev->private->state = DEV_STATE_CLEAR_VERIFY; + if (cdev->handler) + cdev->handler(cdev, cdev->private->intparm, + ERR_PTR(-EIO)); + goto out_unlock; + } /* trigger path verification. */ dev_fsm_event(sch->dev.driver_data, DEV_EVENT_VERIFY); - +out_unlock: spin_unlock(&sch->lock); } @@ -265,7 +344,7 @@ sch = ioinfo[irq]; if (sch == NULL) continue; /* we don't know the device anyway */ - /* FIXME: Kill pending I/O. */ + s390_subchannel_remove_chpid(sch, chpid); } #endif @@ -349,7 +428,7 @@ if (!test_bit(chpid, chpids_logical)) return; /* no need to do the rest */ - for (irq = 0; irq <= __MAX_SUBCHANNELS; irq++) { + for (irq = 0; irq < __MAX_SUBCHANNELS; irq++) { int chp_mask; sch = ioinfo[irq]; @@ -369,7 +448,6 @@ continue; } - /* FIXME: Kill pending I/O. */ spin_lock_irq(&sch->lock); chp_mask = s390_process_res_acc_sch(chpid, fla, fla_mask, sch); @@ -402,92 +480,97 @@ static void do_process_crw(void *ignore) { - int do_sei; + struct { + struct chsc_header request; + u32 reserved1; + u32 reserved2; + u32 reserved3; + struct chsc_header response; + u32 reserved4; + u8 flags; + u8 vf; /* validity flags */ + u8 rs; /* reporting source */ + u8 cc; /* content code */ + u16 fla; /* full link address */ + u16 rsid; /* reporting source id */ + u32 reserved5; + u32 reserved6; + u32 ccdf; /* content-code dependent field */ + u32 reserved7; + u32 reserved8; + u32 reserved9; + } *sei_area; /* * build the chsc request block for store event information * and do the call */ + sei_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); - /* FIXME: chsc_area_sei cannot be on the stack since it needs to - * be page-aligned. Implement proper locking or dynamic - * allocation or prove that this function does not have to be - * reentrant! */ - static struct sei_area chsc_area_sei - __attribute__ ((aligned(PAGE_SIZE))) = { - .request_block = { - .command_code1 = 0x0010, - .command_code2 = 0x000e - } - }; - - typeof (chsc_area_sei.response_block) - *sei_res = &chsc_area_sei.response_block; - + if (!sei_area) { + CIO_CRW_EVENT(0, "No memory for sei area!\n"); + return; + } CIO_TRACE_EVENT( 2, "prcss"); - do_sei = 1; - - while (do_sei) { + do { int ccode; + memset(sei_area, 0, sizeof(*sei_area)); + + sei_area->request = (struct chsc_header) { + .length = 0x0010, + .code = 0x000e, + }; - ccode = chsc(&chsc_area_sei); + ccode = chsc(sei_area); if (ccode > 0) - return; + goto out; - switch (sei_res->response_code) { + switch (sei_area->response.code) { /* for debug purposes, check for problems */ case 0x0001: + CIO_CRW_EVENT(4, "chsc_process_crw: event information " + "successfully stored\n"); break; /* everything ok */ case 0x0002: CIO_CRW_EVENT(2, "chsc_process_crw: invalid command!\n"); - return; + goto out; case 0x0003: CIO_CRW_EVENT(2, "chsc_process_crw: error in chsc " "request block!\n"); - return; + goto out; case 0x0005: CIO_CRW_EVENT(2, "chsc_process_crw: no event " "information stored\n"); - return; + goto out; default: CIO_CRW_EVENT(2, "chsc_process_crw: chsc response %d\n", - sei_res->response_code); - return; + sei_area->response.code); + goto out; } - - CIO_CRW_EVENT(4, "chsc_process_crw: event information " - "successfully stored\n"); - - /* Check if there is more event information pending. */ - if (sei_res->flags & 0x80) - CIO_CRW_EVENT( 2, "chsc_process_crw: " - "further event information pending\n"); - else - do_sei = 0; /* Check if we might have lost some information. */ - if (sei_res->flags & 0x40) - CIO_CRW_EVENT( 2, "chsc_process_crw: Event information " + if (sei_area->flags & 0x40) + CIO_CRW_EVENT(2, "chsc_process_crw: Event information " "has been lost due to overflow!\n"); - if (sei_res->rs != 4) { + if (sei_area->rs != 4) { CIO_CRW_EVENT(2, "chsc_process_crw: reporting source " "(%04X) isn't a chpid!\n", - sei_res->rsid); + sei_area->rsid); continue; } - + /* which kind of information was stored? */ - switch (sei_res->cc) { + switch (sei_area->cc) { case 1: /* link incident*/ CIO_CRW_EVENT(4, "chsc_process_crw: " "channel subsystem reports link incident," - " source is chpid %x\n", sei_res->rsid); + " source is chpid %x\n", sei_area->rsid); - s390_set_chpid_offline(sei_res->rsid); + s390_set_chpid_offline(sei_area->rsid); break; case 2: /* i/o resource accessibiliy */ @@ -495,27 +578,27 @@ "channel subsystem reports some I/O " "devices may have become accessible\n"); pr_debug("Data received after sei: \n"); - pr_debug("Validity flags: %x\n", sei_res->vf); + pr_debug("Validity flags: %x\n", sei_area->vf); /* allocate a new channel path structure, if needed */ - if (chps[sei_res->rsid] == NULL) - new_channel_path(sei_res->rsid, CHP_ONLINE); + if (chps[sei_area->rsid] == NULL) + new_channel_path(sei_area->rsid, CHP_ONLINE); else - set_chp_status(sei_res->rsid, CHP_ONLINE); + set_chp_status(sei_area->rsid, CHP_ONLINE); - if ((sei_res->vf & 0x80) == 0) { - pr_debug("chpid: %x\n", sei_res->rsid); - s390_process_res_acc(sei_res->rsid, 0, 0); - } else if ((sei_res->vf & 0xc0) == 0x80) { + if ((sei_area->vf & 0x80) == 0) { + pr_debug("chpid: %x\n", sei_area->rsid); + s390_process_res_acc(sei_area->rsid, 0, 0); + } else if ((sei_area->vf & 0xc0) == 0x80) { pr_debug("chpid: %x link addr: %x\n", - sei_res->rsid, sei_res->fla); - s390_process_res_acc(sei_res->rsid, - sei_res->fla, 0xff00); - } else if ((sei_res->vf & 0xc0) == 0xc0) { + sei_area->rsid, sei_area->fla); + s390_process_res_acc(sei_area->rsid, + sei_area->fla, 0xff00); + } else if ((sei_area->vf & 0xc0) == 0xc0) { pr_debug("chpid: %x full link addr: %x\n", - sei_res->rsid, sei_res->fla); - s390_process_res_acc(sei_res->rsid, - sei_res->fla, 0xffff); + sei_area->rsid, sei_area->fla); + s390_process_res_acc(sei_area->rsid, + sei_area->fla, 0xffff); } pr_debug("\n"); @@ -523,15 +606,13 @@ default: /* other stuff */ CIO_CRW_EVENT(4, "chsc_process_crw: event %d\n", - sei_res->cc); + sei_area->cc); break; } - if (do_sei) { - memset(&chsc_area_sei, 0, sizeof(struct sei_area)); - chsc_area_sei.request_block.command_code1 = 0x0010; - chsc_area_sei.request_block.command_code2 = 0x000e; - } - } + } while (sei_area->flags & 0x80); + +out: + free_page((unsigned long)sei_area); } void @@ -539,7 +620,7 @@ { static DECLARE_WORK(work, do_process_crw, 0); - schedule_work(&work); + queue_work(ccw_device_work, &work); } static void @@ -555,7 +636,7 @@ sprintf(dbf_txt, "cadd%x", chpid); CIO_TRACE_EVENT(2, dbf_txt); - for (irq = 0; irq <= __MAX_SUBCHANNELS; irq++) { + for (irq = 0; irq < __MAX_SUBCHANNELS; irq++) { int i; sch = ioinfo[irq]; @@ -567,7 +648,6 @@ continue; } - /* FIXME: Kill pending I/O. */ spin_lock(&sch->lock); for (i=0; i<8; i++) if (sch->schib.pmcw.chpid[i] == chpid) { @@ -599,26 +679,9 @@ * Handling of crw machine checks with channel path source. */ void -chp_process_crw(int chpid) +chp_process_crw(int chpid, int on) { - /* - * Update our descriptions. We need this since we don't always - * get machine checks for path come and can't rely on our information - * being consistent otherwise. - */ - chsc_get_sch_descriptions(); - if (!cio_chsc_desc_avail) { - /* - * Something went wrong... - * We can't reliably say whether a path was there before. - */ - CIO_CRW_EVENT(0, "Error: Could not retrieve " - "subchannel descriptions, will not process chp" - "machine check...\n"); - return; - } - - if (!test_bit(chpid, chpids)) { + if (on == 0) { /* Path has gone. We use the link incident routine.*/ s390_set_chpid_offline(chpid); } else { @@ -646,9 +709,6 @@ struct subchannel *sch; int irq; - if (chpid <=0 || chpid >= NR_CHPIDS) - return -EINVAL; - sprintf(dbf_text, on?"varyon%x":"varyoff%x", chpid); CIO_TRACE_EVENT( 2, dbf_text); diff -urN linux-2.5.67/drivers/s390/cio/chsc.h linux-2.5.67-s390/drivers/s390/cio/chsc.h --- linux-2.5.67/drivers/s390/cio/chsc.h Mon Apr 7 19:32:24 2003 +++ linux-2.5.67-s390/drivers/s390/cio/chsc.h Mon Apr 14 19:11:50 2003 @@ -12,85 +12,10 @@ #define CHSC_SEI_ACC_LINKADDR 2 #define CHSC_SEI_ACC_FULLLINKADDR 3 -struct sei_area { - struct { - /* word 0 */ - __u16 command_code1; - __u16 command_code2; - /* word 1 */ - __u32 reserved1; - /* word 2 */ - __u32 reserved2; - /* word 3 */ - __u32 reserved3; - } __attribute__ ((packed,aligned(8))) request_block; - struct { - /* word 0 */ - __u16 length; - __u16 response_code; - /* word 1 */ - __u32 reserved1; - /* word 2 */ - __u8 flags; - __u8 vf; /* validity flags */ - __u8 rs; /* reporting source */ - __u8 cc; /* content code */ - /* word 3 */ - __u16 fla; /* full link address */ - __u16 rsid; /* reporting source id */ - /* word 4 */ - __u32 reserved2; - /* word 5 */ - __u32 reserved3; - /* word 6 */ - __u32 ccdf; /* content-code dependent field */ - /* word 7 */ - __u32 reserved4; - /* word 8 */ - __u32 reserved5; - /* word 9 */ - __u32 reserved6; - } __attribute__ ((packed,aligned(8))) response_block; -} __attribute__ ((packed,aligned(PAGE_SIZE))); - -struct ssd_area { - struct { - /* word 0 */ - __u16 command_code1; - __u16 command_code2; - /* word 1 */ - __u16 reserved1; - __u16 f_sch; /* first subchannel */ - /* word 2 */ - __u16 reserved2; - __u16 l_sch; /* last subchannel */ - /* word 3 */ - __u32 reserved3; - } __attribute__ ((packed,aligned(8))) request_block; - struct { - /* word 0 */ - __u16 length; - __u16 response_code; - /* word 1 */ - __u32 reserved1; - /* word 2 */ - __u8 sch_valid : 1; - __u8 dev_valid : 1; - __u8 st : 3; /* subchannel type */ - __u8 zeroes : 3; - __u8 unit_addr; /* unit address */ - __u16 devno; /* device number */ - /* word 3 */ - __u8 path_mask; - __u8 fla_valid_mask; - __u16 sch; /* subchannel */ - /* words 4-5 */ - __u8 chpid[8]; /* chpids 0-7 */ - /* words 6-9 */ - __u16 fla[8]; /* full link addresses 0-7 */ - } __attribute__ ((packed,aligned(8))) response_block; -} __attribute__ ((packed,aligned(PAGE_SIZE))); - +struct chsc_header { + u16 length; + u16 code; +}; struct channel_path { int id; diff -urN linux-2.5.67/drivers/s390/cio/cio.c linux-2.5.67-s390/drivers/s390/cio/cio.c --- linux-2.5.67/drivers/s390/cio/cio.c Mon Apr 7 19:32:55 2003 +++ linux-2.5.67-s390/drivers/s390/cio/cio.c Mon Apr 14 19:11:50 2003 @@ -1,7 +1,7 @@ /* * drivers/s390/cio/cio.c * S/390 common I/O routines -- low level i/o calls - * $Revision: 1.91 $ + * $Revision: 1.97 $ * * Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH, * IBM Corporation @@ -176,13 +176,13 @@ CIO_TRACE_EVENT(0, dbf_text); CIO_HEX_EVENT(0, &sch->schib, sizeof (struct schib)); - return -ENODEV; + return (sch->lpm ? -EACCES : -ENODEV); } int cio_start (struct subchannel *sch, /* subchannel structure */ struct ccw1 * cpa, /* logical channel prog addr */ - unsigned long intparm, /* interruption parameter */ + unsigned int intparm, /* interruption parameter */ __u8 lpm) /* logical path mask */ { char dbf_txt[15]; @@ -191,7 +191,7 @@ sprintf (dbf_txt, "stIO%x", sch->irq); CIO_TRACE_EVENT (4, dbf_txt); - sch->orb.intparm = (__u32) (long) &sch->u_intparm; + sch->orb.intparm = intparm; sch->orb.fmt = 1; sch->orb.pfch = sch->options.prefetch == 0; @@ -219,7 +219,6 @@ /* * initialize device status information */ - sch->u_intparm = intparm; sch->schib.scsw.actl |= SCSW_ACTL_START_PEND; return 0; case 1: /* status pending */ @@ -265,13 +264,10 @@ } /* - * Note: The "intparm" parameter is not used by the halt_IO() function - * itself, as no ORB is built for the HSCH instruction. However, - * it allows the device interrupt handler to associate the upcoming - * interrupt with the halt_IO() request. + * halt I/O operation */ int -cio_halt(struct subchannel *sch, unsigned long intparm) +cio_halt(struct subchannel *sch) { char dbf_txt[15]; int ccode; @@ -297,7 +293,6 @@ switch (ccode) { case 0: - sch->u_intparm = intparm; sch->schib.scsw.actl |= SCSW_ACTL_HALT_PEND; return 0; case 1: /* status pending */ @@ -309,13 +304,10 @@ } /* - * Note: The "intparm" parameter is not used by the clear_IO() function - * itself, as no ORB is built for the CSCH instruction. However, - * it allows the device interrupt handler to associate the upcoming - * interrupt with the clear_IO() request. + * Clear I/O operation */ int -cio_clear(struct subchannel *sch, unsigned long intparm) +cio_clear(struct subchannel *sch) { char dbf_txt[15]; int ccode; @@ -340,7 +332,6 @@ switch (ccode) { case 0: - sch->u_intparm = intparm; sch->schib.scsw.actl |= SCSW_ACTL_CLEAR_PEND; return 0; default: /* device not operational */ @@ -374,6 +365,8 @@ switch (ccode) { case 0: /* success */ + /* Update information in scsw. */ + stsch (sch->irq, &sch->schib); return 0; case 1: /* status pending */ return -EBUSY; @@ -620,7 +613,7 @@ */ if (tpi_info->adapter_IO == 1 && tpi_info->int_type == IO_INTERRUPT_TYPE) { - do_adapter_IO (tpi_info->intparm); + do_adapter_IO(); continue; } sch = ioinfo[tpi_info->irq]; diff -urN linux-2.5.67/drivers/s390/cio/cio.h linux-2.5.67-s390/drivers/s390/cio/cio.h --- linux-2.5.67/drivers/s390/cio/cio.h Mon Apr 7 19:30:34 2003 +++ linux-2.5.67-s390/drivers/s390/cio/cio.h Mon Apr 14 19:11:50 2003 @@ -98,8 +98,6 @@ __u8 vpm; /* verified path mask */ __u8 lpm; /* logical path mask */ - // TODO: intparm for second start i/o - unsigned long u_intparm; /* user interruption parameter */ struct schib schib; /* subchannel information block */ struct orb orb; /* operation request block */ struct ccw1 sense_ccw; /* static ccw for sense command */ @@ -116,11 +114,10 @@ extern int cio_enable_subchannel (struct subchannel *, unsigned int); extern int cio_disable_subchannel (struct subchannel *); extern int cio_cancel (struct subchannel *); -extern int cio_clear (struct subchannel *, unsigned long); -extern int cio_do_io (struct subchannel *, struct ccw1 *, unsigned long, __u8); +extern int cio_clear (struct subchannel *); extern int cio_resume (struct subchannel *); -extern int cio_halt (struct subchannel *, unsigned long); -extern int cio_start (struct subchannel *, struct ccw1 *, unsigned long, __u8); +extern int cio_halt (struct subchannel *); +extern int cio_start (struct subchannel *, struct ccw1 *, unsigned int, __u8); extern int cio_cancel (struct subchannel *); extern int cio_set_options (struct subchannel *, int); extern int cio_get_options (struct subchannel *); diff -urN linux-2.5.67/drivers/s390/cio/css.c linux-2.5.67-s390/drivers/s390/cio/css.c --- linux-2.5.67/drivers/s390/cio/css.c Mon Apr 7 19:30:43 2003 +++ linux-2.5.67-s390/drivers/s390/cio/css.c Mon Apr 14 19:11:50 2003 @@ -1,7 +1,7 @@ /* * drivers/s390/cio/css.c * driver for channel subsystem - * $Revision: 1.40 $ + * $Revision: 1.43 $ * * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, * IBM Corporation @@ -41,7 +41,7 @@ /* There already is a struct subchannel for this irq. */ return -EBUSY; - sch = kmalloc (sizeof (*sch), GFP_DMA); + sch = kmalloc (sizeof (*sch), GFP_KERNEL | GFP_DMA); if (sch == NULL) return -ENOMEM; ret = cio_validate_subchannel (sch, irq); @@ -161,7 +161,7 @@ sch = ioinfo[irq]; if (sch == NULL) { - schedule_work(&work); + queue_work(ccw_device_work, &work); return; } if (!sch->dev.driver_data) @@ -172,7 +172,7 @@ ccode = stsch(irq, &sch->schib); if (!ccode) if (devno != sch->schib.pmcw.dev) - schedule_work(&work); + queue_work(ccw_device_work, &work); } /* diff -urN linux-2.5.67/drivers/s390/cio/css.h linux-2.5.67-s390/drivers/s390/cio/css.h --- linux-2.5.67/drivers/s390/cio/css.h Mon Apr 7 19:33:04 2003 +++ linux-2.5.67-s390/drivers/s390/cio/css.h Mon Apr 14 19:11:50 2003 @@ -79,6 +79,7 @@ unsigned int esid:1; /* Ext. SenseID supported by HW */ unsigned int dosense:1; /* delayed SENSE required */ } __attribute__((packed)) flags; + unsigned long intparm; /* user interruption parameter */ struct qdio_irq *qdio_data; struct irb irb; /* device status */ struct senseid senseid; /* SenseID info */ diff -urN linux-2.5.67/drivers/s390/cio/device.c linux-2.5.67-s390/drivers/s390/cio/device.c --- linux-2.5.67/drivers/s390/cio/device.c Mon Apr 7 19:32:17 2003 +++ linux-2.5.67-s390/drivers/s390/cio/device.c Mon Apr 14 19:11:50 2003 @@ -1,7 +1,7 @@ /* * drivers/s390/cio/device.c * bus driver for ccw devices - * $Revision: 1.50 $ + * $Revision: 1.53 $ * * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, * IBM Corporation @@ -18,6 +18,7 @@ #include <linux/slab.h> #include <linux/list.h> #include <linux/device.h> +#include <linux/workqueue.h> #include <asm/ccwdev.h> #include <asm/cio.h> @@ -126,14 +127,32 @@ .irq = io_subchannel_irq, }; +struct workqueue_struct *ccw_device_work; +static wait_queue_head_t ccw_device_init_wq; +static atomic_t ccw_device_init_count; + static int __init init_ccw_bus_type (void) { int ret; + + init_waitqueue_head(&ccw_device_init_wq); + atomic_set(&ccw_device_init_count, 0); + + ccw_device_work = create_workqueue("cio"); + if (!ccw_device_work) + return -ENOMEM; /* FIXME: better errno ? */ + if ((ret = bus_register (&ccw_bus_type))) return ret; - return driver_register(&io_subchannel_driver.drv); + if ((ret = driver_register(&io_subchannel_driver.drv))) + return ret; + + wait_event(ccw_device_init_wq, + atomic_read(&ccw_device_init_count) == 0); + flush_workqueue(ccw_device_work); + return 0; } static void __exit @@ -141,6 +160,7 @@ { driver_unregister(&io_subchannel_driver.drv); bus_unregister(&ccw_bus_type); + destroy_workqueue(ccw_device_work); } subsys_initcall(init_ccw_bus_type); @@ -360,7 +380,7 @@ /* * Register recognized device. */ -void +static void io_subchannel_register(void *data) { struct ccw_device *cdev; @@ -389,6 +409,42 @@ put_device(&sch->dev); } +/* + * subchannel recognition done. Called from the state machine. + */ +void +io_subchannel_recog_done(struct ccw_device *cdev) +{ + struct subchannel *sch; + + if (css_init_done == 0) + return; + switch (cdev->private->state) { + case DEV_STATE_NOT_OPER: + /* Remove device found not operational. */ + sch = to_subchannel(cdev->dev.parent); + sch->dev.driver_data = 0; + put_device(&sch->dev); + if (cdev->dev.release) + cdev->dev.release(&cdev->dev); + break; + case DEV_STATE_OFFLINE: + /* + * We can't register the device in interrupt context so + * we schedule a work item. + */ + INIT_WORK(&cdev->private->kick_work, + io_subchannel_register, (void *) cdev); + queue_work(ccw_device_work, &cdev->private->kick_work); + break; + case DEV_STATE_BOXED: + /* Device did not respond in time. */ + break; + } + if (atomic_dec_and_test(&ccw_device_init_count)) + wake_up(&ccw_device_init_wq); +} + static void io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch) { @@ -419,6 +475,9 @@ /* Do first half of device_register. */ device_initialize(&cdev->dev); + /* Increase counter of devices currently in recognition. */ + atomic_inc(&ccw_device_init_count); + /* Start async. device sensing. */ spin_lock_irq(cdev->ccwlock); rc = ccw_device_recognition(cdev); @@ -428,6 +487,8 @@ put_device(&sch->dev); if (cdev->dev.release) cdev->dev.release(&cdev->dev); + if (atomic_dec_and_test(&ccw_device_init_count)) + wake_up(&ccw_device_init_wq); } } @@ -452,7 +513,8 @@ if (!cdev) return -ENOMEM; memset(cdev, 0, sizeof(struct ccw_device)); - cdev->private = kmalloc(sizeof(struct ccw_device_private), GFP_DMA); + cdev->private = kmalloc(sizeof(struct ccw_device_private), + GFP_KERNEL | GFP_DMA); if (!cdev->private) { kfree(cdev); return -ENOMEM; diff -urN linux-2.5.67/drivers/s390/cio/device.h linux-2.5.67-s390/drivers/s390/cio/device.h --- linux-2.5.67/drivers/s390/cio/device.h Mon Apr 7 19:32:23 2003 +++ linux-2.5.67-s390/drivers/s390/cio/device.h Mon Apr 14 19:11:50 2003 @@ -14,13 +14,11 @@ DEV_STATE_W4SENSE, DEV_STATE_DISBAND_PGID, DEV_STATE_BOXED, - /* special states for qdio */ - DEV_STATE_QDIO_INIT, - DEV_STATE_QDIO_ACTIVE, - DEV_STATE_QDIO_CLEANUP, /* states to wait for i/o completion before doing something */ DEV_STATE_ONLINE_VERIFY, DEV_STATE_W4SENSE_VERIFY, + DEV_STATE_CLEAR_VERIFY, + DEV_STATE_TIMEOUT_KILL, /* last element! */ NR_DEV_STATES }; @@ -63,7 +61,9 @@ cdev->private->state == DEV_STATE_BOXED); } -void io_subchannel_register(void *data); +extern struct workqueue_struct *ccw_device_work; + +void io_subchannel_recog_done(struct ccw_device *cdev); int ccw_device_recognition(struct ccw_device *); int ccw_device_online(struct ccw_device *); diff -urN linux-2.5.67/drivers/s390/cio/device_fsm.c linux-2.5.67-s390/drivers/s390/cio/device_fsm.c --- linux-2.5.67/drivers/s390/cio/device_fsm.c Mon Apr 7 19:31:23 2003 +++ linux-2.5.67-s390/drivers/s390/cio/device_fsm.c Mon Apr 14 19:11:50 2003 @@ -81,14 +81,14 @@ if (!(sch->schib.scsw.actl & SCSW_ACTL_CLEAR_PEND)) { /* Stage 2: halt io. */ while (cdev->private->iretry-- > 0) - if (cio_halt (sch, 0xC8C1D3E3) == 0) + if (cio_halt (sch) == 0) return -EBUSY; /* halt io unsuccessful. */ cdev->private->iretry = 255; /* 255 clear retries. */ } /* Stage 3: clear io. */ while (cdev->private->iretry-- > 0) - if (cio_clear (sch, 0x40C3D3D9) == 0) + if (cio_clear (sch) == 0) return -EBUSY; panic("Can't stop i/o on subchannel.\n"); } @@ -112,10 +112,6 @@ CIO_DEBUG(KERN_WARNING, 2, "SenseID : unknown device %04X on subchannel %04X\n", sch->schib.pmcw.dev, sch->irq); - sch->dev.driver_data = 0; - put_device(&sch->dev); - if (cdev->dev.release) - cdev->dev.release(&cdev->dev); break; case DEV_STATE_OFFLINE: /* fill out sense information */ @@ -131,11 +127,6 @@ "%04X/%02X\n", sch->schib.pmcw.dev, cdev->id.cu_type, cdev->id.cu_model, cdev->id.dev_type, cdev->id.dev_model); - if (css_init_done == 0) - break; - INIT_WORK(&cdev->private->kick_work, - io_subchannel_register, (void *) cdev); - schedule_work(&cdev->private->kick_work); break; case DEV_STATE_BOXED: CIO_DEBUG(KERN_WARNING, 2, @@ -143,6 +134,7 @@ sch->schib.pmcw.dev, sch->irq); break; } + io_subchannel_recog_done(cdev); wake_up(&cdev->private->wait_q); } @@ -219,9 +211,6 @@ static void ccw_device_recog_timeout(struct ccw_device *cdev, enum dev_event dev_event) { - struct subchannel *sch; - - sch = to_subchannel(cdev->dev.parent); if (ccw_device_cancel_halt_clear(cdev) == 0) ccw_device_recog_done(cdev, DEV_STATE_BOXED); else @@ -349,9 +338,6 @@ static void ccw_device_onoff_timeout(struct ccw_device *cdev, enum dev_event dev_event) { - struct subchannel *sch; - - sch = to_subchannel(cdev->dev.parent); if (ccw_device_cancel_halt_clear(cdev) == 0) ccw_device_done(cdev, DEV_STATE_BOXED); else @@ -393,8 +379,8 @@ // FIXME: not-oper indication to device driver ? ccw_device_call_handler(cdev); } - device_unregister(&cdev->dev); wake_up(&cdev->private->wait_q); + device_unregister(&cdev->dev); } /* @@ -438,14 +424,39 @@ /* Accumulate status and find out if a basic sense is needed. */ ccw_device_accumulate_irb(cdev, irb); if (cdev->private->flags.dosense) { - if (ccw_device_do_sense(cdev, irb) == 0) - cdev->private->state = DEV_STATE_W4SENSE; + if (ccw_device_do_sense(cdev, irb) == 0) { + /* Check if we have to trigger path verification. */ + if (irb->esw.esw0.erw.pvrf) + cdev->private->state = DEV_STATE_W4SENSE_VERIFY; + else + cdev->private->state = DEV_STATE_W4SENSE; + } return; } + if (irb->esw.esw0.erw.pvrf) + /* Try to start path verification. */ + ccw_device_online_verify(cdev, 0); /* No basic sense required, call the handler. */ ccw_device_call_handler(cdev); } +/* + * Got an timeout in online state. + */ +static void +ccw_device_online_timeout(struct ccw_device *cdev, enum dev_event dev_event) +{ + ccw_device_set_timeout(cdev, 0); + if (ccw_device_cancel_halt_clear(cdev) != 0) { + ccw_device_set_timeout(cdev, 3*HZ); + cdev->private->state = DEV_STATE_TIMEOUT_KILL; + return; + } + if (cdev->handler) + cdev->handler(cdev, cdev->private->intparm, + ERR_PTR(-ETIMEDOUT)); +} + static void ccw_device_irq_verify(struct ccw_device *cdev, enum dev_event dev_event) { @@ -491,11 +502,17 @@ /* Add basic sense info to irb. */ ccw_device_accumulate_basic_sense(cdev, irb); if (cdev->private->flags.dosense) { + /* Check if we have to trigger path verification. */ + if (irb->esw.esw0.erw.pvrf) + cdev->private->state = DEV_STATE_W4SENSE_VERIFY; /* Another basic sense is needed. */ ccw_device_do_sense(cdev, irb); return; } cdev->private->state = DEV_STATE_ONLINE; + if (irb->esw.esw0.erw.pvrf) + /* Try to start path verification. */ + ccw_device_online_verify(cdev, 0); /* Call the handler. */ ccw_device_call_handler(cdev); } @@ -527,103 +544,68 @@ ccw_device_call_handler(cdev); } -/* - * No operation action. This is used e.g. to ignore a timeout event in - * state offline. - */ static void -ccw_device_nop(struct ccw_device *cdev, enum dev_event dev_event) -{ -} - -/* - * Bug operation action. - */ -static void -ccw_device_bug(struct ccw_device *cdev, enum dev_event dev_event) -{ - printk(KERN_EMERG "dev_jumptable[%i][%i] == NULL\n", - cdev->private->state, dev_event); - BUG(); -} - -/* - * We've got an interrupt on establish queues. Check for errors and - * accordingly retry or move on. - */ -static void -ccw_device_qdio_init_irq(struct ccw_device *cdev, enum dev_event dev_event) +ccw_device_clear_verify(struct ccw_device *cdev, enum dev_event dev_event) { struct irb *irb; - struct subchannel *sch; irb = (struct irb *) __LC_IRB; /* Check for unsolicited interrupt. */ if (irb->scsw.stctl == (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) { - if (cdev->private->qdio_data && - cdev->private->qdio_data->establish_irq) - cdev->private->qdio_data->establish_irq(cdev, 0, irb); - wake_up(&cdev->private->wait_q); + if (cdev->handler) + cdev->handler (cdev, 0, irb); return; } + /* Accumulate status. We don't do basic sense. */ ccw_device_accumulate_irb(cdev, irb); - //FIXME: Basic sense? - sch = to_subchannel(cdev->dev.parent); - if (cdev->private->qdio_data && cdev->private->qdio_data->establish_irq) - cdev->private->qdio_data->establish_irq(cdev, sch->u_intparm, - &cdev->private->irb); - wake_up(&cdev->private->wait_q); + /* Try to start delayed device verification. */ + ccw_device_online_verify(cdev, 0); + /* Note: Don't call handler for cio initiated clear! */ } -/* - * Run into a timeout after establish queues, retry if needed. - */ static void -ccw_device_qdio_init_timeout(struct ccw_device *cdev, enum dev_event dev_event) +ccw_device_killing_irq(struct ccw_device *cdev, enum dev_event dev_event) { - ccw_device_set_timeout(cdev, 0); - if (cdev->private->qdio_data && - cdev->private->qdio_data->establish_timeout) - cdev->private->qdio_data->establish_timeout(cdev); - wake_up(&cdev->private->wait_q); + /* OK, i/o is dead now. Call interrupt handler. */ + cdev->private->state = DEV_STATE_ONLINE; + if (cdev->handler) + cdev->handler(cdev, cdev->private->intparm, + ERR_PTR(-ETIMEDOUT)); } static void -ccw_device_qdio_cleanup_irq(struct ccw_device *cdev, - enum dev_event dev_event) +ccw_device_killing_timeout(struct ccw_device *cdev, enum dev_event dev_event) { - struct irb *irb; - struct subchannel *sch; - - irb = (struct irb *) __LC_IRB; - /* Check for unsolicited interrupt. */ - if (irb->scsw.stctl == - (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) { - if (cdev->private->qdio_data && - cdev->private->qdio_data->cleanup_irq) - cdev->private->qdio_data->cleanup_irq(cdev, 0, irb); - wake_up(&cdev->private->wait_q); + if (ccw_device_cancel_halt_clear(cdev) != 0) { + ccw_device_set_timeout(cdev, 3*HZ); return; } - ccw_device_accumulate_irb(cdev, irb); - //FIXME: Basic sense? - sch = to_subchannel(cdev->dev.parent); - if (cdev->private->qdio_data && cdev->private->qdio_data->cleanup_irq) - cdev->private->qdio_data->cleanup_irq(cdev, sch->u_intparm, - &cdev->private->irb); - wake_up(&cdev->private->wait_q); + //FIXME: Can we get here? + cdev->private->state = DEV_STATE_ONLINE; + if (cdev->handler) + cdev->handler(cdev, cdev->private->intparm, + ERR_PTR(-ETIMEDOUT)); } +/* + * No operation action. This is used e.g. to ignore a timeout event in + * state offline. + */ static void -ccw_device_qdio_cleanup_timeout(struct ccw_device *cdev, - enum dev_event dev_event) +ccw_device_nop(struct ccw_device *cdev, enum dev_event dev_event) { - ccw_device_set_timeout(cdev, 0); - if (cdev->private->qdio_data && - cdev->private->qdio_data->cleanup_timeout) - cdev->private->qdio_data->cleanup_timeout(cdev); - wake_up(&cdev->private->wait_q); +} + +/* + * Bug operation action. + */ +static void +ccw_device_bug(struct ccw_device *cdev, enum dev_event dev_event) +{ + printk(KERN_EMERG "dev_jumptable[%i][%i] == NULL\n", + cdev->private->state, dev_event); + BUG(); } /* @@ -663,7 +645,7 @@ [DEV_STATE_ONLINE] { [DEV_EVENT_NOTOPER] ccw_device_online_notoper, [DEV_EVENT_INTERRUPT] ccw_device_irq, - [DEV_EVENT_TIMEOUT] ccw_device_nop, + [DEV_EVENT_TIMEOUT] ccw_device_online_timeout, [DEV_EVENT_VERIFY] ccw_device_online_verify, }, [DEV_STATE_W4SENSE] { @@ -684,25 +666,6 @@ [DEV_EVENT_TIMEOUT] ccw_device_nop, [DEV_EVENT_VERIFY] ccw_device_nop, }, - /* special states for qdio */ - [DEV_STATE_QDIO_INIT] { - [DEV_EVENT_NOTOPER] ccw_device_online_notoper, - [DEV_EVENT_INTERRUPT] ccw_device_qdio_init_irq, - [DEV_EVENT_TIMEOUT] ccw_device_qdio_init_timeout, - [DEV_EVENT_VERIFY] ccw_device_nop, - }, - [DEV_STATE_QDIO_ACTIVE] { - [DEV_EVENT_NOTOPER] ccw_device_online_notoper, - [DEV_EVENT_INTERRUPT] ccw_device_irq, - [DEV_EVENT_TIMEOUT] ccw_device_nop, - [DEV_EVENT_VERIFY] ccw_device_nop, - }, - [DEV_STATE_QDIO_CLEANUP] { - [DEV_EVENT_NOTOPER] ccw_device_online_notoper, - [DEV_EVENT_INTERRUPT] ccw_device_qdio_cleanup_irq, - [DEV_EVENT_TIMEOUT] ccw_device_qdio_cleanup_timeout, - [DEV_EVENT_VERIFY] ccw_device_nop, - }, /* states to wait for i/o completion before doing something */ [DEV_STATE_ONLINE_VERIFY] { [DEV_EVENT_NOTOPER] ccw_device_online_notoper, @@ -716,6 +679,18 @@ [DEV_EVENT_TIMEOUT] ccw_device_nop, [DEV_EVENT_VERIFY] ccw_device_nop, }, + [DEV_STATE_CLEAR_VERIFY] { + [DEV_EVENT_NOTOPER] ccw_device_online_notoper, + [DEV_EVENT_INTERRUPT] ccw_device_clear_verify, + [DEV_EVENT_TIMEOUT] ccw_device_nop, + [DEV_EVENT_VERIFY] ccw_device_nop, + }, + [DEV_STATE_TIMEOUT_KILL] { + [DEV_EVENT_NOTOPER] ccw_device_online_notoper, + [DEV_EVENT_INTERRUPT] ccw_device_killing_irq, + [DEV_EVENT_TIMEOUT] ccw_device_killing_timeout, + [DEV_EVENT_VERIFY] ccw_device_nop, //FIXME + }, }; /* @@ -736,3 +711,4 @@ dev_fsm_event(cdev, DEV_EVENT_INTERRUPT); } +EXPORT_SYMBOL_GPL(ccw_device_set_timeout); diff -urN linux-2.5.67/drivers/s390/cio/device_id.c linux-2.5.67-s390/drivers/s390/cio/device_id.c --- linux-2.5.67/drivers/s390/cio/device_id.c Mon Apr 7 19:30:33 2003 +++ linux-2.5.67-s390/drivers/s390/cio/device_id.c Mon Apr 14 19:11:50 2003 @@ -198,11 +198,13 @@ /* 0x00E2C9C4 == ebcdic "SID" */ ret = cio_start (sch, cdev->private->iccws, 0x00E2C9C4, cdev->private->imask); - /* ret is 0, -EBUSY or -ENODEV */ - if (ret != -EBUSY) + /* ret is 0, -EBUSY, -EACCES or -ENODEV */ + if (ret == -EBUSY) { + udelay(100); + continue; + } + if (ret != -EACCES) return ret; - udelay(100); - continue; } cdev->private->imask >>= 1; cdev->private->iretry = 5; diff -urN linux-2.5.67/drivers/s390/cio/device_ops.c linux-2.5.67-s390/drivers/s390/cio/device_ops.c --- linux-2.5.67/drivers/s390/cio/device_ops.c Mon Apr 7 19:32:17 2003 +++ linux-2.5.67-s390/drivers/s390/cio/device_ops.c Mon Apr 14 19:11:50 2003 @@ -49,12 +49,15 @@ return -ENODEV; if (cdev->private->state != DEV_STATE_ONLINE && cdev->private->state != DEV_STATE_W4SENSE && - cdev->private->state != DEV_STATE_QDIO_ACTIVE) + cdev->private->state != DEV_STATE_ONLINE_VERIFY && + cdev->private->state != DEV_STATE_W4SENSE_VERIFY) return -EINVAL; sch = to_subchannel(cdev->dev.parent); if (!sch) return -ENODEV; - ret = cio_clear(sch, intparm); + ret = cio_clear(sch); + if (ret == 0) + cdev->private->intparm = intparm; return ret; } @@ -67,17 +70,35 @@ if (!cdev) return -ENODEV; - if (cdev->private->state != DEV_STATE_ONLINE && - cdev->private->state != DEV_STATE_W4SENSE && - cdev->private->state != DEV_STATE_QDIO_INIT) - return -EINVAL; sch = to_subchannel(cdev->dev.parent); if (!sch) return -ENODEV; + if (cdev->private->state != DEV_STATE_ONLINE || + sch->schib.scsw.actl != 0) + return -EBUSY; ret = cio_set_options (sch, flags); if (ret) return ret; - ret = cio_start (sch, cpa, intparm, lpm); + /* 0xe4e2c5d9 == ebcdic "USER" */ + ret = cio_start (sch, cpa, 0xe4e2c5d9, lpm); + if (ret == 0) + cdev->private->intparm = intparm; + return ret; +} + +int +ccw_device_start_timeout(struct ccw_device *cdev, struct ccw1 *cpa, + unsigned long intparm, __u8 lpm, unsigned long flags, + int expires) +{ + int ret; + + if (!cdev) + return -ENODEV; + ccw_device_set_timeout(cdev, expires); + ret = ccw_device_start(cdev, cpa, intparm, lpm, flags); + if (ret != 0) + ccw_device_set_timeout(cdev, 0); return ret; } @@ -90,12 +111,16 @@ if (!cdev) return -ENODEV; if (cdev->private->state != DEV_STATE_ONLINE && - cdev->private->state != DEV_STATE_W4SENSE) + cdev->private->state != DEV_STATE_W4SENSE && + cdev->private->state != DEV_STATE_ONLINE_VERIFY && + cdev->private->state != DEV_STATE_W4SENSE_VERIFY) return -EINVAL; sch = to_subchannel(cdev->dev.parent); if (!sch) return -ENODEV; - ret = cio_halt(sch, intparm); + ret = cio_halt(sch); + if (ret == 0) + cdev->private->intparm = intparm; return ret; } @@ -106,12 +131,12 @@ if (!cdev) return -ENODEV; - if (cdev->private->state != DEV_STATE_ONLINE && - cdev->private->state != DEV_STATE_W4SENSE) - return -EINVAL; sch = to_subchannel(cdev->dev.parent); if (!sch) return -ENODEV; + if (cdev->private->state != DEV_STATE_ONLINE || + !(sch->schib.scsw.actl & SCSW_ACTL_SUSPENDED)) + return -EINVAL; return cio_resume(sch); } @@ -123,15 +148,6 @@ { struct subchannel *sch; unsigned int stctl; - void (*handler)(struct ccw_device *, unsigned long, struct irb *); - - if (cdev->private->state == DEV_STATE_QDIO_ACTIVE) { - if (cdev->private->qdio_data) - handler = cdev->private->qdio_data->handler; - else - handler = NULL; - } else - handler = cdev->handler; sch = to_subchannel(cdev->dev.parent); @@ -154,8 +170,9 @@ /* * Now we are ready to call the device driver interrupt handler. */ - if (handler) - handler(cdev, sch->u_intparm, &cdev->private->irb); + if (cdev->handler) + cdev->handler(cdev, cdev->private->intparm, + &cdev->private->irb); /* * Clear the old and now useless interrupt response block. @@ -192,6 +209,11 @@ static void ccw_device_wake_up(struct ccw_device *cdev, unsigned long ip, struct irb *irb) { + struct subchannel *sch; + + sch = to_subchannel(cdev->dev.parent); + if (!IS_ERR(irb)) + memcpy(&sch->schib.scsw, &irb->scsw, sizeof(struct scsw)); wake_up(&cdev->private->wait_q); } @@ -218,8 +240,7 @@ if (!cdev) return -ENODEV; - if (cdev->private->state != DEV_STATE_ONLINE && - cdev->private->state != DEV_STATE_W4SENSE) + if (cdev->private->state != DEV_STATE_ONLINE) return -EINVAL; if (!buffer || !length) return -EINVAL; @@ -251,7 +272,13 @@ wait_event(cdev->private->wait_q, sch->schib.scsw.actl == 0); spin_lock_irqsave(&sch->lock, flags); - /* FIXME: Check if we got sensible stuff. */ + /* Check at least for channel end / device end */ + if ((sch->schib.scsw.dstat != + (DEV_STAT_CHN_END|DEV_STAT_DEV_END)) || + (sch->schib.scsw.cstat != 0)) { + ret = -EIO; + continue; + } break; } } @@ -281,8 +308,7 @@ if (!cdev) return -ENODEV; - if (cdev->private->state != DEV_STATE_ONLINE && - cdev->private->state != DEV_STATE_W4SENSE) + if (cdev->private->state != DEV_STATE_ONLINE) return -EINVAL; if (cdev->private->flags.esid == 0) return -EOPNOTSUPP; @@ -300,7 +326,7 @@ if (!ciw || ciw->cmd == 0) return -EOPNOTSUPP; - rcd_buf = kmalloc(ciw->count, GFP_DMA); + rcd_buf = kmalloc(ciw->count, GFP_KERNEL | GFP_DMA); if (!rcd_buf) return -ENOMEM; memset (rcd_buf, 0, ciw->count); @@ -325,7 +351,13 @@ spin_unlock_irqrestore(&sch->lock, flags); wait_event(cdev->private->wait_q, sch->schib.scsw.actl == 0); spin_lock_irqsave(&sch->lock, flags); - /* FIXME: Check if we got sensible stuff. */ + /* Check at least for channel end / device end */ + if ((sch->schib.scsw.dstat != + (DEV_STAT_CHN_END|DEV_STAT_DEV_END)) || + (sch->schib.scsw.cstat != 0)) { + ret = -EIO; + continue; + } break; } /* Restore interrupt handler. */ @@ -363,13 +395,15 @@ MODULE_LICENSE("GPL"); +EXPORT_SYMBOL(ccw_device_set_options); EXPORT_SYMBOL(ccw_device_clear); EXPORT_SYMBOL(ccw_device_halt); EXPORT_SYMBOL(ccw_device_resume); +EXPORT_SYMBOL(ccw_device_start_timeout); EXPORT_SYMBOL(ccw_device_start); EXPORT_SYMBOL(ccw_device_get_ciw); EXPORT_SYMBOL(ccw_device_get_path_mask); -EXPORT_SYMBOL (read_conf_data); -EXPORT_SYMBOL (read_dev_chars); +EXPORT_SYMBOL(read_conf_data); +EXPORT_SYMBOL(read_dev_chars); EXPORT_SYMBOL(_ccw_device_get_subchannel_number); EXPORT_SYMBOL(_ccw_device_get_device_number); diff -urN linux-2.5.67/drivers/s390/cio/device_pgid.c linux-2.5.67-s390/drivers/s390/cio/device_pgid.c --- linux-2.5.67/drivers/s390/cio/device_pgid.c Mon Apr 7 19:31:42 2003 +++ linux-2.5.67-s390/drivers/s390/cio/device_pgid.c Mon Apr 14 19:11:50 2003 @@ -51,14 +51,23 @@ /* 0xe2d5c9c4 == ebcdic "SNID" */ ret = cio_start (sch, cdev->private->iccws, 0xE2D5C9C4, cdev->private->imask); - /* ret is 0, -EBUSY or -ENODEV */ - if (ret != -EBUSY) + /* ret is 0, -EBUSY, -EACCES or -ENODEV */ + if (ret == -EBUSY) { + CIO_MSG_EVENT(2, + "SNID - device %04X, start_io() " + "reports rc : %d, retrying ...\n", + sch->schib.pmcw.dev, ret); + udelay(100); + continue; + } + if (ret != -EACCES) return ret; - CIO_MSG_EVENT(2, "SNID - device %04X, start_io() " - "reports rc : %d, retrying ...\n", - sch->schib.pmcw.dev, ret); - udelay(100); - continue; + CIO_MSG_EVENT(2, "SNID - Device %04X on Subchannel " + "%04X, lpm %02X, became 'not " + "operational'\n", + sch->schib.pmcw.dev, sch->irq, + cdev->private->imask); + } cdev->private->imask >>= 1; cdev->private->iretry = 5; @@ -231,7 +240,9 @@ /* 0xE2D7C9C4 == ebcdic "SPID" */ ret = cio_start (sch, cdev->private->iccws, 0xE2D7C9C4, cdev->private->imask); - /* ret is 0, -EBUSY or -ENODEV */ + /* ret is 0, -EBUSY, -EACCES or -ENODEV */ + if (ret == -EACCES) + break; if (ret != -EBUSY) return ret; udelay(100); diff -urN linux-2.5.67/drivers/s390/cio/device_status.c linux-2.5.67-s390/drivers/s390/cio/device_status.c --- linux-2.5.67/drivers/s390/cio/device_status.c Mon Apr 7 19:31:16 2003 +++ linux-2.5.67-s390/drivers/s390/cio/device_status.c Mon Apr 14 19:11:50 2003 @@ -167,7 +167,7 @@ /* Copy authorization bit. */ cdev_irb->esw.esw0.erw.auth = irb->esw.esw0.erw.auth; - /* Copy path verification required flag. FIXME: how to verify ?? */ + /* Copy path verification required flag. */ cdev_irb->esw.esw0.erw.pvrf = irb->esw.esw0.erw.pvrf; /* Copy concurrent sense bit. */ cdev_irb->esw.esw0.erw.cons = irb->esw.esw0.erw.cons; @@ -309,7 +309,7 @@ sch->sense_ccw.flags = CCW_FLAG_SLI; /* 0xe2C5D5E2 == "SENS" in ebcdic */ - return cio_start (sch, &sch->sense_ccw, 0xE2C5D5E2, 0); + return cio_start (sch, &sch->sense_ccw, 0xE2C5D5E2, 0xff); } /* diff -urN linux-2.5.67/drivers/s390/cio/qdio.c linux-2.5.67-s390/drivers/s390/cio/qdio.c --- linux-2.5.67/drivers/s390/cio/qdio.c Mon Apr 7 19:31:50 2003 +++ linux-2.5.67-s390/drivers/s390/cio/qdio.c Mon Apr 14 19:11:50 2003 @@ -34,7 +34,6 @@ #include <linux/module.h> #include <linux/init.h> -#include <linux/version.h> #include <linux/slab.h> #include <linux/kernel.h> #include <linux/proc_fs.h> @@ -54,8 +53,9 @@ #include "airq.h" #include "qdio.h" #include "ioasm.h" +#include "chsc.h" -#define VERSION_QDIO_C "$Revision: 1.34 $" +#define VERSION_QDIO_C "$Revision: 1.48 $" /****************** MODULE PARAMETER VARIABLES ********************/ MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com>"); @@ -87,7 +87,6 @@ static debug_info_t *qdio_dbf_slsb_in; #endif /* QDIO_DBF_LIKE_HELL */ -static struct qdio_chsc_area *chsc_area; /* iQDIO stuff: */ static volatile struct qdio_q *iq_list=NULL; /* volatile as it could change during a while loop */ @@ -611,16 +610,13 @@ iqdio_is_inbound_q_done(struct qdio_q *q) { int no_used; -#ifdef QDIO_DBF_LIKE_HELL char dbf_text[15]; -#endif /* QDIO_DBF_LIKE_HELL */ no_used=atomic_read(&q->number_of_buffers_used); /* propagate the change from 82 to 80 through VM */ SYNC_MEMORY; -#ifdef QDIO_DBF_LIKE_HELL if (no_used) { sprintf(dbf_text,"iqisnt%02x",no_used); QDIO_DBF_TEXT4(0,trace,dbf_text); @@ -628,7 +624,6 @@ QDIO_DBF_TEXT4(0,trace,"iniqisdo"); } QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); -#endif /* QDIO_DBF_LIKE_HELL */ if (!no_used) return 1; @@ -664,9 +659,7 @@ qdio_is_inbound_q_done(struct qdio_q *q) { int no_used; -#ifdef QDIO_DBF_LIKE_HELL char dbf_text[15]; -#endif /* QDIO_DBF_LIKE_HELL */ no_used=atomic_read(&q->number_of_buffers_used); @@ -677,11 +670,9 @@ SYNC_MEMORY; if (!no_used) { -#ifdef QDIO_DBF_LIKE_HELL QDIO_DBF_TEXT4(0,trace,"inqisdnA"); QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); QDIO_DBF_TEXT4(0,trace,dbf_text); -#endif /* QDIO_DBF_LIKE_HELL */ return 1; } @@ -703,20 +694,16 @@ * has (probably) not moved (see qdio_inbound_processing) */ if (NOW>GET_SAVED_TIMESTAMP(q)+q->timing.threshold) { -#ifdef QDIO_DBF_LIKE_HELL QDIO_DBF_TEXT4(0,trace,"inqisdon"); QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); sprintf(dbf_text,"pf%02xcn%02x",q->first_to_check,no_used); QDIO_DBF_TEXT4(0,trace,dbf_text); -#endif /* QDIO_DBF_LIKE_HELL */ return 1; } else { -#ifdef QDIO_DBF_LIKE_HELL QDIO_DBF_TEXT4(0,trace,"inqisntd"); QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); sprintf(dbf_text,"pf%02xcn%02x",q->first_to_check,no_used); QDIO_DBF_TEXT4(0,trace,dbf_text); -#endif /* QDIO_DBF_LIKE_HELL */ return 0; } } @@ -725,12 +712,10 @@ qdio_kick_inbound_handler(struct qdio_q *q) { int count, start, end, real_end, i; -#ifdef QDIO_DBF_LIKE_HELL char dbf_text[15]; QDIO_DBF_TEXT4(0,trace,"kickinh"); QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); -#endif /* QDIO_DBF_LIKE_HELL */ start=q->first_element_to_kick; real_end=q->first_to_check; @@ -744,10 +729,8 @@ i=(i+1)&(QDIO_MAX_BUFFERS_PER_Q-1); } -#ifdef QDIO_DBF_LIKE_HELL sprintf(dbf_text,"s=%2xc=%2x",start,count); QDIO_DBF_TEXT4(0,trace,dbf_text); -#endif /* QDIO_DBF_LIKE_HELL */ if (q->state==QDIO_IRQ_STATE_ACTIVE) q->handler(q->cdev, @@ -950,13 +933,10 @@ qdio_is_outbound_q_done(struct qdio_q *q) { int no_used; -#ifdef QDIO_DBF_LIKE_HELL char dbf_text[15]; -#endif /* QDIO_DBF_LIKE_HELL */ no_used=atomic_read(&q->number_of_buffers_used); -#ifdef QDIO_DBF_LIKE_HELL if (no_used) { sprintf(dbf_text,"oqisnt%02x",no_used); QDIO_DBF_TEXT4(0,trace,dbf_text); @@ -964,7 +944,6 @@ QDIO_DBF_TEXT4(0,trace,"oqisdone"); } QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); -#endif /* QDIO_DBF_LIKE_HELL */ return (no_used==0); } @@ -1015,10 +994,7 @@ qdio_kick_outbound_handler(struct qdio_q *q) { int start, end, real_end, count; - -#ifdef QDIO_DBF_LIKE_HELL char dbf_text[15]; -#endif /* QDIO_DBF_LIKE_HELL */ start = q->first_element_to_kick; /* last_move_ftc was just updated */ @@ -1031,10 +1007,8 @@ QDIO_DBF_TEXT4(0,trace,"kickouth"); QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); -#ifdef QDIO_DBF_LIKE_HELL sprintf(dbf_text,"s=%2xc=%2x",start,count); QDIO_DBF_TEXT4(0,trace,dbf_text); -#endif /* QDIO_DBF_LIKE_HELL */ if (q->state==QDIO_IRQ_STATE_ACTIVE) q->handler(q->cdev,QDIO_STATUS_OUTBOUND_INT| @@ -1239,8 +1213,6 @@ if (irq_ptr->qdr) kfree(irq_ptr->qdr); kfree(irq_ptr); - QDIO_DBF_TEXT3(0,setup,"MOD_DEC_"); - MOD_DEC_USE_COUNT; } static void @@ -1482,7 +1454,7 @@ } static int -iqdio_thinint_handler(__u32 intparm) +iqdio_thinint_handler(void) { QDIO_DBF_TEXT4(0,trace,"thin_int"); @@ -1500,7 +1472,7 @@ } static void -qdio_set_state(struct qdio_irq *irq_ptr,int state) +qdio_set_state(struct qdio_irq *irq_ptr, enum qdio_irq_states state) { int i; char dbf_text[15]; @@ -1570,23 +1542,90 @@ } } +static void qdio_establish_handle_irq(struct ccw_device*, int, int); + +static inline void +qdio_handle_activate_check(struct ccw_device *cdev, unsigned long intparm, + int cstat, int dstat) +{ + struct qdio_irq *irq_ptr; + struct qdio_q *q; + char dbf_text[15]; + + irq_ptr = cdev->private->qdio_data; + + QDIO_DBF_TEXT2(1, trace, "ick2"); + sprintf(dbf_text,"%s", cdev->dev.bus_id); + QDIO_DBF_TEXT2(1,trace,dbf_text); + QDIO_DBF_HEX2(0,trace,&intparm,sizeof(int)); + QDIO_DBF_HEX2(0,trace,&dstat,sizeof(int)); + QDIO_DBF_HEX2(0,trace,&cstat,sizeof(int)); + QDIO_PRINT_ERR("received check condition on activate " \ + "queues on device %s (cs=x%x, ds=x%x).\n", + cdev->dev.bus_id, cstat, dstat); + if (irq_ptr->no_input_qs) { + q=irq_ptr->input_qs[0]; + } else if (irq_ptr->no_output_qs) { + q=irq_ptr->output_qs[0]; + } else { + QDIO_PRINT_ERR("oops... no queue registered for device %s!?\n", + cdev->dev.bus_id); + goto omit_handler_call; + } + q->handler(q->cdev,QDIO_STATUS_ACTIVATE_CHECK_CONDITION| + QDIO_STATUS_LOOK_FOR_ERROR, + 0,0,0,-1,-1,q->int_parm); +omit_handler_call: + qdio_set_state(irq_ptr,QDIO_IRQ_STATE_STOPPED); + +} + +static void +qdio_timeout_handler(struct ccw_device *cdev) +{ + struct qdio_irq *irq_ptr; + char dbf_text[15]; + + QDIO_DBF_TEXT2(0, trace, "qtoh"); + sprintf(dbf_text, "%s", cdev->dev.bus_id); + QDIO_DBF_TEXT2(0, trace, dbf_text); + + irq_ptr = cdev->private->qdio_data; + sprintf(dbf_text, "state:%d", irq_ptr->state); + QDIO_DBF_TEXT2(0, trace, dbf_text); + + switch (irq_ptr->state) { + case QDIO_IRQ_STATE_INACTIVE: + QDIO_PRINT_ERR("establish queues on irq %04x: timed out\n", + irq_ptr->irq); + QDIO_DBF_TEXT2(1,setup,"eq:timeo"); + qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR); + break; + case QDIO_IRQ_STATE_CLEANUP: + QDIO_PRINT_INFO("Did not get interrupt on cleanup, irq=0x%x.\n", + irq_ptr->irq); + qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR); + break; + default: + BUG(); + } + wake_up(&cdev->private->wait_q); + +} + static void qdio_handler(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) { struct qdio_irq *irq_ptr; - struct qdio_q *q; int cstat,dstat; char dbf_text[15]; - cstat = irb->scsw.cstat; - dstat = irb->scsw.dstat; - QDIO_DBF_TEXT4(0, trace, "qint"); sprintf(dbf_text, "%s", cdev->dev.bus_id); QDIO_DBF_TEXT4(0, trace, dbf_text); - if (!intparm || !cdev) { - QDIO_PRINT_STUPID("got unsolicited interrupt in qdio " \ + if (!intparm) { + QDIO_PRINT_ERR("got unsolicited interrupt in qdio " \ "handler, device %s\n", cdev->dev.bus_id); return; } @@ -1601,39 +1640,58 @@ return; } + if (IS_ERR(irb)) { + /* Currently running i/o is in error. */ + switch (PTR_ERR(irb)) { + case -EIO: + QDIO_PRINT_ERR("i/o error on device %s\n", + cdev->dev.bus_id); + //FIXME: hm? + return; + case -ETIMEDOUT: + qdio_timeout_handler(cdev); + return; + default: + QDIO_PRINT_ERR("unknown error state %ld on device %s\n", + PTR_ERR(irb), cdev->dev.bus_id); + return; + } + } + qdio_irq_check_sense(irq_ptr->irq, irb); - if (cstat & SCHN_STAT_PCI) { - qdio_handle_pci(irq_ptr); - return; - } + sprintf(dbf_text, "state:%d", irq_ptr->state); + QDIO_DBF_TEXT4(0, trace, dbf_text); - if ((cstat&~SCHN_STAT_PCI)||dstat) { - QDIO_DBF_TEXT2(1, trace, "ick2"); - sprintf(dbf_text,"%s", cdev->dev.bus_id); - QDIO_DBF_TEXT2(1,trace,dbf_text); - QDIO_DBF_HEX2(0,trace,&intparm,sizeof(int)); - QDIO_DBF_HEX2(0,trace,&dstat,sizeof(int)); - QDIO_DBF_HEX2(0,trace,&cstat,sizeof(int)); - QDIO_PRINT_ERR("received check condition on activate " \ - "queues on device %s (cs=x%x, ds=x%x).\n", - cdev->dev.bus_id, cstat, dstat); - if (irq_ptr->no_input_qs) { - q=irq_ptr->input_qs[0]; - } else if (irq_ptr->no_output_qs) { - q=irq_ptr->output_qs[0]; - } else { - QDIO_PRINT_ERR("oops... no queue registered for " \ - "device %s!?\n", cdev->dev.bus_id); - goto omit_handler_call; - } - q->handler(q->cdev,QDIO_STATUS_ACTIVATE_CHECK_CONDITION| - QDIO_STATUS_LOOK_FOR_ERROR, - 0,0,0,-1,-1,q->int_parm); - omit_handler_call: - qdio_set_state(irq_ptr,QDIO_IRQ_STATE_STOPPED); - return; + cstat = irb->scsw.cstat; + dstat = irb->scsw.dstat; + + switch (irq_ptr->state) { + case QDIO_IRQ_STATE_INACTIVE: + qdio_establish_handle_irq(cdev, cstat, dstat); + break; + + case QDIO_IRQ_STATE_CLEANUP: + qdio_set_state(irq_ptr, QDIO_IRQ_STATE_INACTIVE); + break; + + case QDIO_IRQ_STATE_ESTABLISHED: + case QDIO_IRQ_STATE_ACTIVE: + if (cstat & SCHN_STAT_PCI) { + qdio_handle_pci(irq_ptr); + break; + } + + if ((cstat&~SCHN_STAT_PCI)||dstat) { + qdio_handle_activate_check(cdev, intparm, cstat, dstat); + break; + } + default: + QDIO_PRINT_ERR("got interrupt for queues in state %d on " \ + "device %s?!\n", + irq_ptr->state, cdev->dev.bus_id); } + wake_up(&cdev->private->wait_q); } @@ -1680,60 +1738,108 @@ static unsigned char qdio_check_siga_needs(int sch) { - int resp_code,result; + int result; + unsigned char qdioac; + + struct { + struct chsc_header request; + u16 reserved1; + u16 first_sch; + u16 reserved2; + u16 last_sch; + u32 reserved3; + struct chsc_header response; + u32 reserved4; + u8 flags; + u8 reserved5; + u16 sch; + u8 qfmt; + u8 reserved6; + u8 qdioac; + u8 sch_class; + u8 reserved7; + u8 icnt; + u8 reserved8; + u8 ocnt; + } *ssqd_area; + + ssqd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!ssqd_area) { + QDIO_PRINT_WARN("Could not get memory for chsc. Using all " \ + "SIGAs for sch x%x.\n", sch); + return -1; /* all flags set */ + } + ssqd_area->request = (struct chsc_header) { + .length = 0x0010, + .code = 0x0024, + }; - memset(chsc_area,0,sizeof(struct qdio_chsc_area)); - chsc_area->request_block.command_code1=0x0010; /* length */ - chsc_area->request_block.command_code2=0x0024; /* op code */ - chsc_area->request_block.first_sch=sch; - chsc_area->request_block.last_sch=sch; + ssqd_area->first_sch = sch; + ssqd_area->last_sch = sch; - result=chsc(chsc_area); + result=chsc(ssqd_area); if (result) { QDIO_PRINT_WARN("CHSC returned cc %i. Using all " \ "SIGAs for sch x%x.\n", result,sch); - return -1; /* all flags set */ + qdioac = -1; /* all flags set */ + goto out; } - resp_code=chsc_area->request_block.operation_data_area. - store_qdio_data_response.response_code; - if (resp_code!=QDIO_CHSC_RESPONSE_CODE_OK) { + if (ssqd_area->response.code != QDIO_CHSC_RESPONSE_CODE_OK) { QDIO_PRINT_WARN("response upon checking SIGA needs " \ "is 0x%x. Using all SIGAs for sch x%x.\n", - resp_code,sch); - return -1; /* all flags set */ + ssqd_area->response.code, sch); + qdioac = -1; /* all flags set */ + goto out; } - if ( - (!(chsc_area->request_block.operation_data_area. - store_qdio_data_response.flags&CHSC_FLAG_QDIO_CAPABILITY)) || - (!(chsc_area->request_block.operation_data_area. - store_qdio_data_response.flags&CHSC_FLAG_VALIDITY)) || - (chsc_area->request_block.operation_data_area. - store_qdio_data_response.sch!=sch) - ) { + if (!(ssqd_area->flags & CHSC_FLAG_QDIO_CAPABILITY) || + !(ssqd_area->flags & CHSC_FLAG_VALIDITY) || + (ssqd_area->sch != sch)) { QDIO_PRINT_WARN("huh? problems checking out sch x%x... " \ "using all SIGAs.\n",sch); - return CHSC_FLAG_SIGA_INPUT_NECESSARY | + qdioac = CHSC_FLAG_SIGA_INPUT_NECESSARY | CHSC_FLAG_SIGA_OUTPUT_NECESSARY | CHSC_FLAG_SIGA_SYNC_NECESSARY; /* worst case */ + goto out; } - return chsc_area->request_block.operation_data_area. - store_qdio_data_response.qdioac; + qdioac = ssqd_area->qdioac; +out: + free_page ((unsigned long) ssqd_area); + return qdioac; } -/* the chsc_area is locked by the lock in qdio_activate */ static unsigned int -iqdio_check_chsc_availability(void) { +iqdio_check_chsc_availability(void) +{ int result; - int i; - memset(chsc_area,0,sizeof(struct qdio_chsc_area)); - chsc_area->request_block.command_code1=0x0010; - chsc_area->request_block.command_code2=0x0010; - result=chsc(chsc_area); + struct { + struct chsc_header request; + u32 reserved1; + u32 reserved2; + u32 reserved3; + struct chsc_header response; + u32 reserved4; + u32 general_char[510]; + u32 chsc_char[518]; + } *scsc_area; + + scsc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!scsc_area) { + QDIO_PRINT_WARN("Was not able to determine available" \ + "CHSCs due to no memory.\n"); + return -ENOMEM; + } + + scsc_area->request = (struct chsc_header) { + .length = 0x0010, + .code = 0x0010, + }; + + result=chsc(scsc_area); if (result) { QDIO_PRINT_WARN("Was not able to determine " \ "available CHSCs, cc=%i.\n", @@ -1741,10 +1847,8 @@ result=-EIO; goto exit; } - result=0; - i=chsc_area->request_block.operation_data_area. - store_qdio_data_response.response_code; - if (i!=1) { + + if (scsc_area->response.code != 1) { QDIO_PRINT_WARN("Was not able to determine " \ "available CHSCs.\n"); result=-EIO; @@ -1753,24 +1857,24 @@ /* 4: request block * 2: general char * 512: chsc char */ - if ( (*(((unsigned int*)(chsc_area))+4+2+1)&0x00800000)!=0x00800000) { + if ((scsc_area->general_char[1] & 0x00800000) != 0x00800000) { QDIO_PRINT_WARN("Adapter interruption facility not " \ "installed.\n"); result=-ENOENT; goto exit; } - if ( (*(((unsigned int*)(chsc_area))+4+512+3)&0x00180000)!= - 0x00180000 ) { + if ((scsc_area->chsc_char[2] & 0x00180000) != 0x00180000) { QDIO_PRINT_WARN("Set Chan Subsys. Char. & Fast-CHSCs " \ "not available.\n"); result=-ENOENT; goto exit; } exit: + free_page ((unsigned long) scsc_area); return result; } -/* the chsc_area is locked by the lock in qdio_activate */ + static unsigned int iqdio_set_subchannel_ind(struct qdio_irq *irq_ptr, int reset_to_zero) { @@ -1782,7 +1886,27 @@ unsigned int resp_code; int result; - if (!irq_ptr->is_iqdio_irq) return -ENODEV; + struct { + struct chsc_header request; + u16 operation_code; + u16 reserved1; + u32 reserved2; + u32 reserved3; + u64 summary_indicator_addr; + u64 subchannel_indicator_addr; + u32 ks:4; + u32 kc:4; + u32 reserved4:21; + u32 isc:3; + u32 reserved5[2]; + u32 subsystem_id; + u32 reserved6[1004]; + struct chsc_header response; + u32 reserved7; + } *scssc_area; + + if (!irq_ptr->is_iqdio_irq) + return -ENODEV; if (reset_to_zero) { real_addr_local_summary_bit=0; @@ -1794,52 +1918,57 @@ virt_to_phys((volatile void *)irq_ptr->dev_st_chg_ind); } - memset(chsc_area,0,sizeof(struct qdio_chsc_area)); - chsc_area->request_block.command_code1=0x0fe0; - chsc_area->request_block.command_code2=0x0021; - chsc_area->request_block.operation_code=0; - chsc_area->request_block.image_id=0; - - chsc_area->request_block.operation_data_area.set_chsc. - summary_indicator_addr=real_addr_local_summary_bit; - chsc_area->request_block.operation_data_area.set_chsc. - subchannel_indicator_addr=real_addr_dev_st_chg_ind; - chsc_area->request_block.operation_data_area.set_chsc. - ks=QDIO_STORAGE_KEY; - chsc_area->request_block.operation_data_area.set_chsc. - kc=QDIO_STORAGE_KEY; - chsc_area->request_block.operation_data_area.set_chsc. - isc=IQDIO_THININT_ISC; - chsc_area->request_block.operation_data_area.set_chsc. - subsystem_id=(1<<16)+irq_ptr->irq; + scssc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!scssc_area) { + QDIO_PRINT_WARN("No memory for setting indicators on " \ + "subchannel x%x.\n", irq_ptr->irq); + return -ENOMEM; + } + scssc_area->request = (struct chsc_header) { + .length = 0x0fe0, + .code = 0x0021, + }; + scssc_area->operation_code = 0; + + scssc_area->summary_indicator_addr = real_addr_local_summary_bit; + scssc_area->subchannel_indicator_addr = real_addr_dev_st_chg_ind; + scssc_area->ks = QDIO_STORAGE_KEY; + scssc_area->kc = QDIO_STORAGE_KEY; + scssc_area->isc = IQDIO_THININT_ISC; + scssc_area->subsystem_id = (1<<16) + irq_ptr->irq; - result=chsc(chsc_area); + result = chsc(scssc_area); if (result) { QDIO_PRINT_WARN("could not set indicators on irq x%x, " \ "cc=%i.\n",irq_ptr->irq,result); - return -EIO; + result = -EIO; + goto out; } - resp_code=chsc_area->response_block.response_code; + resp_code = scssc_area->response.code; if (resp_code!=QDIO_CHSC_RESPONSE_CODE_OK) { QDIO_PRINT_WARN("response upon setting indicators " \ "is 0x%x.\n",resp_code); sprintf(dbf_text,"sidR%4x",resp_code); QDIO_DBF_TEXT1(0,trace,dbf_text); QDIO_DBF_TEXT1(0,setup,dbf_text); - ptr=&chsc_area->response_block; + ptr=&scssc_area->response; QDIO_DBF_HEX2(1,setup,&ptr,QDIO_DBF_SETUP_LEN); - return -EIO; + result = -EIO; + goto out; } QDIO_DBF_TEXT2(0,setup,"setscind"); QDIO_DBF_HEX2(0,setup,&real_addr_local_summary_bit, sizeof(unsigned long)); QDIO_DBF_HEX2(0,setup,&real_addr_dev_st_chg_ind,sizeof(unsigned long)); - return 0; + result = 0; +out: + free_page ((unsigned long) scssc_area); + return result; + } -/* chsc_area would have to be locked if called from outside qdio_activate */ static unsigned int iqdio_set_delay_target(struct qdio_irq *irq_ptr, unsigned long delay_target) { @@ -1848,34 +1977,59 @@ void *ptr; char dbf_text[15]; - if (!irq_ptr->is_iqdio_irq) return -ENODEV; + struct { + struct chsc_header request; + u16 operation_code; + u16 reserved1; + u32 reserved2; + u32 reserved3; + u32 reserved4[2]; + u32 delay_target; + u32 reserved5[1009]; + struct chsc_header response; + u32 reserved6; + } *scsscf_area; - memset(chsc_area,0,sizeof(struct qdio_chsc_area)); - chsc_area->request_block.command_code1=0x0fe0; - chsc_area->request_block.command_code2=0x1027; - chsc_area->request_block.operation_data_area.set_chsc_fast. - delay_target=delay_target<<16; + if (!irq_ptr->is_iqdio_irq) + return -ENODEV; + + scsscf_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!scsscf_area) { + QDIO_PRINT_WARN("No memory for setting delay target on " \ + "subchannel x%x.\n", irq_ptr->irq); + return -ENOMEM; + } + scsscf_area->request = (struct chsc_header) { + .length = 0x0fe0, + .code = 0x1027, + }; - result=chsc(chsc_area); + scsscf_area->delay_target = delay_target<<16; + + result=chsc(scsscf_area); if (result) { QDIO_PRINT_WARN("could not set delay target on irq x%x, " \ "cc=%i. Continuing.\n",irq_ptr->irq,result); - return -EIO; + result = -EIO; + goto out; } - resp_code=chsc_area->response_block.response_code; + resp_code = scsscf_area->response.code; if (resp_code!=QDIO_CHSC_RESPONSE_CODE_OK) { QDIO_PRINT_WARN("response upon setting delay target " \ "is 0x%x. Continuing.\n",resp_code); sprintf(dbf_text,"sdtR%4x",resp_code); QDIO_DBF_TEXT1(0,trace,dbf_text); QDIO_DBF_TEXT1(0,setup,dbf_text); - ptr=&chsc_area->response_block; + ptr=&scsscf_area->response; QDIO_DBF_HEX2(1,trace,&ptr,QDIO_DBF_TRACE_LEN); } QDIO_DBF_TEXT2(0,trace,"delytrgt"); QDIO_DBF_HEX2(0,trace,&delay_target,sizeof(unsigned long)); - return 0; + result = 0; /* not critical */ +out: + free_page ((unsigned long) scsscf_area); + return result; } int @@ -1972,19 +2126,22 @@ ccw_device_halt(cdev, QDIO_DOING_CLEANUP); timeout=QDIO_CLEANUP_HALT_TIMEOUT; } - cdev->private->state = DEV_STATE_QDIO_CLEANUP; + qdio_set_state(irq_ptr, QDIO_IRQ_STATE_CLEANUP); ccw_device_set_timeout(cdev, timeout); spin_unlock_irqrestore(get_ccwdev_lock(cdev),flags); - wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev)); - + wait_event(cdev->private->wait_q, + irq_ptr->state == QDIO_IRQ_STATE_INACTIVE || + irq_ptr->state == QDIO_IRQ_STATE_ERR); + /* Ignore errors. */ + qdio_set_state(irq_ptr, QDIO_IRQ_STATE_INACTIVE); out: up(&irq_ptr->setting_up_sema); return result; } static inline void -qdio_cleanup_finish(struct qdio_irq *irq_ptr) +qdio_cleanup_finish(struct ccw_device *cdev, struct qdio_irq *irq_ptr) { if (irq_ptr->is_iqdio_irq) { qdio_put_indicator((__u32*)irq_ptr->dev_st_chg_ind); @@ -1992,6 +2149,10 @@ /* reset adapter interrupt indicators */ } + /* exchange int handlers, if necessary */ + if ((void*)cdev->handler == (void*)qdio_handler) + cdev->handler=irq_ptr->original_int_handler; + qdio_set_state(irq_ptr,QDIO_IRQ_STATE_INACTIVE); } @@ -2014,51 +2175,16 @@ if (cdev->private->state != DEV_STATE_ONLINE) return -EINVAL; - qdio_cleanup_finish(irq_ptr); + qdio_cleanup_finish(cdev, irq_ptr); cdev->private->qdio_data = 0; up(&irq_ptr->setting_up_sema); qdio_release_irq_memory(irq_ptr); + module_put(THIS_MODULE); return 0; } -static void -qdio_cleanup_handle_timeout(struct ccw_device *cdev) -{ - unsigned long flags; - struct qdio_irq *irq_ptr; - - irq_ptr = cdev->private->qdio_data; - - spin_lock_irqsave(get_ccwdev_lock(cdev),flags); - QDIO_PRINT_INFO("Did not get interrupt on cleanup, irq=0x%x.\n", - irq_ptr->irq); - - spin_unlock_irqrestore(get_ccwdev_lock(cdev),flags); - - cdev->private->state = DEV_STATE_ONLINE; - wake_up(&cdev->private->wait_q); -} - -static void -qdio_cleanup_handle_irq(struct ccw_device *cdev, unsigned long intparm, - struct irb *irb) -{ - struct qdio_irq *irq_ptr; - - if (intparm == 0) - QDIO_PRINT_WARN("Got unsolicited interrupt on cleanup " - "(irq 0x%x).\n", cdev->private->irq); - - irq_ptr = cdev->private->qdio_data; - - qdio_irq_check_sense(irq_ptr->irq, irb); - - cdev->private->state = DEV_STATE_ONLINE; - wake_up(&cdev->private->wait_q); -} - static inline void qdio_allocate_do_dbf(struct qdio_initialize *init_data) { @@ -2134,24 +2260,6 @@ irq_ptr->qdr->qdf0[i+j].dkey=QDIO_STORAGE_KEY; } -void -qdio_establish_handle_timeout(struct ccw_device *cdev) -{ - struct qdio_irq *irq_ptr; - - irq_ptr = cdev->private->qdio_data; - - QDIO_PRINT_ERR("establish queues on irq %04x: timed out\n", - irq_ptr->irq); - QDIO_DBF_TEXT2(1,setup,"eq:timeo"); - /* - * FIXME: - * this is broken, - * we are in the context of a timer interrupt and - * qdio_shutdown calls schedule - */ - qdio_shutdown(cdev,QDIO_FLAG_CLEANUP_USING_CLEAR); -} static inline void qdio_initialize_set_siga_flags_input(struct qdio_irq *irq_ptr) @@ -2208,7 +2316,7 @@ QDIO_PRINT_ERR("received check condition on establish " \ "queues on irq 0x%x (cs=x%x, ds=x%x).\n", irq_ptr->irq,cstat,dstat); - qdio_set_state(irq_ptr,QDIO_IRQ_STATE_STOPPED); + qdio_set_state(irq_ptr,QDIO_IRQ_STATE_ERR); } if (!(dstat & DEV_STAT_DEV_END)) { @@ -2218,13 +2326,7 @@ QDIO_PRINT_ERR("establish queues on irq %04x: didn't get " "device end: dstat=%02x, cstat=%02x\n", irq_ptr->irq, dstat, cstat); - /* - * FIXME: - * this is broken, - * we are probably in the context of an i/o interrupt and - * qdio_shutdown calls schedule - */ - qdio_shutdown(cdev,QDIO_FLAG_CLEANUP_USING_CLEAR); + qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR); return 1; } @@ -2236,36 +2338,27 @@ "the following devstat: dstat=%02x, " "cstat=%02x\n", irq_ptr->irq, dstat, cstat); - cdev->private->state = DEV_STATE_ONLINE; + qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR); return 1; } return 0; } static void -qdio_establish_handle_irq(struct ccw_device *cdev, unsigned long intparm, - struct irb *irb) +qdio_establish_handle_irq(struct ccw_device *cdev, int cstat, int dstat) { struct qdio_irq *irq_ptr; - int cstat, dstat; char dbf_text[15]; - cstat = irb->scsw.cstat; - dstat = irb->scsw.dstat; - - irq_ptr = cdev->private->qdio_data; - - if (intparm == 0) { - QDIO_PRINT_WARN("Got unsolicited interrupt on establish " - "queues (irq 0x%x).\n", cdev->private->irq); - return; - } - - qdio_irq_check_sense(irq_ptr->irq, irb); + sprintf(dbf_text,"qehi%4x",cdev->private->irq); + QDIO_DBF_TEXT0(0,setup,dbf_text); + QDIO_DBF_TEXT0(0,trace,dbf_text); if (qdio_establish_irq_check_for_errors(cdev, cstat, dstat)) return; + irq_ptr = cdev->private->qdio_data; + if (MACHINE_IS_VM) irq_ptr->qdioac=qdio_check_siga_needs(irq_ptr->irq); else @@ -2287,6 +2380,7 @@ qdio_initialize_set_siga_flags_output(irq_ptr); qdio_set_state(irq_ptr,QDIO_IRQ_STATE_ESTABLISHED); + } int @@ -2334,7 +2428,7 @@ qdio_allocate_do_dbf(init_data); /* create irq */ - irq_ptr=kmalloc(sizeof(struct qdio_irq),GFP_DMA); + irq_ptr=kmalloc(sizeof(struct qdio_irq), GFP_KERNEL | GFP_DMA); QDIO_DBF_TEXT0(0,setup,"irq_ptr:"); QDIO_DBF_HEX0(0,setup,&irq_ptr,sizeof(void*)); @@ -2347,7 +2441,7 @@ memset(irq_ptr,0,sizeof(struct qdio_irq)); /* wipes qib.ac, required by ar7063 */ - irq_ptr->qdr=kmalloc(sizeof(struct qdr),GFP_DMA); + irq_ptr->qdr=kmalloc(sizeof(struct qdr), GFP_KERNEL | GFP_DMA); if (!(irq_ptr->qdr)) { kfree(irq_ptr->qdr); kfree(irq_ptr); @@ -2372,11 +2466,6 @@ if (!irq_ptr->dev_st_chg_ind) { QDIO_PRINT_WARN("no indicator location available " \ "for irq 0x%x\n",irq_ptr->irq); - /* - * FIXME: - * qdio_release_irq_memory does MOD_DEC_USE_COUNT - * in an unbalanced fashion (see 30 lines farther down) - */ qdio_release_irq_memory(irq_ptr); return -ENOBUFS; } @@ -2396,19 +2485,17 @@ init_data->q_format,init_data->flags, init_data->input_sbal_addr_array, init_data->output_sbal_addr_array)) { - /* - * FIXME: - * qdio_release_irq_memory does MOD_DEC_USE_COUNT - * in an unbalanced fashion (see 10 lines farther down) - */ qdio_release_irq_memory(irq_ptr); return -ENOMEM; } qdio_set_state(irq_ptr,QDIO_IRQ_STATE_INACTIVE); - MOD_INC_USE_COUNT; - QDIO_DBF_TEXT3(0,setup,"MOD_INC_"); + if (!try_module_get(THIS_MODULE)) { + QDIO_PRINT_CRIT("try_module_get() failed!\n"); + qdio_release_irq_memory(irq_ptr); + return -EINVAL; + } init_MUTEX_LOCKED(&irq_ptr->setting_up_sema); @@ -2474,6 +2561,10 @@ } else irq_ptr->aqueue = *ciw; + /* Set new interrupt handler. */ + irq_ptr->original_int_handler = init_data->cdev->handler; + init_data->cdev->handler = qdio_handler; + /* the iqdio CHSC stuff */ if (irq_ptr->is_iqdio_irq) { /* iqdio_enable_adapter_int_facility(irq_ptr);*/ @@ -2485,25 +2576,12 @@ result=iqdio_set_subchannel_ind(irq_ptr,0); if (result) { up(&irq_ptr->setting_up_sema); - /* - * FIXME: - * need some callback pointers to be set already, - * i.e. irq_ptr->cleanup_irq and irq_ptr->cleanup_timeout? - * (see 10 lines farther down) - */ qdio_cleanup(init_data->cdev, QDIO_FLAG_CLEANUP_USING_CLEAR); return result; } iqdio_set_delay_target(irq_ptr,IQDIO_DELAY_TARGET); } - /* Set callback functions. */ - irq_ptr->cleanup_irq = qdio_cleanup_handle_irq; - irq_ptr->cleanup_timeout = qdio_cleanup_handle_timeout; - irq_ptr->establish_irq = qdio_establish_handle_irq; - irq_ptr->establish_timeout = qdio_establish_handle_timeout; - irq_ptr->handler = qdio_handler; - up(&irq_ptr->setting_up_sema); return 0; @@ -2538,13 +2616,14 @@ spin_lock_irqsave(get_ccwdev_lock(cdev),saveflags); - ccw_device_set_timeout(cdev, QDIO_ESTABLISH_TIMEOUT); ccw_device_set_options(cdev, 0); - result=ccw_device_start(cdev,&irq_ptr->ccw, - QDIO_DOING_ESTABLISH,0,0); + result=ccw_device_start_timeout(cdev,&irq_ptr->ccw, + QDIO_DOING_ESTABLISH,0,0, + QDIO_ESTABLISH_TIMEOUT); if (result) { - result2=ccw_device_start(cdev,&irq_ptr->ccw, - QDIO_DOING_ESTABLISH,0,0); + result2=ccw_device_start_timeout(cdev,&irq_ptr->ccw, + QDIO_DOING_ESTABLISH,0,0, + QDIO_ESTABLISH_TIMEOUT); sprintf(dbf_text,"eq:io%4x",result); QDIO_DBF_TEXT2(1,setup,dbf_text); if (result2) { @@ -2556,8 +2635,7 @@ irq_ptr->irq,result,result2); result=result2; } - if (result == 0) - cdev->private->state = DEV_STATE_QDIO_INIT; + spin_unlock_irqrestore(get_ccwdev_lock(cdev),saveflags); if (result) { @@ -2567,13 +2645,15 @@ } wait_event(cdev->private->wait_q, - dev_fsm_final_state(cdev) || - (irq_ptr->state == QDIO_IRQ_STATE_ESTABLISHED)); + irq_ptr->state == QDIO_IRQ_STATE_ESTABLISHED || + irq_ptr->state == QDIO_IRQ_STATE_ERR); - if (cdev->private->state == DEV_STATE_QDIO_INIT) + if (irq_ptr->state == QDIO_IRQ_STATE_ESTABLISHED) result = 0; - else + else { + qdio_shutdown(cdev, QDIO_FLAG_CLEANUP_USING_CLEAR); result = -EIO; + } up(&irq_ptr->setting_up_sema); @@ -2593,7 +2673,7 @@ if (!irq_ptr) return -ENODEV; - if (cdev->private->state != DEV_STATE_QDIO_INIT) + if (cdev->private->state != DEV_STATE_ONLINE) return -EINVAL; down(&irq_ptr->setting_up_sema); @@ -2637,8 +2717,6 @@ if (result) goto out; - cdev->private->state = DEV_STATE_QDIO_ACTIVE; - for (i=0;i<irq_ptr->no_input_qs;i++) { if (irq_ptr->is_iqdio_irq) { /* @@ -2659,9 +2737,9 @@ } } - qdio_wait_nonbusy(QDIO_ACTIVATE_DELAY); + qdio_wait_nonbusy(QDIO_ACTIVATE_TIMEOUT); - qdio_set_state(irq_ptr,QDIO_IRQ_STATE_ACTIVE); + qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ACTIVE); out: up(&irq_ptr->setting_up_sema); @@ -2807,12 +2885,10 @@ { struct qdio_irq *irq_ptr; -#ifdef QDIO_DBF_LIKE_HELL char dbf_text[20]; - sprintf(dbf_text,"doQD%04x",irq); + sprintf(dbf_text,"doQD%04x",cdev->private->irq); QDIO_DBF_TEXT3(0,trace,dbf_text); -#endif /* QDIO_DBF_LIKE_HELL */ if ( (qidx>QDIO_MAX_BUFFERS_PER_Q) || (count>QDIO_MAX_BUFFERS_PER_Q) || @@ -2826,7 +2902,6 @@ if (!irq_ptr) return -ENODEV; -#ifdef QDIO_DBF_LIKE_HELL if (callflags&QDIO_FLAG_SYNC_INPUT) QDIO_DBF_HEX3(0,trace,&irq_ptr->input_qs[queue_number], sizeof(void*)); @@ -2837,7 +2912,6 @@ QDIO_DBF_TEXT3(0,trace,dbf_text); sprintf(dbf_text,"qi%02xct%02x",qidx,count); QDIO_DBF_TEXT3(0,trace,dbf_text); -#endif /* QDIO_DBF_LIKE_HELL */ if (irq_ptr->state!=QDIO_IRQ_STATE_ACTIVE) return -EBUSY; @@ -2989,25 +3063,12 @@ GFP_KERNEL); if (!indicators) return -ENOMEM; memset(indicators,0,sizeof(__u32)*(INDICATORS_PER_CACHELINE)); - - chsc_area=(struct qdio_chsc_area *) - kmalloc(sizeof(struct qdio_chsc_area),GFP_KERNEL); - QDIO_DBF_TEXT3(0,trace,"chscarea"); \ - QDIO_DBF_HEX3(0,trace,&chsc_area,sizeof(void*)); \ - if (!chsc_area) { - QDIO_PRINT_ERR("not enough memory for chsc area. Cannot " \ - "initialize QDIO.\n"); - kfree(indicators); - return -ENOMEM; - } - memset(chsc_area,0,sizeof(struct qdio_chsc_area)); return 0; } static void qdio_release_qdio_memory(void) { - kfree(chsc_area); if (indicators) kfree(indicators); } diff -urN linux-2.5.67/drivers/s390/cio/qdio.h linux-2.5.67-s390/drivers/s390/cio/qdio.h --- linux-2.5.67/drivers/s390/cio/qdio.h Mon Apr 7 19:30:45 2003 +++ linux-2.5.67-s390/drivers/s390/cio/qdio.h Mon Apr 14 19:11:50 2003 @@ -1,7 +1,7 @@ #ifndef _CIO_QDIO_H #define _CIO_QDIO_H -#define VERSION_CIO_QDIO_H "$Revision: 1.11 $" +#define VERSION_CIO_QDIO_H "$Revision: 1.16 $" //#define QDIO_DBF_LIKE_HELL @@ -48,25 +48,25 @@ #define QDIO_STATS_CLASSES 2 #define QDIO_STATS_COUNT_NEEDED 2*/ -#define QDIO_ACTIVATE_DELAY 5 /* according to brenton belmar and paul - gioquindo it can take up to 5ms before - queues are really active */ - #define QDIO_NO_USE_COUNT_TIME 10 #define QDIO_NO_USE_COUNT_TIMEOUT 1000 /* wait for 1 sec on each q before exiting without having use_count of the queue to 0 */ #define QDIO_ESTABLISH_TIMEOUT 1000 -#define QDIO_ACTIVATE_TIMEOUT 100 +#define QDIO_ACTIVATE_TIMEOUT 5 #define QDIO_CLEANUP_CLEAR_TIMEOUT 20000 #define QDIO_CLEANUP_HALT_TIMEOUT 10000 -#define QDIO_IRQ_STATE_FRESH 0 /* must be 0 -> memset has set it to 0 */ -#define QDIO_IRQ_STATE_INACTIVE 1 -#define QDIO_IRQ_STATE_ESTABLISHED 2 -#define QDIO_IRQ_STATE_ACTIVE 3 -#define QDIO_IRQ_STATE_STOPPED 4 +enum qdio_irq_states { + QDIO_IRQ_STATE_INACTIVE, + QDIO_IRQ_STATE_ESTABLISHED, + QDIO_IRQ_STATE_ACTIVE, + QDIO_IRQ_STATE_STOPPED, + QDIO_IRQ_STATE_CLEANUP, + QDIO_IRQ_STATE_ERR, + NR_QDIO_IRQ_STATES, +}; /* used as intparm in do_IO: */ #define QDIO_DOING_SENSEID 0 @@ -443,81 +443,6 @@ #define CHSC_FLAG_SIGA_SYNC_DONE_ON_THININTS 0x08 #define CHSC_FLAG_SIGA_SYNC_DONE_ON_OUTB_PCIS 0x04 -struct qdio_chsc_area { - struct { - /* word 0 */ - __u16 command_code1; - __u16 command_code2; - /* word 1 */ - __u16 operation_code; - __u16 first_sch; - /* word 2 */ - __u8 reserved1; - __u8 image_id; - __u16 last_sch; - /* word 3 */ - __u32 reserved2; - - /* word 4 */ - union { - struct { - /* word 4&5 */ - __u64 summary_indicator_addr; - /* word 6&7 */ - __u64 subchannel_indicator_addr; - /* word 8 */ - int ks:4; - int kc:4; - int reserved1:21; - int isc:3; - /* word 9&10 */ - __u32 reserved2[2]; - /* word 11 */ - __u32 subsystem_id; - /* word 12-1015 */ - __u32 reserved3[1004]; - } __attribute__ ((packed,aligned(4))) set_chsc; - struct { - /* word 4&5 */ - __u32 reserved1[2]; - /* word 6 */ - __u32 delay_target; - /* word 7-1015 */ - __u32 reserved4[1009]; - } __attribute__ ((packed,aligned(4))) set_chsc_fast; - struct { - /* word 0 */ - __u16 length; - __u16 response_code; - /* word 1 */ - __u32 reserved1; - /* words 2 to 9 for st sch qdio data */ - __u8 flags; - __u8 reserved2; - __u16 sch; - __u8 qfmt; - __u8 reserved3; - __u8 qdioac; - __u8 sch_class; - __u8 reserved4; - __u8 icnt; - __u8 reserved5; - __u8 ocnt; - /* plus 5 words of reserved fields */ - } __attribute__ ((packed,aligned(8))) - store_qdio_data_response; - } operation_data_area; - } __attribute__ ((packed,aligned(8))) request_block; - struct { - /* word 0 */ - __u16 length; - __u16 response_code; - /* word 1 */ - __u32 reserved1; - } __attribute__ ((packed,aligned(8))) response_block; -} __attribute__ ((packed,aligned(PAGE_SIZE))); - - #ifdef QDIO_PERFORMANCE_STATS struct qdio_perf_stats { unsigned int tl_runs; @@ -623,7 +548,7 @@ struct tasklet_struct tasklet; #endif /* QDIO_USE_TIMERS_FOR_POLLING */ - unsigned int state; + enum qdio_irq_states state; /* used to store the error condition during a data transfer */ unsigned int qdio_error; @@ -674,7 +599,7 @@ unsigned int hydra_gives_outbound_pcis; unsigned int sync_done_on_outb_pcis; - unsigned int state; + enum qdio_irq_states state; struct semaphore setting_up_sema; unsigned int no_input_qs; @@ -694,13 +619,8 @@ struct qib qib; - /* Functions called via the generic cio layer */ - void (*cleanup_irq) (struct ccw_device *, unsigned long, struct irb *); - void (*cleanup_timeout) (struct ccw_device *); - void (*establish_irq) (struct ccw_device *, unsigned long, - struct irb *); - void (*establish_timeout) (struct ccw_device *); - void (*handler) (struct ccw_device *, unsigned long, struct irb *); + void (*original_int_handler) (struct ccw_device *, + unsigned long, struct irb *); }; #endif diff -urN linux-2.5.67/drivers/s390/cio/requestirq.c linux-2.5.67-s390/drivers/s390/cio/requestirq.c --- linux-2.5.67/drivers/s390/cio/requestirq.c Mon Apr 7 19:32:16 2003 +++ linux-2.5.67-s390/drivers/s390/cio/requestirq.c Mon Apr 14 19:11:50 2003 @@ -44,7 +44,11 @@ /* * Let's build our path group ID here. */ - global_pgid.cpu_addr = *(__u16 *) __LC_CPUADDR; +#ifdef CONFIG_SMP + global_pgid.cpu_addr = hard_smp_processor_id(); +#else + global_pgid.cpu_addr = 0; +#endif global_pgid.cpu_id = ((cpuid_t *) __LC_CPUID)->ident; global_pgid.cpu_model = ((cpuid_t *) __LC_CPUID)->machine; global_pgid.tod_high = (__u32) (get_clock() >> 32); diff -urN linux-2.5.67/drivers/s390/s390mach.c linux-2.5.67-s390/drivers/s390/s390mach.c --- linux-2.5.67/drivers/s390/s390mach.c Mon Apr 7 19:32:58 2003 +++ linux-2.5.67-s390/drivers/s390/s390mach.c Mon Apr 14 19:11:50 2003 @@ -21,7 +21,7 @@ extern void css_process_crw(int); extern void chsc_process_crw(void); -extern void chp_process_crw(int); +extern void chp_process_crw(int, int); static void s390_handle_damage(char *msg) @@ -62,7 +62,17 @@ break; case CRW_RSC_CPATH: pr_debug("source is channel path %02X\n", crw.rsid); - chp_process_crw(crw.rsid); + switch (crw.erc) { + case CRW_ERC_IPARM: /* Path has come. */ + chp_process_crw(crw.rsid, 1); + break; + case CRW_ERC_PERRI: /* Path has gone. */ + chp_process_crw(crw.rsid, 0); + break; + default: + pr_debug("Don't know how to handle erc=%x\n", + crw.erc); + } break; case CRW_RSC_CONFIG: pr_debug("source is configuration-alert facility\n"); - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
| |