sg.c

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

C
2,357
字号
	kfree(old_hdr);	return retval;}static ssize_tsg_new_read(Sg_fd * sfp, char __user *buf, size_t count, Sg_request * srp){	sg_io_hdr_t *hp = &srp->header;	int err = 0;	int len;	if (count < SZ_SG_IO_HDR) {		err = -EINVAL;		goto err_out;	}	hp->sb_len_wr = 0;	if ((hp->mx_sb_len > 0) && hp->sbp) {		if ((CHECK_CONDITION & hp->masked_status) ||		    (DRIVER_SENSE & hp->driver_status)) {			int sb_len = SCSI_SENSE_BUFFERSIZE;			sb_len = (hp->mx_sb_len > sb_len) ? sb_len : hp->mx_sb_len;			len = 8 + (int) srp->sense_b[7];	/* Additional sense length field */			len = (len > sb_len) ? sb_len : len;			if (copy_to_user(hp->sbp, srp->sense_b, len)) {				err = -EFAULT;				goto err_out;			}			hp->sb_len_wr = len;		}	}	if (hp->masked_status || hp->host_status || hp->driver_status)		hp->info |= SG_INFO_CHECK;	if (copy_to_user(buf, hp, SZ_SG_IO_HDR)) {		err = -EFAULT;		goto err_out;	}	err = sg_read_xfer(srp);      err_out:	sg_finish_rem_req(srp);	return (0 == err) ? count : err;}static ssize_tsg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos){	int mxsize, cmd_size, k;	int input_size, blocking;	unsigned char opcode;	Sg_device *sdp;	Sg_fd *sfp;	Sg_request *srp;	struct sg_header old_hdr;	sg_io_hdr_t *hp;	unsigned char cmnd[MAX_COMMAND_SIZE];	if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp)))		return -ENXIO;	SCSI_LOG_TIMEOUT(3, printk("sg_write: %s, count=%d\n",				   sdp->disk->disk_name, (int) count));	if (sdp->detached)		return -ENODEV;	if (!((filp->f_flags & O_NONBLOCK) ||	      scsi_block_when_processing_errors(sdp->device)))		return -ENXIO;	if (!access_ok(VERIFY_READ, buf, count))		return -EFAULT;	/* protects following copy_from_user()s + get_user()s */	if (count < SZ_SG_HEADER)		return -EIO;	if (__copy_from_user(&old_hdr, buf, SZ_SG_HEADER))		return -EFAULT;	blocking = !(filp->f_flags & O_NONBLOCK);	if (old_hdr.reply_len < 0)		return sg_new_write(sfp, buf, count, blocking, 0, NULL);	if (count < (SZ_SG_HEADER + 6))		return -EIO;	/* The minimum scsi command length is 6 bytes. */	if (!(srp = sg_add_request(sfp))) {		SCSI_LOG_TIMEOUT(1, printk("sg_write: queue full\n"));		return -EDOM;	}	buf += SZ_SG_HEADER;	__get_user(opcode, buf);	if (sfp->next_cmd_len > 0) {		if (sfp->next_cmd_len > MAX_COMMAND_SIZE) {			SCSI_LOG_TIMEOUT(1, printk("sg_write: command length too long\n"));			sfp->next_cmd_len = 0;			sg_remove_request(sfp, srp);			return -EIO;		}		cmd_size = sfp->next_cmd_len;		sfp->next_cmd_len = 0;	/* reset so only this write() effected */	} else {		cmd_size = COMMAND_SIZE(opcode);	/* based on SCSI command group */		if ((opcode >= 0xc0) && old_hdr.twelve_byte)			cmd_size = 12;	}	SCSI_LOG_TIMEOUT(4, printk(		"sg_write:   scsi opcode=0x%02x, cmd_size=%d\n", (int) opcode, cmd_size));/* Determine buffer size.  */	input_size = count - cmd_size;	mxsize = (input_size > old_hdr.reply_len) ? input_size : old_hdr.reply_len;	mxsize -= SZ_SG_HEADER;	input_size -= SZ_SG_HEADER;	if (input_size < 0) {		sg_remove_request(sfp, srp);		return -EIO;	/* User did not pass enough bytes for this command. */	}	hp = &srp->header;	hp->interface_id = '\0';	/* indicator of old interface tunnelled */	hp->cmd_len = (unsigned char) cmd_size;	hp->iovec_count = 0;	hp->mx_sb_len = 0;	if (input_size > 0)		hp->dxfer_direction = (old_hdr.reply_len > SZ_SG_HEADER) ?		    SG_DXFER_TO_FROM_DEV : SG_DXFER_TO_DEV;	else		hp->dxfer_direction = (mxsize > 0) ? SG_DXFER_FROM_DEV : SG_DXFER_NONE;	hp->dxfer_len = mxsize;	hp->dxferp = (char __user *)buf + cmd_size;	hp->sbp = NULL;	hp->timeout = old_hdr.reply_len;	/* structure abuse ... */	hp->flags = input_size;	/* structure abuse ... */	hp->pack_id = old_hdr.pack_id;	hp->usr_ptr = NULL;	if (__copy_from_user(cmnd, buf, cmd_size))		return -EFAULT;	/*	 * SG_DXFER_TO_FROM_DEV is functionally equivalent to SG_DXFER_FROM_DEV,	 * but is is possible that the app intended SG_DXFER_TO_DEV, because there	 * is a non-zero input_size, so emit a warning.	 */	if (hp->dxfer_direction == SG_DXFER_TO_FROM_DEV)		if (printk_ratelimit())			printk(KERN_WARNING			       "sg_write: data in/out %d/%d bytes for SCSI command 0x%x--"			       "guessing data in;\n" KERN_WARNING "   "			       "program %s not setting count and/or reply_len properly\n",			       old_hdr.reply_len - (int)SZ_SG_HEADER,			       input_size, (unsigned int) cmnd[0],			       current->comm);	k = sg_common_write(sfp, srp, cmnd, sfp->timeout, blocking);	return (k < 0) ? k : count;}static ssize_tsg_new_write(Sg_fd * sfp, const char __user *buf, size_t count,	     int blocking, int read_only, Sg_request ** o_srp){	int k;	Sg_request *srp;	sg_io_hdr_t *hp;	unsigned char cmnd[MAX_COMMAND_SIZE];	int timeout;	unsigned long ul_timeout;	if (count < SZ_SG_IO_HDR)		return -EINVAL;	if (!access_ok(VERIFY_READ, buf, count))		return -EFAULT; /* protects following copy_from_user()s + get_user()s */	sfp->cmd_q = 1;	/* when sg_io_hdr seen, set command queuing on */	if (!(srp = sg_add_request(sfp))) {		SCSI_LOG_TIMEOUT(1, printk("sg_new_write: queue full\n"));		return -EDOM;	}	hp = &srp->header;	if (__copy_from_user(hp, buf, SZ_SG_IO_HDR)) {		sg_remove_request(sfp, srp);		return -EFAULT;	}	if (hp->interface_id != 'S') {		sg_remove_request(sfp, srp);		return -ENOSYS;	}	if (hp->flags & SG_FLAG_MMAP_IO) {		if (hp->dxfer_len > sfp->reserve.bufflen) {			sg_remove_request(sfp, srp);			return -ENOMEM;	/* MMAP_IO size must fit in reserve buffer */		}		if (hp->flags & SG_FLAG_DIRECT_IO) {			sg_remove_request(sfp, srp);			return -EINVAL;	/* either MMAP_IO or DIRECT_IO (not both) */		}		if (sg_res_in_use(sfp)) {			sg_remove_request(sfp, srp);			return -EBUSY;	/* reserve buffer already being used */		}	}	ul_timeout = msecs_to_jiffies(srp->header.timeout);	timeout = (ul_timeout < INT_MAX) ? ul_timeout : INT_MAX;	if ((!hp->cmdp) || (hp->cmd_len < 6) || (hp->cmd_len > sizeof (cmnd))) {		sg_remove_request(sfp, srp);		return -EMSGSIZE;	}	if (!access_ok(VERIFY_READ, hp->cmdp, hp->cmd_len)) {		sg_remove_request(sfp, srp);		return -EFAULT;	/* protects following copy_from_user()s + get_user()s */	}	if (__copy_from_user(cmnd, hp->cmdp, hp->cmd_len)) {		sg_remove_request(sfp, srp);		return -EFAULT;	}	if (read_only &&	    (!sg_allow_access(cmnd[0], sfp->parentdp->device->type))) {		sg_remove_request(sfp, srp);		return -EPERM;	}	k = sg_common_write(sfp, srp, cmnd, timeout, blocking);	if (k < 0)		return k;	if (o_srp)		*o_srp = srp;	return count;}static intsg_common_write(Sg_fd * sfp, Sg_request * srp,		unsigned char *cmnd, int timeout, int blocking){	int k, data_dir;	Sg_device *sdp = sfp->parentdp;	sg_io_hdr_t *hp = &srp->header;	srp->data.cmd_opcode = cmnd[0];	/* hold opcode of command */	hp->status = 0;	hp->masked_status = 0;	hp->msg_status = 0;	hp->info = 0;	hp->host_status = 0;	hp->driver_status = 0;	hp->resid = 0;	SCSI_LOG_TIMEOUT(4, printk("sg_common_write:  scsi opcode=0x%02x, cmd_size=%d\n",			  (int) cmnd[0], (int) hp->cmd_len));	if ((k = sg_start_req(srp))) {		SCSI_LOG_TIMEOUT(1, printk("sg_common_write: start_req err=%d\n", k));		sg_finish_rem_req(srp);		return k;	/* probably out of space --> ENOMEM */	}	if ((k = sg_write_xfer(srp))) {		SCSI_LOG_TIMEOUT(1, printk("sg_common_write: write_xfer, bad address\n"));		sg_finish_rem_req(srp);		return k;	}	if (sdp->detached) {		sg_finish_rem_req(srp);		return -ENODEV;	}	switch (hp->dxfer_direction) {	case SG_DXFER_TO_FROM_DEV:	case SG_DXFER_FROM_DEV:		data_dir = DMA_FROM_DEVICE;		break;	case SG_DXFER_TO_DEV:		data_dir = DMA_TO_DEVICE;		break;	case SG_DXFER_UNKNOWN:		data_dir = DMA_BIDIRECTIONAL;		break;	default:		data_dir = DMA_NONE;		break;	}	hp->duration = jiffies_to_msecs(jiffies);/* Now send everything of to mid-level. The next time we hear about this   packet is when sg_cmd_done() is called (i.e. a callback). */	if (scsi_execute_async(sdp->device, cmnd, hp->cmd_len, data_dir, srp->data.buffer,				hp->dxfer_len, srp->data.k_use_sg, timeout,				SG_DEFAULT_RETRIES, srp, sg_cmd_done,				GFP_ATOMIC)) {		SCSI_LOG_TIMEOUT(1, printk("sg_common_write: scsi_execute_async failed\n"));		/*		 * most likely out of mem, but could also be a bad map		 */		sg_finish_rem_req(srp);		return -ENOMEM;	} else		return 0;}static intsg_srp_done(Sg_request *srp, Sg_fd *sfp){	unsigned long iflags;	int done;	read_lock_irqsave(&sfp->rq_list_lock, iflags);	done = srp->done;	read_unlock_irqrestore(&sfp->rq_list_lock, iflags);	return done;}static intsg_ioctl(struct inode *inode, struct file *filp,	 unsigned int cmd_in, unsigned long arg){	void __user *p = (void __user *)arg;	int __user *ip = p;	int result, val, read_only;	Sg_device *sdp;	Sg_fd *sfp;	Sg_request *srp;	unsigned long iflags;	if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp)))		return -ENXIO;	SCSI_LOG_TIMEOUT(3, printk("sg_ioctl: %s, cmd=0x%x\n",				   sdp->disk->disk_name, (int) cmd_in));	read_only = (O_RDWR != (filp->f_flags & O_ACCMODE));	switch (cmd_in) {	case SG_IO:		{			int blocking = 1;	/* ignore O_NONBLOCK flag */			if (sdp->detached)				return -ENODEV;			if (!scsi_block_when_processing_errors(sdp->device))				return -ENXIO;			if (!access_ok(VERIFY_WRITE, p, SZ_SG_IO_HDR))				return -EFAULT;			result =			    sg_new_write(sfp, p, SZ_SG_IO_HDR,					 blocking, read_only, &srp);			if (result < 0)				return result;			srp->sg_io_owned = 1;			while (1) {				result = 0;	/* following macro to beat race condition */				__wait_event_interruptible(sfp->read_wait,					(sdp->detached || sfp->closed || sg_srp_done(srp, sfp)),							   result);				if (sdp->detached)					return -ENODEV;				if (sfp->closed)					return 0;	/* request packet dropped already */				if (0 == result)					break;				srp->orphan = 1;				return result;	/* -ERESTARTSYS because signal hit process */			}			write_lock_irqsave(&sfp->rq_list_lock, iflags);			srp->done = 2;			write_unlock_irqrestore(&sfp->rq_list_lock, iflags);			result = sg_new_read(sfp, p, SZ_SG_IO_HDR, srp);			return (result < 0) ? result : 0;		}	case SG_SET_TIMEOUT:		result = get_user(val, ip);		if (result)			return result;		if (val < 0)			return -EIO;		if (val >= MULDIV (INT_MAX, USER_HZ, HZ))		    val = MULDIV (INT_MAX, USER_HZ, HZ);		sfp->timeout_user = val;		sfp->timeout = MULDIV (val, HZ, USER_HZ);		return 0;	case SG_GET_TIMEOUT:	/* N.B. User receives timeout as return value */				/* strange ..., for backward compatibility */		return sfp->timeout_user;	case SG_SET_FORCE_LOW_DMA:		result = get_user(val, ip);		if (result)			return result;		if (val) {			sfp->low_dma = 1;			if ((0 == sfp->low_dma) && (0 == sg_res_in_use(sfp))) {				val = (int) sfp->reserve.bufflen;				sg_remove_scat(&sfp->reserve);				sg_build_reserve(sfp, val);			}		} else {			if (sdp->detached)				return -ENODEV;			sfp->low_dma = sdp->device->host->unchecked_isa_dma;		}		return 0;	case SG_GET_LOW_DMA:		return put_user((int) sfp->low_dma, ip);	case SG_GET_SCSI_ID:		if (!access_ok(VERIFY_WRITE, p, sizeof (sg_scsi_id_t)))			return -EFAULT;		else {			sg_scsi_id_t __user *sg_idp = p;			if (sdp->detached)				return -ENODEV;			__put_user((int) sdp->device->host->host_no,				   &sg_idp->host_no);			__put_user((int) sdp->device->channel,				   &sg_idp->channel);			__put_user((int) sdp->device->id, &sg_idp->scsi_id);			__put_user((int) sdp->device->lun, &sg_idp->lun);			__put_user((int) sdp->device->type, &sg_idp->scsi_type);			__put_user((short) sdp->device->host->cmd_per_lun,				   &sg_idp->h_cmd_per_lun);			__put_user((short) sdp->device->queue_depth,				   &sg_idp->d_queue_depth);			__put_user(0, &sg_idp->unused[0]);			__put_user(0, &sg_idp->unused[1]);			return 0;		}	case SG_SET_FORCE_PACK_ID:		result = get_user(val, ip);		if (result)			return result;		sfp->force_packid = val ? 1 : 0;		return 0;	case SG_GET_PACK_ID:		if (!access_ok(VERIFY_WRITE, ip, sizeof (int)))			return -EFAULT;		read_lock_irqsave(&sfp->rq_list_lock, iflags);		for (srp = sfp->headrp; srp; srp = srp->nextrp) {			if ((1 == srp->done) && (!srp->sg_io_owned)) {				read_unlock_irqrestore(&sfp->rq_list_lock,						       iflags);				__put_user(srp->header.pack_id, ip);				return 0;			}		}		read_unlock_irqrestore(&sfp->rq_list_lock, iflags);		__put_user(-1, ip);		return 0;	case SG_GET_NUM_WAITING:		read_lock_irqsave(&sfp->rq_list_lock, iflags);		for (val = 0, srp = sfp->headrp; srp; srp = srp->nextrp) {			if ((1 == srp->done) && (!srp->sg_io_owned))				++val;		}		read_unlock_irqrestore(&sfp->rq_list_lock, iflags);		return put_user(val, ip);	case SG_GET_SG_TABLESIZE:		return put_user(sdp->sg_tablesize, ip);	case SG_SET_RESERVED_SIZE:		result = get_user(val, ip);		if (result)			return result;                if (val < 0)                        return -EINVAL;		val = min_t(int, val,				sdp->device->request_queue->max_sectors * 512);		if (val != sfp->reserve.bufflen) {			if (sg_res_in_use(sfp) || sfp->mmap_called)				return -EBUSY;			sg_remove_scat(&sfp->reserve);			sg_build_reserve(sfp, val);		}		return 0;	case SG_GET_RESERVED_SIZE:		val = min_t(int, sfp->reserve.bufflen,				sdp->device->request_queue->max_sectors * 512);		return put_user(val, ip);	case SG_SET_COMMAND_Q:		result = get_user(val, ip);		if (result)			return result;		sfp->cmd_q = val ? 1 : 0;		return 0;	case SG_GET_COMMAND_Q:		return put_user((int) sfp->cmd_q, ip);	case SG_SET_KEEP_ORPHAN:		result = get_user(val, ip);		if (result)			return result;		sfp->keep_orphan = val;		return 0;	case SG_GET_KEEP_ORPHAN:		return put_user((int) sfp->keep_orphan, ip);

⌨️ 快捷键说明

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