📄 aic79xx_osm.c
字号:
" Shorten the selection timeout to 128ms\n""\n"" options aic79xx 'aic79xx=verbose.tag_info:{{}.{}.{..10}}.seltime:1'\n""\n"" Sample /etc/modprobe.conf line:\n"" Change Read Streaming for Controller's 2 and 3\n""\n"" options aic79xx 'aic79xx=rd_strm:{..0xFFF0.0xC0F0}'");static void ahd_linux_handle_scsi_status(struct ahd_softc *, struct ahd_linux_device *, struct scb *);static void ahd_linux_queue_cmd_complete(struct ahd_softc *ahd, Scsi_Cmnd *cmd);static void ahd_linux_filter_inquiry(struct ahd_softc *ahd, struct ahd_devinfo *devinfo);static void ahd_linux_dev_timed_unfreeze(u_long arg);static void ahd_linux_sem_timeout(u_long arg);static void ahd_linux_initialize_scsi_bus(struct ahd_softc *ahd);static void ahd_linux_size_nseg(void);static void ahd_linux_thread_run_complete_queue(struct ahd_softc *ahd);static void ahd_linux_start_dv(struct ahd_softc *ahd);static void ahd_linux_dv_timeout(struct scsi_cmnd *cmd);static int ahd_linux_dv_thread(void *data);static void ahd_linux_kill_dv_thread(struct ahd_softc *ahd);static void ahd_linux_dv_target(struct ahd_softc *ahd, u_int target);static void ahd_linux_dv_transition(struct ahd_softc *ahd, struct scsi_cmnd *cmd, struct ahd_devinfo *devinfo, struct ahd_linux_target *targ);static void ahd_linux_dv_fill_cmd(struct ahd_softc *ahd, struct scsi_cmnd *cmd, struct ahd_devinfo *devinfo);static void ahd_linux_dv_inq(struct ahd_softc *ahd, struct scsi_cmnd *cmd, struct ahd_devinfo *devinfo, struct ahd_linux_target *targ, u_int request_length);static void ahd_linux_dv_tur(struct ahd_softc *ahd, struct scsi_cmnd *cmd, struct ahd_devinfo *devinfo);static void ahd_linux_dv_rebd(struct ahd_softc *ahd, struct scsi_cmnd *cmd, struct ahd_devinfo *devinfo, struct ahd_linux_target *targ);static void ahd_linux_dv_web(struct ahd_softc *ahd, struct scsi_cmnd *cmd, struct ahd_devinfo *devinfo, struct ahd_linux_target *targ);static void ahd_linux_dv_reb(struct ahd_softc *ahd, struct scsi_cmnd *cmd, struct ahd_devinfo *devinfo, struct ahd_linux_target *targ);static void ahd_linux_dv_su(struct ahd_softc *ahd, struct scsi_cmnd *cmd, struct ahd_devinfo *devinfo, struct ahd_linux_target *targ);static int ahd_linux_fallback(struct ahd_softc *ahd, struct ahd_devinfo *devinfo);static __inline int ahd_linux_dv_fallback(struct ahd_softc *ahd, struct ahd_devinfo *devinfo);static void ahd_linux_dv_complete(Scsi_Cmnd *cmd);static void ahd_linux_generate_dv_pattern(struct ahd_linux_target *targ);static u_int ahd_linux_user_tagdepth(struct ahd_softc *ahd, struct ahd_devinfo *devinfo);static u_int ahd_linux_user_dv_setting(struct ahd_softc *ahd);static void ahd_linux_setup_user_rd_strm_settings(struct ahd_softc *ahd);static void ahd_linux_device_queue_depth(struct ahd_softc *ahd, struct ahd_linux_device *dev);static struct ahd_linux_target* ahd_linux_alloc_target(struct ahd_softc*, u_int, u_int);static void ahd_linux_free_target(struct ahd_softc*, struct ahd_linux_target*);static struct ahd_linux_device* ahd_linux_alloc_device(struct ahd_softc*, struct ahd_linux_target*, u_int);static void ahd_linux_free_device(struct ahd_softc*, struct ahd_linux_device*);static void ahd_linux_run_device_queue(struct ahd_softc*, struct ahd_linux_device*);static void ahd_linux_setup_tag_info_global(char *p);static aic_option_callback_t ahd_linux_setup_tag_info;static aic_option_callback_t ahd_linux_setup_rd_strm_info;static aic_option_callback_t ahd_linux_setup_dv;static aic_option_callback_t ahd_linux_setup_iocell_info;static int ahd_linux_next_unit(void);static void ahd_runq_tasklet(unsigned long data);static int aic79xx_setup(char *c);/****************************** Inlines ***************************************/static __inline void ahd_schedule_completeq(struct ahd_softc *ahd);static __inline void ahd_schedule_runq(struct ahd_softc *ahd);static __inline void ahd_setup_runq_tasklet(struct ahd_softc *ahd);static __inline void ahd_teardown_runq_tasklet(struct ahd_softc *ahd);static __inline struct ahd_linux_device* ahd_linux_get_device(struct ahd_softc *ahd, u_int channel, u_int target, u_int lun, int alloc);static struct ahd_cmd *ahd_linux_run_complete_queue(struct ahd_softc *ahd);static __inline void ahd_linux_check_device_queue(struct ahd_softc *ahd, struct ahd_linux_device *dev);static __inline struct ahd_linux_device * ahd_linux_next_device_to_run(struct ahd_softc *ahd);static __inline void ahd_linux_run_device_queues(struct ahd_softc *ahd);static __inline void ahd_linux_unmap_scb(struct ahd_softc*, struct scb*);static __inline voidahd_schedule_completeq(struct ahd_softc *ahd){ if ((ahd->platform_data->flags & AHD_RUN_CMPLT_Q_TIMER) == 0) { ahd->platform_data->flags |= AHD_RUN_CMPLT_Q_TIMER; ahd->platform_data->completeq_timer.expires = jiffies; add_timer(&ahd->platform_data->completeq_timer); }}/* * Must be called with our lock held. */static __inline voidahd_schedule_runq(struct ahd_softc *ahd){ tasklet_schedule(&ahd->platform_data->runq_tasklet);}static __inlinevoid ahd_setup_runq_tasklet(struct ahd_softc *ahd){ tasklet_init(&ahd->platform_data->runq_tasklet, ahd_runq_tasklet, (unsigned long)ahd);}static __inline voidahd_teardown_runq_tasklet(struct ahd_softc *ahd){ tasklet_kill(&ahd->platform_data->runq_tasklet);}static __inline struct ahd_linux_device*ahd_linux_get_device(struct ahd_softc *ahd, u_int channel, u_int target, u_int lun, int alloc){ struct ahd_linux_target *targ; struct ahd_linux_device *dev; u_int target_offset; target_offset = target; if (channel != 0) target_offset += 8; targ = ahd->platform_data->targets[target_offset]; if (targ == NULL) { if (alloc != 0) { targ = ahd_linux_alloc_target(ahd, channel, target); if (targ == NULL) return (NULL); } else return (NULL); } dev = targ->devices[lun]; if (dev == NULL && alloc != 0) dev = ahd_linux_alloc_device(ahd, targ, lun); return (dev);}#define AHD_LINUX_MAX_RETURNED_ERRORS 4static struct ahd_cmd *ahd_linux_run_complete_queue(struct ahd_softc *ahd){ struct ahd_cmd *acmd; u_long done_flags; int with_errors; with_errors = 0; ahd_done_lock(ahd, &done_flags); while ((acmd = TAILQ_FIRST(&ahd->platform_data->completeq)) != NULL) { Scsi_Cmnd *cmd; if (with_errors > AHD_LINUX_MAX_RETURNED_ERRORS) { /* * Linux uses stack recursion to requeue * commands that need to be retried. Avoid * blowing out the stack by "spoon feeding" * commands that completed with error back * the operating system in case they are going * to be retried. "ick" */ ahd_schedule_completeq(ahd); break; } TAILQ_REMOVE(&ahd->platform_data->completeq, acmd, acmd_links.tqe); cmd = &acmd_scsi_cmd(acmd); cmd->host_scribble = NULL; if (ahd_cmd_get_transaction_status(cmd) != DID_OK || (cmd->result & 0xFF) != SCSI_STATUS_OK) with_errors++; cmd->scsi_done(cmd); } ahd_done_unlock(ahd, &done_flags); return (acmd);}static __inline voidahd_linux_check_device_queue(struct ahd_softc *ahd, struct ahd_linux_device *dev){ if ((dev->flags & AHD_DEV_FREEZE_TIL_EMPTY) != 0 && dev->active == 0) { dev->flags &= ~AHD_DEV_FREEZE_TIL_EMPTY; dev->qfrozen--; } if (TAILQ_FIRST(&dev->busyq) == NULL || dev->openings == 0 || dev->qfrozen != 0) return; ahd_linux_run_device_queue(ahd, dev);}static __inline struct ahd_linux_device *ahd_linux_next_device_to_run(struct ahd_softc *ahd){ if ((ahd->flags & AHD_RESOURCE_SHORTAGE) != 0 || (ahd->platform_data->qfrozen != 0 && AHD_DV_SIMQ_FROZEN(ahd) == 0)) return (NULL); return (TAILQ_FIRST(&ahd->platform_data->device_runq));}static __inline voidahd_linux_run_device_queues(struct ahd_softc *ahd){ struct ahd_linux_device *dev; while ((dev = ahd_linux_next_device_to_run(ahd)) != NULL) { TAILQ_REMOVE(&ahd->platform_data->device_runq, dev, links); dev->flags &= ~AHD_DEV_ON_RUN_LIST; ahd_linux_check_device_queue(ahd, dev); }}static __inline voidahd_linux_unmap_scb(struct ahd_softc *ahd, struct scb *scb){ Scsi_Cmnd *cmd; int direction; cmd = scb->io_ctx; direction = scsi_to_pci_dma_dir(cmd->sc_data_direction); ahd_sync_sglist(ahd, scb, BUS_DMASYNC_POSTWRITE); if (cmd->use_sg != 0) { struct scatterlist *sg; sg = (struct scatterlist *)cmd->request_buffer; pci_unmap_sg(ahd->dev_softc, sg, cmd->use_sg, direction); } else if (cmd->request_bufflen != 0) { pci_unmap_single(ahd->dev_softc, scb->platform_data->buf_busaddr, cmd->request_bufflen, direction); }}/******************************** Macros **************************************/#define BUILD_SCSIID(ahd, cmd) \ ((((cmd)->device->id << TID_SHIFT) & TID) | (ahd)->our_id)/************************ Host template entry points *************************/static int ahd_linux_detect(Scsi_Host_Template *);static const char *ahd_linux_info(struct Scsi_Host *);static int ahd_linux_queue(Scsi_Cmnd *, void (*)(Scsi_Cmnd *));#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)static int ahd_linux_slave_alloc(Scsi_Device *);static int ahd_linux_slave_configure(Scsi_Device *);static void ahd_linux_slave_destroy(Scsi_Device *);#if defined(__i386__)static int ahd_linux_biosparam(struct scsi_device*, struct block_device*, sector_t, int[]);#endif#elsestatic int ahd_linux_release(struct Scsi_Host *);static void ahd_linux_select_queue_depth(struct Scsi_Host *host, Scsi_Device *scsi_devs);#if defined(__i386__)static int ahd_linux_biosparam(Disk *, kdev_t, int[]);#endif#endifstatic int ahd_linux_bus_reset(Scsi_Cmnd *);static int ahd_linux_dev_reset(Scsi_Cmnd *);static int ahd_linux_abort(Scsi_Cmnd *);/* * Calculate a safe value for AHD_NSEG (as expressed through ahd_linux_nseg). * * In pre-2.5.X... * The midlayer allocates an S/G array dynamically when a command is issued * using SCSI malloc. This array, which is in an OS dependent format that * must later be copied to our private S/G list, is sized to house just the * number of segments needed for the current transfer. Since the code that * sizes the SCSI malloc pool does not take into consideration fragmentation * of the pool, executing transactions numbering just a fraction of our * concurrent transaction limit with SG list lengths aproaching AHC_NSEG will * quickly depleat the SCSI malloc pool of usable space. Unfortunately, the * mid-layer does not properly handle this scsi malloc failures for the S/G * array and the result can be a lockup of the I/O subsystem. We try to size * our S/G list so that it satisfies our drivers allocation requirements in * addition to avoiding fragmentation of the SCSI malloc pool. */static voidahd_linux_size_nseg(void){#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) u_int cur_size; u_int best_size; /* * The SCSI allocator rounds to the nearest 512 bytes * an cannot allocate across a page boundary. Our algorithm * is to start at 1K of scsi malloc space per-command and * loop through all factors of the PAGE_SIZE and pick the best. */ best_size = 0; for (cur_size = 1024; cur_size <= PAGE_SIZE; cur_size *= 2) { u_int nseg; nseg = cur_size / sizeof(struct scatterlist); if (nseg < AHD_LINUX_MIN_NSEG) continue; if (best_size == 0) { best_size = cur_size; ahd_linux_nseg = nseg; } else { u_int best_rem; u_int cur_rem; /* * Compare the traits of the current "best_size" * with the current size to determine if the * current size is a better size. */ best_rem = best_size % sizeof(struct scatterlist); cur_rem = cur_size % sizeof(struct scatterlist); if (cur_rem < best_rem) { best_size = cur_size; ahd_linux_nseg = nseg; } } }#endif}/* * Try to detect an Adaptec 79XX controller. */static intahd_linux_detect(Scsi_Host_Template *template){ struct ahd_softc *ahd; int found; int error = 0;#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) /* * It is a bug that the upper layer takes * this lock just prior to calling us. */ spin_unlock_irq(&io_request_lock);#endif /* * Sanity checking of Linux SCSI data structures so * that some of our hacks^H^H^H^H^Hassumptions aren't * violated. */ if (offsetof(struct ahd_cmd_internal, end) > offsetof(struct scsi_cmnd, host_scribble)) { printf("ahd_linux_detect: SCSI data structures changed.\n"); printf("ahd_linux_detect: Unable to attach\n"); return (0); } /* * Determine an appropriate size for our Scatter Gatther lists. */ ahd_linux_size_nseg();#ifdef MODULE /* * If we've been passed any parameters, process them now. */ if (aic79xx) aic79xx_setup(aic79xx);#endif template->proc_name = "aic79xx"; /* * Initialize our softc list lock prior to * probing for any adapters. */ ahd_list_lockinit();#ifdef CONFIG_PCI error = ahd_linux_pci_init(); if (error) return error;#endif /* * Register with the SCSI layer all * controllers we've found. */ found = 0; TAILQ_FOREACH(ahd, &ahd_tailq, links) { if (ahd_linux_register_host(ahd, template) == 0) found++; }#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) spin_lock_irq(&io_request_lock);#endif aic79xx_detect_complete++; return 0;}#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)/* * Free the passed in Scsi_Host memory structures prior to unloading the * module. */static intahd_linux_release(struct Scsi_Host * host){ struct ahd_softc *ahd; u_long l; ahd_list_lock(&l); if (host != NULL) { /* * We should be able to just perform
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -