📄 cpqarray.c
字号:
c->hdr.size = sizeof(rblk_t) >> 2; c->size += sizeof(rblk_t); c->req.hdr.blk = ida[(ctlr<<CTLR_SHIFT) + MINOR(creq->rq_dev)].start_sect + creq->sector; c->bh = bh;DBGPX( if (bh == NULL) panic("bh == NULL?"); printk("sector=%d, nr_sectors=%d\n", creq->sector, creq->nr_sectors);); seg = 0; lastdataend = NULL; sect = 0; while(bh) { sect += bh->b_size/512;DBGPX( if (bh->b_size % 512) { printk("Oh damn. %d+%d, size = %d\n", creq->sector, sect, bh->b_size); panic("b_size %% 512 != 0"); }); if (bh->b_data == lastdataend) { c->req.sg[seg-1].size += bh->b_size; lastdataend += bh->b_size; } else { c->req.sg[seg].size = bh->b_size; c->req.sg[seg].addr = (__u32)virt_to_bus(bh->b_data); lastdataend = bh->b_data + bh->b_size; if (++seg == SG_MAX) break; } bh = bh->b_reqnext; }DBGPX( printk("Submitting %d sectors in %d segments\n", sect, seg); ); c->req.hdr.sg_cnt = seg; c->req.hdr.blk_cnt = sect; creq->sector += sect; creq->nr_sectors -= sect; /* Ready the next request: * Fix up creq if we still have more buffers in the buffer chain, or * mark this request as done and ready the next one. */ if (creq->nr_sectors) {DBGPX( if (bh==NULL) { printk("sector=%d, nr_sectors=%d, sect=%d, seg=%d\n", creq->sector, creq->nr_sectors, sect, seg); panic("mother..."); }); creq->bh = bh->b_reqnext; bh->b_reqnext = NULL;DBGPX( printk("More to do on same request %p\n", creq); ); } else {DBGPX( printk("Done with %p\n", creq); ); blkdev_dequeue_request(creq); end_that_request_last(creq); } c->req.hdr.cmd = (creq->cmd == READ) ? IDA_READ : IDA_WRITE; c->type = CMD_RWREQ; /* Put the request on the tail of the request queue */ addQ(&h->reqQ, c); h->Qdepth++; if (h->Qdepth > h->maxQsinceinit) h->maxQsinceinit = h->Qdepth; start_io(h);}/* * start_io submits everything on a controller's request queue * and moves it to the completion queue. * * Interrupts had better be off if you're in here */static void start_io(ctlr_info_t *h){ cmdlist_t *c; while((c = h->reqQ) != NULL) { /* Can't do anything if we're busy */ if (h->access.fifo_full(h) == 0) return; /* Get the first entry from the request Q */ removeQ(&h->reqQ, c); h->Qdepth--; /* Tell the controller to do our bidding */ h->access.submit_command(h, c); /* Get onto the completion Q */ addQ(&h->cmpQ, c); }}static inline void complete_buffers(struct buffer_head *bh, int ok){ struct buffer_head *xbh; while(bh) { xbh = bh->b_reqnext; bh->b_reqnext = NULL; bh->b_end_io(bh, ok); bh = xbh; }}/* * Mark all buffers that cmd was responsible for */static inline void complete_command(cmdlist_t *cmd, int timeout){ int ok=1; if (cmd->req.hdr.rcode & RCODE_NONFATAL && (hba[cmd->ctlr]->misc_tflags & MISC_NONFATAL_WARN) == 0) { printk(KERN_WARNING "Non Fatal error on ida/c%dd%d\n", cmd->ctlr, cmd->hdr.unit); hba[cmd->ctlr]->misc_tflags |= MISC_NONFATAL_WARN; } if (cmd->req.hdr.rcode & RCODE_FATAL) { printk(KERN_WARNING "Fatal error on ida/c%dd%d\n", cmd->ctlr, cmd->hdr.unit); ok = 0; } if (cmd->req.hdr.rcode & RCODE_INVREQ) { printk(KERN_WARNING "Invalid request on ida/c%dd%d = (cmd=%x sect=%d cnt=%d sg=%d ret=%x)\n", cmd->ctlr, cmd->hdr.unit, cmd->req.hdr.cmd, cmd->req.hdr.blk, cmd->req.hdr.blk_cnt, cmd->req.hdr.sg_cnt, cmd->req.hdr.rcode); ok = 0; } if (timeout) ok = 0; complete_buffers(cmd->bh, ok);}/* * The controller will interrupt us upon completion of commands. * Find the command on the completion queue, remove it, tell the OS and * try to queue up more IO */static void do_ida_intr(int irq, void *dev_id, struct pt_regs *regs){ ctlr_info_t *h = dev_id; cmdlist_t *c; unsigned long istat; unsigned long flags; __u32 a,a1; istat = h->access.intr_pending(h); /* Is this interrupt for us? */ if (istat == 0) return; /* * If there are completed commands in the completion queue, * we had better do something about it. */ spin_lock_irqsave(&io_request_lock, flags); if (istat & FIFO_NOT_EMPTY) { while((a = h->access.command_completed(h))) { a1 = a; a &= ~3; if ((c = h->cmpQ) == NULL) { printk(KERN_WARNING "cpqarray: Completion of %08lx ignored\n", (unsigned long)a1); continue; } while(c->busaddr != a) { c = c->next; if (c == h->cmpQ) break; } /* * If we've found the command, take it off the * completion Q and free it */ if (c->busaddr == a) { removeQ(&h->cmpQ, c); if (c->type == CMD_RWREQ) { complete_command(c, 0); cmd_free(h, c); } else if (c->type == CMD_IOCTL_PEND) { c->type = CMD_IOCTL_DONE; } continue; } } } /* * See if we can queue up some more IO */ do_ida_request(h->ctlr); spin_unlock_irqrestore(&io_request_lock, flags);}/* * This timer was for timing out requests that haven't happened after * IDA_TIMEOUT. That wasn't such a good idea. This timer is used to * reset a flags structure so we don't flood the user with * "Non-Fatal error" messages. */static void ida_timer(unsigned long tdata){ ctlr_info_t *h = (ctlr_info_t*)tdata; h->timer.expires = jiffies + IDA_TIMER; add_timer(&h->timer); h->misc_tflags = 0;}/* * ida_ioctl does some miscellaneous stuff like reporting drive geometry, * setting readahead and submitting commands from userspace to the controller. */static int ida_ioctl(struct inode *inode, struct file *filep, unsigned int cmd, unsigned long arg){ int ctlr = MAJOR(inode->i_rdev) - MAJOR_NR; int dsk = MINOR(inode->i_rdev) >> NWD_SHIFT; int error; int diskinfo[4]; struct hd_geometry *geo = (struct hd_geometry *)arg; ida_ioctl_t *io = (ida_ioctl_t*)arg; ida_ioctl_t my_io; switch(cmd) { case HDIO_GETGEO: if (hba[ctlr]->drv[dsk].cylinders) { diskinfo[0] = hba[ctlr]->drv[dsk].heads; diskinfo[1] = hba[ctlr]->drv[dsk].sectors; diskinfo[2] = hba[ctlr]->drv[dsk].cylinders; } else { diskinfo[0] = 0xff; diskinfo[1] = 0x3f; diskinfo[2] = hba[ctlr]->drv[dsk].nr_blks / (0xff*0x3f); } put_user(diskinfo[0], &geo->heads); put_user(diskinfo[1], &geo->sectors); put_user(diskinfo[2], &geo->cylinders); put_user(ida[(ctlr<<CTLR_SHIFT)+MINOR(inode->i_rdev)].start_sect, &geo->start); return 0; case IDAGETDRVINFO: return copy_to_user(&io->c.drv,&hba[ctlr]->drv[dsk],sizeof(drv_info_t)); case BLKGETSIZE: if (!arg) return -EINVAL; put_user(ida[(ctlr<<CTLR_SHIFT)+MINOR(inode->i_rdev)].nr_sects, (long*)arg); return 0; case BLKRRPART: return revalidate_logvol(inode->i_rdev, 1); case IDAPASSTHRU: if (!suser()) return -EPERM; error = copy_from_user(&my_io, io, sizeof(my_io)); if (error) return error; error = ida_ctlr_ioctl(ctlr, dsk, &my_io); if (error) return error; error = copy_to_user(io, &my_io, sizeof(my_io)); return error; case IDAGETCTLRSIG: if (!arg) return -EINVAL; put_user(hba[ctlr]->ctlr_sig, (int*)arg); return 0; case IDAREVALIDATEVOLS: return revalidate_allvol(inode->i_rdev); case IDADRIVERVERSION: if (!arg) return -EINVAL; put_user(DRIVER_VERSION, (unsigned long*)arg); return 0; case IDAGETPCIINFO: { ida_pci_info_struct pciinfo; if (!arg) return -EINVAL; pciinfo.bus = hba[ctlr]->pci_dev->bus->number; pciinfo.dev_fn = hba[ctlr]->pci_dev->devfn; pciinfo.board_id = hba[ctlr]->board_id; if(copy_to_user((void *) arg, &pciinfo, sizeof( ida_pci_info_struct))) return -EFAULT; return(0); } case BLKFLSBUF: case BLKROSET: case BLKROGET: case BLKRASET: case BLKRAGET: case BLKPG: return blk_ioctl(inode->i_rdev, cmd, arg); default: return -EINVAL; } }/* * ida_ctlr_ioctl is for passing commands to the controller from userspace. * The command block (io) has already been copied to kernel space for us, * however, any elements in the sglist need to be copied to kernel space * or copied back to userspace. * * Only root may perform a controller passthru command, however I'm not doing * any serious sanity checking on the arguments. Doing an IDA_WRITE_MEDIA and * putting a 64M buffer in the sglist is probably a *bad* idea. */static int ida_ctlr_ioctl(int ctlr, int dsk, ida_ioctl_t *io){ ctlr_info_t *h = hba[ctlr]; cmdlist_t *c; void *p = NULL; unsigned long flags; int error; if ((c = cmd_alloc(NULL)) == NULL) return -ENOMEM; c->ctlr = ctlr; c->hdr.unit = (io->unit & UNITVALID) ? (io->unit & ~UNITVALID) : dsk; c->hdr.size = sizeof(rblk_t) >> 2; c->size += sizeof(rblk_t); c->req.hdr.cmd = io->cmd; c->req.hdr.blk = io->blk; c->req.hdr.blk_cnt = io->blk_cnt; c->type = CMD_IOCTL_PEND; /* Pre submit processing */ switch(io->cmd) { case PASSTHRU_A: p = kmalloc(io->sg[0].size, GFP_KERNEL); if (!p) { error = -ENOMEM; cmd_free(NULL, c); return(error); } copy_from_user(p, (void*)io->sg[0].addr, io->sg[0].size); c->req.hdr.blk = virt_to_bus(&(io->c)); c->req.sg[0].size = io->sg[0].size; c->req.sg[0].addr = virt_to_bus(p); c->req.hdr.sg_cnt = 1; break; case IDA_READ: p = kmalloc(io->sg[0].size, GFP_KERNEL); if (!p) { error = -ENOMEM; cmd_free(NULL, c); return(error); } c->req.sg[0].size = io->sg[0].size; c->req.sg[0].addr = virt_to_bus(p); c->req.hdr.sg_cnt = 1; break; case IDA_WRITE: case IDA_WRITE_MEDIA: case DIAG_PASS_THRU: p = kmalloc(io->sg[0].size, GFP_KERNEL); if (!p) { error = -ENOMEM; cmd_free(NULL, c); return(error); } copy_from_user(p, (void*)io->sg[0].addr, io->sg[0].size); c->req.sg[0].size = io->sg[0].size; c->req.sg[0].addr = virt_to_bus(p); c->req.hdr.sg_cnt = 1; break; default: c->req.sg[0].size = sizeof(io->c); c->req.sg[0].addr = virt_to_bus(&io->c); c->req.hdr.sg_cnt = 1; } /* Put the request on the tail of the request queue */ spin_lock_irqsave(&io_request_lock, flags); addQ(&h->reqQ, c); h->Qdepth++; start_io(h); spin_unlock_irqrestore(&io_request_lock, flags); /* Wait for completion */ while(c->type != CMD_IOCTL_DONE) schedule(); /* Post submit processing */ switch(io->cmd) { case PASSTHRU_A: case IDA_READ: case DIAG_PASS_THRU: copy_to_user((void*)io->sg[0].addr, p, io->sg[0].size); /* fall through and free p */ case IDA_WRITE: case IDA_WRITE_MEDIA: kfree(p); break; default: /* Nothing to do */ } io->rcode = c->req.hdr.rcode; cmd_free(NULL, c); return(0);}/* * Commands are pre-allocated in a large block. Here we use a simple bitmap * scheme to suballocte them to the driver. Operations that are not time * critical (and can wait for kmalloc and possibly sleep) can pass in NULL * as the first argument to get a new command. */static cmdlist_t * cmd_alloc(ctlr_info_t *h){ cmdlist_t * c; int i; if (h == NULL) { c = (cmdlist_t*)kmalloc(sizeof(cmdlist_t), GFP_KERNEL); if(c==NULL) return NULL; } else { do { i = find_first_zero_bit(h->cmd_pool_bits, NR_CMDS); if (i == NR_CMDS) return NULL; } while(test_and_set_bit(i%32, h->cmd_pool_bits+(i/32)) != 0); c = h->cmd_pool + i; h->nr_allocs++; } memset(c, 0, sizeof(cmdlist_t)); c->busaddr = virt_to_bus(c); return c;}static void cmd_free(ctlr_info_t *h, cmdlist_t *c){ int i; if (h == NULL) { kfree(c); } else { i = c - h->cmd_pool; clear_bit(i%32, h->cmd_pool_bits+(i/32)); h->nr_frees++; }}/*********************************************************************** name: sendcmd Send a command to an IDA using the memory mapped FIFO interface and wait for it to complete. This routine should only be called at init time.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -