⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pktcdvd.c

📁 Linux块设备驱动源码
💻 C
📖 第 1 页 / 共 5 页
字号:
	mempool_free(psd, psd_pool);	pkt_bio_finished(pd);	return 0;}static int pkt_make_request(request_queue_t *q, struct bio *bio){	struct pktcdvd_device *pd;	char b[BDEVNAME_SIZE];	sector_t zone;	struct packet_data *pkt;	int was_empty, blocked_bio;	struct pkt_rb_node *node;	pd = q->queuedata;	if (!pd) {		printk("pktcdvd: %s incorrect request queue\n", bdevname(bio->bi_bdev, b));		goto end_io;	}	/*	 * Clone READ bios so we can have our own bi_end_io callback.	 */	if (bio_data_dir(bio) == READ) {		struct bio *cloned_bio = bio_clone(bio, GFP_NOIO);		struct packet_stacked_data *psd = mempool_alloc(psd_pool, GFP_NOIO);		psd->pd = pd;		psd->bio = bio;		cloned_bio->bi_bdev = pd->bdev;		cloned_bio->bi_private = psd;		cloned_bio->bi_end_io = pkt_end_io_read_cloned;		pd->stats.secs_r += bio->bi_size >> 9;		pkt_queue_bio(pd, cloned_bio);		return 0;	}	if (!test_bit(PACKET_WRITABLE, &pd->flags)) {		printk("pktcdvd: WRITE for ro device %s (%llu)\n",			pd->name, (unsigned long long)bio->bi_sector);		goto end_io;	}	if (!bio->bi_size || (bio->bi_size % CD_FRAMESIZE)) {		printk("pktcdvd: wrong bio size\n");		goto end_io;	}	blk_queue_bounce(q, &bio);	zone = ZONE(bio->bi_sector, pd);	VPRINTK("pkt_make_request: start = %6llx stop = %6llx\n",		(unsigned long long)bio->bi_sector,		(unsigned long long)(bio->bi_sector + bio_sectors(bio)));	/* Check if we have to split the bio */	{		struct bio_pair *bp;		sector_t last_zone;		int first_sectors;		last_zone = ZONE(bio->bi_sector + bio_sectors(bio) - 1, pd);		if (last_zone != zone) {			BUG_ON(last_zone != zone + pd->settings.size);			first_sectors = last_zone - bio->bi_sector;			bp = bio_split(bio, bio_split_pool, first_sectors);			BUG_ON(!bp);			pkt_make_request(q, &bp->bio1);			pkt_make_request(q, &bp->bio2);			bio_pair_release(bp);			return 0;		}	}	/*	 * If we find a matching packet in state WAITING or READ_WAIT, we can	 * just append this bio to that packet.	 */	spin_lock(&pd->cdrw.active_list_lock);	blocked_bio = 0;	list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {		if (pkt->sector == zone) {			spin_lock(&pkt->lock);			if ((pkt->state == PACKET_WAITING_STATE) ||			    (pkt->state == PACKET_READ_WAIT_STATE)) {				pkt_add_list_last(bio, &pkt->orig_bios,						  &pkt->orig_bios_tail);				pkt->write_size += bio->bi_size / CD_FRAMESIZE;				if ((pkt->write_size >= pkt->frames) &&				    (pkt->state == PACKET_WAITING_STATE)) {					atomic_inc(&pkt->run_sm);					wake_up(&pd->wqueue);				}				spin_unlock(&pkt->lock);				spin_unlock(&pd->cdrw.active_list_lock);				return 0;			} else {				blocked_bio = 1;			}			spin_unlock(&pkt->lock);		}	}	spin_unlock(&pd->cdrw.active_list_lock);	/*	 * No matching packet found. Store the bio in the work queue.	 */	node = mempool_alloc(pd->rb_pool, GFP_NOIO);	node->bio = bio;	spin_lock(&pd->lock);	BUG_ON(pd->bio_queue_size < 0);	was_empty = (pd->bio_queue_size == 0);	pkt_rbtree_insert(pd, node);	spin_unlock(&pd->lock);	/*	 * Wake up the worker thread.	 */	atomic_set(&pd->scan_queue, 1);	if (was_empty) {		/* This wake_up is required for correct operation */		wake_up(&pd->wqueue);	} else if (!list_empty(&pd->cdrw.pkt_free_list) && !blocked_bio) {		/*		 * This wake up is not required for correct operation,		 * but improves performance in some cases.		 */		wake_up(&pd->wqueue);	}	return 0;end_io:	bio_io_error(bio, bio->bi_size);	return 0;}static int pkt_merge_bvec(request_queue_t *q, struct bio *bio, struct bio_vec *bvec){	struct pktcdvd_device *pd = q->queuedata;	sector_t zone = ZONE(bio->bi_sector, pd);	int used = ((bio->bi_sector - zone) << 9) + bio->bi_size;	int remaining = (pd->settings.size << 9) - used;	int remaining2;	/*	 * A bio <= PAGE_SIZE must be allowed. If it crosses a packet	 * boundary, pkt_make_request() will split the bio.	 */	remaining2 = PAGE_SIZE - bio->bi_size;	remaining = max(remaining, remaining2);	BUG_ON(remaining < 0);	return remaining;}static void pkt_init_queue(struct pktcdvd_device *pd){	request_queue_t *q = pd->disk->queue;	blk_queue_make_request(q, pkt_make_request);	blk_queue_hardsect_size(q, CD_FRAMESIZE);	blk_queue_max_sectors(q, PACKET_MAX_SECTORS);	blk_queue_merge_bvec(q, pkt_merge_bvec);	q->queuedata = pd;}static int pkt_seq_show(struct seq_file *m, void *p){	struct pktcdvd_device *pd = m->private;	char *msg;	char bdev_buf[BDEVNAME_SIZE];	int states[PACKET_NUM_STATES];	seq_printf(m, "Writer %s mapped to %s:\n", pd->name,		   bdevname(pd->bdev, bdev_buf));	seq_printf(m, "\nSettings:\n");	seq_printf(m, "\tpacket size:\t\t%dkB\n", pd->settings.size / 2);	if (pd->settings.write_type == 0)		msg = "Packet";	else		msg = "Unknown";	seq_printf(m, "\twrite type:\t\t%s\n", msg);	seq_printf(m, "\tpacket type:\t\t%s\n", pd->settings.fp ? "Fixed" : "Variable");	seq_printf(m, "\tlink loss:\t\t%d\n", pd->settings.link_loss);	seq_printf(m, "\ttrack mode:\t\t%d\n", pd->settings.track_mode);	if (pd->settings.block_mode == PACKET_BLOCK_MODE1)		msg = "Mode 1";	else if (pd->settings.block_mode == PACKET_BLOCK_MODE2)		msg = "Mode 2";	else		msg = "Unknown";	seq_printf(m, "\tblock mode:\t\t%s\n", msg);	seq_printf(m, "\nStatistics:\n");	seq_printf(m, "\tpackets started:\t%lu\n", pd->stats.pkt_started);	seq_printf(m, "\tpackets ended:\t\t%lu\n", pd->stats.pkt_ended);	seq_printf(m, "\twritten:\t\t%lukB\n", pd->stats.secs_w >> 1);	seq_printf(m, "\tread gather:\t\t%lukB\n", pd->stats.secs_rg >> 1);	seq_printf(m, "\tread:\t\t\t%lukB\n", pd->stats.secs_r >> 1);	seq_printf(m, "\nMisc:\n");	seq_printf(m, "\treference count:\t%d\n", pd->refcnt);	seq_printf(m, "\tflags:\t\t\t0x%lx\n", pd->flags);	seq_printf(m, "\tread speed:\t\t%ukB/s\n", pd->read_speed);	seq_printf(m, "\twrite speed:\t\t%ukB/s\n", pd->write_speed);	seq_printf(m, "\tstart offset:\t\t%lu\n", pd->offset);	seq_printf(m, "\tmode page offset:\t%u\n", pd->mode_offset);	seq_printf(m, "\nQueue state:\n");	seq_printf(m, "\tbios queued:\t\t%d\n", pd->bio_queue_size);	seq_printf(m, "\tbios pending:\t\t%d\n", atomic_read(&pd->cdrw.pending_bios));	seq_printf(m, "\tcurrent sector:\t\t0x%llx\n", (unsigned long long)pd->current_sector);	pkt_count_states(pd, states);	seq_printf(m, "\tstate:\t\t\ti:%d ow:%d rw:%d ww:%d rec:%d fin:%d\n",		   states[0], states[1], states[2], states[3], states[4], states[5]);	return 0;}static int pkt_seq_open(struct inode *inode, struct file *file){	return single_open(file, pkt_seq_show, PDE(inode)->data);}static struct file_operations pkt_proc_fops = {	.open	= pkt_seq_open,	.read	= seq_read,	.llseek	= seq_lseek,	.release = single_release};static int pkt_new_dev(struct pktcdvd_device *pd, dev_t dev){	int i;	int ret = 0;	char b[BDEVNAME_SIZE];	struct proc_dir_entry *proc;	struct block_device *bdev;	if (pd->pkt_dev == dev) {		printk("pktcdvd: Recursive setup not allowed\n");		return -EBUSY;	}	for (i = 0; i < MAX_WRITERS; i++) {		struct pktcdvd_device *pd2 = pkt_devs[i];		if (!pd2)			continue;		if (pd2->bdev->bd_dev == dev) {			printk("pktcdvd: %s already setup\n", bdevname(pd2->bdev, b));			return -EBUSY;		}		if (pd2->pkt_dev == dev) {			printk("pktcdvd: Can't chain pktcdvd devices\n");			return -EBUSY;		}	}	bdev = bdget(dev);	if (!bdev)		return -ENOMEM;	ret = blkdev_get(bdev, FMODE_READ, O_RDONLY | O_NONBLOCK);	if (ret)		return ret;	/* This is safe, since we have a reference from open(). */	__module_get(THIS_MODULE);	if (!pkt_grow_pktlist(pd, CONFIG_CDROM_PKTCDVD_BUFFERS)) {		printk("pktcdvd: not enough memory for buffers\n");		ret = -ENOMEM;		goto out_mem;	}	pd->bdev = bdev;	set_blocksize(bdev, CD_FRAMESIZE);	pkt_init_queue(pd);	atomic_set(&pd->cdrw.pending_bios, 0);	pd->cdrw.thread = kthread_run(kcdrwd, pd, "%s", pd->name);	if (IS_ERR(pd->cdrw.thread)) {		printk("pktcdvd: can't start kernel thread\n");		ret = -ENOMEM;		goto out_thread;	}	proc = create_proc_entry(pd->name, 0, pkt_proc);	if (proc) {		proc->data = pd;		proc->proc_fops = &pkt_proc_fops;	}	DPRINTK("pktcdvd: writer %s mapped to %s\n", pd->name, bdevname(bdev, b));	return 0;out_thread:	pkt_shrink_pktlist(pd);out_mem:	blkdev_put(bdev);	/* This is safe: open() is still holding a reference. */	module_put(THIS_MODULE);	return ret;}static int pkt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){	struct pktcdvd_device *pd = inode->i_bdev->bd_disk->private_data;	VPRINTK("pkt_ioctl: cmd %x, dev %d:%d\n", cmd, imajor(inode), iminor(inode));	switch (cmd) {	/*	 * forward selected CDROM ioctls to CD-ROM, for UDF	 */	case CDROMMULTISESSION:	case CDROMREADTOCENTRY:	case CDROM_LAST_WRITTEN:	case CDROM_SEND_PACKET:	case SCSI_IOCTL_SEND_COMMAND:		return blkdev_ioctl(pd->bdev->bd_inode, file, cmd, arg);	case CDROMEJECT:		/*		 * The door gets locked when the device is opened, so we		 * have to unlock it or else the eject command fails.		 */		pkt_lock_door(pd, 0);		return blkdev_ioctl(pd->bdev->bd_inode, file, cmd, arg);	default:		printk("pktcdvd: Unknown ioctl for %s (%x)\n", pd->name, cmd);		return -ENOTTY;	}	return 0;}static int pkt_media_changed(struct gendisk *disk){	struct pktcdvd_device *pd = disk->private_data;	struct gendisk *attached_disk;	if (!pd)		return 0;	if (!pd->bdev)		return 0;	attached_disk = pd->bdev->bd_disk;	if (!attached_disk)		return 0;	return attached_disk->fops->media_changed(attached_disk);}static struct block_device_operations pktcdvd_ops = {	.owner =		THIS_MODULE,	.open =			pkt_open,	.release =		pkt_close,	.ioctl =		pkt_ioctl,	.media_changed =	pkt_media_changed,};/* * Set up mapping from pktcdvd device to CD-ROM device. */static int pkt_setup_dev(struct pkt_ctrl_command *ctrl_cmd){	int idx;	int ret = -ENOMEM;	struct pktcdvd_device *pd;	struct gendisk *disk;	dev_t dev = new_decode_dev(ctrl_cmd->dev);	for (idx = 0; idx < MAX_WRITERS; idx++)		if (!pkt_devs[idx])			break;	if (idx == MAX_WRITERS) {		printk("pktcdvd: max %d writers supported\n", MAX_WRITERS);		return -EBUSY;	}	pd = kzalloc(sizeof(struct pktcdvd_device), GFP_KERNEL);	if (!pd)		return ret;	pd->rb_pool = mempool_create(PKT_RB_POOL_SIZE, pkt_rb_alloc, pkt_rb_free, NULL);	if (!pd->rb_pool)		goto out_mem;	disk = alloc_disk(1);	if (!disk)		goto out_mem;	pd->disk = disk;	spin_lock_init(&pd->lock);	spin_lock_init(&pd->iosched.lock);	sprintf(pd->name, "pktcdvd%d", idx);	init_waitqueue_head(&pd->wqueue);	pd->bio_queue = RB_ROOT;	disk->major = pkt_major;	disk->first_minor = idx;	disk->fops = &pktcdvd_ops;	disk->flags = GENHD_FL_REMOVABLE;	sprintf(disk->disk_name, "pktcdvd%d", idx);	disk->private_data = pd;	disk->queue = blk_alloc_queue(GFP_KERNEL);	if (!disk->queue)		goto out_mem2;	pd->pkt_dev = MKDEV(disk->major, disk->first_minor);	ret = pkt_new_dev(pd, dev);	if (ret)		goto out_new_dev;	add_disk(disk);	pkt_devs[idx] = pd;	ctrl_cmd->pkt_dev = new_encode_dev(pd->pkt_dev);	return 0;out_new_dev:	blk_put_queue(disk->queue);out_mem2:	put_disk(disk);out_mem:	if (pd->rb_pool)		mempool_destroy(pd->rb_pool);	kfree(pd);	return ret;}/* * Tear down mapping from pktcdvd device to CD-ROM device. */static int pkt_remove_dev(struct pkt_ctrl_command *ctrl_cmd){	struct pktcdvd_device *pd;	int idx;	dev_t pkt_dev = new_decode_dev(ctrl_cmd->pkt_dev);	for (idx = 0; idx < MAX_WRITERS; idx++) {		pd = pkt_devs[idx];		if (pd && (pd->pkt_dev == pkt_dev))			break;	}	if (idx == MAX_WRITERS) {		DPRINTK("pktcdvd: dev not setup\n");		return -ENXIO;	}	if (pd->refcnt > 0)		return -EBUSY;	if (!IS_ERR(pd->cdrw.thread))		kthread_stop(pd->cdrw.thread);	blkdev_put(pd->bdev);	pkt_shrink_pktlist(pd);	remove_proc_entry(pd->name, pkt_proc);	DPRINTK("pktcdvd: writer %s unmapped\n", pd->name);	del_gendisk(pd->disk);	blk_put_queue(pd->disk->queue);	put_disk(pd->disk);	pkt_devs[idx] = NULL;	mempool_destroy(pd->rb_pool);	kfree(pd);	/* This is safe: open() is still holding a reference. */	module_put(THIS_MODULE);	return 0;}static void pkt_get_status(struct pkt_ctrl_command *ctrl_cmd){	struct pktcdvd_device *pd = pkt_find_dev_from_minor(ctrl_cmd->dev_index);	if (pd) {		ctrl_cmd->dev = new_encode_dev(pd->bdev->bd_dev);		ctrl_cmd->pkt_dev = new_encode_dev(pd->pkt_dev);	} else {		ctrl_cmd->dev = 0;		ctrl_cmd->pkt_dev = 0;	}	ctrl_cmd->num_devices = MAX_WRITERS;}static int pkt_ctl_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){	void __user *argp = (void __user *)arg;	struct pkt_ctrl_command ctrl_cmd;	int ret = 0;	if (cmd != PACKET_CTRL_CMD)		return -ENOTTY;	if (copy_from_user(&ctrl_cmd, argp, sizeof(struct pkt_ctrl_command)))		return -EFAULT;	switch (ctrl_cmd.command) {	case PKT_CTRL_CMD_SETUP:		if (!capable(CAP_SYS_ADMIN))			return -EPERM;		down(&ctl_mutex);		ret = pkt_setup_dev(&ctrl_cmd);		up(&ctl_mutex);		break;	case PKT_CTRL_CMD_TEARDOWN:		if (!capable(CAP_SYS_ADMIN))			return -EPERM;		down(&ctl_mutex);		ret = pkt_remove_dev(&ctrl_cmd);		up(&ctl_mutex);		break;	case PKT_CTRL_CMD_STATUS:	

⌨️ 快捷键说明

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