sym_glue.c
来自「linux 内核源代码」· C语言 代码 · 共 2,086 行 · 第 1/4 页
C
2,086 行
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) return SCSI_MLQUEUE_HOST_BUSY; sts = sym_queue_command(np, cmd); if (sts) return SCSI_MLQUEUE_HOST_BUSY; return 0;}/* * Linux entry point of the interrupt handler. */static irqreturn_t sym53c8xx_intr(int irq, void *dev_id){ struct Scsi_Host *shost = dev_id; struct sym_data *sym_data = shost_priv(shost); irqreturn_t result; /* Avoid spinloop trying to handle interrupts on frozen device */ if (pci_channel_offline(sym_data->pdev)) return IRQ_NONE; if (DEBUG_FLAGS & DEBUG_TINY) printf_debug ("["); spin_lock(shost->host_lock); result = sym_interrupt(shost); spin_unlock(shost->host_lock); if (DEBUG_FLAGS & DEBUG_TINY) printf_debug ("]\n"); return result;}/* * 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); 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/* * 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_ucmd *ucmd = SYM_UCMD_PTR(cmd); struct Scsi_Host *shost = cmd->device->host; struct sym_data *sym_data = shost_priv(shost); struct pci_dev *pdev = sym_data->pdev; struct sym_hcb *np = sym_data->ncb; SYM_QUEHEAD *qp; int cmd_queued = 0; int sts = -1; struct completion eh_done; scmd_printk(KERN_WARNING, cmd, "%s operation started\n", opname); /* We may be in an error condition because the PCI bus * went down. In this case, we need to wait until the * PCI bus is reset, the card is reset, and only then * proceed with the scsi error recovery. There's no * point in hurrying; take a leisurely wait. */#define WAIT_FOR_PCI_RECOVERY 35 if (pci_channel_offline(pdev)) { struct completion *io_reset; int finished_reset = 0; init_completion(&eh_done); spin_lock_irq(shost->host_lock); /* Make sure we didn't race */ if (pci_channel_offline(pdev)) { if (!sym_data->io_reset) sym_data->io_reset = &eh_done; io_reset = sym_data->io_reset; } else { finished_reset = 1; } spin_unlock_irq(shost->host_lock); if (!finished_reset) finished_reset = wait_for_completion_timeout(io_reset, WAIT_FOR_PCI_RECOVERY*HZ); if (!finished_reset) return SCSI_FAILED; } spin_lock_irq(shost->host_lock); /* 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->cmd == cmd) { cmd_queued = 1; break; } } /* 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(shost, 1); sts = 0; break; default: break; } /* On error, restore everything and cross fingers :) */ if (sts) cmd_queued = 0; if (cmd_queued) { init_completion(&eh_done); ucmd->eh_done = &eh_done; spin_unlock_irq(shost->host_lock); if (!wait_for_completion_timeout(&eh_done, 5*HZ)) { ucmd->eh_done = NULL; sts = -2; } } else { spin_unlock_irq(shost->host_lock); } dev_warn(&cmd->device->sdev_gendev, "%s operation %s.\n", 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. */static void sym_tune_dev_queuing(struct sym_tcb *tp, int lun, u_short reqtags){ struct sym_lcb *lp = sym_lp(tp, lun); u_short oldtags; if (!lp) return; oldtags = lp->s.reqtags; if (reqtags > lp->s.scdev_depth) reqtags = lp->s.scdev_depth; lp->s.reqtags = reqtags; if (reqtags != oldtags) { dev_info(&tp->starget->dev, "tagged command queuing %s, command queue depth %d.\n", lp->s.reqtags ? "enabled" : "disabled", reqtags); }}static int sym53c8xx_slave_alloc(struct scsi_device *sdev){ struct sym_hcb *np = sym_get_hcb(sdev->host); struct sym_tcb *tp = &np->target[sdev->id]; struct sym_lcb *lp; if (sdev->id >= SYM_CONF_MAX_TARGET || sdev->lun >= SYM_CONF_MAX_LUN) return -ENXIO; tp->starget = sdev->sdev_target; /* * Fail the device init if the device is flagged NOSCAN at BOOT in * the NVRAM. This may speed up boot and maintain coherency with * BIOS device numbering. Clearing the flag allows the user to * rescan skipped devices later. We also return an error for * devices not flagged for SCAN LUNS in the NVRAM since some single * lun devices behave badly when asked for a non zero LUN. */ if (tp->usrflags & SYM_SCAN_BOOT_DISABLED) { tp->usrflags &= ~SYM_SCAN_BOOT_DISABLED; starget_printk(KERN_INFO, tp->starget, "Scan at boot disabled in NVRAM\n"); return -ENXIO; } if (tp->usrflags & SYM_SCAN_LUNS_DISABLED) { if (sdev->lun != 0) return -ENXIO; starget_printk(KERN_INFO, tp->starget, "Multiple LUNs disabled in NVRAM\n"); } lp = sym_alloc_lcb(np, sdev->id, sdev->lun); if (!lp) return -ENOMEM; spi_min_period(tp->starget) = tp->usr_period; spi_max_width(tp->starget) = tp->usr_width; return 0;}/* * Linux entry point for device queue sizing. */static int sym53c8xx_slave_configure(struct scsi_device *sdev){ struct sym_hcb *np = sym_get_hcb(sdev->host); struct sym_tcb *tp = &np->target[sdev->id]; struct sym_lcb *lp = sym_lp(tp, sdev->lun); int reqtags, depth_to_use; /* * Get user flags. */ lp->curr_flags = lp->user_flags; /* * Select queue depth from driver setup. * Donnot use more than configured by user. * Use at least 2. * Donnot use more than our maximum. */ reqtags = sym_driver_setup.max_tag; if (reqtags > tp->usrtags) reqtags = tp->usrtags; if (!sdev->tagged_supported) reqtags = 0; if (reqtags > SYM_CONF_MAX_TAG) reqtags = SYM_CONF_MAX_TAG; depth_to_use = reqtags ? reqtags : 2; scsi_adjust_queue_depth(sdev, sdev->tagged_supported ? MSG_SIMPLE_TAG : 0, depth_to_use); lp->s.scdev_depth = depth_to_use; sym_tune_dev_queuing(tp, sdev->lun, reqtags); if (!spi_initial_dv(sdev->sdev_target)) spi_dv_device(sdev); return 0;}static void sym53c8xx_slave_destroy(struct scsi_device *sdev){ struct sym_hcb *np = sym_get_hcb(sdev->host); struct sym_lcb *lp = sym_lp(&np->target[sdev->id], sdev->lun); if (lp->itlq_tbl) sym_mfree_dma(lp->itlq_tbl, SYM_CONF_MAX_TASK * 4, "ITLQ_TBL"); kfree(lp->cb_tags); sym_mfree_dma(lp, sizeof(*lp), "LCB");}/* * Linux entry point for info() function */static const char *sym53c8xx_info (struct Scsi_Host *host){ return SYM_DRIVER_NAME;}#ifdef SYM_LINUX_PROC_INFO_SUPPORT/* * Proc file system stuff * * A read operation returns adapter information. * A write operation is a control command. * The string is parsed in the driver code and the command is passed * to the sym_usercmd() function. */#ifdef SYM_LINUX_USER_COMMAND_SUPPORTstruct sym_usrcmd { u_long target; u_long lun; u_long data; u_long cmd;};#define UC_SETSYNC 10#define UC_SETTAGS 11#define UC_SETDEBUG 12#define UC_SETWIDE 14#define UC_SETFLAG 15#define UC_SETVERBOSE 17#define UC_RESETDEV 18#define UC_CLEARDEV 19static void sym_exec_user_command (struct sym_hcb *np, struct sym_usrcmd *uc){ struct sym_tcb *tp; int t, l; switch (uc->cmd) { case 0: return;#ifdef SYM_LINUX_DEBUG_CONTROL_SUPPORT case UC_SETDEBUG: sym_debug_flags = uc->data; break;#endif case UC_SETVERBOSE: np->verbose = uc->data; break; default: /* * We assume that other commands apply to targets. * This should always be the case and avoid the below * 4 lines to be repeated 6 times. */ for (t = 0; t < SYM_CONF_MAX_TARGET; t++) { if (!((uc->target >> t) & 1)) continue; tp = &np->target[t]; switch (uc->cmd) { case UC_SETSYNC: if (!uc->data || uc->data >= 255) { tp->tgoal.iu = tp->tgoal.dt = tp->tgoal.qas = 0; tp->tgoal.offset = 0; } else if (uc->data <= 9 && np->minsync_dt) { if (uc->data < np->minsync_dt) uc->data = np->minsync_dt; tp->tgoal.iu = tp->tgoal.dt = tp->tgoal.qas = 1; tp->tgoal.width = 1; tp->tgoal.period = uc->data; tp->tgoal.offset = np->maxoffs_dt; } else { if (uc->data < np->minsync) uc->data = np->minsync; tp->tgoal.iu = tp->tgoal.dt = tp->tgoal.qas = 0; tp->tgoal.period = uc->data; tp->tgoal.offset = np->maxoffs; } tp->tgoal.check_nego = 1; break; case UC_SETWIDE: tp->tgoal.width = uc->data ? 1 : 0; tp->tgoal.check_nego = 1; break; case UC_SETTAGS: for (l = 0; l < SYM_CONF_MAX_LUN; l++) sym_tune_dev_queuing(tp, l, uc->data); break; case UC_RESETDEV: tp->to_reset = 1; np->istat_sem = SEM; OUTB(np, nc_istat, SIGP|SEM); break; case UC_CLEARDEV: for (l = 0; l < SYM_CONF_MAX_LUN; l++) { struct sym_lcb *lp = sym_lp(tp, l); if (lp) lp->to_clear = 1; } np->istat_sem = SEM; OUTB(np, nc_istat, SIGP|SEM); break; case UC_SETFLAG: tp->usrflags = uc->data; break; } } break; }}static int skip_spaces(char *ptr, int len){ int cnt, c; for (cnt = len; cnt > 0 && (c = *ptr++) && isspace(c); cnt--); return (len - cnt);}static int get_int_arg(char *ptr, int len, u_long *pv){ char *end; *pv = simple_strtoul(ptr, &end, 10); return (end - ptr);}static int is_keyword(char *ptr, int len, char *verb){ int verb_len = strlen(verb); if (len >= verb_len && !memcmp(verb, ptr, verb_len)) return verb_len; else return 0;}#define SKIP_SPACES(ptr, len) \ if ((arg_len = skip_spaces(ptr, len)) < 1) \ return -EINVAL; \ ptr += arg_len; len -= arg_len;#define GET_INT_ARG(ptr, len, v) \ if (!(arg_len = get_int_arg(ptr, len, &(v)))) \ return -EINVAL; \ ptr += arg_len; len -= arg_len;/* * Parse a control command */static int sym_user_command(struct Scsi_Host *shost, char *buffer, int length){ struct sym_hcb *np = sym_get_hcb(shost); char *ptr = buffer; int len = length; struct sym_usrcmd cmd, *uc = &cmd; int arg_len; u_long target; memset(uc, 0, sizeof(*uc)); if (len > 0 && ptr[len-1] == '\n') --len; if ((arg_len = is_keyword(ptr, len, "setsync")) != 0) uc->cmd = UC_SETSYNC; else if ((arg_len = is_keyword(ptr, len, "settags")) != 0) uc->cmd = UC_SETTAGS; else if ((arg_len = is_keyword(ptr, len, "setverbose")) != 0) uc->cmd = UC_SETVERBOSE; else if ((arg_len = is_keyword(ptr, len, "setwide")) != 0) uc->cmd = UC_SETWIDE;#ifdef SYM_LINUX_DEBUG_CONTROL_SUPPORT else if ((arg_len = is_keyword(ptr, len, "setdebug")) != 0) uc->cmd = UC_SETDEBUG;#endif else if ((arg_len = is_keyword(ptr, len, "setflag")) != 0) uc->cmd = UC_SETFLAG; else if ((arg_len = is_keyword(ptr, len, "resetdev")) != 0) uc->cmd = UC_RESETDEV; else if ((arg_len = is_keyword(ptr, len, "cleardev")) != 0) uc->cmd = UC_CLEARDEV; else arg_len = 0;#ifdef DEBUG_PROC_INFOprintk("sym_user_command: arg_len=%d, cmd=%ld\n", arg_len, uc->cmd);#endif if (!arg_len) return -EINVAL; ptr += arg_len; len -= arg_len; switch(uc->cmd) { case UC_SETSYNC: case UC_SETTAGS: case UC_SETWIDE: case UC_SETFLAG: case UC_RESETDEV: case UC_CLEARDEV: SKIP_SPACES(ptr, len); if ((arg_len = is_keyword(ptr, len, "all")) != 0) { ptr += arg_len; len -= arg_len; uc->target = ~0; } else {
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?