sym_glue.c
来自「优龙2410linux2.6.8内核源代码」· C语言 代码 · 共 2,552 行 · 第 1/5 页
C
2,552 行
* Minimal checkings, so that we will not * go outside our tables. */ if (ccb->device->id == np->myaddr || ccb->device->id >= SYM_CONF_MAX_TARGET || ccb->device->lun >= SYM_CONF_MAX_LUN) { sym_xpt_done2(np, ccb, CAM_DEV_NOT_THERE); return 0; } /* * Retreive the target descriptor. */ tp = &np->target[ccb->device->id]; /* * Complete the 1st INQUIRY command with error * condition if the device is flagged NOSCAN * at BOOT in the NVRAM. This may speed up * the boot and maintain coherency with BIOS * device numbering. Clearing the flag allows * user to rescan skipped devices later. * We also return error for devices not flagged * for SCAN LUNS in the NVRAM since some mono-lun * devices behave badly when asked for some non * zero LUN. Btw, this is an absolute hack.:-) */ if (ccb->cmnd[0] == 0x12 || ccb->cmnd[0] == 0x0) { if ((tp->usrflags & SYM_SCAN_BOOT_DISABLED) || ((tp->usrflags & SYM_SCAN_LUNS_DISABLED) && ccb->device->lun != 0)) { tp->usrflags &= ~SYM_SCAN_BOOT_DISABLED; sym_xpt_done2(np, ccb, CAM_DEV_NOT_THERE); return 0; } } /* * Select tagged/untagged. */ lp = sym_lp(np, tp, ccb->device->lun); order = (lp && lp->s.reqtags) ? M_SIMPLE_TAG : 0; /* * Queue the SCSI IO. */ cp = sym_get_ccb(np, ccb->device->id, ccb->device->lun, order); if (!cp) return 1; /* Means resource shortage */ sym_queue_scsiio(np, ccb, cp); return 0;}/* * Setup buffers and pointers that address the CDB. */static inline int sym_setup_cdb(struct sym_hcb *np, struct scsi_cmnd *ccb, struct sym_ccb *cp){ u32 cmd_ba; int cmd_len; /* * CDB is 16 bytes max. */ if (ccb->cmd_len > sizeof(cp->cdb_buf)) { sym_set_cam_status(cp->cam_ccb, CAM_REQ_INVALID); return -1; } memcpy(cp->cdb_buf, ccb->cmnd, ccb->cmd_len); cmd_ba = CCB_BA (cp, cdb_buf[0]); cmd_len = ccb->cmd_len; cp->phys.cmd.addr = cpu_to_scr(cmd_ba); cp->phys.cmd.size = cpu_to_scr(cmd_len); return 0;}/* * Setup pointers that address the data and start the I/O. */int sym_setup_data_and_start(struct sym_hcb *np, struct scsi_cmnd *csio, struct sym_ccb *cp){ int dir; struct sym_tcb *tp = &np->target[cp->target]; struct sym_lcb *lp = sym_lp(np, tp, cp->lun); /* * Build the CDB. */ if (sym_setup_cdb(np, csio, cp)) goto out_abort; /* * No direction means no data. */ dir = csio->sc_data_direction; if (dir != DMA_NONE) { cp->segments = sym_scatter(np, cp, csio); if (cp->segments < 0) { if (cp->segments == -2) sym_set_cam_status(csio, CAM_RESRC_UNAVAIL); else sym_set_cam_status(csio, CAM_REQ_TOO_BIG); goto out_abort; } } else { cp->data_len = 0; cp->segments = 0; } /* * Set data pointers. */ sym_setup_data_pointers(np, cp, dir); /* * When `#ifed 1', the code below makes the driver * panic on the first attempt to write to a SCSI device. * It is the first test we want to do after a driver * change that does not seem obviously safe. :) */#if 0 switch (cp->cdb_buf[0]) { case 0x0A: case 0x2A: case 0xAA: panic("XXXXXXXXXXXXX WRITE NOT YET ALLOWED XXXXXXXXXXXXXX\n"); MDELAY(10000); break; default: break; }#endif /* * activate this job. */ if (lp) sym_start_next_ccbs(np, lp, 2); else sym_put_start_queue(np, cp); return 0;out_abort: sym_free_ccb(np, cp); sym_xpt_done(np, csio); return 0;}/* * timer daemon. * * Misused to keep the driver running when * interrupts are not configured correctly. */static void sym_timer(struct sym_hcb *np){ unsigned long thistime = jiffies; /* * Restart the timer. */ np->s.timer.expires = thistime + SYM_CONF_TIMER_INTERVAL; add_timer(&np->s.timer); /* * If we are resetting the ncr, wait for settle_time before * clearing it. Then command processing will be resumed. */ if (np->s.settle_time_valid) { if (time_before_eq(np->s.settle_time, thistime)) { if (sym_verbose >= 2 ) printk("%s: command processing resumed\n", sym_name(np)); np->s.settle_time_valid = 0; } return; } /* * Nothing to do for now, but that may come. */ if (np->s.lasttime + 4*HZ < thistime) { np->s.lasttime = thistime; }#ifdef SYM_CONF_PCIQ_MAY_MISS_COMPLETIONS /* * Some way-broken PCI bridges may lead to * completions being lost when the clearing * of the INTFLY flag by the CPU occurs * concurrently with the chip raising this flag. * If this ever happen, lost completions will * be reaped here. */ sym_wakeup_done(np);#endif}/* * PCI BUS error handler. */void sym_log_bus_error(struct sym_hcb *np){ u_short pci_sts; pci_read_config_word(np->s.device, PCI_STATUS, &pci_sts); if (pci_sts & 0xf900) { pci_write_config_word(np->s.device, PCI_STATUS, pci_sts); printf("%s: PCI STATUS = 0x%04x\n", sym_name(np), pci_sts & 0xf900); }}/* * Requeue awaiting commands. */static void sym_requeue_awaiting_cmds(struct sym_hcb *np){ struct scsi_cmnd *cmd; struct sym_ucmd *ucp = SYM_UCMD_PTR(cmd); SYM_QUEHEAD tmp_cmdq; int sts; sym_que_move(&np->s.wait_cmdq, &tmp_cmdq); while ((ucp = (struct sym_ucmd *) sym_remque_head(&tmp_cmdq)) != 0) { sym_insque_tail(&ucp->link_cmdq, &np->s.busy_cmdq); cmd = SYM_SCMD_PTR(ucp); sts = sym_queue_command(np, cmd); if (sts) { sym_remque(&ucp->link_cmdq); sym_insque_head(&ucp->link_cmdq, &np->s.wait_cmdq); } }}/* * queuecommand method. Entered with the host adapter lock held and * interrupts disabled. */static int sym53c8xx_queue_command(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)){ struct sym_hcb *np = SYM_SOFTC_PTR(cmd); struct sym_ucmd *ucp = SYM_UCMD_PTR(cmd); int sts = 0; cmd->scsi_done = done; cmd->host_scribble = NULL; memset(ucp, 0, sizeof(*ucp)); /* * Shorten our settle_time if needed for * this command not to time out. */ if (np->s.settle_time_valid && cmd->timeout_per_command) { unsigned long tlimit = jiffies + cmd->timeout_per_command; tlimit -= SYM_CONF_TIMER_INTERVAL*2; if (time_after(np->s.settle_time, tlimit)) { np->s.settle_time = tlimit; } } if (np->s.settle_time_valid || !sym_que_empty(&np->s.wait_cmdq)) { sym_insque_tail(&ucp->link_cmdq, &np->s.wait_cmdq); goto out; } sym_insque_tail(&ucp->link_cmdq, &np->s.busy_cmdq); sts = sym_queue_command(np, cmd); if (sts) { sym_remque(&ucp->link_cmdq); sym_insque_tail(&ucp->link_cmdq, &np->s.wait_cmdq); }out: return 0;}/* * Linux entry point of the interrupt handler. */static irqreturn_t sym53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs){ unsigned long flags; struct sym_hcb *np = (struct sym_hcb *)dev_id; if (DEBUG_FLAGS & DEBUG_TINY) printf_debug ("["); spin_lock_irqsave(np->s.host->host_lock, flags); sym_interrupt(np); /* * push queue walk-through to tasklet */ if (!sym_que_empty(&np->s.wait_cmdq) && !np->s.settle_time_valid) sym_requeue_awaiting_cmds(np); spin_unlock_irqrestore(np->s.host->host_lock, flags); if (DEBUG_FLAGS & DEBUG_TINY) printf_debug ("]\n"); return IRQ_HANDLED;}/* * Linux entry point of the timer handler */static void sym53c8xx_timer(unsigned long npref){ struct sym_hcb *np = (struct sym_hcb *)npref; unsigned long flags; spin_lock_irqsave(np->s.host->host_lock, flags); sym_timer(np); if (!sym_que_empty(&np->s.wait_cmdq) && !np->s.settle_time_valid) sym_requeue_awaiting_cmds(np); spin_unlock_irqrestore(np->s.host->host_lock, flags);}/* * What the eh thread wants us to perform. */#define SYM_EH_ABORT 0#define SYM_EH_DEVICE_RESET 1#define SYM_EH_BUS_RESET 2#define SYM_EH_HOST_RESET 3/* * What we will do regarding the involved SCSI command. */#define SYM_EH_DO_IGNORE 0#define SYM_EH_DO_COMPLETE 1#define SYM_EH_DO_WAIT 2/* * Our general completion handler. */static void __sym_eh_done(struct scsi_cmnd *cmd, int timed_out){ struct sym_eh_wait *ep = SYM_UCMD_PTR(cmd)->eh_wait; if (!ep) return; /* Try to avoid a race here (not 100% safe) */ if (!timed_out) { ep->timed_out = 0; if (ep->to_do == SYM_EH_DO_WAIT && !del_timer(&ep->timer)) return; } /* Revert everything */ SYM_UCMD_PTR(cmd)->eh_wait = NULL; cmd->scsi_done = ep->old_done; /* Wake up the eh thread if it wants to sleep */ if (ep->to_do == SYM_EH_DO_WAIT) up(&ep->sem);}/* * scsi_done() alias when error recovery is in progress. */static void sym_eh_done(struct scsi_cmnd *cmd) { __sym_eh_done(cmd, 0); }/* * Some timeout handler to avoid waiting too long. */static void sym_eh_timeout(u_long p) { __sym_eh_done((struct scsi_cmnd *)p, 1); }/* * Generic method for our eh processing. * The 'op' argument tells what we have to do. */static int sym_eh_handler(int op, char *opname, struct scsi_cmnd *cmd){ struct sym_hcb *np = SYM_SOFTC_PTR(cmd); SYM_QUEHEAD *qp; int to_do = SYM_EH_DO_IGNORE; int sts = -1; struct sym_eh_wait eh, *ep = &eh; char devname[20]; sprintf(devname, "%s:%d:%d", sym_name(np), cmd->device->id, cmd->device->lun); printf_warning("%s: %s operation started.\n", devname, opname);#if 0 /* This one should be the result of some race, thus to ignore */ if (cmd->serial_number != cmd->serial_number_at_timeout) goto prepare;#endif /* This one is not queued to the core driver -> to complete here */ FOR_EACH_QUEUED_ELEMENT(&np->s.wait_cmdq, qp) { if (SYM_SCMD_PTR(qp) == cmd) { to_do = SYM_EH_DO_COMPLETE; goto prepare; } } /* This one is queued in some place -> to wait for completion */ FOR_EACH_QUEUED_ELEMENT(&np->busy_ccbq, qp) { struct sym_ccb *cp = sym_que_entry(qp, struct sym_ccb, link_ccbq); if (cp->cam_ccb == cmd) { to_do = SYM_EH_DO_WAIT; goto prepare; } }prepare: /* Prepare stuff to either ignore, complete or wait for completion */ switch(to_do) { default: case SYM_EH_DO_IGNORE: break; case SYM_EH_DO_WAIT: init_MUTEX_LOCKED(&ep->sem); /* fall through */ case SYM_EH_DO_COMPLETE: ep->old_done = cmd->scsi_done; cmd->scsi_done = sym_eh_done; SYM_UCMD_PTR(cmd)->eh_wait = ep; } /* Try to proceed the operation we have been asked for */ sts = -1; switch(op) { case SYM_EH_ABORT: sts = sym_abort_scsiio(np, cmd, 1); break; case SYM_EH_DEVICE_RESET: sts = sym_reset_scsi_target(np, cmd->device->id); break; case SYM_EH_BUS_RESET: sym_reset_scsi_bus(np, 1); sts = 0; break; case SYM_EH_HOST_RESET: sym_reset_scsi_bus(np, 0); sym_start_up (np, 1); sts = 0; break; default: break; } /* On error, restore everything and cross fingers :) */ if (sts) { SYM_UCMD_PTR(cmd)->eh_wait = NULL; cmd->scsi_done = ep->old_done; to_do = SYM_EH_DO_IGNORE; } ep->to_do = to_do; /* Complete the command with locks held as required by the driver */ if (to_do == SYM_EH_DO_COMPLETE) sym_xpt_done2(np, cmd, CAM_REQ_ABORTED); /* Wait for completion with locks released, as required by kernel */ if (to_do == SYM_EH_DO_WAIT) { init_timer(&ep->timer); ep->timer.expires = jiffies + (5*HZ); ep->timer.function = sym_eh_timeout; ep->timer.data = (u_long)cmd; ep->timed_out = 1; /* Be pessimistic for once :) */ add_timer(&ep->timer); spin_unlock_irq(np->s.host->host_lock); down(&ep->sem); spin_lock_irq(np->s.host->host_lock); if (ep->timed_out) sts = -2; } printf_warning("%s: %s operation %s.\n", devname, opname, sts==0?"complete":sts==-2?"timed-out":"failed"); return sts? SCSI_FAILED : SCSI_SUCCESS;}/* * Error handlers called from the eh thread (one thread per HBA). */static int sym53c8xx_eh_abort_handler(struct scsi_cmnd *cmd){ return sym_eh_handler(SYM_EH_ABORT, "ABORT", cmd);}static int sym53c8xx_eh_device_reset_handler(struct scsi_cmnd *cmd){ return sym_eh_handler(SYM_EH_DEVICE_RESET, "DEVICE RESET", cmd);}static int sym53c8xx_eh_bus_reset_handler(struct scsi_cmnd *cmd){ return sym_eh_handler(SYM_EH_BUS_RESET, "BUS RESET", cmd);}static int sym53c8xx_eh_host_reset_handler(struct scsi_cmnd *cmd){ return sym_eh_handler(SYM_EH_HOST_RESET, "HOST RESET", cmd);}/* * Tune device queuing depth, according to various limits.
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?