📄 sym_glue.c
字号:
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 = 0; 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(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((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, Scsi_Cmnd *cmd){ hcb_p np = SYM_SOFTC_PTR(cmd); unsigned long flags; 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->target, cmd->lun); printf_warning("%s: %s operation started.\n", devname, opname); SYM_LOCK_HCB(np, flags);#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) { ccb_p 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: goto finish; break; case SYM_EH_DO_WAIT:#if LINUX_VERSION_CODE > LinuxVersionCode(2,3,0) init_MUTEX_LOCKED(&ep->sem);#else ep->sem = MUTEX_LOCKED;#endif /* 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->target); 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 = 0; cmd->scsi_done = ep->old_done; to_do = SYM_EH_DO_IGNORE; }finish: 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); SYM_UNLOCK_HCB(np, flags); /* 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); SYM_UNLOCK_SCSI_NORESTORE(np); down(&ep->sem); SYM_LOCK_SCSI_NOSAVE(np); 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). */int sym53c8xx_eh_abort_handler(Scsi_Cmnd *cmd){ return sym_eh_handler(SYM_EH_ABORT, "ABORT", cmd);}int sym53c8xx_eh_device_reset_handler(Scsi_Cmnd *cmd){ return sym_eh_handler(SYM_EH_DEVICE_RESET, "DEVICE RESET", cmd);}int sym53c8xx_eh_bus_reset_handler(Scsi_Cmnd *cmd){ return sym_eh_handler(SYM_EH_BUS_RESET, "BUS RESET", cmd);}int sym53c8xx_eh_host_reset_handler(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(hcb_p np, int target, int lun, u_short reqtags){ tcb_p tp = &np->target[target]; lcb_p lp = sym_lp(np, tp, lun); u_short oldtags; if (!lp) return; oldtags = lp->s.reqtags; if (reqtags > lp->s.scdev_depth) reqtags = lp->s.scdev_depth; lp->started_limit = reqtags ? reqtags : 2; lp->started_max = 1; lp->s.reqtags = reqtags; if (reqtags != oldtags) { printf_info("%s:%d:%d: " "tagged command queuing %s, command queue depth %d.\n", sym_name(np), target, lun, lp->s.reqtags ? "enabled" : "disabled", lp->started_limit); }}#ifdef SYM_LINUX_BOOT_COMMAND_LINE_SUPPORT/* * Linux select queue depths function */#define DEF_DEPTH (sym_driver_setup.max_tag)#define ALL_TARGETS -2#define NO_TARGET -1#define ALL_LUNS -2#define NO_LUN -1static int device_queue_depth(hcb_p np, int target, int lun){ int c, h, t, u, v; char *p = sym_driver_setup.tag_ctrl; char *ep; h = -1; t = NO_TARGET; u = NO_LUN; while ((c = *p++) != 0) { v = simple_strtoul(p, &ep, 0); switch(c) { case '/': ++h; t = ALL_TARGETS; u = ALL_LUNS; break; case 't': if (t != target) t = (target == v) ? v : NO_TARGET; u = ALL_LUNS; break; case 'u': if (u != lun) u = (lun == v) ? v : NO_LUN; break; case 'q': if (h == np->s.unit && (t == ALL_TARGETS || t == target) && (u == ALL_LUNS || u == lun)) return v; break; case '-': t = ALL_TARGETS; u = ALL_LUNS; break; default: break; } p = ep; } return DEF_DEPTH;}#else#define device_queue_depth(np, t, l) (sym_driver_setup.max_tag)#endif /* SYM_LINUX_BOOT_COMMAND_LINE_SUPPORT *//* * Linux entry point for device queue sizing. */static void sym53c8xx_select_queue_depths(struct Scsi_Host *host, struct scsi_device *devlist){ struct scsi_device *device; for (device = devlist; device; device = device->next) { hcb_p np; tcb_p tp; lcb_p lp; int reqtags; if (device->host != host) continue; np = ((struct host_data *) host->hostdata)->ncb; tp = &np->target[device->id]; /* * Get user settings for transfer parameters. */ tp->inq_byte7_valid = (INQ7_SYNC|INQ7_WIDE16); sym_update_trans_settings(np, tp); /* * Allocate the LCB if not yet. * If it fail, we may well be in the sh*t. :) */ lp = sym_alloc_lcb(np, device->id, device->lun); if (!lp) { device->queue_depth = 1; continue; } /* * 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 = device_queue_depth(np, device->id, device->lun); if (reqtags > tp->usrtags) reqtags = tp->usrtags; if (!device->tagged_supported) reqtags = 0;#if 1 /* Avoid to locally queue commands for no good reasons */ if (reqtags > SYM_CONF_MAX_TAG) reqtags = SYM_CONF_MAX_TAG; device->queue_depth = reqtags ? reqtags : 2;#else device->queue_depth = reqtags ? SYM_CONF_MAX_TAG : 2;#endif lp->s.scdev_depth = device->queue_depth; sym_tune_dev_queuing(np, device->id, device->lun, reqtags); }}/* * Linux entry point for info() function */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 (hcb_p np, struct sym_usrcmd *uc){ tcb_p 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->tinfo.goal.options = 0; tp->tinfo.goal.offset = 0; break; } if (uc->data <= 9 && np->minsync_dt) { if (uc->data < np->minsync_dt) uc->data = np->minsync_dt; tp->tinfo.goal.options = PPR_OPT_DT; tp->tinfo.goal.width = 1; tp->tinfo.goal.period = uc->data; tp->tinfo.goal.offset = np->maxoffs_dt; } else { if (uc->data < np->minsync) uc->data = np->minsync; tp->tinfo.goal.options = 0; tp->tinfo.goal.period = uc->data; tp->tinfo.goal.offset = np->maxoffs; } break; case UC_SETWIDE: tp->tinfo.goal.width = uc->data ? 1 : 0; break; case UC_SETTAGS: for (l = 0; l < SYM_CONF_MAX_LUN; l++) sym_tune_dev_queuing(np, t,l, uc->data); break; case UC_RESETDEV: tp->to_reset = 1; np->istat_sem = SEM; OUTB (nc_istat, SIGP|SEM); break; case UC_CLEARDEV: for (l = 0; l < SYM_CONF_MAX_LUN; l++) { lcb_p lp = sym_lp(np, tp, l); if (lp) lp->to_clear = 1; } np->istat_sem = SEM; OUTB (nc_istat, SIGP|SEM); break; case UC_SETFLAG: tp->usrflags = uc->data; break; } } break; }}#define is_digit(c) ((c) >= '0' && (c) <= '9')#define digit_to_bin(c) ((c) - '0')#define is_space(c) ((c) == ' ' || (c) == '\t')static int skip_spaces(char *ptr, int len){ int cnt, c; for (cnt = len; cnt > 0 && (c = *ptr++) && is_space(c); cnt--); return (len - cnt);}static int get_int_arg(char *ptr, int len, u_long *pv){ int cnt, c; u_long v; for (v = 0, cnt = len; cnt > 0 && (c = *ptr++) && is_digit(c); cnt--) { v = (v * 10) + digit_to_bin(c); } if (pv) *pv = v; return (len - cnt);}static int is_keyword(char *ptr, int len, char *verb){ int verb_len = strlen(verb); if (len >= strlen(verb) && !memcmp(verb, ptr, verb_len)) return verb_len; else return 0;}#define SKIP_SPACES(min_spaces) \ if ((arg_len = skip_spaces(ptr, len)) < (min_spaces)) \ return -EINVAL; \ ptr += arg_len; len -= arg_len;#define GET_INT_ARG(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(hcb_p np, char *buffer, int length){ char *ptr = buffer; int len = length; struct sym_usrcmd cmd, *uc = &cmd; int arg_len; u_long target; bzero(uc, 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -