[PATCH] fix kill_proc_info() vs fork() theoretical race
[safe/jmp/linux-2.6] / drivers / block / pktcdvd.c
index 7e22a58..93e44d0 100644 (file)
@@ -43,8 +43,6 @@
  *
  *************************************************************************/
 
-#define VERSION_CODE   "v0.2.0a 2004-07-14 Jens Axboe (axboe@suse.de) and petero2@telia.com"
-
 #include <linux/pktcdvd.h>
 #include <linux/config.h>
 #include <linux/module.h>
@@ -131,7 +129,7 @@ static struct bio *pkt_bio_alloc(int nr_iovecs)
 /*
  * Allocate a packet_data struct
  */
-static struct packet_data *pkt_alloc_packet_data(void)
+static struct packet_data *pkt_alloc_packet_data(int frames)
 {
        int i;
        struct packet_data *pkt;
@@ -140,11 +138,12 @@ static struct packet_data *pkt_alloc_packet_data(void)
        if (!pkt)
                goto no_pkt;
 
-       pkt->w_bio = pkt_bio_alloc(PACKET_MAX_SIZE);
+       pkt->frames = frames;
+       pkt->w_bio = pkt_bio_alloc(frames);
        if (!pkt->w_bio)
                goto no_bio;
 
-       for (i = 0; i < PAGES_PER_PACKET; i++) {
+       for (i = 0; i < frames / FRAMES_PER_PAGE; i++) {
                pkt->pages[i] = alloc_page(GFP_KERNEL|__GFP_ZERO);
                if (!pkt->pages[i])
                        goto no_page;
@@ -152,7 +151,7 @@ static struct packet_data *pkt_alloc_packet_data(void)
 
        spin_lock_init(&pkt->lock);
 
-       for (i = 0; i < PACKET_MAX_SIZE; i++) {
+       for (i = 0; i < frames; i++) {
                struct bio *bio = pkt_bio_alloc(1);
                if (!bio)
                        goto no_rd_bio;
@@ -162,14 +161,14 @@ static struct packet_data *pkt_alloc_packet_data(void)
        return pkt;
 
 no_rd_bio:
-       for (i = 0; i < PACKET_MAX_SIZE; i++) {
+       for (i = 0; i < frames; i++) {
                struct bio *bio = pkt->r_bios[i];
                if (bio)
                        bio_put(bio);
        }
 
 no_page:
-       for (i = 0; i < PAGES_PER_PACKET; i++)
+       for (i = 0; i < frames / FRAMES_PER_PAGE; i++)
                if (pkt->pages[i])
                        __free_page(pkt->pages[i]);
        bio_put(pkt->w_bio);
@@ -186,12 +185,12 @@ static void pkt_free_packet_data(struct packet_data *pkt)
 {
        int i;
 
-       for (i = 0; i < PACKET_MAX_SIZE; i++) {
+       for (i = 0; i < pkt->frames; i++) {
                struct bio *bio = pkt->r_bios[i];
                if (bio)
                        bio_put(bio);
        }
-       for (i = 0; i < PAGES_PER_PACKET; i++)
+       for (i = 0; i < pkt->frames / FRAMES_PER_PAGE; i++)
                __free_page(pkt->pages[i]);
        bio_put(pkt->w_bio);
        kfree(pkt);
@@ -206,17 +205,17 @@ static void pkt_shrink_pktlist(struct pktcdvd_device *pd)
        list_for_each_entry_safe(pkt, next, &pd->cdrw.pkt_free_list, list) {
                pkt_free_packet_data(pkt);
        }
+       INIT_LIST_HEAD(&pd->cdrw.pkt_free_list);
 }
 
 static int pkt_grow_pktlist(struct pktcdvd_device *pd, int nr_packets)
 {
        struct packet_data *pkt;
 
-       INIT_LIST_HEAD(&pd->cdrw.pkt_free_list);
-       INIT_LIST_HEAD(&pd->cdrw.pkt_active_list);
-       spin_lock_init(&pd->cdrw.active_list_lock);
+       BUG_ON(!list_empty(&pd->cdrw.pkt_free_list));
+
        while (nr_packets > 0) {
-               pkt = pkt_alloc_packet_data();
+               pkt = pkt_alloc_packet_data(pd->settings.size >> 2);
                if (!pkt) {
                        pkt_shrink_pktlist(pd);
                        return 0;
@@ -229,7 +228,7 @@ static int pkt_grow_pktlist(struct pktcdvd_device *pd, int nr_packets)
        return 1;
 }
 
-static void *pkt_rb_alloc(unsigned int __nocast gfp_mask, void *data)
+static void *pkt_rb_alloc(gfp_t gfp_mask, void *data)
 {
        return kmalloc(sizeof(struct pkt_rb_node), gfp_mask);
 }
@@ -247,7 +246,7 @@ static inline struct pkt_rb_node *pkt_rbtree_next(struct pkt_rb_node *node)
        return rb_entry(n, struct pkt_rb_node, rb_node);
 }
 
-static inline void pkt_rbtree_erase(struct pktcdvd_device *pd, struct pkt_rb_node *node)
+static void pkt_rbtree_erase(struct pktcdvd_device *pd, struct pkt_rb_node *node)
 {
        rb_erase(&node->rb_node, &pd->bio_queue);
        mempool_free(node, pd->rb_pool);
@@ -315,7 +314,7 @@ static void pkt_rbtree_insert(struct pktcdvd_device *pd, struct pkt_rb_node *nod
 /*
  * Add a bio to a single linked list defined by its head and tail pointers.
  */
-static inline void pkt_add_list_last(struct bio *bio, struct bio **list_head, struct bio **list_tail)
+static void pkt_add_list_last(struct bio *bio, struct bio **list_head, struct bio **list_tail)
 {
        bio->bi_next = NULL;
        if (*list_tail) {
@@ -511,14 +510,11 @@ static void pkt_queue_bio(struct pktcdvd_device *pd, struct bio *bio)
  */
 static void pkt_iosched_process_queue(struct pktcdvd_device *pd)
 {
-       request_queue_t *q;
 
        if (atomic_read(&pd->iosched.attention) == 0)
                return;
        atomic_set(&pd->iosched.attention, 0);
 
-       q = bdev_get_queue(pd->bdev);
-
        for (;;) {
                struct bio *bio;
                int reads_queued, writes_queued;
@@ -649,7 +645,7 @@ static void pkt_copy_bio_data(struct bio *src_bio, int seg, int offs, struct pag
  * b) The data can be used as cache to avoid read requests if we receive a
  *    new write request for the same zone.
  */
-static void pkt_make_local_copy(struct packet_data *pkt, struct page **pages, int *offsets)
+static void pkt_make_local_copy(struct packet_data *pkt, struct bio_vec *bvec)
 {
        int f, p, offs;
 
@@ -657,15 +653,15 @@ static void pkt_make_local_copy(struct packet_data *pkt, struct page **pages, in
        p = 0;
        offs = 0;
        for (f = 0; f < pkt->frames; f++) {
-               if (pages[f] != pkt->pages[p]) {
-                       void *vfrom = kmap_atomic(pages[f], KM_USER0) + offsets[f];
+               if (bvec[f].bv_page != pkt->pages[p]) {
+                       void *vfrom = kmap_atomic(bvec[f].bv_page, KM_USER0) + bvec[f].bv_offset;
                        void *vto = page_address(pkt->pages[p]) + offs;
                        memcpy(vto, vfrom, CD_FRAMESIZE);
                        kunmap_atomic(vfrom, KM_USER0);
-                       pages[f] = pkt->pages[p];
-                       offsets[f] = offs;
+                       bvec[f].bv_page = pkt->pages[p];
+                       bvec[f].bv_offset = offs;
                } else {
-                       BUG_ON(offsets[f] != offs);
+                       BUG_ON(bvec[f].bv_offset != offs);
                }
                offs += CD_FRAMESIZE;
                if (offs >= PAGE_SIZE) {
@@ -954,7 +950,7 @@ try_next_bio:
 
        pd->current_sector = zone + pd->settings.size;
        pkt->sector = zone;
-       pkt->frames = pd->settings.size >> 2;
+       BUG_ON(pkt->frames != pd->settings.size >> 2);
        pkt->write_size = 0;
 
        /*
@@ -995,18 +991,17 @@ try_next_bio:
 static void pkt_start_write(struct pktcdvd_device *pd, struct packet_data *pkt)
 {
        struct bio *bio;
-       struct page *pages[PACKET_MAX_SIZE];
-       int offsets[PACKET_MAX_SIZE];
        int f;
        int frames_write;
+       struct bio_vec *bvec = pkt->w_bio->bi_io_vec;
 
        for (f = 0; f < pkt->frames; f++) {
-               pages[f] = pkt->pages[(f * CD_FRAMESIZE) / PAGE_SIZE];
-               offsets[f] = (f * CD_FRAMESIZE) % PAGE_SIZE;
+               bvec[f].bv_page = pkt->pages[(f * CD_FRAMESIZE) / PAGE_SIZE];
+               bvec[f].bv_offset = (f * CD_FRAMESIZE) % PAGE_SIZE;
        }
 
        /*
-        * Fill-in pages[] and offsets[] with data from orig_bios.
+        * Fill-in bvec with data from orig_bios.
         */
        frames_write = 0;
        spin_lock(&pkt->lock);
@@ -1028,11 +1023,11 @@ static void pkt_start_write(struct pktcdvd_device *pd, struct packet_data *pkt)
                        }
 
                        if (src_bvl->bv_len - src_offs >= CD_FRAMESIZE) {
-                               pages[f] = src_bvl->bv_page;
-                               offsets[f] = src_bvl->bv_offset + src_offs;
+                               bvec[f].bv_page = src_bvl->bv_page;
+                               bvec[f].bv_offset = src_bvl->bv_offset + src_offs;
                        } else {
                                pkt_copy_bio_data(bio, segment, src_offs,
-                                                 pages[f], offsets[f]);
+                                                 bvec[f].bv_page, bvec[f].bv_offset);
                        }
                        src_offs += CD_FRAMESIZE;
                        frames_write++;
@@ -1046,7 +1041,7 @@ static void pkt_start_write(struct pktcdvd_device *pd, struct packet_data *pkt)
        BUG_ON(frames_write != pkt->write_size);
 
        if (test_bit(PACKET_MERGE_SEGS, &pd->flags) || (pkt->write_size < pkt->frames)) {
-               pkt_make_local_copy(pkt, pages, offsets);
+               pkt_make_local_copy(pkt, bvec);
                pkt->cache_valid = 1;
        } else {
                pkt->cache_valid = 0;
@@ -1059,17 +1054,9 @@ static void pkt_start_write(struct pktcdvd_device *pd, struct packet_data *pkt)
        pkt->w_bio->bi_bdev = pd->bdev;
        pkt->w_bio->bi_end_io = pkt_end_io_packet_write;
        pkt->w_bio->bi_private = pkt;
-       for (f = 0; f < pkt->frames; f++) {
-               if ((f + 1 < pkt->frames) && (pages[f + 1] == pages[f]) &&
-                   (offsets[f + 1] = offsets[f] + CD_FRAMESIZE)) {
-                       if (!bio_add_page(pkt->w_bio, pages[f], CD_FRAMESIZE * 2, offsets[f]))
-                               BUG();
-                       f++;
-               } else {
-                       if (!bio_add_page(pkt->w_bio, pages[f], CD_FRAMESIZE, offsets[f]))
-                               BUG();
-               }
-       }
+       for (f = 0; f < pkt->frames; f++)
+               if (!bio_add_page(pkt->w_bio, bvec[f].bv_page, CD_FRAMESIZE, bvec[f].bv_offset))
+                       BUG();
        VPRINTK("pktcdvd: vcnt=%d\n", pkt->w_bio->bi_vcnt);
 
        atomic_set(&pkt->io_wait, 1);
@@ -1191,7 +1178,7 @@ static void pkt_count_states(struct pktcdvd_device *pd, int *states)
        struct packet_data *pkt;
        int i;
 
-       for (i = 0; i <= PACKET_NUM_STATES; i++)
+       for (i = 0; i < PACKET_NUM_STATES; i++)
                states[i] = 0;
 
        spin_lock(&pd->cdrw.active_list_lock);
@@ -1552,7 +1539,7 @@ static int pkt_good_disc(struct pktcdvd_device *pd, disc_information *di)
                case 0x12: /* DVD-RAM */
                        return 0;
                default:
-                       printk("pktcdvd: Wrong disc profile (%x)\n", pd->mmc3_profile);
+                       VPRINTK("pktcdvd: Wrong disc profile (%x)\n", pd->mmc3_profile);
                        return 1;
        }
 
@@ -1642,7 +1629,7 @@ static int pkt_probe_settings(struct pktcdvd_device *pd)
        pd->settings.size = be32_to_cpu(ti.fixed_packet_size) << 2;
        if (pd->settings.size == 0) {
                printk("pktcdvd: detected zero packet size!\n");
-               pd->settings.size = 128;
+               return -ENXIO;
        }
        if (pd->settings.size > PACKET_MAX_SECTORS) {
                printk("pktcdvd: packet size is too big\n");
@@ -1898,8 +1885,8 @@ static int pkt_open_write(struct pktcdvd_device *pd)
        unsigned int write_speed, media_write_speed, read_speed;
 
        if ((ret = pkt_probe_settings(pd))) {
-               DPRINTK("pktcdvd: %s failed probe\n", pd->name);
-               return -EIO;
+               VPRINTK("pktcdvd: %s failed probe\n", pd->name);
+               return -EROFS;
        }
 
        if ((ret = pkt_set_write_settings(pd))) {
@@ -1958,9 +1945,12 @@ static int pkt_open_dev(struct pktcdvd_device *pd, int write)
        if ((ret = blkdev_get(pd->bdev, FMODE_READ, O_RDONLY)))
                goto out;
 
+       if ((ret = bd_claim(pd->bdev, pd)))
+               goto out_putdev;
+
        if ((ret = pkt_get_last_written(pd, &lba))) {
                printk("pktcdvd: pkt_get_last_written failed\n");
-               goto out_putdev;
+               goto out_unclaim;
        }
 
        set_capacity(pd->disk, lba << 2);
@@ -1970,7 +1960,7 @@ static int pkt_open_dev(struct pktcdvd_device *pd, int write)
        q = bdev_get_queue(pd->bdev);
        if (write) {
                if ((ret = pkt_open_write(pd)))
-                       goto out_putdev;
+                       goto out_unclaim;
                /*
                 * Some CDRW drives can not handle writes larger than one packet,
                 * even if the size is a multiple of the packet size.
@@ -1985,13 +1975,21 @@ static int pkt_open_dev(struct pktcdvd_device *pd, int write)
        }
 
        if ((ret = pkt_set_segment_merging(pd, q)))
-               goto out_putdev;
+               goto out_unclaim;
 
-       if (write)
+       if (write) {
+               if (!pkt_grow_pktlist(pd, CONFIG_CDROM_PKTCDVD_BUFFERS)) {
+                       printk("pktcdvd: not enough memory for buffers\n");
+                       ret = -ENOMEM;
+                       goto out_unclaim;
+               }
                printk("pktcdvd: %lukB available on disc\n", lba << 1);
+       }
 
        return 0;
 
+out_unclaim:
+       bd_release(pd->bdev);
 out_putdev:
        blkdev_put(pd->bdev);
 out:
@@ -2010,7 +2008,10 @@ static void pkt_release_dev(struct pktcdvd_device *pd, int flush)
        pkt_lock_door(pd, 0);
 
        pkt_set_speed(pd, MAX_SPEED, MAX_SPEED);
+       bd_release(pd->bdev);
        blkdev_put(pd->bdev);
+
+       pkt_shrink_pktlist(pd);
 }
 
 static struct pktcdvd_device *pkt_find_dev_from_minor(int dev_minor)
@@ -2043,10 +2044,9 @@ static int pkt_open(struct inode *inode, struct file *file)
                        goto out_dec;
                }
        } else {
-               if (pkt_open_dev(pd, file->f_mode & FMODE_WRITE)) {
-                       ret = -EIO;
+               ret = pkt_open_dev(pd, file->f_mode & FMODE_WRITE);
+               if (ret)
                        goto out_dec;
-               }
                /*
                 * needed here as well, since ext2 (among others) may change
                 * the blocksize at mount time
@@ -2082,7 +2082,7 @@ static int pkt_close(struct inode *inode, struct file *file)
 }
 
 
-static void *psd_pool_alloc(unsigned int __nocast gfp_mask, void *data)
+static void *psd_pool_alloc(gfp_t gfp_mask, void *data)
 {
        return kmalloc(sizeof(struct packet_stacked_data), gfp_mask);
 }
@@ -2376,12 +2376,6 @@ static int pkt_new_dev(struct pktcdvd_device *pd, dev_t dev)
        /* 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);
 
@@ -2392,7 +2386,7 @@ static int pkt_new_dev(struct pktcdvd_device *pd, dev_t dev)
        if (IS_ERR(pd->cdrw.thread)) {
                printk("pktcdvd: can't start kernel thread\n");
                ret = -ENOMEM;
-               goto out_thread;
+               goto out_mem;
        }
 
        proc = create_proc_entry(pd->name, 0, pkt_proc);
@@ -2403,8 +2397,6 @@ static int pkt_new_dev(struct pktcdvd_device *pd, dev_t dev)
        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. */
@@ -2434,11 +2426,12 @@ static int pkt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, u
                 * 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);
+               if (pd->refcnt == 1)
+                       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);
+               VPRINTK("pktcdvd: Unknown ioctl for %s (%x)\n", pd->name, cmd);
                return -ENOTTY;
        }
 
@@ -2500,6 +2493,10 @@ static int pkt_setup_dev(struct pkt_ctrl_command *ctrl_cmd)
                goto out_mem;
        pd->disk = disk;
 
+       INIT_LIST_HEAD(&pd->cdrw.pkt_free_list);
+       INIT_LIST_HEAD(&pd->cdrw.pkt_active_list);
+       spin_lock_init(&pd->cdrw.active_list_lock);
+
        spin_lock_init(&pd->lock);
        spin_lock_init(&pd->iosched.lock);
        sprintf(pd->name, "pktcdvd%d", idx);
@@ -2564,8 +2561,6 @@ static int pkt_remove_dev(struct pkt_ctrl_command *ctrl_cmd)
 
        blkdev_put(pd->bdev);
 
-       pkt_shrink_pktlist(pd);
-
        remove_proc_entry(pd->name, pkt_proc);
        DPRINTK("pktcdvd: writer %s unmapped\n", pd->name);
 
@@ -2675,7 +2670,6 @@ static int __init pkt_init(void)
 
        pkt_proc = proc_mkdir("pktcdvd", proc_root_driver);
 
-       DPRINTK("pktcdvd: %s\n", VERSION_CODE);
        return 0;
 
 out: