diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1998-05-07 02:55:41 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1998-05-07 02:55:41 +0000 |
commit | dcec8a13bf565e47942a1751a9cec21bec5648fe (patch) | |
tree | 548b69625b18cc2e88c3e68d0923be546c9ebb03 /drivers/fc4/fc.c | |
parent | 2e0f55e79c49509b7ff70ff1a10e1e9e90a3dfd4 (diff) |
o Merge with Linux 2.1.99.
o Fix ancient bug in the ELF loader making ldd crash.
o Fix ancient bug in the keyboard code for SGI, SNI and Jazz.
Diffstat (limited to 'drivers/fc4/fc.c')
-rw-r--r-- | drivers/fc4/fc.c | 305 |
1 files changed, 250 insertions, 55 deletions
diff --git a/drivers/fc4/fc.c b/drivers/fc4/fc.c index 04a18c0ff..720453a87 100644 --- a/drivers/fc4/fc.c +++ b/drivers/fc4/fc.c @@ -1,7 +1,7 @@ /* fc.c: Generic Fibre Channel and FC4 SCSI driver. * - * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) - * Copyright (C) 1997 Jiri Hanika (geo@ff.cuni.cz) + * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997,1998 Jiri Hanika (geo@ff.cuni.cz) * * Sources: * Fibre Channel Physical & Signaling Interface (FC-PH), dpANS, 1994 @@ -51,9 +51,9 @@ static inline dma_handle fc_sync_dma_entry(void *buf, int len, fc_channel *fc) return mmu_get_scsi_one (buf, len, fc->dev->my_bus); } -static inline void fc_sync_dma_exit(void *buf, long size, fc_channel *fc) +static inline void fc_sync_dma_exit(dma_handle dmh, long size, fc_channel *fc) { - mmu_release_scsi_one ((u32)(long)buf, size, fc->dev->my_bus); + mmu_release_scsi_one (dmh, size, fc->dev->my_bus); } static inline void fc_sync_dma_entry_sg(struct scatterlist *list, int count, fc_channel *fc) @@ -73,28 +73,28 @@ static inline void fc_sync_dma_exit_sg(struct scatterlist *list, int count, fc_c #define FC_SCMND(SCpnt) ((fc_channel *)(SCpnt->host->hostdata[0])) #define SC_FCMND(fcmnd) ((Scsi_Cmnd *)((long)fcmnd - (long)&(((Scsi_Cmnd *)0)->SCp))) -static void fcp_scsi_insert_queue (fc_channel *fc, int no, fcp_cmnd *fcmd) +static void fcp_scsi_insert_queue (fc_channel *fc, fcp_cmnd *fcmd) { - if (!fc->scsi_que[no]) { - fc->scsi_que[no] = fcmd; + if (!fc->scsi_que) { + fc->scsi_que = fcmd; fcmd->next = fcmd; fcmd->prev = fcmd; } else { - fc->scsi_que[no]->prev->next = fcmd; - fcmd->prev = fc->scsi_que[no]->prev; - fc->scsi_que[no]->prev = fcmd; - fcmd->next = fc->scsi_que[no]; + fc->scsi_que->prev->next = fcmd; + fcmd->prev = fc->scsi_que->prev; + fc->scsi_que->prev = fcmd; + fcmd->next = fc->scsi_que; } } -static void fcp_scsi_remove_queue (fc_channel *fc, int no, fcp_cmnd *fcmd) +static void fcp_scsi_remove_queue (fc_channel *fc, fcp_cmnd *fcmd) { if (fcmd == fcmd->next) { - fc->scsi_que[no] = NULL; + fc->scsi_que = NULL; return; } - if (fcmd == fc->scsi_que[no]) - fc->scsi_que[no] = fcmd->next; + if (fcmd == fc->scsi_que) + fc->scsi_que = fcmd->next; fcmd->prev->next = fcmd->next; fcmd->next->prev = fcmd->prev; } @@ -151,7 +151,7 @@ static void fcp_login_done(fc_channel *fc, int i, int status) fc->state = FC_STATE_FPORT_OK; fcmd = l->fcmds + i; plogi = l->logi + 3 * i; - fc_sync_dma_exit (plogi, 3 * sizeof(logi), fc); + fc_sync_dma_exit (fcmd->cmd, 3 * sizeof(logi), fc); plogi->code = LS_PLOGI; memcpy (&plogi->nport_wwn, &fc->wwn_nport, sizeof(fc_wwn)); memcpy (&plogi->node_wwn, &fc->wwn_node, sizeof(fc_wwn)); @@ -159,7 +159,6 @@ static void fcp_login_done(fc_channel *fc, int i, int status) memcpy (&plogi->class1, fc->class_svcs, 3*sizeof(svc_parm)); fch = &fcmd->fch; fcmd->token += l->count; - fcmd->rsp += sizeof(logi); FILL_FCHDR_RCTL_DID(fch, R_CTL_ELS_REQ, fc->did); FILL_FCHDR_SID(fch, fc->sid); #ifdef FCDEBUG @@ -172,7 +171,8 @@ static void fcp_login_done(fc_channel *fc, int i, int status) printk ("\n"); } #endif - fc_sync_dma_entry (plogi, 3 * sizeof(logi), fc); + fcmd->cmd = fc_sync_dma_entry (plogi, 3 * sizeof(logi), fc); + fcmd->rsp = fcmd->cmd + 2 * sizeof(logi); if (fc->hw_enque (fc, fcmd)) printk ("FC: Cannot enque PLOGI packet on %s\n", fc->name); break; @@ -194,7 +194,7 @@ static void fcp_login_done(fc_channel *fc, int i, int status) switch (status) { case FC_STATUS_OK: plogi = l->logi + 3 * i; - fc_sync_dma_exit (plogi, 3 * sizeof(logi), fc); + fc_sync_dma_exit (l->fcmds[i].cmd, 3 * sizeof(logi), fc); if (!fc->wwn_dest.lo && !fc->wwn_dest.hi) { memcpy (&fc->wwn_dest, &plogi[1].node_wwn, sizeof(fc_wwn)); FCD(("Dest WWN %08x%08x\n", *(u32 *)&fc->wwn_dest, fc->wwn_dest.lo)) @@ -212,7 +212,7 @@ static void fcp_login_done(fc_channel *fc, int i, int status) break; case FC_STATUS_ERR_OFFLINE: fc->state = FC_STATE_OFFLINE; - fc_sync_dma_exit (l->logi + 3 * i, 3 * sizeof(logi), fc); + fc_sync_dma_exit (l->fcmds[i].cmd, 3 * sizeof(logi), fc); printk ("%s: FC is offline\n", fc->name); if (atomic_dec_and_test (&l->todo)) up(&l->sem); @@ -228,26 +228,41 @@ static void fcp_login_done(fc_channel *fc, int i, int status) void fcp_register(fc_channel *fc, u8 type, int unregister) { int size, i; + int slots = (fc->can_queue * 3) >> 1; + + FCND(("Going to %sregister\n", unregister ? "un" : "")) if (type == TYPE_SCSI_FCP) { if (!unregister) { fc->scsi_cmd_pool = - (fcp_cmd *) fc_dma_alloc (fc->can_queue * (sizeof (fcp_cmd) + fc->rsp_size), + (fcp_cmd *) fc_dma_alloc (slots * (sizeof (fcp_cmd) + fc->rsp_size), "FCP SCSI cmd & rsp queues", &fc->dma_scsi_cmd); - fc->scsi_rsp_pool = (char *)(fc->scsi_cmd_pool + fc->can_queue); - fc->dma_scsi_rsp = fc->dma_scsi_cmd + fc->can_queue * sizeof (fcp_cmd); - fc->scsi_bitmap_end = ((fc->can_queue + 63) & ~63); + fc->scsi_rsp_pool = (char *)(fc->scsi_cmd_pool + slots); + fc->dma_scsi_rsp = fc->dma_scsi_cmd + slots * sizeof (fcp_cmd); + fc->scsi_bitmap_end = (slots + 63) & ~63; size = fc->scsi_bitmap_end / 8; fc->scsi_bitmap = kmalloc (size, GFP_KERNEL); memset (fc->scsi_bitmap, 0, size); + set_bit (0, fc->scsi_bitmap); for (i = fc->can_queue; i < fc->scsi_bitmap_end; i++) set_bit (i, fc->scsi_bitmap); fc->scsi_free = fc->can_queue; - fc->token_tab = (fcp_cmnd **)kmalloc(fc->can_queue * sizeof(fcp_cmnd*), GFP_KERNEL); + fc->token_tab = (fcp_cmnd **)kmalloc(slots * sizeof(fcp_cmnd*), GFP_KERNEL); + fc->abort_count = 0; } else { fc->scsi_name[0] = 0; kfree (fc->scsi_bitmap); kfree (fc->token_tab); + FCND(("Unregistering\n")); + if (fc->rst_pkt) { + if (fc->rst_pkt->eh_state == SCSI_STATE_UNUSED) + kfree(fc->rst_pkt); + else { + /* Can't happen. Some memory would be lost. */ + printk("FC: Reset in progress. Now?!"); + } + } + FCND(("Unregistered\n")); } } else printk ("FC: %segistering unknown type %02x\n", unregister ? "Unr" : "R", type); @@ -273,6 +288,7 @@ static inline void fcp_scsi_receive(fc_channel *fc, int token, int status, fc_hd return; rsp_status = rsp->fcp_status; + FCD(("rsp_status %08x status %08x\n", rsp_status, status)) switch (status) { case FC_STATUS_OK: host_status=DID_OK; @@ -305,11 +321,11 @@ static inline void fcp_scsi_receive(fc_channel *fc, int token, int status, fc_hd if (SCpnt->use_sg) fc_sync_dma_exit_sg((struct scatterlist *)SCpnt->buffer, SCpnt->use_sg, fc); else - fc_sync_dma_exit(SCpnt->request_buffer, SCpnt->request_bufflen, fc); + fc_sync_dma_exit(fcmd->data, SCpnt->request_bufflen, fc); } break; default: - host_status=DID_ERROR; + host_status=DID_ERROR; /* FIXME */ FCD(("Wrong FC status %d for token %d\n", status, token)) break; } @@ -319,16 +335,21 @@ static inline void fcp_scsi_receive(fc_channel *fc, int token, int status, fc_hd } SCpnt->result = (host_status << 16) | (rsp_status & 0xff); +#ifdef FCDEBUG + if (host_status || SCpnt->result || rsp_status) printk("FC: host_status %d, packet status %d\n", + host_status, SCpnt->result); +#endif SCpnt->done = fcmd->done; fcmd->done=NULL; clear_bit(token, fc->scsi_bitmap); fc->scsi_free++; + FCD(("Calling scsi_done with %08lx\n", SCpnt->result)) SCpnt->scsi_done(SCpnt); } void fcp_receive_solicited(fc_channel *fc, int proto, int token, int status, fc_hdr *fch) { - FCD(("Receive solicited %d %d %d\n", proto, token, status)) + FCD(("receive_solicited %d %d %d\n", proto, token, status)) switch (proto) { case TYPE_SCSI_FCP: fcp_scsi_receive(fc, token, status, fch); break; @@ -337,7 +358,7 @@ void fcp_receive_solicited(fc_channel *fc, int proto, int token, int status, fc_ ls *l = (ls *)fc->ls; int i = (token >= l->count) ? token - l->count : token; - /* Make us sure */ + /* Let's be sure */ if ((unsigned)i < l->count && l->fcmds[i].fc == fc) { fcp_login_done(fc, token, status); break; @@ -417,6 +438,7 @@ int fcp_initialize(fc_channel *fcchain, int count) fcmd = l->fcmds + i; fc->login = fcmd; fc->ls = (void *)l; + fc->rst_pkt = NULL; /* kmalloc when first used */ fch = &fcmd->fch; FILL_FCHDR_RCTL_DID(fch, R_CTL_ELS_REQ, FS_FABRIC_F_PORT); FILL_FCHDR_SID(fch, 0); @@ -448,7 +470,7 @@ int fcp_initialize(fc_channel *fcchain, int count) } else { fc->state = FC_STATE_OFFLINE; enable_irq(fc->irq); - fc_sync_dma_exit (l->logi + 3 * i, 3 * sizeof(logi), fc); + fc_sync_dma_exit (l->fcmds[i].cmd, 3 * sizeof(logi), fc); if (atomic_dec_and_test (&l->todo)) goto all_done; } @@ -472,7 +494,7 @@ all_done: switch (fc->state) { case FC_STATE_ONLINE: break; case FC_STATE_OFFLINE: break; - default: fc_sync_dma_exit (l->logi + i, 3 * sizeof(logi), fc); + default: fc_sync_dma_exit (l->fcmds[i].cmd, 3 * sizeof(logi), fc); break; } fc->ls = NULL; @@ -535,24 +557,54 @@ int fcp_init(fc_channel *fcchain) { fc_channel *fc; int count=0; + int ret; for (fc = fcchain; fc; fc = fc->next) { fc->fcp_register = fcp_register; - fc->fcp_state_change = fcp_state_change; count++; } - fcp_initialize (fcchain, count); + ret = fcp_initialize (fcchain, count); + + if (!ret) { + if (!fc_channels) + fc_channels = fcchain; + else { + for (fc = fc_channels; fc->next; fc = fc->next); + fc->next = fcchain; + } + } + return ret; +} + +void fcp_release(fc_channel *fcchain, int count) /* count must > 0 */ +{ + fc_channel *fc; + fc_channel *fcx; + + for (fc = fcchain; --count && fc->next; fc = fc->next); + if (count) { + printk("FC: nothing to release\n"); + return; + } - if (!fc_channels) - fc_channels = fcchain; + if (fc_channels == fcchain) + fc_channels = fc->next; else { - for (fc = fc_channels; fc->next; fc = fc->next); - fc->next = fcchain; + for (fcx = fc_channels; fcx->next != fcchain; fcx = fcx->next); + fcx->next = fc->next; } - return 0; + fc->next = NULL; + + /* + * We've just grabbed fcchain out of the fc_channel list + * and zero-terminated it, while destroying the count. + * + * Freeing the fc's is the low level driver's responsibility. + */ } + static void fcp_scsi_done (Scsi_Cmnd *SCpnt) { if (FCP_CMND(SCpnt)->done) @@ -561,16 +613,15 @@ static void fcp_scsi_done (Scsi_Cmnd *SCpnt) static int fcp_scsi_queue_it(fc_channel *fc, Scsi_Cmnd *SCpnt, fcp_cmnd *fcmd, int prepare) { + long i; + fcp_cmd *cmd; + u32 fcp_cntl; if (prepare) { - long i; - fcp_cmd *cmd; - u32 fcp_cntl; - i = find_first_zero_bit (fc->scsi_bitmap, fc->scsi_bitmap_end); set_bit (i, fc->scsi_bitmap); fcmd->token = i; cmd = fc->scsi_cmd_pool + i; - FCD(("Chose token %ld %08lx\n", i, (long)cmd)) + if (fc->encode_addr (SCpnt, cmd->fcp_addr)) { /* Invalid channel/id/lun and couldn't map it into fcp_addr */ clear_bit (i, fc->scsi_bitmap); @@ -620,14 +671,14 @@ static int fcp_scsi_queue_it(fc_channel *fc, Scsi_Cmnd *SCpnt, fcp_cmnd *fcmd, i FCD(("XXX: %04x.%04x.%04x.%04x - %08x%08x%08x\n", cmd->fcp_addr[0], cmd->fcp_addr[1], cmd->fcp_addr[2], cmd->fcp_addr[3], *(u32 *)SCpnt->cmnd, *(u32 *)(SCpnt->cmnd+4), *(u32 *)(SCpnt->cmnd+8))) } FCD(("Trying to enque %08x\n", (int)fcmd)) - if (!fc->scsi_que[1]) { + if (!fc->scsi_que) { if (!fc->hw_enque (fc, fcmd)) { FCD(("hw_enque succeeded for %08x\n", (int)fcmd)) return 0; } } FCD(("Putting into que1 %08x\n", (int)fcmd)) - fcp_scsi_insert_queue (fc, 1, fcmd); + fcp_scsi_insert_queue (fc, fcmd); return 0; } @@ -643,8 +694,10 @@ int fcp_scsi_queuecommand(Scsi_Cmnd *SCpnt, void (* done)(Scsi_Cmnd *)) SCpnt->scsi_done = done; fcmd->proto = TYPE_SCSI_FCP; if (!fc->scsi_free) { - FCD(("Putting into que0\n")) - fcp_scsi_insert_queue (fc, 0, fcmd); + FCD(("FC: !scsi_free, putting cmd on ML queue\n")) +#if (FCP_SCSI_USE_NEW_EH_CODE == 0) + printk("fcp_scsi_queue_command: queue full, losing cmd, bad\n"); +#endif return 1; } return fcp_scsi_queue_it(fc, SCpnt, fcmd, 1); @@ -656,24 +709,166 @@ void fcp_queue_empty(fc_channel *fc) { fcp_cmnd *fcmd; FCD(("Queue empty\n")) - while ((fcmd = fc->scsi_que[1])) { - /* The hw tell us we can try again queue some packet */ + while ((fcmd = fc->scsi_que)) { + /* The hw told us we can try again queue some packet */ if (fc->hw_enque (fc, fcmd)) return; - fcp_scsi_remove_queue (fc, 1, fcmd); + fcp_scsi_remove_queue (fc, fcmd); } } +int fcp_old_abort(Scsi_Cmnd *SCpnt) +{ + printk("FC: Abort not implemented\n"); + return 1; +} + int fcp_scsi_abort(Scsi_Cmnd *SCpnt) { - printk ("FC: AIEEE, abort!\n"); - return 0; + /* Internal bookkeeping only. Lose 1 token_tab slot. */ + fcp_cmnd *fcmd = FCP_CMND(SCpnt); + fc_channel *fc = FC_SCMND(SCpnt); + + /* + * We react to abort requests by simply forgetting + * about the command and pretending everything's sweet. + * This may or may not be silly. We can't, however, + * immediately reuse the command's token_tab slot, + * as its result may arrive later and we cannot + * check whether it is the aborted one, can't we? + * + * Therefore, after the first few aborts are done, + * we tell the scsi error handler to do something clever. + * It will eventually call host reset, refreshing + * token_tab for us. + * + * There is a theoretical chance that we sometimes allow + * more than can_queue packets to the jungle this way, + * but the worst outcome possible is a series of + * more aborts and eventually the dev_reset catharsis. + */ + + if (++fc->abort_count < (fc->can_queue >> 1)) { + SCpnt->result = DID_ABORT; + fcmd->done(SCpnt); + printk("FC: soft abort\n"); + return SUCCESS; + } else { + printk("FC: hard abort refused\n"); + return FAILED; + } } -int fcp_scsi_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags) +void fcp_scsi_reset_done(Scsi_Cmnd *SCpnt) { - printk ("FC: AIEEE, reset!\n"); - return 0; + fc_channel *fc = FC_SCMND(SCpnt); + + fc->rst_pkt->eh_state = SCSI_STATE_FINISHED; + up(fc->rst_pkt->host->eh_action); +} + +#define FCP_RESET_TIMEOUT (2*HZ) + +int fcp_scsi_dev_reset(Scsi_Cmnd *SCpnt) +{ + fcp_cmd *cmd; + fcp_cmnd *fcmd; + fc_channel *fc = FC_SCMND(SCpnt); + struct semaphore sem = MUTEX_LOCKED; + + if (!fc->rst_pkt) { + fc->rst_pkt = (Scsi_Cmnd *) kmalloc(sizeof(SCpnt), GFP_KERNEL); + if (!fc->rst_pkt) return FAILED; + + fcmd = FCP_CMND(fc->rst_pkt); + + + fcmd->token = 0; + cmd = fc->scsi_cmd_pool + 0; + FCD(("Preparing rst packet\n")) + if (fc->encode_addr (SCpnt, /*?*/cmd->fcp_addr)) + fc->rst_pkt->channel = SCpnt->channel; + fc->rst_pkt->target = SCpnt->target; + fc->rst_pkt->lun = 0; + fc->rst_pkt->cmd_len = 0; + + fc->token_tab[0] = fcmd; + + cmd->fcp_cntl = FCP_CNTL_QTYPE_ORDERED | FCP_CNTL_RESET; + fcmd->data = (dma_handle)NULL; + fcmd->proto = TYPE_SCSI_FCP; + + memcpy (cmd->fcp_cdb, SCpnt->cmnd, SCpnt->cmd_len); + memset (cmd->fcp_cdb+SCpnt->cmd_len, 0, sizeof(cmd->fcp_cdb)-SCpnt->cmd_len); + FCD(("XXX: %04x.%04x.%04x.%04x - %08x%08x%08x\n", cmd->fcp_addr[0], cmd->fcp_addr[1], cmd->fcp_addr[2], cmd->fcp_addr[3], *(u32 *)SCpnt->cmnd, *(u32 *)(SCpnt->cmnd+4), *(u32 *)(SCpnt->cmnd+8))) + } else { + fcmd = FCP_CMND(fc->rst_pkt); + if (fc->rst_pkt->eh_state == SCSI_STATE_QUEUED) + return FAILED; /* or SUCCESS. Only these */ + } + fc->rst_pkt->done = NULL; + + + fc->rst_pkt->eh_state = SCSI_STATE_QUEUED; + + fc->rst_pkt->eh_timeout.data = (unsigned long) fc->rst_pkt; + fc->rst_pkt->eh_timeout.expires = jiffies + FCP_RESET_TIMEOUT; + fc->rst_pkt->eh_timeout.function = (void (*)(unsigned long))fcp_scsi_reset_done; + + add_timer(&fc->rst_pkt->eh_timeout); + + /* + * Set up the semaphore so we wait for the command to complete. + */ + + fc->rst_pkt->host->eh_action = &sem; + fc->rst_pkt->request.rq_status = RQ_SCSI_BUSY; + + fc->rst_pkt->done = fcp_scsi_reset_done; + fcp_scsi_queue_it(fc, fc->rst_pkt, fcmd, 0); + + down(&sem); + + fc->rst_pkt->host->eh_action = NULL; + del_timer(&fc->rst_pkt->eh_timeout); + + /* + * See if timeout. If so, tell the host to forget about it. + * In other words, we don't want a callback any more. + */ + if (fc->rst_pkt->eh_state == SCSI_STATE_TIMEOUT ) { + fc->rst_pkt->eh_state = SCSI_STATE_UNUSED; + return FAILED; + } + fc->rst_pkt->eh_state = SCSI_STATE_UNUSED; + return SUCCESS; +} + +int fcp_scsi_bus_reset(Scsi_Cmnd *SCpnt) +{ + printk ("FC: bus reset!\n"); + return FAILED; +} + +int fcp_scsi_host_reset(Scsi_Cmnd *SCpnt) +{ + fc_channel *fc = FC_SCMND(SCpnt); + fcp_cmnd *fcmd = FCP_CMND(SCpnt); + int i; + + printk ("FC: host reset\n"); + + for (i=0; i < fc->can_queue; i++) { + if (fc->token_tab[i] && SCpnt->result != DID_ABORT) { + SCpnt->result = DID_RESET; + fcmd->done(SCpnt); + fc->token_tab[i] = NULL; + } + } + fc->reset(fc); + fc->abort_count = 0; + if (fcp_initialize(fc, 1)) return SUCCESS; + else return FAILED; } #ifdef MODULE |