Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

floppy: use a separate gendisk for each media format

The floppy driver usually autodetects the media when used with the
normal /dev/fd? devices, which also are the only nodes created by udev.
But it also supports various aliases that force a given media format.
That is currently supported using the blk_register_region framework
which finds the floppy gendisk even for a 'mismatched' dev_t. The
problem with this (besides the code complexity) is that it creates
multiple struct block_device instances for the whole device of a
single gendisk, which can lead to interesting issues in code not
aware of that fact.

To fix this just create a separate gendisk for each of the aliases
if they are accessed.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Jens Axboe <axboe@kernel.dk>

authored by

Christoph Hellwig and committed by
Jens Axboe
302cfee1 bbc26e8d

+97 -57
+97 -57
drivers/block/floppy.c
··· 402 402 static struct floppy_drive_struct drive_state[N_DRIVE]; 403 403 static struct floppy_write_errors write_errors[N_DRIVE]; 404 404 static struct timer_list motor_off_timer[N_DRIVE]; 405 - static struct gendisk *disks[N_DRIVE]; 406 405 static struct blk_mq_tag_set tag_sets[N_DRIVE]; 407 406 static struct block_device *opened_bdev[N_DRIVE]; 408 407 static DEFINE_MUTEX(open_lock); ··· 475 476 { 1600,10,2,80,0,0x25,0x02,0xDF,0x2E,"D800" }, /* 30 800KB 3.5" */ 476 477 { 3200,20,2,80,0,0x1C,0x00,0xCF,0x2C,"H1600" }, /* 31 1.6MB 3.5" */ 477 478 }; 479 + 480 + static struct gendisk *disks[N_DRIVE][ARRAY_SIZE(floppy_type)]; 478 481 479 482 #define SECTSIZE (_FD_SECTSIZE(*floppy)) 480 483 ··· 4112 4111 4113 4112 new_dev = MINOR(bdev->bd_dev); 4114 4113 drive_state[drive].fd_device = new_dev; 4115 - set_capacity(disks[drive], floppy_sizes[new_dev]); 4114 + set_capacity(disks[drive][ITYPE(new_dev)], floppy_sizes[new_dev]); 4116 4115 if (old_dev != -1 && old_dev != new_dev) { 4117 4116 if (buffer_drive == drive) 4118 4117 buffer_track = -1; ··· 4580 4579 return true; 4581 4580 } 4582 4581 4583 - static struct kobject *floppy_find(dev_t dev, int *part, void *data) 4582 + static int floppy_alloc_disk(unsigned int drive, unsigned int type) 4584 4583 { 4585 - int drive = (*part & 3) | ((*part & 0x80) >> 5); 4586 - if (drive >= N_DRIVE || !floppy_available(drive)) 4587 - return NULL; 4588 - if (((*part >> 2) & 0x1f) >= ARRAY_SIZE(floppy_type)) 4589 - return NULL; 4590 - *part = 0; 4591 - return get_disk_and_module(disks[drive]); 4584 + struct gendisk *disk; 4585 + int err; 4586 + 4587 + disk = alloc_disk(1); 4588 + if (!disk) 4589 + return -ENOMEM; 4590 + 4591 + disk->queue = blk_mq_init_queue(&tag_sets[drive]); 4592 + if (IS_ERR(disk->queue)) { 4593 + err = PTR_ERR(disk->queue); 4594 + disk->queue = NULL; 4595 + put_disk(disk); 4596 + return err; 4597 + } 4598 + 4599 + blk_queue_bounce_limit(disk->queue, BLK_BOUNCE_HIGH); 4600 + blk_queue_max_hw_sectors(disk->queue, 64); 4601 + disk->major = FLOPPY_MAJOR; 4602 + disk->first_minor = TOMINOR(drive) | (type << 2); 4603 + disk->fops = &floppy_fops; 4604 + disk->events = DISK_EVENT_MEDIA_CHANGE; 4605 + if (type) 4606 + sprintf(disk->disk_name, "fd%d_type%d", drive, type); 4607 + else 4608 + sprintf(disk->disk_name, "fd%d", drive); 4609 + /* to be cleaned up... */ 4610 + disk->private_data = (void *)(long)drive; 4611 + disk->flags |= GENHD_FL_REMOVABLE; 4612 + 4613 + disks[drive][type] = disk; 4614 + return 0; 4615 + } 4616 + 4617 + static DEFINE_MUTEX(floppy_probe_lock); 4618 + 4619 + static void floppy_probe(dev_t dev) 4620 + { 4621 + unsigned int drive = (MINOR(dev) & 3) | ((MINOR(dev) & 0x80) >> 5); 4622 + unsigned int type = (MINOR(dev) >> 2) & 0x1f; 4623 + 4624 + if (drive >= N_DRIVE || !floppy_available(drive) || 4625 + type >= ARRAY_SIZE(floppy_type)) 4626 + return; 4627 + 4628 + mutex_lock(&floppy_probe_lock); 4629 + if (!disks[drive][type]) { 4630 + if (floppy_alloc_disk(drive, type) == 0) 4631 + add_disk(disks[drive][type]); 4632 + } 4633 + mutex_unlock(&floppy_probe_lock); 4592 4634 } 4593 4635 4594 4636 static int __init do_floppy_init(void) ··· 4653 4609 return -ENOMEM; 4654 4610 4655 4611 for (drive = 0; drive < N_DRIVE; drive++) { 4656 - disks[drive] = alloc_disk(1); 4657 - if (!disks[drive]) { 4658 - err = -ENOMEM; 4612 + memset(&tag_sets[drive], 0, sizeof(tag_sets[drive])); 4613 + tag_sets[drive].ops = &floppy_mq_ops; 4614 + tag_sets[drive].nr_hw_queues = 1; 4615 + tag_sets[drive].nr_maps = 1; 4616 + tag_sets[drive].queue_depth = 2; 4617 + tag_sets[drive].numa_node = NUMA_NO_NODE; 4618 + tag_sets[drive].flags = BLK_MQ_F_SHOULD_MERGE; 4619 + err = blk_mq_alloc_tag_set(&tag_sets[drive]); 4620 + if (err) 4659 4621 goto out_put_disk; 4660 - } 4661 4622 4662 - disks[drive]->queue = blk_mq_init_sq_queue(&tag_sets[drive], 4663 - &floppy_mq_ops, 2, 4664 - BLK_MQ_F_SHOULD_MERGE); 4665 - if (IS_ERR(disks[drive]->queue)) { 4666 - err = PTR_ERR(disks[drive]->queue); 4667 - disks[drive]->queue = NULL; 4623 + err = floppy_alloc_disk(drive, 0); 4624 + if (err) 4668 4625 goto out_put_disk; 4669 - } 4670 - 4671 - blk_queue_bounce_limit(disks[drive]->queue, BLK_BOUNCE_HIGH); 4672 - blk_queue_max_hw_sectors(disks[drive]->queue, 64); 4673 - disks[drive]->major = FLOPPY_MAJOR; 4674 - disks[drive]->first_minor = TOMINOR(drive); 4675 - disks[drive]->fops = &floppy_fops; 4676 - disks[drive]->events = DISK_EVENT_MEDIA_CHANGE; 4677 - sprintf(disks[drive]->disk_name, "fd%d", drive); 4678 4626 4679 4627 timer_setup(&motor_off_timer[drive], motor_off_callback, 0); 4680 4628 } 4681 4629 4682 - err = register_blkdev(FLOPPY_MAJOR, "fd"); 4630 + err = __register_blkdev(FLOPPY_MAJOR, "fd", floppy_probe); 4683 4631 if (err) 4684 4632 goto out_put_disk; 4685 4633 4686 4634 err = platform_driver_register(&floppy_driver); 4687 4635 if (err) 4688 4636 goto out_unreg_blkdev; 4689 - 4690 - blk_register_region(MKDEV(FLOPPY_MAJOR, 0), 256, THIS_MODULE, 4691 - floppy_find, NULL, NULL); 4692 4637 4693 4638 for (i = 0; i < 256; i++) 4694 4639 if (ITYPE(i)) ··· 4706 4673 if (fdc_state[0].address == -1) { 4707 4674 cancel_delayed_work(&fd_timeout); 4708 4675 err = -ENODEV; 4709 - goto out_unreg_region; 4676 + goto out_unreg_driver; 4710 4677 } 4711 4678 #if N_FDC > 1 4712 4679 fdc_state[1].address = FDC2; ··· 4717 4684 if (err) { 4718 4685 cancel_delayed_work(&fd_timeout); 4719 4686 err = -EBUSY; 4720 - goto out_unreg_region; 4687 + goto out_unreg_driver; 4721 4688 } 4722 4689 4723 4690 /* initialise drive state */ ··· 4794 4761 if (err) 4795 4762 goto out_remove_drives; 4796 4763 4797 - /* to be cleaned up... */ 4798 - disks[drive]->private_data = (void *)(long)drive; 4799 - disks[drive]->flags |= GENHD_FL_REMOVABLE; 4800 - device_add_disk(&floppy_device[drive].dev, disks[drive], NULL); 4764 + device_add_disk(&floppy_device[drive].dev, disks[drive][0], 4765 + NULL); 4801 4766 } 4802 4767 4803 4768 return 0; ··· 4803 4772 out_remove_drives: 4804 4773 while (drive--) { 4805 4774 if (floppy_available(drive)) { 4806 - del_gendisk(disks[drive]); 4775 + del_gendisk(disks[drive][0]); 4807 4776 platform_device_unregister(&floppy_device[drive]); 4808 4777 } 4809 4778 } 4810 4779 out_release_dma: 4811 4780 if (atomic_read(&usage_count)) 4812 4781 floppy_release_irq_and_dma(); 4813 - out_unreg_region: 4814 - blk_unregister_region(MKDEV(FLOPPY_MAJOR, 0), 256); 4782 + out_unreg_driver: 4815 4783 platform_driver_unregister(&floppy_driver); 4816 4784 out_unreg_blkdev: 4817 4785 unregister_blkdev(FLOPPY_MAJOR, "fd"); 4818 4786 out_put_disk: 4819 4787 destroy_workqueue(floppy_wq); 4820 4788 for (drive = 0; drive < N_DRIVE; drive++) { 4821 - if (!disks[drive]) 4789 + if (!disks[drive][0]) 4822 4790 break; 4823 - if (disks[drive]->queue) { 4824 - del_timer_sync(&motor_off_timer[drive]); 4825 - blk_cleanup_queue(disks[drive]->queue); 4826 - disks[drive]->queue = NULL; 4827 - blk_mq_free_tag_set(&tag_sets[drive]); 4828 - } 4829 - put_disk(disks[drive]); 4791 + del_timer_sync(&motor_off_timer[drive]); 4792 + blk_cleanup_queue(disks[drive][0]->queue); 4793 + disks[drive][0]->queue = NULL; 4794 + blk_mq_free_tag_set(&tag_sets[drive]); 4795 + put_disk(disks[drive][0]); 4830 4796 } 4831 4797 return err; 4832 4798 } ··· 5034 5006 5035 5007 static void __exit floppy_module_exit(void) 5036 5008 { 5037 - int drive; 5009 + int drive, i; 5038 5010 5039 - blk_unregister_region(MKDEV(FLOPPY_MAJOR, 0), 256); 5040 5011 unregister_blkdev(FLOPPY_MAJOR, "fd"); 5041 5012 platform_driver_unregister(&floppy_driver); 5042 5013 ··· 5045 5018 del_timer_sync(&motor_off_timer[drive]); 5046 5019 5047 5020 if (floppy_available(drive)) { 5048 - del_gendisk(disks[drive]); 5021 + for (i = 0; i < ARRAY_SIZE(floppy_type); i++) { 5022 + if (disks[drive][i]) 5023 + del_gendisk(disks[drive][i]); 5024 + } 5049 5025 platform_device_unregister(&floppy_device[drive]); 5050 5026 } 5051 - blk_cleanup_queue(disks[drive]->queue); 5027 + for (i = 0; i < ARRAY_SIZE(floppy_type); i++) { 5028 + if (disks[drive][i]) 5029 + blk_cleanup_queue(disks[drive][i]->queue); 5030 + } 5052 5031 blk_mq_free_tag_set(&tag_sets[drive]); 5053 5032 5054 5033 /* ··· 5062 5029 * queue reference in put_disk(). 5063 5030 */ 5064 5031 if (!(allowed_drive_mask & (1 << drive)) || 5065 - fdc_state[FDC(drive)].version == FDC_NONE) 5066 - disks[drive]->queue = NULL; 5032 + fdc_state[FDC(drive)].version == FDC_NONE) { 5033 + for (i = 0; i < ARRAY_SIZE(floppy_type); i++) { 5034 + if (disks[drive][i]) 5035 + disks[drive][i]->queue = NULL; 5036 + } 5037 + } 5067 5038 5068 - put_disk(disks[drive]); 5039 + for (i = 0; i < ARRAY_SIZE(floppy_type); i++) { 5040 + if (disks[drive][i]) 5041 + put_disk(disks[drive][i]); 5042 + } 5069 5043 } 5070 5044 5071 5045 cancel_delayed_work_sync(&fd_timeout);