sg.c
来自「linux 内核源代码」· C语言 代码 · 共 2,357 行 · 第 1/5 页
C
2,357 行
error = PTR_ERR(sdp); goto out; } class_set_devdata(cl_dev, sdp); error = cdev_add(cdev, MKDEV(SCSI_GENERIC_MAJOR, sdp->index), 1); if (error) goto cdev_add_err; sdp->cdev = cdev; if (sg_sysfs_valid) { struct class_device * sg_class_member; sg_class_member = class_device_create(sg_sysfs_class, NULL, MKDEV(SCSI_GENERIC_MAJOR, sdp->index), cl_dev->dev, "%s", disk->disk_name); if (IS_ERR(sg_class_member)) printk(KERN_WARNING "sg_add: " "class_device_create failed\n"); class_set_devdata(sg_class_member, sdp); error = sysfs_create_link(&scsidp->sdev_gendev.kobj, &sg_class_member->kobj, "generic"); if (error) printk(KERN_ERR "sg_add: unable to make symlink " "'generic' back to sg%d\n", sdp->index); } else printk(KERN_WARNING "sg_add: sg_sys Invalid\n"); sdev_printk(KERN_NOTICE, scsidp, "Attached scsi generic sg%d type %d\n", sdp->index, scsidp->type); return 0;cdev_add_err: write_lock_irqsave(&sg_index_lock, iflags); idr_remove(&sg_index_idr, sdp->index); write_unlock_irqrestore(&sg_index_lock, iflags); kfree(sdp);out: put_disk(disk); if (cdev) cdev_del(cdev); return error;}static voidsg_remove(struct class_device *cl_dev, struct class_interface *cl_intf){ struct scsi_device *scsidp = to_scsi_device(cl_dev->dev); Sg_device *sdp = class_get_devdata(cl_dev); unsigned long iflags; Sg_fd *sfp; Sg_fd *tsfp; Sg_request *srp; Sg_request *tsrp; int delay; if (!sdp) return; delay = 0; write_lock_irqsave(&sg_index_lock, iflags); if (sdp->headfp) { sdp->detached = 1; for (sfp = sdp->headfp; sfp; sfp = tsfp) { tsfp = sfp->nextfp; for (srp = sfp->headrp; srp; srp = tsrp) { tsrp = srp->nextrp; if (sfp->closed || (0 == sg_srp_done(srp, sfp))) sg_finish_rem_req(srp); } if (sfp->closed) { scsi_device_put(sdp->device); __sg_remove_sfp(sdp, sfp); } else { delay = 1; wake_up_interruptible(&sfp->read_wait); kill_fasync(&sfp->async_qp, SIGPOLL, POLL_HUP); } } SCSI_LOG_TIMEOUT(3, printk("sg_remove: dev=%d, dirty\n", sdp->index)); if (NULL == sdp->headfp) { idr_remove(&sg_index_idr, sdp->index); } } else { /* nothing active, simple case */ SCSI_LOG_TIMEOUT(3, printk("sg_remove: dev=%d\n", sdp->index)); idr_remove(&sg_index_idr, sdp->index); } write_unlock_irqrestore(&sg_index_lock, iflags); sysfs_remove_link(&scsidp->sdev_gendev.kobj, "generic"); class_device_destroy(sg_sysfs_class, MKDEV(SCSI_GENERIC_MAJOR, sdp->index)); cdev_del(sdp->cdev); sdp->cdev = NULL; put_disk(sdp->disk); sdp->disk = NULL; if (NULL == sdp->headfp) kfree(sdp); if (delay) msleep(10); /* dirty detach so delay device destruction */}module_param_named(scatter_elem_sz, scatter_elem_sz, int, S_IRUGO | S_IWUSR);module_param_named(def_reserved_size, def_reserved_size, int, S_IRUGO | S_IWUSR);module_param_named(allow_dio, sg_allow_dio, int, S_IRUGO | S_IWUSR);MODULE_AUTHOR("Douglas Gilbert");MODULE_DESCRIPTION("SCSI generic (sg) driver");MODULE_LICENSE("GPL");MODULE_VERSION(SG_VERSION_STR);MODULE_ALIAS_CHARDEV_MAJOR(SCSI_GENERIC_MAJOR);MODULE_PARM_DESC(scatter_elem_sz, "scatter gather element " "size (default: max(SG_SCATTER_SZ, PAGE_SIZE))");MODULE_PARM_DESC(def_reserved_size, "size of buffer reserved for each fd");MODULE_PARM_DESC(allow_dio, "allow direct I/O (default: 0 (disallow))");static int __initinit_sg(void){ int rc; if (scatter_elem_sz < PAGE_SIZE) { scatter_elem_sz = PAGE_SIZE; scatter_elem_sz_prev = scatter_elem_sz; } if (def_reserved_size >= 0) sg_big_buff = def_reserved_size; else def_reserved_size = sg_big_buff; rc = register_chrdev_region(MKDEV(SCSI_GENERIC_MAJOR, 0), SG_MAX_DEVS, "sg"); if (rc) return rc; sg_sysfs_class = class_create(THIS_MODULE, "scsi_generic"); if ( IS_ERR(sg_sysfs_class) ) { rc = PTR_ERR(sg_sysfs_class); goto err_out; } sg_sysfs_valid = 1; rc = scsi_register_interface(&sg_interface); if (0 == rc) {#ifdef CONFIG_SCSI_PROC_FS sg_proc_init();#endif /* CONFIG_SCSI_PROC_FS */ return 0; } class_destroy(sg_sysfs_class);err_out: unregister_chrdev_region(MKDEV(SCSI_GENERIC_MAJOR, 0), SG_MAX_DEVS); return rc;}static void __exitexit_sg(void){#ifdef CONFIG_SCSI_PROC_FS sg_proc_cleanup();#endif /* CONFIG_SCSI_PROC_FS */ scsi_unregister_interface(&sg_interface); class_destroy(sg_sysfs_class); sg_sysfs_valid = 0; unregister_chrdev_region(MKDEV(SCSI_GENERIC_MAJOR, 0), SG_MAX_DEVS); idr_destroy(&sg_index_idr);}static intsg_start_req(Sg_request * srp){ int res; Sg_fd *sfp = srp->parentfp; sg_io_hdr_t *hp = &srp->header; int dxfer_len = (int) hp->dxfer_len; int dxfer_dir = hp->dxfer_direction; Sg_scatter_hold *req_schp = &srp->data; Sg_scatter_hold *rsv_schp = &sfp->reserve; SCSI_LOG_TIMEOUT(4, printk("sg_start_req: dxfer_len=%d\n", dxfer_len)); if ((dxfer_len <= 0) || (dxfer_dir == SG_DXFER_NONE)) return 0; if (sg_allow_dio && (hp->flags & SG_FLAG_DIRECT_IO) && (dxfer_dir != SG_DXFER_UNKNOWN) && (0 == hp->iovec_count) && (!sfp->parentdp->device->host->unchecked_isa_dma)) { res = sg_build_direct(srp, sfp, dxfer_len); if (res <= 0) /* -ve -> error, 0 -> done, 1 -> try indirect */ return res; } if ((!sg_res_in_use(sfp)) && (dxfer_len <= rsv_schp->bufflen)) sg_link_reserve(sfp, srp, dxfer_len); else { res = sg_build_indirect(req_schp, sfp, dxfer_len); if (res) { sg_remove_scat(req_schp); return res; } } return 0;}static voidsg_finish_rem_req(Sg_request * srp){ Sg_fd *sfp = srp->parentfp; Sg_scatter_hold *req_schp = &srp->data; SCSI_LOG_TIMEOUT(4, printk("sg_finish_rem_req: res_used=%d\n", (int) srp->res_used)); if (srp->res_used) sg_unlink_reserve(sfp, srp); else sg_remove_scat(req_schp); sg_remove_request(sfp, srp);}static intsg_build_sgat(Sg_scatter_hold * schp, const Sg_fd * sfp, int tablesize){ int sg_bufflen = tablesize * sizeof(struct scatterlist); gfp_t gfp_flags = GFP_ATOMIC | __GFP_NOWARN; /* * TODO: test without low_dma, we should not need it since * the block layer will bounce the buffer for us * * XXX(hch): we shouldn't need GFP_DMA for the actual S/G list. */ if (sfp->low_dma) gfp_flags |= GFP_DMA; schp->buffer = kzalloc(sg_bufflen, gfp_flags); if (!schp->buffer) return -ENOMEM; sg_init_table(schp->buffer, tablesize); schp->sglist_len = sg_bufflen; return tablesize; /* number of scat_gath elements allocated */}#ifdef SG_ALLOW_DIO_CODE/* vvvvvvvv following code borrowed from st driver's direct IO vvvvvvvvv */ /* TODO: hopefully we can use the generic block layer code *//* Pin down user pages and put them into a scatter gather list. Returns <= 0 if - mapping of all pages not successful (i.e., either completely successful or fails)*/static int st_map_user_pages(struct scatterlist *sgl, const unsigned int max_pages, unsigned long uaddr, size_t count, int rw){ unsigned long end = (uaddr + count + PAGE_SIZE - 1) >> PAGE_SHIFT; unsigned long start = uaddr >> PAGE_SHIFT; const int nr_pages = end - start; int res, i, j; struct page **pages; /* User attempted Overflow! */ if ((uaddr + count) < uaddr) return -EINVAL; /* Too big */ if (nr_pages > max_pages) return -ENOMEM; /* Hmm? */ if (count == 0) return 0; if ((pages = kmalloc(max_pages * sizeof(*pages), GFP_ATOMIC)) == NULL) return -ENOMEM; /* Try to fault in all of the necessary pages */ down_read(¤t->mm->mmap_sem); /* rw==READ means read from drive, write into memory area */ res = get_user_pages( current, current->mm, uaddr, nr_pages, rw == READ, 0, /* don't force */ pages, NULL); up_read(¤t->mm->mmap_sem); /* Errors and no page mapped should return here */ if (res < nr_pages) goto out_unmap; for (i=0; i < nr_pages; i++) { /* FIXME: flush superflous for rw==READ, * probably wrong function for rw==WRITE */ flush_dcache_page(pages[i]); /* ?? Is locking needed? I don't think so */ /* if (TestSetPageLocked(pages[i])) goto out_unlock; */ } sg_set_page(sgl, pages[0], 0, uaddr & ~PAGE_MASK); if (nr_pages > 1) { sgl[0].length = PAGE_SIZE - sgl[0].offset; count -= sgl[0].length; for (i=1; i < nr_pages ; i++) sg_set_page(&sgl[i], pages[i], count < PAGE_SIZE ? count : PAGE_SIZE, 0); } else { sgl[0].length = count; } kfree(pages); return nr_pages; out_unmap: if (res > 0) { for (j=0; j < res; j++) page_cache_release(pages[j]); res = 0; } kfree(pages); return res;}/* And unmap them... */static int st_unmap_user_pages(struct scatterlist *sgl, const unsigned int nr_pages, int dirtied){ int i; for (i=0; i < nr_pages; i++) { struct page *page = sg_page(&sgl[i]); if (dirtied) SetPageDirty(page); /* unlock_page(page); */ /* FIXME: cache flush missing for rw==READ * FIXME: call the correct reference counting function */ page_cache_release(page); } return 0;}/* ^^^^^^^^ above code borrowed from st driver's direct IO ^^^^^^^^^ */#endif/* Returns: -ve -> error, 0 -> done, 1 -> try indirect */static intsg_build_direct(Sg_request * srp, Sg_fd * sfp, int dxfer_len){#ifdef SG_ALLOW_DIO_CODE sg_io_hdr_t *hp = &srp->header; Sg_scatter_hold *schp = &srp->data; int sg_tablesize = sfp->parentdp->sg_tablesize; int mx_sc_elems, res; struct scsi_device *sdev = sfp->parentdp->device; if (((unsigned long)hp->dxferp & queue_dma_alignment(sdev->request_queue)) != 0) return 1; mx_sc_elems = sg_build_sgat(schp, sfp, sg_tablesize); if (mx_sc_elems <= 0) { return 1; } res = st_map_user_pages(schp->buffer, mx_sc_elems, (unsigned long)hp->dxferp, dxfer_len, (SG_DXFER_TO_DEV == hp->dxfer_direction) ? 1 : 0); if (res <= 0) { sg_remove_scat(schp); return 1; } schp->k_use_sg = res; schp->dio_in_use = 1; hp->info |= SG_INFO_DIRECT_IO; return 0;#else return 1;#endif}static intsg_build_indirect(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size){ struct scatterlist *sg; int ret_sz = 0, k, rem_sz, num, mx_sc_elems; int sg_tablesize = sfp->parentdp->sg_tablesize; int blk_size = buff_size; struct page *p = NULL; if (blk_size < 0) return -EFAULT; if (0 == blk_size) ++blk_size; /* don't know why *//* round request up to next highest SG_SECTOR_SZ byte boundary */ blk_size = (blk_size + SG_SECTOR_MSK) & (~SG_SECTOR_MSK); SCSI_LOG_TIMEOUT(4, printk("sg_build_indirect: buff_size=%d, blk_size=%d\n", buff_size, blk_size)); /* N.B. ret_sz carried into this block ... */ mx_sc_elems = sg_build_sgat(schp, sfp, sg_tablesize); if (mx_sc_elems < 0) return mx_sc_elems; /* most likely -ENOMEM */ num = scatter_elem_sz; if (unlikely(num != scatter_elem_sz_prev)) { if (num < PAGE_SIZE) { scatter_elem_sz = PAGE_SIZE; scatter_elem_sz_prev = PAGE_SIZE; } else scatter_elem_sz_prev = num; } for (k = 0, sg = schp->buffer, rem_sz = blk_size; (rem_sz > 0) && (k < mx_sc_elems); ++k, rem_sz -= ret_sz, sg = sg_next(sg)) { num = (rem_sz > scatter_elem_sz_prev) ? scatter_elem_sz_prev : rem_sz; p = sg_page_malloc(num, sfp->low_dma, &ret_sz); if (!p) return -ENOMEM; if (num == scatter_elem_sz_prev) { if (unlikely(ret_sz > scatter_elem_sz_prev)) { scatter_elem_sz = ret_sz; scatter_elem_sz_prev = ret_sz; } } sg_set_page(sg, p, (ret_sz > num) ? num : ret_sz, 0); SCSI_LOG_TIMEOUT(5, printk("sg_build_indirect: k=%d, num=%d, " "ret_sz=%d\n", k, num, ret_sz)); } /* end of for loop */ schp->k_use_sg = k; SCSI_LOG_TIMEOUT(5, printk("sg_build_indirect: k_use_sg=%d, " "rem_sz=%d\n", k, rem_sz)); schp->bufflen = blk_size; if (rem_sz > 0) /* must have failed */ return -ENOMEM; return 0;}static intsg_write_xfer(Sg_request * srp){ sg_io_hdr_t *hp = &srp->header; Sg_scatter_hold *schp = &srp->data; struct scatterlist *sg = schp->buffer; int num_xfer = 0; int j, k, onum, usglen, ksglen, res; int iovec_count = (int) hp->iovec_count; int dxfer_dir = hp->dxfer_direction; unsigned char *p; unsigned char __user *up; int new_interface = ('\0' == hp->interface_id) ? 0 : 1; if ((SG_DXFER_UNKNOWN == dxfer_dir) || (SG_DXFER_TO_DEV == dxfer_dir) || (SG_DXFER_TO_FROM_DEV == dxfer_dir)) { num_xfer = (int) (new_interface ? hp->dxfer_len : hp->flags); if (schp->bufflen < num_xfer)
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?