📄 dpt_i2o.c
字号:
adpt_hba* p2; struct i2o_device* d; struct i2o_device* next; int i; int j; struct adpt_device* pDev; struct adpt_device* pNext; down(&adpt_configuration_lock); // scsi_unregister calls our adpt_release which // does a quiese if(pHba->host){ free_irq(pHba->host->irq, pHba); } for(i=0;i<DPTI_MAX_HBA;i++) { if(hbas[i]==pHba) { hbas[i] = NULL; } } p2 = NULL; for( p1 = hba_chain; p1; p2 = p1,p1=p1->next){ if(p1 == pHba) { if(p2) { p2->next = p1->next; } else { hba_chain = p1->next; } break; } } hba_count--; up(&adpt_configuration_lock); iounmap((void*)pHba->base_addr_virt); if(pHba->msg_addr_virt != pHba->base_addr_virt){ iounmap((void*)pHba->msg_addr_virt); } if(pHba->hrt) { kfree(pHba->hrt); } if(pHba->lct){ kfree(pHba->lct); } if(pHba->status_block) { kfree(pHba->status_block); } if(pHba->reply_pool){ kfree(pHba->reply_pool); } for(d = pHba->devices; d ; d = next){ next = d->next; kfree(d); } for(i = 0 ; i < pHba->top_scsi_channel ; i++){ for(j = 0; j < MAX_ID; j++){ if(pHba->channel[i].device[j] != NULL){ for(pDev = pHba->channel[i].device[j]; pDev; pDev = pNext){ pNext = pDev->next_lun; kfree(pDev); } } } } kfree(pHba); if(hba_count <= 0){ unregister_chrdev(DPTI_I2O_MAJOR, DPT_DRIVER); }}static int adpt_init(void){ int i; printk(KERN_INFO"Loading Adaptec I2O RAID: Version " DPT_I2O_VERSION "\n"); for (i = 0; i < DPTI_MAX_HBA; i++) { hbas[i] = NULL; }#ifdef REBOOT_NOTIFIER register_reboot_notifier(&adpt_reboot_notifier);#endif return 0;}static struct adpt_device* adpt_find_device(adpt_hba* pHba, u32 chan, u32 id, u32 lun){ struct adpt_device* d; if(chan < 0 || chan >= MAX_CHANNEL) return NULL; if( pHba->channel[chan].device == NULL){ printk(KERN_DEBUG"Adaptec I2O RAID: Trying to find device before they are allocated\n"); return NULL; } d = pHba->channel[chan].device[id]; if(!d || d->tid == 0) { return NULL; } /* If it is the only lun at that address then this should match*/ if(d->scsi_lun == lun){ return d; } /* else we need to look through all the luns */ for(d=d->next_lun ; d ; d = d->next_lun){ if(d->scsi_lun == lun){ return d; } } return NULL;}static int adpt_i2o_post_wait(adpt_hba* pHba, u32* msg, int len, int timeout){ // I used my own version of the WAIT_QUEUE_HEAD // to handle some version differences // When embedded in the kernel this could go back to the vanilla one ADPT_DECLARE_WAIT_QUEUE_HEAD(adpt_wq_i2o_post); int status = 0; ulong flags = 0; struct adpt_i2o_post_wait_data *p1, *p2; struct adpt_i2o_post_wait_data *wait_data = kmalloc(sizeof(struct adpt_i2o_post_wait_data),GFP_KERNEL); adpt_wait_queue_t wait; if(!wait_data){ return -ENOMEM; } /* * The spin locking is needed to keep anyone from playing * with the queue pointers and id while we do the same */ spin_lock_irqsave(&adpt_post_wait_lock, flags); // TODO we need a MORE unique way of getting ids // to support async LCT get wait_data->next = adpt_post_wait_queue; adpt_post_wait_queue = wait_data; adpt_post_wait_id = (++adpt_post_wait_id & 0x7fff); wait_data->id = adpt_post_wait_id; spin_unlock_irqrestore(&adpt_post_wait_lock, flags); wait_data->wq = &adpt_wq_i2o_post; wait_data->status = -ETIMEDOUT; // this code is taken from kernel/sched.c:interruptible_sleep_on_timeout wait.task = current; init_waitqueue_entry(&wait, current); wq_write_lock_irqsave(&adpt_wq_i2o_post.lock,flags); __add_wait_queue(&adpt_wq_i2o_post, &wait); wq_write_unlock(&adpt_wq_i2o_post.lock); msg[2] |= 0x80000000 | ((u32)wait_data->id); timeout *= HZ; if((status = adpt_i2o_post_this(pHba, msg, len)) == 0){ if(!timeout){ set_current_state(TASK_INTERRUPTIBLE); spin_unlock_irq(&io_request_lock); schedule(); spin_lock_irq(&io_request_lock); } else { set_current_state(TASK_INTERRUPTIBLE); spin_unlock_irq(&io_request_lock); schedule_timeout(timeout*HZ); spin_lock_irq(&io_request_lock); } } wq_write_lock_irq(&adpt_wq_i2o_post.lock); __remove_wait_queue(&adpt_wq_i2o_post, &wait); wq_write_unlock_irqrestore(&adpt_wq_i2o_post.lock,flags); if(status == -ETIMEDOUT){ printk(KERN_INFO"dpti%d: POST WAIT TIMEOUT\n",pHba->unit); // We will have to free the wait_data memory during shutdown return status; } /* Remove the entry from the queue. */ p2 = NULL; spin_lock_irqsave(&adpt_post_wait_lock, flags); for(p1 = adpt_post_wait_queue; p1; p2 = p1, p1 = p1->next) { if(p1 == wait_data) { if(p1->status == I2O_DETAIL_STATUS_UNSUPPORTED_FUNCTION ) { status = -EOPNOTSUPP; } if(p2) { p2->next = p1->next; } else { adpt_post_wait_queue = p1->next; } break; } } spin_unlock_irqrestore(&adpt_post_wait_lock, flags); kfree(wait_data); return status;}static s32 adpt_i2o_post_this(adpt_hba* pHba, u32* data, int len){ u32 m = EMPTY_QUEUE; u32 *msg; ulong timeout = jiffies + 30*HZ; do { rmb(); m = readl(pHba->post_port); if (m != EMPTY_QUEUE) { break; } if(time_after(jiffies,timeout)){ printk(KERN_WARNING"dpti%d: Timeout waiting for message frame!\n", pHba->unit); return -ETIMEDOUT; } } while(m == EMPTY_QUEUE); msg = (u32*) (pHba->msg_addr_virt + m); memcpy_toio(msg, data, len); wmb(); //post message writel(m, pHba->post_port); wmb(); return 0;}static void adpt_i2o_post_wait_complete(u32 context, int status){ struct adpt_i2o_post_wait_data *p1 = NULL; /* * We need to search through the adpt_post_wait * queue to see if the given message is still * outstanding. If not, it means that the IOP * took longer to respond to the message than we * had allowed and timer has already expired. * Not much we can do about that except log * it for debug purposes, increase timeout, and recompile * * Lock needed to keep anyone from moving queue pointers * around while we're looking through them. */ context &= 0x7fff; spin_lock(&adpt_post_wait_lock); for(p1 = adpt_post_wait_queue; p1; p1 = p1->next) { if(p1->id == context) { p1->status = status; spin_unlock(&adpt_post_wait_lock); wake_up_interruptible(p1->wq); return; } } spin_unlock(&adpt_post_wait_lock); // If this happens we loose commands that probably really completed printk(KERN_DEBUG"dpti: Could Not find task %d in wait queue\n",context); printk(KERN_DEBUG" Tasks in wait queue:\n"); for(p1 = adpt_post_wait_queue; p1; p1 = p1->next) { printk(KERN_DEBUG" %d\n",p1->id); } return;}static s32 adpt_i2o_reset_hba(adpt_hba* pHba) { u32 msg[8]; u8* status; u32 m = EMPTY_QUEUE ; ulong timeout = jiffies + (TMOUT_IOPRESET*HZ); if(pHba->initialized == FALSE) { // First time reset should be quick timeout = jiffies + (25*HZ); } else { adpt_i2o_quiesce_hba(pHba); } do { rmb(); m = readl(pHba->post_port); if (m != EMPTY_QUEUE) { break; } if(time_after(jiffies,timeout)){ printk(KERN_WARNING"Timeout waiting for message!\n"); return -ETIMEDOUT; } } while (m == EMPTY_QUEUE); status = (u8*)kmalloc(4, GFP_KERNEL|ADDR32); if(status == NULL) { adpt_send_nop(pHba, m); printk(KERN_ERR"IOP reset failed - no free memory.\n"); return -ENOMEM; } memset(status,0,4); msg[0]=EIGHT_WORD_MSG_SIZE|SGL_OFFSET_0; msg[1]=I2O_CMD_ADAPTER_RESET<<24|HOST_TID<<12|ADAPTER_TID; msg[2]=0; msg[3]=0; msg[4]=0; msg[5]=0; msg[6]=virt_to_bus(status); msg[7]=0; memcpy_toio(pHba->msg_addr_virt+m, msg, sizeof(msg)); wmb(); writel(m, pHba->post_port); wmb(); while(*status == 0){ if(time_after(jiffies,timeout)){ printk(KERN_WARNING"%s: IOP Reset Timeout\n",pHba->name); kfree(status); return -ETIMEDOUT; } rmb(); } if(*status == 0x01 /*I2O_EXEC_IOP_RESET_IN_PROGRESS*/) { PDEBUG("%s: Reset in progress...\n", pHba->name); // Here we wait for message frame to become available // indicated that reset has finished do { rmb(); m = readl(pHba->post_port); if (m != EMPTY_QUEUE) { break; } if(time_after(jiffies,timeout)){ printk(KERN_ERR "%s:Timeout waiting for IOP Reset.\n",pHba->name); return -ETIMEDOUT; } } while (m == EMPTY_QUEUE); // Flush the offset adpt_send_nop(pHba, m); } adpt_i2o_status_get(pHba); if(*status == 0x02 || pHba->status_block->iop_state != ADAPTER_STATE_RESET) { printk(KERN_WARNING"%s: Reset reject, trying to clear\n", pHba->name); } else { PDEBUG("%s: Reset completed.\n", pHba->name); } kfree(status);#ifdef UARTDELAY // This delay is to allow someone attached to the card through the debug UART to // set up the dump levels that they want before the rest of the initialization sequence adpt_delay(20000);#endif return 0;}static int adpt_i2o_parse_lct(adpt_hba* pHba){ int i; int max; int tid; struct i2o_device *d; i2o_lct *lct = pHba->lct; u8 bus_no = 0; s16 scsi_id; s16 scsi_lun; u32 buf[10]; // larger than 7, or 8 ... struct adpt_device* pDev; if (lct == NULL) { printk(KERN_ERR "%s: LCT is empty???\n",pHba->name); return -1; } max = lct->table_size; max -= 3; max /= 9; for(i=0;i<max;i++) { if( lct->lct_entry[i].user_tid != 0xfff){ /* * If we have hidden devices, we need to inform the upper layers about * the possible maximum id reference to handle device access when * an array is disassembled. This code has no other purpose but to * allow us future access to devices that are currently hidden * behind arrays, hotspares or have not been configured (JBOD mode). */ if( lct->lct_entry[i].class_id != I2O_CLASS_RANDOM_BLOCK_STORAGE && lct->lct_entry[i].class_id != I2O_CLASS_SCSI_PERIPHERAL && lct->lct_entry[i].class_id != I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL ){ continue; } tid = lct->lct_entry[i].tid; // I2O_DPT_DEVICE_INFO_GROUP_NO; if(adpt_i2o_query_scalar(pHba, tid, 0x8000, -1, buf, 32)<0) { continue; } bus_no = buf[0]>>16; scsi_id = buf[1]; scsi_lun = (buf[2]>>8 )&0xff; if(bus_no >= MAX_CHANNEL) { // Something wrong skip it printk(KERN_WARNING"%s: Channel number %d out of range \n", pHba->name, bus_no); continue; } if(scsi_id > MAX_ID){ printk(KERN_WARNING"%s: SCSI ID %d out of range \n", pHba->name, bus_no); continue; } if(bus_no > pHba->top_scsi_channel){ pHba->top_scsi_channel = bus_no; } if(scsi_id > pHba->top_scsi_id){ pHba->top_scsi_id = scsi_id; } if(scsi_lun > pHba->top_scsi_lun){ pHba->top_scsi_lun = scsi_lun; } continue; } d = (struct i2o_device *)kmalloc(sizeof(struct i2o_device), GFP_KERNEL); if(d==NULL) { printk(KERN_CRIT"%s: Out of memory for I2O device data.\n",pHba->name); return -ENOMEM; } d->controller = (void*)pHba; d->next = NULL; memcpy(&d->lct_data, &lct->lct_entry[i], sizeof(i2o_lct_entry)); d->flags = 0; tid = d->lct_data.tid; adpt_i2o_report_hba_unit(pHba, d); adpt_i2o_install_device(pHba, d); } bus_no = 0; for(d = pHba->devices; d ; d = d->next) { if(d->lct_data.class_id == I2O_CLASS_BUS_ADAPTER_PORT || d->lct_data.class_id == I2O_CLASS_FIBRE_CHANNEL_PORT){ tid = d->lct_data.tid; // TODO get the bus_no from hrt-but for now they are in order //bus_no = if(bus_no > pHba->top_scsi_channel){ pHba->top_scsi_channel = bus_no; } pHba->channel[bus_no].type = d->lct_data.class_id; pHba->channel[bus_no].tid = tid; if(adpt_i2o_query_scalar(pHba, tid, 0x0200, -1, buf, 28)>=0) { pHba->channel[bus_no].scsi_id = buf[1]; PDEBUG("Bus %d - SCSI ID %d.\n", bus_no, buf[1]); } // TODO remove - this is just until we get from hrt bus_no++; if(bus_no >= MAX_CHANNEL) { // Something wrong skip it printk(KERN_WARNING"%s: Channel number %d out of range - LCT\n", pHba->name, bus_no); break; } } } // Setup adpt_device table for(d = pHba->devices; d ; d = d->next) { if(d->lct_data.class_id == I2O_CLASS_RANDOM_BLOCK_STORAGE || d->lct_data.class_id == I2O_CLASS_SCSI_PERIPHERAL || d->lct_data.class_id == I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL ){ tid = d->lct_data.tid; scsi_id = -1; // I2O_DPT_DEVICE_INFO_GROUP_NO; if(adpt_i2o_query_scalar(pHba, tid, 0x8000, -1, buf, 32)>=0) { bus_no = buf[0]>>16; scsi_id = buf[1]; scsi_lun = (buf[2]>>8 )&0xff; if(bus_no >= MAX_CHANNEL) { // Something wrong skip it continue; } if(scsi_id > MAX_ID){ continue; } if( pHba->channel[bus_no].device[scsi_id] == NULL){ pDev = kmalloc(sizeof(struct adpt_device),GFP_KERNEL); if(pDev == NULL) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -