sg.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,278 行 · 第 1/5 页
C
2,278 行
sg_nr_dev++; sg_dev_arr[k] = sdp; write_unlock_irqrestore(&sg_dev_arr_lock, iflags); error = k; out: if (error < 0) kfree(sdp); kfree(old_sg_dev_arr); return error; expand_failed: printk(KERN_WARNING "sg_alloc: device array cannot be resized\n"); error = -ENOMEM; goto out; overflow: write_unlock_irqrestore(&sg_dev_arr_lock, iflags); printk(KERN_WARNING "Unable to attach sg device <%d, %d, %d, %d> type=%d, minor " "number exceeds %d\n", scsidp->host->host_no, scsidp->channel, scsidp->id, scsidp->lun, scsidp->type, SG_MAX_DEVS - 1); error = -ENODEV; goto out;}static intsg_add(struct class_device *cl_dev){ struct scsi_device *scsidp = to_scsi_device(cl_dev->dev); struct gendisk *disk; Sg_device *sdp = NULL; struct cdev * cdev = NULL; int error, k; disk = alloc_disk(1); if (!disk) { printk(KERN_WARNING "alloc_disk failed\n"); return -ENOMEM; } disk->major = SCSI_GENERIC_MAJOR; error = -ENOMEM; cdev = cdev_alloc(); if (!cdev) { printk(KERN_WARNING "cdev_alloc failed\n"); goto out; } cdev->owner = THIS_MODULE; cdev->ops = &sg_fops; error = sg_alloc(disk, scsidp); if (error < 0) { printk(KERN_WARNING "sg_alloc failed\n"); goto out; } k = error; sdp = sg_dev_arr[k]; devfs_mk_cdev(MKDEV(SCSI_GENERIC_MAJOR, k), S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, "%s/generic", scsidp->devfs_name); error = cdev_add(cdev, MKDEV(SCSI_GENERIC_MAJOR, k), 1); if (error) { devfs_remove("%s/generic", scsidp->devfs_name); goto out; } sdp->cdev = cdev; if (sg_sysfs_valid) { struct class_device * sg_class_member; sg_class_member = class_simple_device_add(sg_sysfs_class, MKDEV(SCSI_GENERIC_MAJOR, k), cl_dev->dev, "%s", disk->disk_name); if (IS_ERR(sg_class_member)) printk(KERN_WARNING "sg_add: " "class_simple_device_add 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", k); } else printk(KERN_WARNING "sg_add: sg_sys INvalid\n"); printk(KERN_NOTICE "Attached scsi generic sg%d at scsi%d, channel" " %d, id %d, lun %d, type %d\n", k, scsidp->host->host_no, scsidp->channel, scsidp->id, scsidp->lun, scsidp->type); return 0;out: put_disk(disk); if (cdev) cdev_del(cdev); return error;}static voidsg_remove(struct class_device *cl_dev){ struct scsi_device *scsidp = to_scsi_device(cl_dev->dev); Sg_device *sdp = NULL; unsigned long iflags; Sg_fd *sfp; Sg_fd *tsfp; Sg_request *srp; Sg_request *tsrp; int k, delay; if (NULL == sg_dev_arr) return; delay = 0; write_lock_irqsave(&sg_dev_arr_lock, iflags); for (k = 0; k < sg_dev_max; k++) { sdp = sg_dev_arr[k]; if ((NULL == sdp) || (sdp->device != scsidp)) continue; /* dirty but lowers nesting */ 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 == srp->done)) 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_detach: dev=%d, dirty\n", k)); if (NULL == sdp->headfp) { sg_dev_arr[k] = NULL; } } else { /* nothing active, simple case */ SCSI_LOG_TIMEOUT(3, printk("sg_detach: dev=%d\n", k)); sg_dev_arr[k] = NULL; } sg_nr_dev--; break; } write_unlock_irqrestore(&sg_dev_arr_lock, iflags); if (sdp) { sysfs_remove_link(&scsidp->sdev_gendev.kobj, "generic"); class_simple_device_remove(MKDEV(SCSI_GENERIC_MAJOR, k)); cdev_del(sdp->cdev); sdp->cdev = NULL; devfs_remove("%s/generic", scsidp->devfs_name); put_disk(sdp->disk); sdp->disk = NULL; if (NULL == sdp->headfp) kfree((char *) sdp); } if (delay) msleep(10); /* dirty detach so delay device destruction */}/* Set 'perm' (4th argument) to 0 to disable module_param's definition * of sysfs parameters (which module_param doesn't yet support). * Sysfs parameters defined explicitly below. */module_param_named(def_reserved_size, def_reserved_size, int, 0);module_param_named(allow_dio, sg_allow_dio, int, 0);MODULE_AUTHOR("Douglas Gilbert");MODULE_DESCRIPTION("SCSI generic (sg) driver");MODULE_LICENSE("GPL");MODULE_VERSION(SG_VERSION_STR);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 (def_reserved_size >= 0) sg_big_buff = def_reserved_size; rc = register_chrdev_region(MKDEV(SCSI_GENERIC_MAJOR, 0), SG_MAX_DEVS, "sg"); if (rc) return rc; sg_sysfs_class = class_simple_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_simple_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_simple_destroy(sg_sysfs_class); sg_sysfs_valid = 0; unregister_chrdev_region(MKDEV(SCSI_GENERIC_MAJOR, 0), SG_MAX_DEVS); if (sg_dev_arr != NULL) { kfree((char *) sg_dev_arr); sg_dev_arr = NULL; } sg_dev_max = 0;}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 ret_sz; int elem_sz = sizeof (struct scatterlist); int sg_bufflen = tablesize * elem_sz; int mx_sc_elems = tablesize; schp->buffer = sg_page_malloc(sg_bufflen, sfp->low_dma, &ret_sz); if (!schp->buffer) return -ENOMEM; else if (ret_sz != sg_bufflen) { sg_bufflen = ret_sz; mx_sc_elems = sg_bufflen / elem_sz; } schp->sglist_len = sg_bufflen; memset(schp->buffer, 0, sg_bufflen); return mx_sc_elems; /* number of scat_gath elements allocated */}#ifdef SG_ALLOW_DIO_CODE/* vvvvvvvv following code borrowed from st driver's direct IO vvvvvvvvv */ /* hopefully this generic code will moved to a library *//* Pin down user pages and put them into a scatter gather list. Returns <= 0 if - mapping of all pages not successful - any page is above max_pfn (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 max_pfn){ int res, i, j; unsigned int nr_pages; struct page **pages; nr_pages = ((uaddr & ~PAGE_MASK) + count + ~PAGE_MASK) >> PAGE_SHIFT; /* 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]); if (page_to_pfn(pages[i]) > max_pfn) goto out_unlock; /* ?? Is locking needed? I don't think so */ /* if (TestSetPageLocked(pages[i])) goto out_unlock; */ } /* Populate the scatter/gather list */ sgl[0].page = pages[0]; sgl[0].offset = 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++) { sgl[i].offset = 0; sgl[i].page = pages[i]; sgl[i].length = count < PAGE_SIZE ? count : PAGE_SIZE; count -= PAGE_SIZE; } } else { sgl[0].length = count; } kfree(pages); return nr_pages; out_unlock: /* for (j=0; j < i; j++) unlock_page(pages[j]); */ res = 0; out_unmap: if (res > 0) for (j=0; j < res; j++) page_cache_release(pages[j]); 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++) { if (dirtied && !PageReserved(sgl[i].page)) SetPageDirty(sgl[i].page); /* unlock_page(sgl[i].page); */ /* FIXME: cache flush missing for rw==READ * FIXME: call the correct reference counting function */ page_cache_release(sgl[i].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; struct scatterlist *sgl; 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; } sgl = (struct scatterlist *)schp->buffer; res = st_map_user_pages(sgl, mx_sc_elems, (unsigned long)hp->dxferp, dxfer_len, (SG_DXFER_TO_DEV == hp->dxfer_direction) ? 1 : 0, ULONG_MAX); if (res <= 0) return 1; schp->k_use_sg = res; schp->dio_in_use = 1; hp->info |= SG_INFO_DIRECT_IO; return 0;#else return 1;#endif}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?