Merge branch 'for-2.6.39/stack-plug' into for-2.6.39/core
[linux-3.10.git] / drivers / s390 / char / tape_block.c
index 1efc9f2..83cea9a 100644 (file)
  *              Stefan Bader <shbader@de.ibm.com>
  */
 
+#define KMSG_COMPONENT "tape"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
 #include <linux/fs.h>
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/blkdev.h>
+#include <linux/mutex.h>
 #include <linux/interrupt.h>
 #include <linux/buffer_head.h>
+#include <linux/kernel.h>
 
 #include <asm/debug.h>
 
@@ -23,8 +27,6 @@
 
 #include "tape.h"
 
-#define PRINTK_HEADER "TAPE_BLOCK: "
-
 #define TAPEBLOCK_MAX_SEC      100
 #define TAPEBLOCK_MIN_REQUEUE  3
 
 /*
  * file operation structure for tape block frontend
  */
-static int tapeblock_open(struct inode *, struct file *);
-static int tapeblock_release(struct inode *, struct file *);
-static int tapeblock_ioctl(struct inode *, struct file *, unsigned int,
-                               unsigned long);
-static int tapeblock_medium_changed(struct gendisk *);
+static DEFINE_MUTEX(tape_block_mutex);
+static int tapeblock_open(struct block_device *, fmode_t);
+static int tapeblock_release(struct gendisk *, fmode_t);
+static unsigned int tapeblock_check_events(struct gendisk *, unsigned int);
 static int tapeblock_revalidate_disk(struct gendisk *);
 
-static struct block_device_operations tapeblock_fops = {
+static const struct block_device_operations tapeblock_fops = {
        .owner           = THIS_MODULE,
        .open            = tapeblock_open,
        .release         = tapeblock_release,
-       .ioctl           = tapeblock_ioctl,
-       .media_changed   = tapeblock_medium_changed,
+       .check_events    = tapeblock_check_events,
        .revalidate_disk = tapeblock_revalidate_disk,
 };
 
@@ -65,7 +65,7 @@ static void
 tapeblock_trigger_requeue(struct tape_device *device)
 {
        /* Protect against rescheduling. */
-       if (atomic_compare_and_swap(0, 1, &device->blk_data.requeue_scheduled))
+       if (atomic_cmpxchg(&device->blk_data.requeue_scheduled, 0, 1) != 0)
                return;
        schedule_work(&device->blk_data.requeue_task);
 }
@@ -73,14 +73,6 @@ tapeblock_trigger_requeue(struct tape_device *device)
 /*
  * Post finished request.
  */
-static inline void
-tapeblock_end_request(struct request *req, int uptodate)
-{
-       if (end_that_request_first(req, uptodate, req->hard_nr_sectors))
-               BUG();
-       end_that_request_last(req);
-}
-
 static void
 __tapeblock_end_request(struct tape_request *ccw_req, void *data)
 {
@@ -91,24 +83,24 @@ __tapeblock_end_request(struct tape_request *ccw_req, void *data)
 
        device = ccw_req->device;
        req = (struct request *) data;
-       tapeblock_end_request(req, ccw_req->rc == 0);
+       blk_end_request_all(req, (ccw_req->rc == 0) ? 0 : -EIO);
        if (ccw_req->rc == 0)
                /* Update position. */
                device->blk_data.block_position =
-                       (req->sector + req->nr_sectors) >> TAPEBLOCK_HSEC_S2B;
+                 (blk_rq_pos(req) + blk_rq_sectors(req)) >> TAPEBLOCK_HSEC_S2B;
        else
                /* We lost the position information due to an error. */
                device->blk_data.block_position = -1;
        device->discipline->free_bread(ccw_req);
        if (!list_empty(&device->req_queue) ||
-           elv_next_request(device->blk_data.request_queue))
+           blk_peek_request(device->blk_data.request_queue))
                tapeblock_trigger_requeue(device);
 }
 
 /*
  * Feed the tape device CCW queue with requests supplied in a list.
  */
-static inline int
+static int
 tapeblock_start_request(struct tape_device *device, struct request *req)
 {
        struct tape_request *   ccw_req;
@@ -119,7 +111,7 @@ tapeblock_start_request(struct tape_device *device, struct request *req)
        ccw_req = device->discipline->bread(device, req);
        if (IS_ERR(ccw_req)) {
                DBF_EVENT(1, "TBLOCK: bread failed\n");
-               tapeblock_end_request(req, 0);
+               blk_end_request_all(req, -EIO);
                return PTR_ERR(ccw_req);
        }
        ccw_req->callback = __tapeblock_end_request;
@@ -132,7 +124,7 @@ tapeblock_start_request(struct tape_device *device, struct request *req)
                 * Start/enqueueing failed. No retries in
                 * this case.
                 */
-               tapeblock_end_request(req, 0);
+               blk_end_request_all(req, -EIO);
                device->discipline->free_bread(ccw_req);
        }
 
@@ -144,15 +136,17 @@ tapeblock_start_request(struct tape_device *device, struct request *req)
  * queue.
  */
 static void
-tapeblock_requeue(void *data) {
+tapeblock_requeue(struct work_struct *work) {
+       struct tape_blk_data *  blkdat;
        struct tape_device *    device;
-       request_queue_t *       queue;
+       struct request_queue *  queue;
        int                     nr_queued;
        struct request *        req;
        struct list_head *      l;
        int                     rc;
 
-       device = (struct tape_device *) data;
+       blkdat = container_of(work, struct tape_blk_data, requeue_task);
+       device = blkdat->device;
        if (!device)
                return;
 
@@ -165,24 +159,23 @@ tapeblock_requeue(void *data) {
                nr_queued++;
        spin_unlock(get_ccwdev_lock(device->cdev));
 
-       spin_lock(&device->blk_data.request_queue_lock);
+       spin_lock_irq(&device->blk_data.request_queue_lock);
        while (
-               !blk_queue_plugged(queue) &&
-               elv_next_request(queue)   &&
+               blk_peek_request(queue) &&
                nr_queued < TAPEBLOCK_MIN_REQUEUE
        ) {
-               req = elv_next_request(queue);
+               req = blk_fetch_request(queue);
                if (rq_data_dir(req) == WRITE) {
                        DBF_EVENT(1, "TBLOCK: Rejecting write request\n");
-                       blkdev_dequeue_request(req);
-                       tapeblock_end_request(req, 0);
+                       spin_unlock_irq(&device->blk_data.request_queue_lock);
+                       blk_end_request_all(req, -EIO);
+                       spin_lock_irq(&device->blk_data.request_queue_lock);
                        continue;
                }
+               nr_queued++;
                spin_unlock_irq(&device->blk_data.request_queue_lock);
                rc = tapeblock_start_request(device, req);
                spin_lock_irq(&device->blk_data.request_queue_lock);
-               blkdev_dequeue_request(req);
-               nr_queued++;
        }
        spin_unlock_irq(&device->blk_data.request_queue_lock);
        atomic_set(&device->blk_data.requeue_scheduled, 0);
@@ -192,15 +185,13 @@ tapeblock_requeue(void *data) {
  * Tape request queue function. Called from ll_rw_blk.c
  */
 static void
-tapeblock_request_fn(request_queue_t *queue)
+tapeblock_request_fn(struct request_queue *queue)
 {
        struct tape_device *device;
 
        device = (struct tape_device *) queue->queuedata;
        DBF_LH(6, "tapeblock_request_fn(device=%p)\n", device);
-       if (device == NULL)
-               BUG();
-
+       BUG_ON(device == NULL);
        tapeblock_trigger_requeue(device);
 }
 
@@ -215,6 +206,7 @@ tapeblock_setup_device(struct tape_device * device)
        int                     rc;
 
        blkdat = &device->blk_data;
+       blkdat->device = device;
        spin_lock_init(&blkdat->request_queue_lock);
        atomic_set(&blkdat->requeue_scheduled, 0);
 
@@ -225,15 +217,13 @@ tapeblock_setup_device(struct tape_device * device)
        if (!blkdat->request_queue)
                return -ENOMEM;
 
-       elevator_exit(blkdat->request_queue->elevator);
-       rc = elevator_init(blkdat->request_queue, "noop");
+       rc = elevator_change(blkdat->request_queue, "noop");
        if (rc)
                goto cleanup_queue;
 
-       blk_queue_hardsect_size(blkdat->request_queue, TAPEBLOCK_HSEC_SIZE);
-       blk_queue_max_sectors(blkdat->request_queue, TAPEBLOCK_MAX_SEC);
-       blk_queue_max_phys_segments(blkdat->request_queue, -1L);
-       blk_queue_max_hw_segments(blkdat->request_queue, -1L);
+       blk_queue_logical_block_size(blkdat->request_queue, TAPEBLOCK_HSEC_SIZE);
+       blk_queue_max_hw_sectors(blkdat->request_queue, TAPEBLOCK_MAX_SEC);
+       blk_queue_max_segments(blkdat->request_queue, -1L);
        blk_queue_max_segment_size(blkdat->request_queue, -1L);
        blk_queue_segment_boundary(blkdat->request_queue, -1L);
 
@@ -246,7 +236,8 @@ tapeblock_setup_device(struct tape_device * device)
        disk->major = tapeblock_major;
        disk->first_minor = device->first_minor;
        disk->fops = &tapeblock_fops;
-       disk->private_data = tape_get_device_reference(device);
+       disk->events = DISK_EVENT_MEDIA_CHANGE;
+       disk->private_data = tape_get_device(device);
        disk->queue = blkdat->request_queue;
        set_capacity(disk, 0);
        sprintf(disk->disk_name, "btibm%d",
@@ -254,12 +245,12 @@ tapeblock_setup_device(struct tape_device * device)
 
        blkdat->disk = disk;
        blkdat->medium_changed = 1;
-       blkdat->request_queue->queuedata = tape_get_device_reference(device);
+       blkdat->request_queue->queuedata = tape_get_device(device);
 
        add_disk(disk);
 
-       INIT_WORK(&blkdat->requeue_task, tapeblock_requeue,
-               tape_get_device_reference(device));
+       tape_get_device(device);
+       INIT_WORK(&blkdat->requeue_task, tapeblock_requeue);
 
        return 0;
 
@@ -273,23 +264,22 @@ cleanup_queue:
 void
 tapeblock_cleanup_device(struct tape_device *device)
 {
-       flush_scheduled_work();
-       device->blk_data.requeue_task.data = tape_put_device(device);
+       flush_work_sync(&device->blk_data.requeue_task);
+       tape_put_device(device);
 
        if (!device->blk_data.disk) {
-               PRINT_ERR("(%s): No gendisk to clean up!\n",
-                       device->cdev->dev.bus_id);
                goto cleanup_queue;
        }
 
        del_gendisk(device->blk_data.disk);
-       device->blk_data.disk->private_data =
-               tape_put_device(device->blk_data.disk->private_data);
+       device->blk_data.disk->private_data = NULL;
+       tape_put_device(device);
        put_disk(device->blk_data.disk);
 
        device->blk_data.disk = NULL;
 cleanup_queue:
-       device->blk_data.request_queue->queuedata = tape_put_device(device);
+       device->blk_data.request_queue->queuedata = NULL;
+       tape_put_device(device);
 
        blk_cleanup_queue(device->blk_data.request_queue);
        device->blk_data.request_queue = NULL;
@@ -307,13 +297,11 @@ tapeblock_revalidate_disk(struct gendisk *disk)
        int                     rc;
 
        device = (struct tape_device *) disk->private_data;
-       if (!device)
-               BUG();
+       BUG_ON(!device);
 
        if (!device->blk_data.medium_changed)
                return 0;
 
-       PRINT_INFO("Detecting media size...\n");
        rc = tape_mtop(device, MTFSFM, 1);
        if (rc)
                return rc;
@@ -322,6 +310,8 @@ tapeblock_revalidate_disk(struct gendisk *disk)
        if (rc < 0)
                return rc;
 
+       pr_info("%s: Determining the size of the recorded area...\n",
+               dev_name(&device->cdev->dev));
        DBF_LH(3, "Image file ends at %d\n", rc);
        nr_of_blks = rc;
 
@@ -340,7 +330,8 @@ tapeblock_revalidate_disk(struct gendisk *disk)
        device->bof = rc;
        nr_of_blks -= rc;
 
-       PRINT_INFO("Found %i blocks on media\n", nr_of_blks);
+       pr_info("%s: The size of the recorded area is %i blocks\n",
+               dev_name(&device->cdev->dev), nr_of_blks);
        set_capacity(device->blk_data.disk,
                nr_of_blks*(TAPEBLOCK_HSEC_SIZE/512));
 
@@ -349,8 +340,8 @@ tapeblock_revalidate_disk(struct gendisk *disk)
        return 0;
 }
 
-static int
-tapeblock_medium_changed(struct gendisk *disk)
+static unsigned int
+tapeblock_check_events(struct gendisk *disk, unsigned int clearing)
 {
        struct tape_device *device;
 
@@ -358,26 +349,26 @@ tapeblock_medium_changed(struct gendisk *disk)
        DBF_LH(6, "tapeblock_medium_changed(%p) = %d\n",
                device, device->blk_data.medium_changed);
 
-       return device->blk_data.medium_changed;
+       return device->blk_data.medium_changed ? DISK_EVENT_MEDIA_CHANGE : 0;
 }
 
 /*
  * Block frontend tape device open function.
  */
 static int
-tapeblock_open(struct inode *inode, struct file *filp)
+tapeblock_open(struct block_device *bdev, fmode_t mode)
 {
-       struct gendisk *        disk;
+       struct gendisk *        disk = bdev->bd_disk;
        struct tape_device *    device;
        int                     rc;
 
-       disk   = inode->i_bdev->bd_disk;
-       device = tape_get_device_reference(disk->private_data);
+       mutex_lock(&tape_block_mutex);
+       device = tape_get_device(disk->private_data);
 
        if (device->required_tapemarks) {
                DBF_EVENT(2, "TBLOCK: missing tapemarks\n");
-               PRINT_ERR("TBLOCK: Refusing to open tape with missing"
-                       " end of file marks.\n");
+               pr_warning("%s: Opening the tape failed because of missing "
+                          "end-of-file marks\n", dev_name(&device->cdev->dev));
                rc = -EPERM;
                goto put_device;
        }
@@ -395,12 +386,14 @@ tapeblock_open(struct inode *inode, struct file *filp)
         *       is called.
         */
        tape_state_set(device, TS_BLKUSE);
+       mutex_unlock(&tape_block_mutex);
        return 0;
 
 release:
        tape_release(device);
  put_device:
        tape_put_device(device);
+       mutex_unlock(&tape_block_mutex);
        return rc;
 }
 
@@ -411,59 +404,20 @@ release:
  *       we just get the pointer here and release the reference.
  */
 static int
-tapeblock_release(struct inode *inode, struct file *filp)
+tapeblock_release(struct gendisk *disk, fmode_t mode)
 {
-       struct gendisk *disk = inode->i_bdev->bd_disk;
        struct tape_device *device = disk->private_data;
-
+       mutex_lock(&tape_block_mutex);
        tape_state_set(device, TS_IN_USE);
        tape_release(device);
        tape_put_device(device);
+       mutex_unlock(&tape_block_mutex);
 
        return 0;
 }
 
 /*
- * Support of some generic block device IOCTLs.
- */
-static int
-tapeblock_ioctl(
-       struct inode *          inode,
-       struct file *           file,
-       unsigned int            command,
-       unsigned long           arg
-) {
-       int rc;
-       int minor;
-       struct gendisk *disk = inode->i_bdev->bd_disk;
-       struct tape_device *device = disk->private_data;
-
-       rc     = 0;
-       disk   = inode->i_bdev->bd_disk;
-       if (!disk)
-               BUG();
-       device = disk->private_data;
-       if (!device)
-               BUG();
-       minor  = iminor(inode);
-
-       DBF_LH(6, "tapeblock_ioctl(0x%0x)\n", command);
-       DBF_LH(6, "device = %d:%d\n", tapeblock_major, minor);
-
-       switch (command) {
-               /* Refuse some IOCTL calls without complaining (mount). */
-               case 0x5310:            /* CDROMMULTISESSION */
-                       rc = -EINVAL;
-                       break;
-               default:
-                       PRINT_WARN("invalid ioctl 0x%x\n", command);
-                       rc = -EINVAL;
-       }
-
-       return rc;
-}
-
-/*
  * Initialize block device frontend.
  */
 int
@@ -478,7 +432,6 @@ tapeblock_init(void)
 
        if (tapeblock_major == 0)
                tapeblock_major = rc;
-       PRINT_INFO("tape gets major %d for block device\n", tapeblock_major);
        return 0;
 }