diff options
Diffstat (limited to 'drivers/block/pktcdvd.c')
-rw-r--r-- | drivers/block/pktcdvd.c | 85 |
1 files changed, 47 insertions, 38 deletions
diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c index 7b838342f0a3..7e22a58926b8 100644 --- a/drivers/block/pktcdvd.c +++ b/drivers/block/pktcdvd.c | |||
@@ -5,29 +5,41 @@ | |||
5 | * May be copied or modified under the terms of the GNU General Public | 5 | * May be copied or modified under the terms of the GNU General Public |
6 | * License. See linux/COPYING for more information. | 6 | * License. See linux/COPYING for more information. |
7 | * | 7 | * |
8 | * Packet writing layer for ATAPI and SCSI CD-R, CD-RW, DVD-R, and | 8 | * Packet writing layer for ATAPI and SCSI CD-RW, DVD+RW, DVD-RW and |
9 | * DVD-RW devices (aka an exercise in block layer masturbation) | 9 | * DVD-RAM devices. |
10 | * | 10 | * |
11 | * Theory of operation: | ||
11 | * | 12 | * |
12 | * TODO: (circa order of when I will fix it) | 13 | * At the lowest level, there is the standard driver for the CD/DVD device, |
13 | * - Only able to write on CD-RW media right now. | 14 | * typically ide-cd.c or sr.c. This driver can handle read and write requests, |
14 | * - check host application code on media and set it in write page | 15 | * but it doesn't know anything about the special restrictions that apply to |
15 | * - interface for UDF <-> packet to negotiate a new location when a write | 16 | * packet writing. One restriction is that write requests must be aligned to |
16 | * fails. | 17 | * packet boundaries on the physical media, and the size of a write request |
17 | * - handle OPC, especially for -RW media | 18 | * must be equal to the packet size. Another restriction is that a |
19 | * GPCMD_FLUSH_CACHE command has to be issued to the drive before a read | ||
20 | * command, if the previous command was a write. | ||
18 | * | 21 | * |
19 | * Theory of operation: | 22 | * The purpose of the packet writing driver is to hide these restrictions from |
23 | * higher layers, such as file systems, and present a block device that can be | ||
24 | * randomly read and written using 2kB-sized blocks. | ||
25 | * | ||
26 | * The lowest layer in the packet writing driver is the packet I/O scheduler. | ||
27 | * Its data is defined by the struct packet_iosched and includes two bio | ||
28 | * queues with pending read and write requests. These queues are processed | ||
29 | * by the pkt_iosched_process_queue() function. The write requests in this | ||
30 | * queue are already properly aligned and sized. This layer is responsible for | ||
31 | * issuing the flush cache commands and scheduling the I/O in a good order. | ||
20 | * | 32 | * |
21 | * We use a custom make_request_fn function that forwards reads directly to | 33 | * The next layer transforms unaligned write requests to aligned writes. This |
22 | * the underlying CD device. Write requests are either attached directly to | 34 | * transformation requires reading missing pieces of data from the underlying |
23 | * a live packet_data object, or simply stored sequentially in a list for | 35 | * block device, assembling the pieces to full packets and queuing them to the |
24 | * later processing by the kcdrwd kernel thread. This driver doesn't use | 36 | * packet I/O scheduler. |
25 | * any elevator functionally as defined by the elevator_s struct, but the | ||
26 | * underlying CD device uses a standard elevator. | ||
27 | * | 37 | * |
28 | * This strategy makes it possible to do very late merging of IO requests. | 38 | * At the top layer there is a custom make_request_fn function that forwards |
29 | * A new bio sent to pkt_make_request can be merged with a live packet_data | 39 | * read requests directly to the iosched queue and puts write requests in the |
30 | * object even if the object is in the data gathering state. | 40 | * unaligned write queue. A kernel thread performs the necessary read |
41 | * gathering to convert the unaligned writes to aligned writes and then feeds | ||
42 | * them to the packet I/O scheduler. | ||
31 | * | 43 | * |
32 | *************************************************************************/ | 44 | *************************************************************************/ |
33 | 45 | ||
@@ -100,10 +112,9 @@ static struct bio *pkt_bio_alloc(int nr_iovecs) | |||
100 | goto no_bio; | 112 | goto no_bio; |
101 | bio_init(bio); | 113 | bio_init(bio); |
102 | 114 | ||
103 | bvl = kmalloc(nr_iovecs * sizeof(struct bio_vec), GFP_KERNEL); | 115 | bvl = kcalloc(nr_iovecs, sizeof(struct bio_vec), GFP_KERNEL); |
104 | if (!bvl) | 116 | if (!bvl) |
105 | goto no_bvl; | 117 | goto no_bvl; |
106 | memset(bvl, 0, nr_iovecs * sizeof(struct bio_vec)); | ||
107 | 118 | ||
108 | bio->bi_max_vecs = nr_iovecs; | 119 | bio->bi_max_vecs = nr_iovecs; |
109 | bio->bi_io_vec = bvl; | 120 | bio->bi_io_vec = bvl; |
@@ -125,10 +136,9 @@ static struct packet_data *pkt_alloc_packet_data(void) | |||
125 | int i; | 136 | int i; |
126 | struct packet_data *pkt; | 137 | struct packet_data *pkt; |
127 | 138 | ||
128 | pkt = kmalloc(sizeof(struct packet_data), GFP_KERNEL); | 139 | pkt = kzalloc(sizeof(struct packet_data), GFP_KERNEL); |
129 | if (!pkt) | 140 | if (!pkt) |
130 | goto no_pkt; | 141 | goto no_pkt; |
131 | memset(pkt, 0, sizeof(struct packet_data)); | ||
132 | 142 | ||
133 | pkt->w_bio = pkt_bio_alloc(PACKET_MAX_SIZE); | 143 | pkt->w_bio = pkt_bio_alloc(PACKET_MAX_SIZE); |
134 | if (!pkt->w_bio) | 144 | if (!pkt->w_bio) |
@@ -659,7 +669,6 @@ static void pkt_make_local_copy(struct packet_data *pkt, struct page **pages, in | |||
659 | } | 669 | } |
660 | offs += CD_FRAMESIZE; | 670 | offs += CD_FRAMESIZE; |
661 | if (offs >= PAGE_SIZE) { | 671 | if (offs >= PAGE_SIZE) { |
662 | BUG_ON(offs > PAGE_SIZE); | ||
663 | offs = 0; | 672 | offs = 0; |
664 | p++; | 673 | p++; |
665 | } | 674 | } |
@@ -724,12 +733,6 @@ static void pkt_gather_data(struct pktcdvd_device *pd, struct packet_data *pkt) | |||
724 | atomic_set(&pkt->io_wait, 0); | 733 | atomic_set(&pkt->io_wait, 0); |
725 | atomic_set(&pkt->io_errors, 0); | 734 | atomic_set(&pkt->io_errors, 0); |
726 | 735 | ||
727 | if (pkt->cache_valid) { | ||
728 | VPRINTK("pkt_gather_data: zone %llx cached\n", | ||
729 | (unsigned long long)pkt->sector); | ||
730 | goto out_account; | ||
731 | } | ||
732 | |||
733 | /* | 736 | /* |
734 | * Figure out which frames we need to read before we can write. | 737 | * Figure out which frames we need to read before we can write. |
735 | */ | 738 | */ |
@@ -738,6 +741,7 @@ static void pkt_gather_data(struct pktcdvd_device *pd, struct packet_data *pkt) | |||
738 | for (bio = pkt->orig_bios; bio; bio = bio->bi_next) { | 741 | for (bio = pkt->orig_bios; bio; bio = bio->bi_next) { |
739 | int first_frame = (bio->bi_sector - pkt->sector) / (CD_FRAMESIZE >> 9); | 742 | int first_frame = (bio->bi_sector - pkt->sector) / (CD_FRAMESIZE >> 9); |
740 | int num_frames = bio->bi_size / CD_FRAMESIZE; | 743 | int num_frames = bio->bi_size / CD_FRAMESIZE; |
744 | pd->stats.secs_w += num_frames * (CD_FRAMESIZE >> 9); | ||
741 | BUG_ON(first_frame < 0); | 745 | BUG_ON(first_frame < 0); |
742 | BUG_ON(first_frame + num_frames > pkt->frames); | 746 | BUG_ON(first_frame + num_frames > pkt->frames); |
743 | for (f = first_frame; f < first_frame + num_frames; f++) | 747 | for (f = first_frame; f < first_frame + num_frames; f++) |
@@ -745,6 +749,12 @@ static void pkt_gather_data(struct pktcdvd_device *pd, struct packet_data *pkt) | |||
745 | } | 749 | } |
746 | spin_unlock(&pkt->lock); | 750 | spin_unlock(&pkt->lock); |
747 | 751 | ||
752 | if (pkt->cache_valid) { | ||
753 | VPRINTK("pkt_gather_data: zone %llx cached\n", | ||
754 | (unsigned long long)pkt->sector); | ||
755 | goto out_account; | ||
756 | } | ||
757 | |||
748 | /* | 758 | /* |
749 | * Schedule reads for missing parts of the packet. | 759 | * Schedule reads for missing parts of the packet. |
750 | */ | 760 | */ |
@@ -778,7 +788,6 @@ out_account: | |||
778 | frames_read, (unsigned long long)pkt->sector); | 788 | frames_read, (unsigned long long)pkt->sector); |
779 | pd->stats.pkt_started++; | 789 | pd->stats.pkt_started++; |
780 | pd->stats.secs_rg += frames_read * (CD_FRAMESIZE >> 9); | 790 | pd->stats.secs_rg += frames_read * (CD_FRAMESIZE >> 9); |
781 | pd->stats.secs_w += pd->settings.size; | ||
782 | } | 791 | } |
783 | 792 | ||
784 | /* | 793 | /* |
@@ -794,10 +803,11 @@ static struct packet_data *pkt_get_packet_data(struct pktcdvd_device *pd, int zo | |||
794 | list_del_init(&pkt->list); | 803 | list_del_init(&pkt->list); |
795 | if (pkt->sector != zone) | 804 | if (pkt->sector != zone) |
796 | pkt->cache_valid = 0; | 805 | pkt->cache_valid = 0; |
797 | break; | 806 | return pkt; |
798 | } | 807 | } |
799 | } | 808 | } |
800 | return pkt; | 809 | BUG(); |
810 | return NULL; | ||
801 | } | 811 | } |
802 | 812 | ||
803 | static void pkt_put_packet_data(struct pktcdvd_device *pd, struct packet_data *pkt) | 813 | static void pkt_put_packet_data(struct pktcdvd_device *pd, struct packet_data *pkt) |
@@ -941,12 +951,10 @@ try_next_bio: | |||
941 | } | 951 | } |
942 | 952 | ||
943 | pkt = pkt_get_packet_data(pd, zone); | 953 | pkt = pkt_get_packet_data(pd, zone); |
944 | BUG_ON(!pkt); | ||
945 | 954 | ||
946 | pd->current_sector = zone + pd->settings.size; | 955 | pd->current_sector = zone + pd->settings.size; |
947 | pkt->sector = zone; | 956 | pkt->sector = zone; |
948 | pkt->frames = pd->settings.size >> 2; | 957 | pkt->frames = pd->settings.size >> 2; |
949 | BUG_ON(pkt->frames > PACKET_MAX_SIZE); | ||
950 | pkt->write_size = 0; | 958 | pkt->write_size = 0; |
951 | 959 | ||
952 | /* | 960 | /* |
@@ -1636,6 +1644,10 @@ static int pkt_probe_settings(struct pktcdvd_device *pd) | |||
1636 | printk("pktcdvd: detected zero packet size!\n"); | 1644 | printk("pktcdvd: detected zero packet size!\n"); |
1637 | pd->settings.size = 128; | 1645 | pd->settings.size = 128; |
1638 | } | 1646 | } |
1647 | if (pd->settings.size > PACKET_MAX_SECTORS) { | ||
1648 | printk("pktcdvd: packet size is too big\n"); | ||
1649 | return -ENXIO; | ||
1650 | } | ||
1639 | pd->settings.fp = ti.fp; | 1651 | pd->settings.fp = ti.fp; |
1640 | pd->offset = (be32_to_cpu(ti.track_start) << 2) & (pd->settings.size - 1); | 1652 | pd->offset = (be32_to_cpu(ti.track_start) << 2) & (pd->settings.size - 1); |
1641 | 1653 | ||
@@ -2198,7 +2210,6 @@ static int pkt_make_request(request_queue_t *q, struct bio *bio) | |||
2198 | * No matching packet found. Store the bio in the work queue. | 2210 | * No matching packet found. Store the bio in the work queue. |
2199 | */ | 2211 | */ |
2200 | node = mempool_alloc(pd->rb_pool, GFP_NOIO); | 2212 | node = mempool_alloc(pd->rb_pool, GFP_NOIO); |
2201 | BUG_ON(!node); | ||
2202 | node->bio = bio; | 2213 | node->bio = bio; |
2203 | spin_lock(&pd->lock); | 2214 | spin_lock(&pd->lock); |
2204 | BUG_ON(pd->bio_queue_size < 0); | 2215 | BUG_ON(pd->bio_queue_size < 0); |
@@ -2406,7 +2417,6 @@ static int pkt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, u | |||
2406 | struct pktcdvd_device *pd = inode->i_bdev->bd_disk->private_data; | 2417 | struct pktcdvd_device *pd = inode->i_bdev->bd_disk->private_data; |
2407 | 2418 | ||
2408 | VPRINTK("pkt_ioctl: cmd %x, dev %d:%d\n", cmd, imajor(inode), iminor(inode)); | 2419 | VPRINTK("pkt_ioctl: cmd %x, dev %d:%d\n", cmd, imajor(inode), iminor(inode)); |
2409 | BUG_ON(!pd); | ||
2410 | 2420 | ||
2411 | switch (cmd) { | 2421 | switch (cmd) { |
2412 | /* | 2422 | /* |
@@ -2477,10 +2487,9 @@ static int pkt_setup_dev(struct pkt_ctrl_command *ctrl_cmd) | |||
2477 | return -EBUSY; | 2487 | return -EBUSY; |
2478 | } | 2488 | } |
2479 | 2489 | ||
2480 | pd = kmalloc(sizeof(struct pktcdvd_device), GFP_KERNEL); | 2490 | pd = kzalloc(sizeof(struct pktcdvd_device), GFP_KERNEL); |
2481 | if (!pd) | 2491 | if (!pd) |
2482 | return ret; | 2492 | return ret; |
2483 | memset(pd, 0, sizeof(struct pktcdvd_device)); | ||
2484 | 2493 | ||
2485 | pd->rb_pool = mempool_create(PKT_RB_POOL_SIZE, pkt_rb_alloc, pkt_rb_free, NULL); | 2494 | pd->rb_pool = mempool_create(PKT_RB_POOL_SIZE, pkt_rb_alloc, pkt_rb_free, NULL); |
2486 | if (!pd->rb_pool) | 2495 | if (!pd->rb_pool) |