sg.c

来自「linux 内核源代码」· C语言 代码 · 共 2,357 行 · 第 1/5 页

C
2,357
字号
			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 (!access_ok(VERIFY_READ, hp->dxferp, SZ_SG_IOVEC * onum))			return -EFAULT;	} else		onum = 1;	ksglen = sg->length;	p = page_address(sg_page(sg));	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; sg = sg_next(sg), ksglen = sg->length,		     p = page_address(sg_page(sg))) {			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;	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 (!access_ok(wr_xf ? VERIFY_READ : VERIFY_WRITE, p, count))		return -EFAULT;	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 *sg = schp->buffer;		if (schp->dio_in_use) {#ifdef SG_ALLOW_DIO_CODE			st_unmap_user_pages(sg, schp->k_use_sg, TRUE);#endif		} else {			int k;			for (k = 0; (k < schp->k_use_sg) && sg_page(sg);			     ++k, sg = sg_next(sg)) {				SCSI_LOG_TIMEOUT(5, printk(				    "sg_remove_scat: k=%d, pg=0x%p, len=%d\n",				    k, sg_page(sg), sg->length));				sg_page_free(sg_page(sg), sg->length);			}		}		kfree(schp->buffer);	}	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;	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_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 (!access_ok(VERIFY_READ, hp->dxferp, SZ_SG_IOVEC * onum))			return -EFAULT;	} else		onum = 1;	p = page_address(sg_page(sg));	ksglen = sg->length;	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; sg = sg_next(sg), ksglen = sg->length,		     p = page_address(sg_page(sg))) {			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;	struct scatterlist *sg = schp->buffer;	int k, num;	SCSI_LOG_TIMEOUT(4, printk("sg_read_oxfer: num_read_xfer=%d\n",				   num_read_xfer));	if ((!outp) || (num_read_xfer <= 0))		return 0;	for (k = 0; (k < schp->k_use_sg) && sg_page(sg); ++k, sg = sg_next(sg)) {		num = sg->length;		if (num > num_read_xfer) {			if (__copy_to_user(outp, page_address(sg_page(sg)),					   num_read_xfer))				return -EFAULT;			break;		} else {			if (__copy_to_user(outp, page_address(sg_page(sg)),					   num))				return -EFAULT;			num_read_xfer -= num;			if (num_read_xfer <= 0)				break;			outp += num;		}	}	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;	struct scatterlist *sg = rsv_schp->buffer;	int k, num, rem;	srp->res_used = 1;	SCSI_LOG_TIMEOUT(4, printk("sg_link_reserve: size=%d\n", size));	rem = size;	for (k = 0; k < rsv_schp->k_use_sg; ++k, sg = sg_next(sg)) {		num = sg->length;		if (rem <= num) {			sfp->save_scat_len = num;			sg->length = 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"));}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 *sg = rsv_schp->buffer;		if (sfp->save_scat_len > 0)			(sg + (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_lock_irqsave(&sfp->rq_list_lock, iflags);	for (resp = sfp->headrp; resp; resp = resp->nextrp) {		/* look for requests that are ready + not SG_IO owned */		if ((1 == resp->done) && (!resp->sg_io_owned) &&		    ((-1 == pack_id) || (resp->header.pack_id == pack_id))) {			resp->done = 2;	/* guard against other readers */			break;		}	}	write_unlock_irqrestore(&sfp->rq_list_lock, iflags);	return resp;}#ifdef CONFIG_SCSI_PROC_FSstatic Sg_request *sg_get_nth_request(Sg_fd * sfp, int nth){	Sg_request *resp;	unsigned long iflags;	int k;	read_lock_irqsave(&sfp->rq_list_lock, iflags);	for (k = 0, resp = sfp->headrp; resp && (k < nth);	     ++k, resp = resp->nextrp) ;	read_unlock_irqrestore(&sfp->rq_list_lock, iflags);	return resp;}#endif/* always adds to end of list */static Sg_request *sg_add_request(Sg_fd * sfp){	int k;	unsigned long iflags;	Sg_request *resp;	Sg_request *rp = sfp->req_arr;	write_lock_irqsave(&sfp->rq_list_lock, iflags);	resp = sfp->headrp;	if (!resp) {		memset(rp, 0, sizeof (Sg_request));		rp->parentfp = sfp;		resp = rp;		sfp->headrp = resp;	} else {		if (0 == sfp->cmd_q)			resp = NULL;	/* command queuing disallowed */		else {			for (k = 0; k < SG_MAX_QUEUE; ++k, ++rp) {				if (!rp->parentfp)					break;			}			if (k < SG_MAX_QUEUE) {				memset(rp, 0, sizeof (Sg_request));				rp->parentfp = sfp;				while (resp->nextrp)					resp = resp->nextrp;				resp->nextrp = rp;				resp = rp;			} else				resp = NULL;		}	}	if (resp) {		resp->nextrp = NULL;		resp->header.duration = jiffies_to_msecs(jiffies);	}	write_unlock_irqrestore(&sfp->rq_list_lock, iflags);	return resp;}/* Return of 1 for found; 0 for not found */static intsg_remove_request(Sg_fd * sfp, Sg_request * srp){	Sg_request *prev_rp;	Sg_request *rp;	unsigned long iflags;	int res = 0;	if ((!sfp) || (!srp) || (!sfp->headrp))		return res;	write_lock_irqsave(&sfp->rq_list_lock, iflags);	prev_rp = sfp->headrp;	if (srp == prev_rp) {		sfp->headrp = prev_rp->nextrp;		prev_rp->parentfp = NULL;		res = 1;	} else {		while ((rp = prev_rp->nextrp)) {			if (srp == rp) {				prev_rp->nextrp = rp->nextrp;				rp->parentfp = NULL;				res = 1;				break;			}			prev_rp = rp;		}	}	write_unlock_irqrestore(&sfp->rq_list_lock, iflags);	return res;}#ifdef CONFIG_SCSI_PROC_FSstatic Sg_fd *sg_get_nth_sfp(Sg_device * sdp, int nth){	Sg_fd *resp;	unsigned long iflags;	int k;	read_lock_irqsave(&sg_index_lock, iflags);	for (k = 0, resp = sdp->headfp; resp && (k < nth);	     ++k, resp = resp->nextfp) ;	read_unlock_irqrestore(&sg_index_lock, iflags);	return resp;}#endifstatic Sg_fd *sg_add_sfp(Sg_device * sdp, int dev){	Sg_fd *sfp;	unsigned long iflags;	int bufflen;	sfp = kzalloc(sizeof(*sfp), GFP_ATOMIC | __GFP_NOWARN);	if (!sfp)		return NULL;	init_waitqueue_head(&sfp->read_wait);	rwlock_init(&sfp->rq_list_lock);	sfp->timeout = SG_DEFAULT_TIMEOUT;	sfp->timeout_user = SG_DEFAULT_TIMEOUT_USER;	sfp->force_packid = SG_DEF_FORCE_PACK_ID;	sfp->low_dma = (SG_DEF_FORCE_LOW_DMA == 0) ?	    sdp->device->host->unchecked_isa_dma : 1;	sfp->cmd_q = SG_DEF_COMMAND_Q;	sfp->keep_orphan = SG_DEF_KEEP_ORPHAN;	sfp->parentdp = sdp;	write_lock_irqsave(&sg_index_lock, iflags);	if (!sdp->headfp)		sdp->headfp = sfp;	else {			/* add to tail of existing list */		Sg_fd *pfp = sdp->headfp;		while (pfp->nextfp)			pfp = pfp->nextfp;		pfp->nextfp = sfp;	}	write_unlock_irqrestore(&sg_index_lock, iflags);	SCSI_LOG_TIMEOUT(3, printk("sg_add_sfp: sfp=0x%p\n", sfp));	if (unlikely(sg_big_buff != def_reserved_size))		sg_big_buff = def_reserved_size;	bufflen = min_t(int, sg_big_buff,			sdp->device->request_queue->max_sectors * 512);	sg_build_reserve(sfp, bufflen);	SCSI_LOG_TIMEOUT(3, printk("sg_add_sfp:   bufflen=%d, k_use_sg=%d\n",			   sfp->reserve.bufflen, sfp->reserve.k_use_sg));	return sfp;}static void__sg_remove_sfp(Sg_dev

⌨️ 快捷键说明

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