sg.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,278 行 · 第 1/5 页

C
2,278
字号
static intsg_build_indirect(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size){	int ret_sz;	int blk_size = buff_size;	unsigned char *p = NULL;	if ((blk_size < 0) || (!sfp))		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));	if (blk_size <= SG_SCATTER_SZ) {		p = sg_page_malloc(blk_size, sfp->low_dma, &ret_sz);		if (!p)			return -ENOMEM;		if (blk_size == ret_sz) {	/* got it on the first attempt */			schp->k_use_sg = 0;			schp->buffer = p;			schp->bufflen = blk_size;			schp->b_malloc_len = blk_size;			return 0;		}	} else {		p = sg_page_malloc(SG_SCATTER_SZ, sfp->low_dma, &ret_sz);		if (!p)			return -ENOMEM;	}/* Want some local declarations, so start new block ... */	{			/* lets try and build a scatter gather list */		struct scatterlist *sclp;		int k, rem_sz, num;		int mx_sc_elems;		int sg_tablesize = sfp->parentdp->sg_tablesize;		int first = 1;		/* 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 */		for (k = 0, sclp = schp->buffer, rem_sz = blk_size;		     (rem_sz > 0) && (k < mx_sc_elems);		     ++k, rem_sz -= ret_sz, ++sclp) {			if (first)				first = 0;			else {				num =				    (rem_sz >				     SG_SCATTER_SZ) ? SG_SCATTER_SZ : rem_sz;				p = sg_page_malloc(num, sfp->low_dma, &ret_sz);				if (!p)					break;			}			sclp->page = virt_to_page(p);			sclp->offset = offset_in_page(p);			sclp->length = ret_sz;			SCSI_LOG_TIMEOUT(5, printk("sg_build_build: k=%d, a=0x%p, len=%d\n",					  k, sg_scatg2virt(sclp), 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;	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)			num_xfer = schp->bufflen;	}	if ((num_xfer <= 0) || (schp->dio_in_use) ||	    (new_interface	     && ((SG_FLAG_NO_DXFER | SG_FLAG_MMAP_IO) & hp->flags)))		return 0;	SCSI_LOG_TIMEOUT(4, printk("sg_write_xfer: num_xfer=%d, iovec_count=%d, k_use_sg=%d\n",			  num_xfer, iovec_count, schp->k_use_sg));	if (iovec_count) {		onum = iovec_count;		if ((k = verify_area(VERIFY_READ, hp->dxferp,				     SZ_SG_IOVEC * onum)))			return k;	} else		onum = 1;	if (0 == schp->k_use_sg) {	/* kernel has single buffer */		for (j = 0, p = schp->buffer; j < onum; ++j) {			res = sg_u_iovec(hp, iovec_count, j, 1, &usglen, &up);			if (res)				return res;			usglen = (num_xfer > usglen) ? usglen : num_xfer;			if (__copy_from_user(p, up, usglen))				return -EFAULT;			p += usglen;			num_xfer -= usglen;			if (num_xfer <= 0)				return 0;		}	} else {		/* kernel using scatter gather list */		struct scatterlist *sclp = (struct scatterlist *) schp->buffer;		ksglen = (int) sclp->length;		p = sg_scatg2virt(sclp);		for (j = 0, k = 0; j < onum; ++j) {			res = sg_u_iovec(hp, iovec_count, j, 1, &usglen, &up);			if (res)				return res;			for (; p; ++sclp, ksglen = (int) sclp->length,				  p = sg_scatg2virt(sclp)) {				if (usglen <= 0)					break;				if (ksglen > usglen) {					if (usglen >= num_xfer) {						if (__copy_from_user						    (p, up, num_xfer))							return -EFAULT;						return 0;					}					if (__copy_from_user(p, up, usglen))						return -EFAULT;					p += usglen;					ksglen -= usglen;					break;				} else {					if (ksglen >= num_xfer) {						if (__copy_from_user						    (p, up, num_xfer))							return -EFAULT;						return 0;					}					if (__copy_from_user(p, up, ksglen))						return -EFAULT;					up += ksglen;					usglen -= ksglen;				}				++k;				if (k >= schp->k_use_sg)					return 0;			}		}	}	return 0;}static intsg_u_iovec(sg_io_hdr_t * hp, int sg_num, int ind,	   int wr_xf, int *countp, unsigned char __user **up){	int num_xfer = (int) hp->dxfer_len;	unsigned char __user *p = hp->dxferp;	int count, k;	if (0 == sg_num) {		if (wr_xf && ('\0' == hp->interface_id))			count = (int) hp->flags;	/* holds "old" input_size */		else			count = num_xfer;	} else {		sg_iovec_t iovec;		if (__copy_from_user(&iovec, p + ind*SZ_SG_IOVEC, SZ_SG_IOVEC))			return -EFAULT;		p = iovec.iov_base;		count = (int) iovec.iov_len;	}	if ((k = verify_area(wr_xf ? VERIFY_READ : VERIFY_WRITE, p, count)))		return k;	if (up)		*up = p;	if (countp)		*countp = count;	return 0;}static voidsg_remove_scat(Sg_scatter_hold * schp){	SCSI_LOG_TIMEOUT(4, printk("sg_remove_scat: k_use_sg=%d\n", schp->k_use_sg));	if (schp->buffer && (schp->sglist_len > 0)) {		struct scatterlist *sclp = (struct scatterlist *) schp->buffer;		if (schp->dio_in_use) {#ifdef SG_ALLOW_DIO_CODE			st_unmap_user_pages(sclp, schp->k_use_sg, TRUE);#endif		} else {			int k;			for (k = 0; (k < schp->k_use_sg) && sg_scatg2virt(sclp);			     ++k, ++sclp) {				SCSI_LOG_TIMEOUT(5, printk(				    "sg_remove_scat: k=%d, a=0x%p, len=%d\n",				    k, sg_scatg2virt(sclp), sclp->length));				sg_page_free(sg_scatg2virt(sclp), sclp->length);				sclp->page = NULL;				sclp->offset = 0;				sclp->length = 0;			}		}		sg_page_free(schp->buffer, schp->sglist_len);	} else if (schp->buffer)		sg_page_free(schp->buffer, schp->b_malloc_len);	memset(schp, 0, sizeof (*schp));}static intsg_read_xfer(Sg_request * srp){	sg_io_hdr_t *hp = &srp->header;	Sg_scatter_hold *schp = &srp->data;	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_FROM_DEV == dxfer_dir)	    || (SG_DXFER_TO_FROM_DEV == dxfer_dir)) {		num_xfer = hp->dxfer_len;		if (schp->bufflen < num_xfer)			num_xfer = schp->bufflen;	}	if ((num_xfer <= 0) || (schp->dio_in_use) ||	    (new_interface	     && ((SG_FLAG_NO_DXFER | SG_FLAG_MMAP_IO) & hp->flags)))		return 0;	SCSI_LOG_TIMEOUT(4, printk("sg_read_xfer: num_xfer=%d, iovec_count=%d, k_use_sg=%d\n",			  num_xfer, iovec_count, schp->k_use_sg));	if (iovec_count) {		onum = iovec_count;		if ((k = verify_area(VERIFY_READ, hp->dxferp,				     SZ_SG_IOVEC * onum)))			return k;	} else		onum = 1;	if (0 == schp->k_use_sg) {	/* kernel has single buffer */		for (j = 0, p = schp->buffer; j < onum; ++j) {			res = sg_u_iovec(hp, iovec_count, j, 0, &usglen, &up);			if (res)				return res;			usglen = (num_xfer > usglen) ? usglen : num_xfer;			if (__copy_to_user(up, p, usglen))				return -EFAULT;			p += usglen;			num_xfer -= usglen;			if (num_xfer <= 0)				return 0;		}	} else {		/* kernel using scatter gather list */		struct scatterlist *sclp = (struct scatterlist *) schp->buffer;		ksglen = (int) sclp->length;		p = sg_scatg2virt(sclp);		for (j = 0, k = 0; j < onum; ++j) {			res = sg_u_iovec(hp, iovec_count, j, 0, &usglen, &up);			if (res)				return res;			for (; p; ++sclp, ksglen = (int) sclp->length,				  p = sg_scatg2virt(sclp)) {				if (usglen <= 0)					break;				if (ksglen > usglen) {					if (usglen >= num_xfer) {						if (__copy_to_user						    (up, p, num_xfer))							return -EFAULT;						return 0;					}					if (__copy_to_user(up, p, usglen))						return -EFAULT;					p += usglen;					ksglen -= usglen;					break;				} else {					if (ksglen >= num_xfer) {						if (__copy_to_user						    (up, p, num_xfer))							return -EFAULT;						return 0;					}					if (__copy_to_user(up, p, ksglen))						return -EFAULT;					up += ksglen;					usglen -= ksglen;				}				++k;				if (k >= schp->k_use_sg)					return 0;			}		}	}	return 0;}static intsg_read_oxfer(Sg_request * srp, char __user *outp, int num_read_xfer){	Sg_scatter_hold *schp = &srp->data;	SCSI_LOG_TIMEOUT(4, printk("sg_read_oxfer: num_read_xfer=%d\n",				   num_read_xfer));	if ((!outp) || (num_read_xfer <= 0))		return 0;	if (schp->k_use_sg > 0) {		int k, num;		struct scatterlist *sclp = (struct scatterlist *) schp->buffer;		for (k = 0; (k < schp->k_use_sg) && sg_scatg2virt(sclp);		     ++k, ++sclp) {			num = (int) sclp->length;			if (num > num_read_xfer) {				if (__copy_to_user				    (outp, sg_scatg2virt(sclp), num_read_xfer))					return -EFAULT;				break;			} else {				if (__copy_to_user				    (outp, sg_scatg2virt(sclp), num))					return -EFAULT;				num_read_xfer -= num;				if (num_read_xfer <= 0)					break;				outp += num;			}		}	} else {		if (__copy_to_user(outp, schp->buffer, num_read_xfer))			return -EFAULT;	}	return 0;}static voidsg_build_reserve(Sg_fd * sfp, int req_size){	Sg_scatter_hold *schp = &sfp->reserve;	SCSI_LOG_TIMEOUT(4, printk("sg_build_reserve: req_size=%d\n", req_size));	do {		if (req_size < PAGE_SIZE)			req_size = PAGE_SIZE;		if (0 == sg_build_indirect(schp, sfp, req_size))			return;		else			sg_remove_scat(schp);		req_size >>= 1;	/* divide by 2 */	} while (req_size > (PAGE_SIZE / 2));}static voidsg_link_reserve(Sg_fd * sfp, Sg_request * srp, int size){	Sg_scatter_hold *req_schp = &srp->data;	Sg_scatter_hold *rsv_schp = &sfp->reserve;	srp->res_used = 1;	SCSI_LOG_TIMEOUT(4, printk("sg_link_reserve: size=%d\n", size));	size = (size + 1) & (~1);	/* round to even for aha1542 */	if (rsv_schp->k_use_sg > 0) {		int k, num;		int rem = size;		struct scatterlist *sclp =		    (struct scatterlist *) rsv_schp->buffer;		for (k = 0; k < rsv_schp->k_use_sg; ++k, ++sclp) {			num = (int) sclp->length;			if (rem <= num) {				if (0 == k) {					req_schp->k_use_sg = 0;					req_schp->buffer = sg_scatg2virt(sclp);				} else {					sfp->save_scat_len = num;					sclp->length = (unsigned) rem;					req_schp->k_use_sg = k + 1;					req_schp->sglist_len =					    rsv_schp->sglist_len;					req_schp->buffer = rsv_schp->buffer;				}				req_schp->bufflen = size;				req_schp->b_malloc_len = rsv_schp->b_malloc_len;				break;			} else				rem -= num;		}		if (k >= rsv_schp->k_use_sg)			SCSI_LOG_TIMEOUT(1, printk("sg_link_reserve: BAD size\n"));	} else {		req_schp->k_use_sg = 0;		req_schp->bufflen = size;		req_schp->buffer = rsv_schp->buffer;		req_schp->b_malloc_len = rsv_schp->b_malloc_len;	}}static voidsg_unlink_reserve(Sg_fd * sfp, Sg_request * srp){	Sg_scatter_hold *req_schp = &srp->data;	Sg_scatter_hold *rsv_schp = &sfp->reserve;	SCSI_LOG_TIMEOUT(4, printk("sg_unlink_reserve: req->k_use_sg=%d\n",				   (int) req_schp->k_use_sg));	if ((rsv_schp->k_use_sg > 0) && (req_schp->k_use_sg > 0)) {		struct scatterlist *sclp =		    (struct scatterlist *) rsv_schp->buffer;		if (sfp->save_scat_len > 0)			(sclp + (req_schp->k_use_sg - 1))->length =			    (unsigned) sfp->save_scat_len;		else			SCSI_LOG_TIMEOUT(1, printk ("sg_unlink_reserve: BAD save_scat_len\n"));	}	req_schp->k_use_sg = 0;	req_schp->bufflen = 0;	req_schp->buffer = NULL;	req_schp->sglist_len = 0;	sfp->save_scat_len = 0;	srp->res_used = 0;}static Sg_request *sg_get_rq_mark(Sg_fd * sfp, int pack_id){	Sg_request *resp;	unsigned long iflags;	write_

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?