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