📄 s390io.c
字号:
ioinfo[irq]->devstat.dstat = 0; ioinfo[irq]->devstat.lpum = 0; ioinfo[irq]->devstat.flag = DEVSTAT_CLEAR_FUNCTION; ioinfo[irq]->devstat.scnt = 0; } else { ioinfo[irq]->devstat.flag |= DEVSTAT_CLEAR_FUNCTION; } /* endif */ /* * If synchronous I/O processing is requested, we have * to wait for the corresponding interrupt to occur by * polling the interrupt condition. However, as multiple * interrupts may be outstanding, we must not just wait * for the first interrupt, but must poll until ours * pops up. */ if ( flag & DOIO_WAIT_FOR_INTERRUPT ) { int io_sub; __u32 io_parm; psw_t io_new_psw; int ccode; int ready = 0; struct _lowcore *lc = NULL; /* * We shouldn't perform a TPI loop, waiting for * an interrupt to occur, but should load a * WAIT PSW instead. Otherwise we may keep the * channel subsystem busy, not able to present * the interrupt. When our sync. interrupt * arrived we reset the I/O old PSW to its * original value. */ memcpy( &io_new_psw, &lc->io_new_psw, sizeof(psw_t)); ccode = iac(); switch (ccode) { case 0: // primary-space io_sync_wait.mask = _IO_PSW_MASK | _PSW_PRIM_SPACE_MODE | _PSW_IO_WAIT; break; case 1: // secondary-space io_sync_wait.mask = _IO_PSW_MASK | _PSW_SEC_SPACE_MODE | _PSW_IO_WAIT; break; case 2: // access-register io_sync_wait.mask = _IO_PSW_MASK | _PSW_ACC_REG_MODE | _PSW_IO_WAIT; break; case 3: // home-space io_sync_wait.mask = _IO_PSW_MASK | _PSW_HOME_SPACE_MODE | _PSW_IO_WAIT; break; default: panic( "halt_IO() : unexpected " "address-space-control %d\n", ccode); break; } /* endswitch */ io_sync_wait.addr = FIX_PSW(&&cio_wakeup); /* * Martin didn't like modifying the new PSW, now we take * a fast exit in do_IRQ() instead */ *(__u32 *)__LC_SYNC_IO_WORD = 1; do { asm volatile ( "lpsw %0" : : "m" (io_sync_wait) );cio_wakeup: io_parm = *(__u32 *)__LC_IO_INT_PARM; io_sub = (__u32)*(__u16 *)__LC_SUBCHANNEL_NR; ready = s390_process_IRQ( io_sub ); } while ( !((io_sub == irq) && (ready == 1)) ); *(__u32 *)__LC_SYNC_IO_WORD = 0; } /* endif */ ret = 0; break; case 1 : /* status pending */ ioinfo[irq]->devstat.flag |= DEVSTAT_STATUS_PENDING; /* * initialize the device driver specific devstat irb area */ memset( &((devstat_t *) ioinfo[irq]->irq_desc.action->dev_id)->ii.irb, '\0', sizeof( irb_t) ); /* * Let the common interrupt handler process the pending * status. However, we must avoid calling the user * action handler, as it won't be prepared to handle * a pending status during do_IO() processing inline. * This also implies that s390_process_IRQ must * terminate synchronously - especially if device * sensing is required. */ ioinfo[irq]->ui.flags.s_pend = 1; ioinfo[irq]->ui.flags.busy = 1; ioinfo[irq]->ui.flags.doio = 1; s390_process_IRQ( irq ); ioinfo[irq]->ui.flags.s_pend = 0; ioinfo[irq]->ui.flags.busy = 0; ioinfo[irq]->ui.flags.doio = 0; ioinfo[irq]->ui.flags.repall = 0; ioinfo[irq]->ui.flags.w4final = 0; ioinfo[irq]->devstat.flag |= DEVSTAT_FINAL_STATUS; /* * In multipath mode a condition code 3 implies the last * path has gone, except we have previously restricted * the I/O to a particular path. A condition code 1 * (0 won't occur) results in return code EIO as well * as 3 with another path than the one used (i.e. path available mask is non-zero). */ if ( ioinfo[irq]->devstat.ii.irb.scsw.cc == 3 ) { ret = -ENODEV; ioinfo[irq]->devstat.flag |= DEVSTAT_NOT_OPER; ioinfo[irq]->ui.flags.oper = 0; } else { ret = -EIO; ioinfo[irq]->devstat.flag &= ~DEVSTAT_NOT_OPER; ioinfo[irq]->ui.flags.oper = 1; } /* endif */ break; case 2 : /* busy */ ret = -EBUSY; break; default: /* device not operational */ ret = -ENODEV; break; } /* endswitch */ if ( ( flag & DOIO_WAIT_FOR_INTERRUPT ) && ( sync_isc_locked ) ) { sync_isc_locked = 0; // local setting ioinfo[irq]->ui.flags.syncio = 0; // global setting disable_cpu_sync_isc( irq ); spin_unlock_irqrestore( &sync_isc, psw_flags); } /* endif */ } /* endif */ return( ret );}/* * do_IRQ() handles all normal I/O device IRQ's (the special * SMP cross-CPU interrupts have their own specific * handlers). * * Returns: 0 - no ending status received, no further action taken * 1 - interrupt handler was called with ending status */asmlinkage void do_IRQ( struct pt_regs regs, unsigned int irq, __u32 s390_intparm ){#ifdef CONFIG_FAST_IRQ int ccode; tpi_info_t tpi_info; int new_irq;#endif int use_irq = irq;// __u32 use_intparm = s390_intparm; // // fix me !!! // // We need to schedule device recognition, the interrupt stays // pending. We need to dynamically allocate an ioinfo structure. // if ( ioinfo[irq] == INVALID_STORAGE_AREA ) { return; /* this keeps the device boxed ... */ } /* * take fast exit if CPU is in sync. I/O state * * Note: we have to turn off the WAIT bit and re-disable * interrupts prior to return as this was the initial * entry condition to synchronous I/O. */ if ( *(__u32 *)__LC_SYNC_IO_WORD ) { regs.psw.mask &= ~(_PSW_WAIT_MASK_BIT | _PSW_IO_MASK_BIT); return; } /* endif */ s390irq_spin_lock(use_irq);#ifdef CONFIG_FAST_IRQ do {#endif /* CONFIG_FAST_IRQ */ s390_process_IRQ( use_irq );#ifdef CONFIG_FAST_IRQ /* * more interrupts pending ? */ ccode = tpi( &tpi_info ); if ( ! ccode ) break; // no, leave ... new_irq = tpi_info.irq;// use_intparm = tpi_info.intparm; /* * if the interrupt is for a different irq we * release the current irq lock and obtain * a new one ... */ if ( new_irq != use_irq ) { s390irq_spin_unlock(use_irq); use_irq = new_irq; s390irq_spin_lock(use_irq); } /* endif */ } while ( 1 );#endif /* CONFIG_FAST_IRQ */ s390irq_spin_unlock(use_irq); return;}/* * s390_process_IRQ() handles status pending situations and interrupts * * Called by : do_IRQ() - for "real" interrupts * s390_start_IO, halt_IO() * - status pending cond. after SSCH, or HSCH * disable_subchannel() - status pending conditions (after MSCH) * * Returns: 0 - no ending status received, no further action taken * 1 - interrupt handler was called with ending status */int s390_process_IRQ( unsigned int irq ){ int ccode; /* condition code from tsch() operation */ int irb_cc; /* condition code from irb */ int sdevstat; /* effective struct devstat size to copy */ unsigned int fctl; /* function control */ unsigned int stctl; /* status control */ unsigned int actl; /* activity control */ struct irqaction *action; struct pt_regs regs; /* for interface compatibility only */ int issense = 0; int ending_status = 0; int allow4handler = 1; int chnchk = 0;#if 0 int cpu = smp_processor_id(); kstat.irqs[cpu][irq]++;#endif action = ioinfo[irq]->irq_desc.action; /* * It might be possible that a device was not-oper. at the time * of free_irq() processing. This means the handler is no longer * available when the device possibly becomes ready again. In * this case we perform delayed disable_subchannel() processing. */ if ( action == NULL ) { if ( !ioinfo[irq]->ui.flags.d_disable ) { printk( KERN_CRIT"s390_process_IRQ(%04X) " "- no interrupt handler registered" "for device %04X !\n", irq, ioinfo[irq]->devstat.devno); } /* endif */ } /* endif */ /* * retrieve the i/o interrupt information (irb), * update the device specific status information * and possibly call the interrupt handler. * * Note 1: At this time we don't process the resulting * condition code (ccode) from tsch(), although * we probably should. * * Note 2: Here we will have to check for channel * check conditions and call a channel check * handler. * * Note 3: If a start function was issued, the interruption * parameter relates to it. If a halt function was * issued for an idle device, the intparm must not * be taken from lowcore, but from the devstat area. */ ccode = tsch( irq, &(ioinfo[irq]->devstat.ii.irb) ); // // We must only accumulate the status if initiated by do_IO() or halt_IO() // if ( ioinfo[irq]->ui.flags.busy ) { ioinfo[irq]->devstat.dstat |= ioinfo[irq]->devstat.ii.irb.scsw.dstat; ioinfo[irq]->devstat.cstat |= ioinfo[irq]->devstat.ii.irb.scsw.cstat; } else { ioinfo[irq]->devstat.dstat = ioinfo[irq]->devstat.ii.irb.scsw.dstat; ioinfo[irq]->devstat.cstat = ioinfo[irq]->devstat.ii.irb.scsw.cstat; ioinfo[irq]->devstat.flag = 0; // reset status flags } /* endif */ ioinfo[irq]->devstat.lpum = ioinfo[irq]->devstat.ii.irb.esw.esw1.lpum; if ( ioinfo[irq]->ui.flags.busy) { ioinfo[irq]->devstat.intparm = ioinfo[irq]->u_intparm; } /* endif */ /* * reset device-busy bit if no longer set in irb */ if ( (ioinfo[irq]->devstat.dstat & DEV_STAT_BUSY ) && ((ioinfo[irq]->devstat.ii.irb.scsw.dstat & DEV_STAT_BUSY) == 0)) { ioinfo[irq]->devstat.dstat &= ~DEV_STAT_BUSY; } /* endif */ /* * Save residual count and CCW information in case primary and * secondary status are presented with different interrupts. */ if ( ioinfo[irq]->devstat.ii.irb.scsw.stctl & SCSW_STCTL_PRIM_STATUS ) { ioinfo[irq]->devstat.rescnt = ioinfo[irq]->devstat.ii.irb.scsw.count;#if CONFIG_DEBUG_IO if ( irq != cons_dev ) printk( "s390_process_IRQ( %04X ) : " "residual count from irb after tsch() %d\n", irq, ioinfo[irq]->devstat.rescnt );#endif } /* endif */ if ( ioinfo[irq]->devstat.ii.irb.scsw.cpa != 0 ) { ioinfo[irq]->devstat.cpa = ioinfo[irq]->devstat.ii.irb.scsw.cpa; } /* endif */ irb_cc = ioinfo[irq]->devstat.ii.irb.scsw.cc; // // check for any kind of channel or interface control check but don't // issue the message for the console device // if ( (ioinfo[irq]->devstat.ii.irb.scsw.cstat & ( SCHN_STAT_CHN_DATA_CHK | SCHN_STAT_CHN_CTRL_CHK | SCHN_STAT_INTF_CTRL_CHK ) ) && (irq != cons_dev ) ) { printk( "Channel-Check or Interface-Control-Check " "received\n" " ... device %04X on subchannel %04X, dev_stat " ": %02X sch_stat : %02X\n", ioinfo[irq]->devstat.devno, irq, ioinfo[irq]->devstat.dstat, ioinfo[irq]->devstat.cstat); chnchk = 1; } /* endif */ issense = ioinfo[irq]->devstat.ii.irb.esw.esw0.erw.cons; if ( issense ) { ioinfo[irq]->devstat.scnt = ioinfo[irq]->devstat.ii.irb.esw.esw0.erw.scnt; ioinfo[irq]->devstat.flag |= DEVSTAT_FLAG_SENSE_AVAIL; sdevstat = sizeof( devstat_t);#if CONFIG_DEBUG_IO if ( irq != cons_dev ) printk( "s390_process_IRQ( %04X ) : " "concurrent sense bytes avail %d\n", irq, ioinfo[irq]->devstat.scnt );#endif } else { /* don't copy the sense data area ! */ sdevstat = sizeof( devstat_t) - SENSE_MAX_COUNT; } /* endif */ switch ( irb_cc ) { case 1: /* status pending */ ioinfo[irq]->devstat.flag |= DEVSTAT_STATUS_PENDING; case 0: /* normal i/o interruption */ fctl = ioinfo[irq]->devstat.ii.irb.scsw.fctl; stctl = ioinfo[irq]->devstat.ii.irb.scsw.stctl; actl = ioinfo[irq]->devstat.ii.irb.scsw.actl; if ( chnchk && (ioinfo[irq]->senseid.cu_type == 0x3088)) { char buffer[80]; sprintf( buffer, "s390_process_IRQ(%04X) - irb for " "device %04X after channel check\n", irq, ioinfo[irq]->devstat.devno ); s390_displayhex( buffer, &(ioinfo[irq]->devstat.ii.irb) , sizeof(irb_t)); } /* endif */ ioinfo[irq]->stctl |= stctl; ending_status = ( stctl & SCSW_STCTL_SEC_STATUS ) || ( stctl == (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND) ) || ( (fctl == SCSW_FCTL_HALT_FUNC) && (stctl == SCSW_STCTL_STATUS_PEND) ); /* * Check for unsolicited interrupts - for debug purposes only * * We only consider an interrupt as unsolicited, if the device was not * actively in use (busy) and an interrupt other than an ALERT status * was received. * * Note: We must not issue a message to the console, if the * unsolicited interrupt applies to the console device * itself ! */#if CONFIG_DEBUG_IO if ( ( irq != cons_dev ) && !( stctl & SCSW_STCTL_ALERT_STATUS ) && ( ioinfo[irq]->ui.flags.busy == 0 ) ) { char buffer[80]; printk( "Unsolicited interrupt received for device %04X on subchannel %04X\n" " ... device status : %02X subchannel status : %02X\n", ioinfo[irq]->devstat.devno, irq, ioinfo[irq]->devstat.dstat, ioinfo[irq]->devstat.cstat); sprintf( buffer, "s390_process_IRQ(%04X) - irb for " "device %04X, ending_status %d\n", irq, ioinfo[irq]->devstat.devno, ending_status); s390_displayhex( buffer, &(ioinfo[irq]->devstat.ii.irb) , sizeof(irb_t)); } /* endif */ /* * take fast exit if no handler is available */ if ( !action ) return( ending_status ); #endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -