📄 pktcdvd.c
字号:
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 + -