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(&current->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(&current->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 + -
显示快捷键?