📄 s390io.c
字号:
* *** must not occur ! *** * *** *** * *** indicates our internal *** * *** interrupt accounting is out *** * *** of sync ===> panic() *** */ printk( KERN_CRIT"disable_subchannel(%04X) " "- unexpected busy condition for " "device %04X received !\n", irq, ioinfo[irq]->devstat.devno); ret = -ENODEV; // never reached break; case 3 : /* * should hardly occur ?! */ ioinfo[irq]->ui.flags.oper = 0; ioinfo[irq]->ui.flags.d_disable = 1; ret = 0; /* if the device has gone we */ /* ... don't need to disable */ /* ... it anymore ! */ break; default : ret = -ENODEV; // never reached ... break; } /* endswitch */ } while ( (cc == 1) && retry ); } /* endif */ } /* endif */ return( ret);}int s390_setup_irq( unsigned int irq, struct irqaction * new){ unsigned long flags; int rc = 0; if ( ioinfo[irq] == INVALID_STORAGE_AREA ) { return( -ENODEV); } /* * The following block of code has to be executed atomically */ s390irq_spin_lock_irqsave( irq, flags); if ( ioinfo[irq]->irq_desc.action == NULL ) { ioinfo[irq]->irq_desc.action = new; ioinfo[irq]->irq_desc.status = 0; ioinfo[irq]->irq_desc.handler->enable = &enable_subchannel; ioinfo[irq]->irq_desc.handler->disable = &disable_subchannel; ioinfo[irq]->irq_desc.handler->handle = &handle_IRQ_event; ioinfo[irq]->ui.flags.ready = 1; ioinfo[irq]->irq_desc.handler->enable(irq); } else { /* * interrupt already owned, and shared interrupts * aren't supported on S/390. */ rc = -EBUSY; } /* endif */ s390irq_spin_unlock_irqrestore(irq,flags); return( rc);}void s390_init_IRQ( void ){ unsigned long flags; /* PSW flags */ long cr6 __attribute__ ((aligned (8))); // Hopefully bh_count's will get set when we copy the prefix lowcore // structure to other CPI's ( DJB ) softirq_active(smp_processor_id()) = 0; softirq_mask(smp_processor_id()) = 0; local_bh_count(smp_processor_id()) = 0; local_irq_count(smp_processor_id()) = 0; syscall_count(smp_processor_id()) = 0; asm volatile ("STCK %0" : "=m" (irq_IPL_TOD)); /* * As we don't know about the calling environment * we assure running disabled. Before leaving the * function we resestablish the old environment. * * Note : as we don't need a system wide lock, therefore * we shouldn't use cli(), but __cli() as this * affects the current CPU only. */ __save_flags(flags); __cli(); /* * disable all interrupts */ cr6 = 0; asm volatile ("LCTL 6,6,%0":: "m" (cr6):"memory"); s390_process_subchannels(); /* * enable default I/O-interrupt sublass 3 */ cr6 = 0x10000000; asm volatile ("LCTL 6,6,%0":: "m" (cr6):"memory"); s390_device_recognition(); init_IRQ_complete = 1; s390_init_machine_check(); __restore_flags(flags); return;}/* * dummy handler, used during init_IRQ() processing for compatibility only */void init_IRQ_handler( int irq, void *dev_id, struct pt_regs *regs){ /* this is a dummy handler only ... */}int s390_start_IO( int irq, /* IRQ */ ccw1_t *cpa, /* logical channel prog addr */ unsigned long user_intparm, /* interruption parameter */ __u8 lpm, /* logical path mask */ unsigned long flag) /* flags */{ int ccode; unsigned long psw_flags; int sync_isc_locked = 0; int ret = 0; /* * The flag usage is mutal exclusive ... */ if ( (flag & DOIO_RETURN_CHAN_END) && (flag & DOIO_REPORT_ALL ) ) { return( -EINVAL ); } /* endif */ memset( &(ioinfo[irq]->orb), '\0', sizeof( orb_t) ); /* * setup ORB */ ioinfo[irq]->orb.intparm = (__u32)&ioinfo[irq]->u_intparm; ioinfo[irq]->orb.fmt = 1; ioinfo[irq]->orb.pfch = !(flag & DOIO_DENY_PREFETCH); ioinfo[irq]->orb.spnd = (flag & DOIO_ALLOW_SUSPEND); ioinfo[irq]->orb.ssic = ( (flag & DOIO_ALLOW_SUSPEND ) && (flag & DOIO_SUPPRESS_INTER) ); if ( flag & DOIO_VALID_LPM ) { ioinfo[irq]->orb.lpm = lpm; } else { ioinfo[irq]->orb.lpm = ioinfo[irq]->opm; } /* endif */ ioinfo[irq]->orb.cpa = (__u32)virt_to_phys( cpa); /* * If sync processing was requested we lock the sync ISC, modify the * device to present interrupts for this ISC only and switch the * CPU to handle this ISC + the console ISC exclusively. */ if ( flag & DOIO_WAIT_FOR_INTERRUPT ) { // // check whether we run recursively (sense processing) // if ( !ioinfo[irq]->ui.flags.syncio ) { spin_lock_irqsave( &sync_isc, psw_flags); ret = enable_cpu_sync_isc( irq); if ( ret ) { spin_unlock_irqrestore( &sync_isc, psw_flags); return( ret); } else { sync_isc_locked = 1; // local ioinfo[irq]->ui.flags.syncio = 1; // global } /* endif */ } /* endif */ } /* endif */ /* * Issue "Start subchannel" and process condition code */ ccode = ssch( irq, &(ioinfo[irq]->orb) ); switch ( ccode ) { case 0: if ( !ioinfo[irq]->ui.flags.w4sense ) { /* * init the device driver specific devstat irb area * * Note : don磘 clear saved irb info in case of sense ! */ memset( &((devstat_t *)ioinfo[irq]->irq_desc.action->dev_id)->ii.irb, '\0', sizeof( irb_t) ); } /* endif */ /* * initialize device status information */ ioinfo[irq]->ui.flags.busy = 1; ioinfo[irq]->ui.flags.doio = 1; ioinfo[irq]->u_intparm = user_intparm; ioinfo[irq]->devstat.cstat = 0; ioinfo[irq]->devstat.dstat = 0; ioinfo[irq]->devstat.lpum = 0; ioinfo[irq]->devstat.flag = DEVSTAT_START_FUNCTION; ioinfo[irq]->devstat.scnt = 0; ioinfo[irq]->ui.flags.fast = 0; ioinfo[irq]->ui.flags.repall = 0; /* * Check for either early (FAST) notification requests * or if we are to return all interrupt info. * Default is to call IRQ handler at secondary status only */ if ( flag & DOIO_RETURN_CHAN_END ) { ioinfo[irq]->ui.flags.fast = 1; } else if ( flag & DOIO_REPORT_ALL ) { ioinfo[irq]->ui.flags.repall = 1; } /* endif */ ioinfo[irq]->ulpm = ioinfo[irq]->orb.lpm; /* * 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 ) { // __u32 io_parm; psw_t io_new_psw; int ccode; int ready = 0; int io_sub = -1; struct _lowcore *lc = NULL; int count = 30000; /* * 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( "start_IO() : unexpected " "address-space-control %d\n", ccode); break; } /* endswitch */ io_sync_wait.addr = FIX_PSW(&&io_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 { if ( flag & DOIO_TIMEOUT ) { tpi_info_t tpi_info; do { if ( tpi(&tpi_info) == 1 ) { io_sub = tpi_info.irq; break; } else { count--; tod_wait(100); /* usecs */ } /* endif */ } while ( count ); } else { asm volatile ("lpsw %0" : : "m" (io_sync_wait));io_wakeup: io_sub = (__u32)*(__u16 *)__LC_SUBCHANNEL_NR; } /* endif */ if ( count ) ready = s390_process_IRQ( io_sub ); /* * surrender when retry count's exceeded ... */ } while ( !( ( io_sub == irq ) && ( ready == 1 )) && count ); *(__u32 *)__LC_SYNC_IO_WORD = 0; if ( !count ) ret = -ETIMEDOUT; } /* endif */ 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 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;#if CONFIG_DEBUG_IO { char buffer[80]; stsch(irq, &(ioinfo[irq]->schib) ); sprintf( buffer, "s390_start_IO(%04X) - irb for " "device %04X, after status pending\n", irq, ioinfo[irq]->devstat.devno ); s390_displayhex( buffer, &(ioinfo[irq]->devstat.ii.irb) , sizeof(irb_t)); sprintf( buffer, "s390_start_IO(%04X) - schib for " "device %04X, after status pending\n", irq, ioinfo[irq]->devstat.devno ); s390_displayhex( buffer, &(ioinfo[irq]->schib) , sizeof(schib_t)); if (ioinfo[irq]->devstat.flag & DEVSTAT_FLAG_SENSE_AVAIL) { sprintf( buffer, "s390_start_IO(%04X) - sense " "data for " "device %04X, after status pending\n", irq, ioinfo[irq]->devstat.devno ); s390_displayhex( buffer, ((devstat_t *)(ioinfo[irq]->irq_desc.action->dev_id))->ii.sense.data, ((devstat_t *)(ioinfo[irq]->irq_desc.action->dev_id))->rescnt); } }#endif } 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; ioinfo[irq]->ui.flags.oper = 0; ioinfo[irq]->devstat.flag |= DEVSTAT_NOT_OPER; memcpy( ioinfo[irq]->irq_desc.action->dev_id, &(ioinfo[irq]->devstat), sizeof( devstat_t) );#if CONFIG_DEBUG_IO { char buffer[80]; stsch(irq, &(ioinfo[irq]->schib) ); sprintf( buffer, "s390_start_IO(%04X) - schib for " "device %04X, after 'not oper' status\n", irq, ioinfo[irq]->devstat.devno ); s390_displayhex( buffer, &(ioinfo[irq]->schib), sizeof(schib_t)); }#endif break; } /* endswitch */ if ( ( flag & DOIO_WAIT_FOR_INTERRUPT )
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -