sg.c

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

C
2,357
字号
	case SG_NEXT_CMD_LEN:		result = get_user(val, ip);		if (result)			return result;		sfp->next_cmd_len = (val > 0) ? val : 0;		return 0;	case SG_GET_VERSION_NUM:		return put_user(sg_version_num, ip);	case SG_GET_ACCESS_COUNT:		/* faked - we don't have a real access count anymore */		val = (sdp->device ? 1 : 0);		return put_user(val, ip);	case SG_GET_REQUEST_TABLE:		if (!access_ok(VERIFY_WRITE, p, SZ_SG_REQ_INFO * SG_MAX_QUEUE))			return -EFAULT;		else {			sg_req_info_t *rinfo;			unsigned int ms;			rinfo = kmalloc(SZ_SG_REQ_INFO * SG_MAX_QUEUE,								GFP_KERNEL);			if (!rinfo)				return -ENOMEM;			read_lock_irqsave(&sfp->rq_list_lock, iflags);			for (srp = sfp->headrp, val = 0; val < SG_MAX_QUEUE;			     ++val, srp = srp ? srp->nextrp : srp) {				memset(&rinfo[val], 0, SZ_SG_REQ_INFO);				if (srp) {					rinfo[val].req_state = srp->done + 1;					rinfo[val].problem =					    srp->header.masked_status & 					    srp->header.host_status & 					    srp->header.driver_status;					if (srp->done)						rinfo[val].duration =							srp->header.duration;					else {						ms = jiffies_to_msecs(jiffies);						rinfo[val].duration =						    (ms > srp->header.duration) ?						    (ms - srp->header.duration) : 0;					}					rinfo[val].orphan = srp->orphan;					rinfo[val].sg_io_owned =							srp->sg_io_owned;					rinfo[val].pack_id =							srp->header.pack_id;					rinfo[val].usr_ptr =							srp->header.usr_ptr;				}			}			read_unlock_irqrestore(&sfp->rq_list_lock, iflags);			result = __copy_to_user(p, rinfo, 						SZ_SG_REQ_INFO * SG_MAX_QUEUE);			result = result ? -EFAULT : 0;			kfree(rinfo);			return result;		}	case SG_EMULATED_HOST:		if (sdp->detached)			return -ENODEV;		return put_user(sdp->device->host->hostt->emulated, ip);	case SG_SCSI_RESET:		if (sdp->detached)			return -ENODEV;		if (filp->f_flags & O_NONBLOCK) {			if (scsi_host_in_recovery(sdp->device->host))				return -EBUSY;		} else if (!scsi_block_when_processing_errors(sdp->device))			return -EBUSY;		result = get_user(val, ip);		if (result)			return result;		if (SG_SCSI_RESET_NOTHING == val)			return 0;		switch (val) {		case SG_SCSI_RESET_DEVICE:			val = SCSI_TRY_RESET_DEVICE;			break;		case SG_SCSI_RESET_BUS:			val = SCSI_TRY_RESET_BUS;			break;		case SG_SCSI_RESET_HOST:			val = SCSI_TRY_RESET_HOST;			break;		default:			return -EINVAL;		}		if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))			return -EACCES;		return (scsi_reset_provider(sdp->device, val) ==			SUCCESS) ? 0 : -EIO;	case SCSI_IOCTL_SEND_COMMAND:		if (sdp->detached)			return -ENODEV;		if (read_only) {			unsigned char opcode = WRITE_6;			Scsi_Ioctl_Command __user *siocp = p;			if (copy_from_user(&opcode, siocp->data, 1))				return -EFAULT;			if (!sg_allow_access(opcode, sdp->device->type))				return -EPERM;		}		return sg_scsi_ioctl(filp, sdp->device->request_queue, NULL, p);	case SG_SET_DEBUG:		result = get_user(val, ip);		if (result)			return result;		sdp->sgdebug = (char) val;		return 0;	case SCSI_IOCTL_GET_IDLUN:	case SCSI_IOCTL_GET_BUS_NUMBER:	case SCSI_IOCTL_PROBE_HOST:	case SG_GET_TRANSFORM:		if (sdp->detached)			return -ENODEV;		return scsi_ioctl(sdp->device, cmd_in, p);	case BLKSECTGET:		return put_user(sdp->device->request_queue->max_sectors * 512,				ip);	default:		if (read_only)			return -EPERM;	/* don't know so take safe approach */		return scsi_ioctl(sdp->device, cmd_in, p);	}}#ifdef CONFIG_COMPATstatic long sg_compat_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg){	Sg_device *sdp;	Sg_fd *sfp;	struct scsi_device *sdev;	if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp)))		return -ENXIO;	sdev = sdp->device;	if (sdev->host->hostt->compat_ioctl) { 		int ret;		ret = sdev->host->hostt->compat_ioctl(sdev, cmd_in, (void __user *)arg);		return ret;	}		return -ENOIOCTLCMD;}#endifstatic unsigned intsg_poll(struct file *filp, poll_table * wait){	unsigned int res = 0;	Sg_device *sdp;	Sg_fd *sfp;	Sg_request *srp;	int count = 0;	unsigned long iflags;	if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp))	    || sfp->closed)		return POLLERR;	poll_wait(filp, &sfp->read_wait, wait);	read_lock_irqsave(&sfp->rq_list_lock, iflags);	for (srp = sfp->headrp; srp; srp = srp->nextrp) {		/* if any read waiting, flag it */		if ((0 == res) && (1 == srp->done) && (!srp->sg_io_owned))			res = POLLIN | POLLRDNORM;		++count;	}	read_unlock_irqrestore(&sfp->rq_list_lock, iflags);	if (sdp->detached)		res |= POLLHUP;	else if (!sfp->cmd_q) {		if (0 == count)			res |= POLLOUT | POLLWRNORM;	} else if (count < SG_MAX_QUEUE)		res |= POLLOUT | POLLWRNORM;	SCSI_LOG_TIMEOUT(3, printk("sg_poll: %s, res=0x%x\n",				   sdp->disk->disk_name, (int) res));	return res;}static intsg_fasync(int fd, struct file *filp, int mode){	int retval;	Sg_device *sdp;	Sg_fd *sfp;	if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp)))		return -ENXIO;	SCSI_LOG_TIMEOUT(3, printk("sg_fasync: %s, mode=%d\n",				   sdp->disk->disk_name, mode));	retval = fasync_helper(fd, filp, mode, &sfp->async_qp);	return (retval < 0) ? retval : 0;}static struct page *sg_vma_nopage(struct vm_area_struct *vma, unsigned long addr, int *type){	Sg_fd *sfp;	struct page *page = NOPAGE_SIGBUS;	unsigned long offset, len, sa;	Sg_scatter_hold *rsv_schp;	struct scatterlist *sg;	int k;	if ((NULL == vma) || (!(sfp = (Sg_fd *) vma->vm_private_data)))		return page;	rsv_schp = &sfp->reserve;	offset = addr - vma->vm_start;	if (offset >= rsv_schp->bufflen)		return page;	SCSI_LOG_TIMEOUT(3, printk("sg_vma_nopage: offset=%lu, scatg=%d\n",				   offset, rsv_schp->k_use_sg));	sg = rsv_schp->buffer;	sa = vma->vm_start;	for (k = 0; (k < rsv_schp->k_use_sg) && (sa < vma->vm_end);	     ++k, sg = sg_next(sg)) {		len = vma->vm_end - sa;		len = (len < sg->length) ? len : sg->length;		if (offset < len) {			page = virt_to_page(page_address(sg_page(sg)) + offset);			get_page(page);	/* increment page count */			break;		}		sa += len;		offset -= len;	}	if (type)		*type = VM_FAULT_MINOR;	return page;}static struct vm_operations_struct sg_mmap_vm_ops = {	.nopage = sg_vma_nopage,};static intsg_mmap(struct file *filp, struct vm_area_struct *vma){	Sg_fd *sfp;	unsigned long req_sz, len, sa;	Sg_scatter_hold *rsv_schp;	int k;	struct scatterlist *sg;	if ((!filp) || (!vma) || (!(sfp = (Sg_fd *) filp->private_data)))		return -ENXIO;	req_sz = vma->vm_end - vma->vm_start;	SCSI_LOG_TIMEOUT(3, printk("sg_mmap starting, vm_start=%p, len=%d\n",				   (void *) vma->vm_start, (int) req_sz));	if (vma->vm_pgoff)		return -EINVAL;	/* want no offset */	rsv_schp = &sfp->reserve;	if (req_sz > rsv_schp->bufflen)		return -ENOMEM;	/* cannot map more than reserved buffer */	sa = vma->vm_start;	sg = rsv_schp->buffer;	for (k = 0; (k < rsv_schp->k_use_sg) && (sa < vma->vm_end);	     ++k, sg = sg_next(sg)) {		len = vma->vm_end - sa;		len = (len < sg->length) ? len : sg->length;		sa += len;	}	sfp->mmap_called = 1;	vma->vm_flags |= VM_RESERVED;	vma->vm_private_data = sfp;	vma->vm_ops = &sg_mmap_vm_ops;	return 0;}/* This function is a "bottom half" handler that is called by the * mid level when a command is completed (or has failed). */static voidsg_cmd_done(void *data, char *sense, int result, int resid){	Sg_request *srp = data;	Sg_device *sdp = NULL;	Sg_fd *sfp;	unsigned long iflags;	unsigned int ms;	if (NULL == srp) {		printk(KERN_ERR "sg_cmd_done: NULL request\n");		return;	}	sfp = srp->parentfp;	if (sfp)		sdp = sfp->parentdp;	if ((NULL == sdp) || sdp->detached) {		printk(KERN_INFO "sg_cmd_done: device detached\n");		return;	}	SCSI_LOG_TIMEOUT(4, printk("sg_cmd_done: %s, pack_id=%d, res=0x%x\n",		sdp->disk->disk_name, srp->header.pack_id, result));	srp->header.resid = resid;	ms = jiffies_to_msecs(jiffies);	srp->header.duration = (ms > srp->header.duration) ?				(ms - srp->header.duration) : 0;	if (0 != result) {		struct scsi_sense_hdr sshdr;		memcpy(srp->sense_b, sense, sizeof (srp->sense_b));		srp->header.status = 0xff & result;		srp->header.masked_status = status_byte(result);		srp->header.msg_status = msg_byte(result);		srp->header.host_status = host_byte(result);		srp->header.driver_status = driver_byte(result);		if ((sdp->sgdebug > 0) &&		    ((CHECK_CONDITION == srp->header.masked_status) ||		     (COMMAND_TERMINATED == srp->header.masked_status)))			__scsi_print_sense("sg_cmd_done", sense,					   SCSI_SENSE_BUFFERSIZE);		/* Following if statement is a patch supplied by Eric Youngdale */		if (driver_byte(result) != 0		    && scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr)		    && !scsi_sense_is_deferred(&sshdr)		    && sshdr.sense_key == UNIT_ATTENTION		    && sdp->device->removable) {			/* Detected possible disc change. Set the bit - this */			/* may be used if there are filesystems using this device */			sdp->device->changed = 1;		}	}	/* Rely on write phase to clean out srp status values, so no "else" */	if (sfp->closed) {	/* whoops this fd already released, cleanup */		SCSI_LOG_TIMEOUT(1, printk("sg_cmd_done: already closed, freeing ...\n"));		sg_finish_rem_req(srp);		srp = NULL;		if (NULL == sfp->headrp) {			SCSI_LOG_TIMEOUT(1, printk("sg_cmd_done: already closed, final cleanup\n"));			if (0 == sg_remove_sfp(sdp, sfp)) {	/* device still present */				scsi_device_put(sdp->device);			}			sfp = NULL;		}	} else if (srp && srp->orphan) {		if (sfp->keep_orphan)			srp->sg_io_owned = 0;		else {			sg_finish_rem_req(srp);			srp = NULL;		}	}	if (sfp && srp) {		/* Now wake up any sg_read() that is waiting for this packet. */		kill_fasync(&sfp->async_qp, SIGPOLL, POLL_IN);		write_lock_irqsave(&sfp->rq_list_lock, iflags);		srp->done = 1;		wake_up_interruptible(&sfp->read_wait);		write_unlock_irqrestore(&sfp->rq_list_lock, iflags);	}}static struct file_operations sg_fops = {	.owner = THIS_MODULE,	.read = sg_read,	.write = sg_write,	.poll = sg_poll,	.ioctl = sg_ioctl,#ifdef CONFIG_COMPAT	.compat_ioctl = sg_compat_ioctl,#endif	.open = sg_open,	.mmap = sg_mmap,	.release = sg_release,	.fasync = sg_fasync,};static struct class *sg_sysfs_class;static int sg_sysfs_valid = 0;static Sg_device *sg_alloc(struct gendisk *disk, struct scsi_device *scsidp){	struct request_queue *q = scsidp->request_queue;	Sg_device *sdp;	unsigned long iflags;	int error;	u32 k;	sdp = kzalloc(sizeof(Sg_device), GFP_KERNEL);	if (!sdp) {		printk(KERN_WARNING "kmalloc Sg_device failure\n");		return ERR_PTR(-ENOMEM);	}	error = -ENOMEM;	if (!idr_pre_get(&sg_index_idr, GFP_KERNEL)) {		printk(KERN_WARNING "idr expansion Sg_device failure\n");		goto out;	}	write_lock_irqsave(&sg_index_lock, iflags);	error = idr_get_new(&sg_index_idr, sdp, &k);	write_unlock_irqrestore(&sg_index_lock, iflags);	if (error) {		printk(KERN_WARNING "idr allocation Sg_device failure: %d\n",		       error);		goto out;	}	if (unlikely(k >= SG_MAX_DEVS))		goto overflow;	SCSI_LOG_TIMEOUT(3, printk("sg_alloc: dev=%d \n", k));	sprintf(disk->disk_name, "sg%d", k);	disk->first_minor = k;	sdp->disk = disk;	sdp->device = scsidp;	init_waitqueue_head(&sdp->o_excl_wait);	sdp->sg_tablesize = min(q->max_hw_segments, q->max_phys_segments);	sdp->index = k;	error = 0; out:	if (error) {		kfree(sdp);		return ERR_PTR(error);	}	return sdp; overflow:	sdev_printk(KERN_WARNING, scsidp,		    "Unable to attach sg device type=%d, minor "		    "number exceeds %d\n", scsidp->type, SG_MAX_DEVS - 1);	error = -ENODEV;	goto out;}static intsg_add(struct class_device *cl_dev, struct class_interface *cl_intf){	struct scsi_device *scsidp = to_scsi_device(cl_dev->dev);	struct gendisk *disk;	Sg_device *sdp = NULL;	struct cdev * cdev = NULL;	int error;	unsigned long iflags;	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;	sdp = sg_alloc(disk, scsidp);	if (IS_ERR(sdp)) {		printk(KERN_WARNING "sg_alloc failed\n");

⌨️ 快捷键说明

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