# S/390 block devices
 #
 
-dasd_eckd_mod-objs := dasd_eckd.o dasd_3990_erp.o dasd_9343_erp.o
-dasd_fba_mod-objs  := dasd_fba.o dasd_3370_erp.o dasd_9336_erp.o
+dasd_eckd_mod-objs := dasd_eckd.o dasd_3990_erp.o dasd_alias.o
+dasd_fba_mod-objs  := dasd_fba.o
 dasd_diag_mod-objs := dasd_diag.o
 dasd_mod-objs      := dasd.o dasd_ioctl.o dasd_proc.o dasd_devmap.o \
                        dasd_genhd.o dasd_erp.o
 
 /*
  * SECTION: prototypes for static functions of dasd.c
  */
-static int  dasd_alloc_queue(struct dasd_device * device);
-static void dasd_setup_queue(struct dasd_device * device);
-static void dasd_free_queue(struct dasd_device * device);
-static void dasd_flush_request_queue(struct dasd_device *);
-static int dasd_flush_ccw_queue(struct dasd_device *, int);
-static void dasd_tasklet(struct dasd_device *);
+static int  dasd_alloc_queue(struct dasd_block *);
+static void dasd_setup_queue(struct dasd_block *);
+static void dasd_free_queue(struct dasd_block *);
+static void dasd_flush_request_queue(struct dasd_block *);
+static int dasd_flush_block_queue(struct dasd_block *);
+static void dasd_device_tasklet(struct dasd_device *);
+static void dasd_block_tasklet(struct dasd_block *);
 static void do_kick_device(struct work_struct *);
+static void dasd_return_cqr_cb(struct dasd_ccw_req *, void *);
 
 /*
  * SECTION: Operations on the device structure.
 /*
  * Allocate memory for a new device structure.
  */
-struct dasd_device *
-dasd_alloc_device(void)
+struct dasd_device *dasd_alloc_device(void)
 {
        struct dasd_device *device;
 
-       device = kzalloc(sizeof (struct dasd_device), GFP_ATOMIC);
-       if (device == NULL)
+       device = kzalloc(sizeof(struct dasd_device), GFP_ATOMIC);
+       if (!device)
                return ERR_PTR(-ENOMEM);
-       /* open_count = 0 means device online but not in use */
-       atomic_set(&device->open_count, -1);
 
        /* Get two pages for normal block device operations. */
        device->ccw_mem = (void *) __get_free_pages(GFP_ATOMIC | GFP_DMA, 1);
-       if (device->ccw_mem == NULL) {
+       if (!device->ccw_mem) {
                kfree(device);
                return ERR_PTR(-ENOMEM);
        }
        /* Get one page for error recovery. */
        device->erp_mem = (void *) get_zeroed_page(GFP_ATOMIC | GFP_DMA);
-       if (device->erp_mem == NULL) {
+       if (!device->erp_mem) {
                free_pages((unsigned long) device->ccw_mem, 1);
                kfree(device);
                return ERR_PTR(-ENOMEM);
        dasd_init_chunklist(&device->ccw_chunks, device->ccw_mem, PAGE_SIZE*2);
        dasd_init_chunklist(&device->erp_chunks, device->erp_mem, PAGE_SIZE);
        spin_lock_init(&device->mem_lock);
-       spin_lock_init(&device->request_queue_lock);
-       atomic_set (&device->tasklet_scheduled, 0);
+       atomic_set(&device->tasklet_scheduled, 0);
        tasklet_init(&device->tasklet,
-                    (void (*)(unsigned long)) dasd_tasklet,
+                    (void (*)(unsigned long)) dasd_device_tasklet,
                     (unsigned long) device);
        INIT_LIST_HEAD(&device->ccw_queue);
        init_timer(&device->timer);
 /*
  * Free memory of a device structure.
  */
-void
-dasd_free_device(struct dasd_device *device)
+void dasd_free_device(struct dasd_device *device)
 {
        kfree(device->private);
        free_page((unsigned long) device->erp_mem);
        kfree(device);
 }
 
+/*
+ * Allocate memory for a new device structure.
+ */
+struct dasd_block *dasd_alloc_block(void)
+{
+       struct dasd_block *block;
+
+       block = kzalloc(sizeof(*block), GFP_ATOMIC);
+       if (!block)
+               return ERR_PTR(-ENOMEM);
+       /* open_count = 0 means device online but not in use */
+       atomic_set(&block->open_count, -1);
+
+       spin_lock_init(&block->request_queue_lock);
+       atomic_set(&block->tasklet_scheduled, 0);
+       tasklet_init(&block->tasklet,
+                    (void (*)(unsigned long)) dasd_block_tasklet,
+                    (unsigned long) block);
+       INIT_LIST_HEAD(&block->ccw_queue);
+       spin_lock_init(&block->queue_lock);
+       init_timer(&block->timer);
+
+       return block;
+}
+
+/*
+ * Free memory of a device structure.
+ */
+void dasd_free_block(struct dasd_block *block)
+{
+       kfree(block);
+}
+
 /*
  * Make a new device known to the system.
  */
-static int
-dasd_state_new_to_known(struct dasd_device *device)
+static int dasd_state_new_to_known(struct dasd_device *device)
 {
        int rc;
 
         */
        dasd_get_device(device);
 
-       rc = dasd_alloc_queue(device);
-       if (rc) {
-               dasd_put_device(device);
-               return rc;
+       if (device->block) {
+               rc = dasd_alloc_queue(device->block);
+               if (rc) {
+                       dasd_put_device(device);
+                       return rc;
+               }
        }
-
        device->state = DASD_STATE_KNOWN;
        return 0;
 }
 /*
  * Let the system forget about a device.
  */
-static int
-dasd_state_known_to_new(struct dasd_device * device)
+static int dasd_state_known_to_new(struct dasd_device *device)
 {
        /* Disable extended error reporting for this device. */
        dasd_eer_disable(device);
        /* Forget the discipline information. */
-       if (device->discipline)
+       if (device->discipline) {
+               if (device->discipline->uncheck_device)
+                       device->discipline->uncheck_device(device);
                module_put(device->discipline->owner);
+       }
        device->discipline = NULL;
        if (device->base_discipline)
                module_put(device->base_discipline->owner);
        device->base_discipline = NULL;
        device->state = DASD_STATE_NEW;
 
-       dasd_free_queue(device);
+       if (device->block)
+               dasd_free_queue(device->block);
 
        /* Give up reference we took in dasd_state_new_to_known. */
        dasd_put_device(device);
 /*
  * Request the irq line for the device.
  */
-static int
-dasd_state_known_to_basic(struct dasd_device * device)
+static int dasd_state_known_to_basic(struct dasd_device *device)
 {
        int rc;
 
        /* Allocate and register gendisk structure. */
-       rc = dasd_gendisk_alloc(device);
-       if (rc)
-               return rc;
-
+       if (device->block) {
+               rc = dasd_gendisk_alloc(device->block);
+               if (rc)
+                       return rc;
+       }
        /* register 'device' debug area, used for all DBF_DEV_XXX calls */
        device->debug_area = debug_register(device->cdev->dev.bus_id, 1, 2,
-                                           8 * sizeof (long));
+                                           8 * sizeof(long));
        debug_register_view(device->debug_area, &debug_sprintf_view);
        debug_set_level(device->debug_area, DBF_WARNING);
        DBF_DEV_EVENT(DBF_EMERG, device, "%s", "debug area created");
 /*
  * Release the irq line for the device. Terminate any running i/o.
  */
-static int
-dasd_state_basic_to_known(struct dasd_device * device)
+static int dasd_state_basic_to_known(struct dasd_device *device)
 {
        int rc;
-
-       dasd_gendisk_free(device);
-       rc = dasd_flush_ccw_queue(device, 1);
+       if (device->block) {
+               dasd_gendisk_free(device->block);
+               dasd_block_clear_timer(device->block);
+       }
+       rc = dasd_flush_device_queue(device);
        if (rc)
                return rc;
-       dasd_clear_timer(device);
+       dasd_device_clear_timer(device);
 
        DBF_DEV_EVENT(DBF_EMERG, device, "%p debug area deleted", device);
        if (device->debug_area != NULL) {
  * In case the analysis returns an error, the device setup is stopped
  * (a fake disk was already added to allow formatting).
  */
-static int
-dasd_state_basic_to_ready(struct dasd_device * device)
+static int dasd_state_basic_to_ready(struct dasd_device *device)
 {
        int rc;
+       struct dasd_block *block;
 
        rc = 0;
-       if (device->discipline->do_analysis != NULL)
-               rc = device->discipline->do_analysis(device);
-       if (rc) {
-               if (rc != -EAGAIN)
-                       device->state = DASD_STATE_UNFMT;
-               return rc;
-       }
+       block = device->block;
        /* make disk known with correct capacity */
-       dasd_setup_queue(device);
-       set_capacity(device->gdp, device->blocks << device->s2b_shift);
-       device->state = DASD_STATE_READY;
-       rc = dasd_scan_partitions(device);
-       if (rc)
-               device->state = DASD_STATE_BASIC;
+       if (block) {
+               if (block->base->discipline->do_analysis != NULL)
+                       rc = block->base->discipline->do_analysis(block);
+               if (rc) {
+                       if (rc != -EAGAIN)
+                               device->state = DASD_STATE_UNFMT;
+                       return rc;
+               }
+               dasd_setup_queue(block);
+               set_capacity(block->gdp,
+                            block->blocks << block->s2b_shift);
+               device->state = DASD_STATE_READY;
+               rc = dasd_scan_partitions(block);
+               if (rc)
+                       device->state = DASD_STATE_BASIC;
+       } else {
+               device->state = DASD_STATE_READY;
+       }
        return rc;
 }
 
  * Forget format information. Check if the target level is basic
  * and if it is create fake disk for formatting.
  */
-static int
-dasd_state_ready_to_basic(struct dasd_device * device)
+static int dasd_state_ready_to_basic(struct dasd_device *device)
 {
        int rc;
 
-       rc = dasd_flush_ccw_queue(device, 0);
-       if (rc)
-               return rc;
-       dasd_destroy_partitions(device);
-       dasd_flush_request_queue(device);
-       device->blocks = 0;
-       device->bp_block = 0;
-       device->s2b_shift = 0;
        device->state = DASD_STATE_BASIC;
+       if (device->block) {
+               struct dasd_block *block = device->block;
+               rc = dasd_flush_block_queue(block);
+               if (rc) {
+                       device->state = DASD_STATE_READY;
+                       return rc;
+               }
+               dasd_destroy_partitions(block);
+               dasd_flush_request_queue(block);
+               block->blocks = 0;
+               block->bp_block = 0;
+               block->s2b_shift = 0;
+       }
        return 0;
 }
 
 /*
  * Back to basic.
  */
-static int
-dasd_state_unfmt_to_basic(struct dasd_device * device)
+static int dasd_state_unfmt_to_basic(struct dasd_device *device)
 {
        device->state = DASD_STATE_BASIC;
        return 0;
 static int
 dasd_state_ready_to_online(struct dasd_device * device)
 {
+       int rc;
+
+       if (device->discipline->ready_to_online) {
+               rc = device->discipline->ready_to_online(device);
+               if (rc)
+                       return rc;
+       }
        device->state = DASD_STATE_ONLINE;
-       dasd_schedule_bh(device);
+       if (device->block)
+               dasd_schedule_block_bh(device->block);
        return 0;
 }
 
 /*
  * Stop the requeueing of requests again.
  */
-static int
-dasd_state_online_to_ready(struct dasd_device * device)
+static int dasd_state_online_to_ready(struct dasd_device *device)
 {
+       int rc;
+
+       if (device->discipline->online_to_ready) {
+               rc = device->discipline->online_to_ready(device);
+               if (rc)
+                       return rc;
+       }
        device->state = DASD_STATE_READY;
        return 0;
 }
 /*
  * Device startup state changes.
  */
-static int
-dasd_increase_state(struct dasd_device *device)
+static int dasd_increase_state(struct dasd_device *device)
 {
        int rc;
 
 /*
  * Device shutdown state changes.
  */
-static int
-dasd_decrease_state(struct dasd_device *device)
+static int dasd_decrease_state(struct dasd_device *device)
 {
        int rc;
 
 /*
  * This is the main startup/shutdown routine.
  */
-static void
-dasd_change_state(struct dasd_device *device)
+static void dasd_change_state(struct dasd_device *device)
 {
         int rc;
 
  * dasd_kick_device will schedule a call do do_kick_device to the kernel
  * event daemon.
  */
-static void
-do_kick_device(struct work_struct *work)
+static void do_kick_device(struct work_struct *work)
 {
        struct dasd_device *device = container_of(work, struct dasd_device, kick_work);
        dasd_change_state(device);
-       dasd_schedule_bh(device);
+       dasd_schedule_device_bh(device);
        dasd_put_device(device);
 }
 
-void
-dasd_kick_device(struct dasd_device *device)
+void dasd_kick_device(struct dasd_device *device)
 {
        dasd_get_device(device);
        /* queue call to dasd_kick_device to the kernel event daemon. */
 /*
  * Set the target state for a device and starts the state change.
  */
-void
-dasd_set_target_state(struct dasd_device *device, int target)
+void dasd_set_target_state(struct dasd_device *device, int target)
 {
        /* If we are in probeonly mode stop at DASD_STATE_READY. */
        if (dasd_probeonly && target > DASD_STATE_READY)
 /*
  * Enable devices with device numbers in [from..to].
  */
-static inline int
-_wait_for_device(struct dasd_device *device)
+static inline int _wait_for_device(struct dasd_device *device)
 {
        return (device->state == device->target);
 }
 
-void
-dasd_enable_device(struct dasd_device *device)
+void dasd_enable_device(struct dasd_device *device)
 {
        dasd_set_target_state(device, DASD_STATE_ONLINE);
        if (device->state <= DASD_STATE_KNOWN)
 /*
  * Increments counter in global and local profiling structures.
  */
-#define dasd_profile_counter(value, counter, device) \
+#define dasd_profile_counter(value, counter, block) \
 { \
        int index; \
        for (index = 0; index < 31 && value >> (2+index); index++); \
        dasd_global_profile.counter[index]++; \
-       device->profile.counter[index]++; \
+       block->profile.counter[index]++; \
 }
 
 /*
  * Add profiling information for cqr before execution.
  */
-static void
-dasd_profile_start(struct dasd_device *device, struct dasd_ccw_req * cqr,
-                  struct request *req)
+static void dasd_profile_start(struct dasd_block *block,
+                              struct dasd_ccw_req *cqr,
+                              struct request *req)
 {
        struct list_head *l;
        unsigned int counter;
 
        /* count the length of the chanq for statistics */
        counter = 0;
-       list_for_each(l, &device->ccw_queue)
+       list_for_each(l, &block->ccw_queue)
                if (++counter >= 31)
                        break;
        dasd_global_profile.dasd_io_nr_req[counter]++;
-       device->profile.dasd_io_nr_req[counter]++;
+       block->profile.dasd_io_nr_req[counter]++;
 }
 
 /*
  * Add profiling information for cqr after execution.
  */
-static void
-dasd_profile_end(struct dasd_device *device, struct dasd_ccw_req * cqr,
-                struct request *req)
+static void dasd_profile_end(struct dasd_block *block,
+                            struct dasd_ccw_req *cqr,
+                            struct request *req)
 {
        long strtime, irqtime, endtime, tottime;        /* in microseconds */
        long tottimeps, sectors;
 
        if (!dasd_global_profile.dasd_io_reqs)
                memset(&dasd_global_profile, 0,
-                      sizeof (struct dasd_profile_info_t));
+                      sizeof(struct dasd_profile_info_t));
        dasd_global_profile.dasd_io_reqs++;
        dasd_global_profile.dasd_io_sects += sectors;
 
-       if (!device->profile.dasd_io_reqs)
-               memset(&device->profile, 0,
-                      sizeof (struct dasd_profile_info_t));
-       device->profile.dasd_io_reqs++;
-       device->profile.dasd_io_sects += sectors;
+       if (!block->profile.dasd_io_reqs)
+               memset(&block->profile, 0,
+                      sizeof(struct dasd_profile_info_t));
+       block->profile.dasd_io_reqs++;
+       block->profile.dasd_io_sects += sectors;
 
-       dasd_profile_counter(sectors, dasd_io_secs, device);
-       dasd_profile_counter(tottime, dasd_io_times, device);
-       dasd_profile_counter(tottimeps, dasd_io_timps, device);
-       dasd_profile_counter(strtime, dasd_io_time1, device);
-       dasd_profile_counter(irqtime, dasd_io_time2, device);
-       dasd_profile_counter(irqtime / sectors, dasd_io_time2ps, device);
-       dasd_profile_counter(endtime, dasd_io_time3, device);
+       dasd_profile_counter(sectors, dasd_io_secs, block);
+       dasd_profile_counter(tottime, dasd_io_times, block);
+       dasd_profile_counter(tottimeps, dasd_io_timps, block);
+       dasd_profile_counter(strtime, dasd_io_time1, block);
+       dasd_profile_counter(irqtime, dasd_io_time2, block);
+       dasd_profile_counter(irqtime / sectors, dasd_io_time2ps, block);
+       dasd_profile_counter(endtime, dasd_io_time3, block);
 }
 #else
-#define dasd_profile_start(device, cqr, req) do {} while (0)
-#define dasd_profile_end(device, cqr, req) do {} while (0)
+#define dasd_profile_start(block, cqr, req) do {} while (0)
+#define dasd_profile_end(block, cqr, req) do {} while (0)
 #endif                         /* CONFIG_DASD_PROFILE */
 
 /*
  * memory and 2) dasd_smalloc_request uses the static ccw memory
  * that gets allocated for each device.
  */
-struct dasd_ccw_req *
-dasd_kmalloc_request(char *magic, int cplength, int datasize,
-                  struct dasd_device * device)
+struct dasd_ccw_req *dasd_kmalloc_request(char *magic, int cplength,
+                                         int datasize,
+                                         struct dasd_device *device)
 {
        struct dasd_ccw_req *cqr;
 
        return cqr;
 }
 
-struct dasd_ccw_req *
-dasd_smalloc_request(char *magic, int cplength, int datasize,
-                  struct dasd_device * device)
+struct dasd_ccw_req *dasd_smalloc_request(char *magic, int cplength,
+                                         int datasize,
+                                         struct dasd_device *device)
 {
        unsigned long flags;
        struct dasd_ccw_req *cqr;
  * idal lists that might have been created by dasd_set_cda and the
  * struct dasd_ccw_req itself.
  */
-void
-dasd_kfree_request(struct dasd_ccw_req * cqr, struct dasd_device * device)
+void dasd_kfree_request(struct dasd_ccw_req *cqr, struct dasd_device *device)
 {
 #ifdef CONFIG_64BIT
        struct ccw1 *ccw;
        dasd_put_device(device);
 }
 
-void
-dasd_sfree_request(struct dasd_ccw_req * cqr, struct dasd_device * device)
+void dasd_sfree_request(struct dasd_ccw_req *cqr, struct dasd_device *device)
 {
        unsigned long flags;
 
 /*
  * Check discipline magic in cqr.
  */
-static inline int
-dasd_check_cqr(struct dasd_ccw_req *cqr)
+static inline int dasd_check_cqr(struct dasd_ccw_req *cqr)
 {
        struct dasd_device *device;
 
        if (cqr == NULL)
                return -EINVAL;
-       device = cqr->device;
+       device = cqr->startdev;
        if (strncmp((char *) &cqr->magic, device->discipline->ebcname, 4)) {
                DEV_MESSAGE(KERN_WARNING, device,
                            " dasd_ccw_req 0x%08x magic doesn't match"
  * ccw_device_clear can fail if the i/o subsystem
  * is in a bad mood.
  */
-int
-dasd_term_IO(struct dasd_ccw_req * cqr)
+int dasd_term_IO(struct dasd_ccw_req *cqr)
 {
        struct dasd_device *device;
        int retries, rc;
        if (rc)
                return rc;
        retries = 0;
-       device = (struct dasd_device *) cqr->device;
+       device = (struct dasd_device *) cqr->startdev;
        while ((retries < 5) && (cqr->status == DASD_CQR_IN_IO)) {
                rc = ccw_device_clear(device->cdev, (long) cqr);
                switch (rc) {
                case 0: /* termination successful */
                        cqr->retries--;
-                       cqr->status = DASD_CQR_CLEAR;
+                       cqr->status = DASD_CQR_CLEAR_PENDING;
                        cqr->stopclk = get_clock();
                        cqr->starttime = 0;
                        DBF_DEV_EVENT(DBF_DEBUG, device,
                }
                retries++;
        }
-       dasd_schedule_bh(device);
+       dasd_schedule_device_bh(device);
        return rc;
 }
 
  * Start the i/o. This start_IO can fail if the channel is really busy.
  * In that case set up a timer to start the request later.
  */
-int
-dasd_start_IO(struct dasd_ccw_req * cqr)
+int dasd_start_IO(struct dasd_ccw_req *cqr)
 {
        struct dasd_device *device;
        int rc;
        rc = dasd_check_cqr(cqr);
        if (rc)
                return rc;
-       device = (struct dasd_device *) cqr->device;
+       device = (struct dasd_device *) cqr->startdev;
        if (cqr->retries < 0) {
                DEV_MESSAGE(KERN_DEBUG, device,
                            "start_IO: request %p (%02x/%i) - no retry left.",
                            cqr, cqr->status, cqr->retries);
-               cqr->status = DASD_CQR_FAILED;
+               cqr->status = DASD_CQR_ERROR;
                return -EIO;
        }
        cqr->startclk = get_clock();
  * The head of the ccw queue will have status DASD_CQR_IN_IO for 1),
  * DASD_CQR_QUEUED for 2) and 3).
  */
-static void
-dasd_timeout_device(unsigned long ptr)
+static void dasd_device_timeout(unsigned long ptr)
 {
        unsigned long flags;
        struct dasd_device *device;
        /* re-activate request queue */
         device->stopped &= ~DASD_STOPPED_PENDING;
        spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
-       dasd_schedule_bh(device);
+       dasd_schedule_device_bh(device);
 }
 
 /*
  * Setup timeout for a device in jiffies.
  */
-void
-dasd_set_timer(struct dasd_device *device, int expires)
+void dasd_device_set_timer(struct dasd_device *device, int expires)
 {
        if (expires == 0) {
                if (timer_pending(&device->timer))
                if (mod_timer(&device->timer, jiffies + expires))
                        return;
        }
-       device->timer.function = dasd_timeout_device;
+       device->timer.function = dasd_device_timeout;
        device->timer.data = (unsigned long) device;
        device->timer.expires = jiffies + expires;
        add_timer(&device->timer);
 /*
  * Clear timeout for a device.
  */
-void
-dasd_clear_timer(struct dasd_device *device)
+void dasd_device_clear_timer(struct dasd_device *device)
 {
        if (timer_pending(&device->timer))
                del_timer(&device->timer);
 }
 
-static void
-dasd_handle_killed_request(struct ccw_device *cdev, unsigned long intparm)
+static void dasd_handle_killed_request(struct ccw_device *cdev,
+                                      unsigned long intparm)
 {
        struct dasd_ccw_req *cqr;
        struct dasd_device *device;
                return;
        }
 
-       device = (struct dasd_device *) cqr->device;
+       device = (struct dasd_device *) cqr->startdev;
        if (device == NULL ||
            device != dasd_device_from_cdev_locked(cdev) ||
            strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) {
        /* Schedule request to be retried. */
        cqr->status = DASD_CQR_QUEUED;
 
-       dasd_clear_timer(device);
-       dasd_schedule_bh(device);
+       dasd_device_clear_timer(device);
+       dasd_schedule_device_bh(device);
        dasd_put_device(device);
 }
 
-static void
-dasd_handle_state_change_pending(struct dasd_device *device)
+void dasd_generic_handle_state_change(struct dasd_device *device)
 {
-       struct dasd_ccw_req *cqr;
-       struct list_head *l, *n;
-
        /* First of all start sense subsystem status request. */
        dasd_eer_snss(device);
 
        device->stopped &= ~DASD_STOPPED_PENDING;
-
-        /* restart all 'running' IO on queue */
-       list_for_each_safe(l, n, &device->ccw_queue) {
-               cqr = list_entry(l, struct dasd_ccw_req, list);
-                if (cqr->status == DASD_CQR_IN_IO) {
-                        cqr->status = DASD_CQR_QUEUED;
-               }
-        }
-       dasd_clear_timer(device);
-       dasd_schedule_bh(device);
+       dasd_schedule_device_bh(device);
+       if (device->block)
+               dasd_schedule_block_bh(device->block);
 }
 
 /*
  * Interrupt handler for "normal" ssch-io based dasd devices.
  */
-void
-dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
-                struct irb *irb)
+void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
+                     struct irb *irb)
 {
        struct dasd_ccw_req *cqr, *next;
        struct dasd_device *device;
        unsigned long long now;
        int expires;
-       dasd_era_t era;
-       char mask;
 
        if (IS_ERR(irb)) {
                switch (PTR_ERR(irb)) {
                  cdev->dev.bus_id, ((irb->scsw.cstat<<8)|irb->scsw.dstat),
                  (unsigned int) intparm);
 
-       /* first of all check for state change pending interrupt */
-       mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP;
-       if ((irb->scsw.dstat & mask) == mask) {
+       /* check for unsolicited interrupts */
+       cqr = (struct dasd_ccw_req *) intparm;
+       if (!cqr || ((irb->scsw.cc == 1) &&
+                    (irb->scsw.fctl & SCSW_FCTL_START_FUNC) &&
+                    (irb->scsw.stctl & SCSW_STCTL_STATUS_PEND)) ) {
+               if (cqr && cqr->status == DASD_CQR_IN_IO)
+                       cqr->status = DASD_CQR_QUEUED;
                device = dasd_device_from_cdev_locked(cdev);
                if (!IS_ERR(device)) {
-                       dasd_handle_state_change_pending(device);
+                       dasd_device_clear_timer(device);
+                       device->discipline->handle_unsolicited_interrupt(device,
+                                                                        irb);
                        dasd_put_device(device);
                }
                return;
        }
 
-       cqr = (struct dasd_ccw_req *) intparm;
-
-       /* check for unsolicited interrupts */
-       if (cqr == NULL) {
-               MESSAGE(KERN_DEBUG,
-                       "unsolicited interrupt received: bus_id %s",
-                       cdev->dev.bus_id);
-               return;
-       }
-
-       device = (struct dasd_device *) cqr->device;
-       if (device == NULL ||
+       device = (struct dasd_device *) cqr->startdev;
+       if (!device ||
            strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) {
                MESSAGE(KERN_DEBUG, "invalid device in request: bus_id %s",
                        cdev->dev.bus_id);
        }
 
        /* Check for clear pending */
-       if (cqr->status == DASD_CQR_CLEAR &&
+       if (cqr->status == DASD_CQR_CLEAR_PENDING &&
            irb->scsw.fctl & SCSW_FCTL_CLEAR_FUNC) {
-               cqr->status = DASD_CQR_QUEUED;
-               dasd_clear_timer(device);
+               cqr->status = DASD_CQR_CLEARED;
+               dasd_device_clear_timer(device);
                wake_up(&dasd_flush_wq);
-               dasd_schedule_bh(device);
+               dasd_schedule_device_bh(device);
                return;
        }
 
        }
        DBF_DEV_EVENT(DBF_DEBUG, device, "Int: CS/DS 0x%04x for cqr %p",
                      ((irb->scsw.cstat << 8) | irb->scsw.dstat), cqr);
-
-       /* Find out the appropriate era_action. */
-       if (irb->scsw.fctl & SCSW_FCTL_HALT_FUNC)
-               era = dasd_era_fatal;
-       else if (irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END) &&
-                irb->scsw.cstat == 0 &&
-                !irb->esw.esw0.erw.cons)
-               era = dasd_era_none;
-       else if (irb->esw.esw0.erw.cons)
-               era = device->discipline->examine_error(cqr, irb);
-       else
-               era = dasd_era_recover;
-
-       DBF_DEV_EVENT(DBF_DEBUG, device, "era_code %d", era);
+       next = NULL;
        expires = 0;
-       if (era == dasd_era_none) {
-               cqr->status = DASD_CQR_DONE;
+       if (irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END) &&
+           irb->scsw.cstat == 0 && !irb->esw.esw0.erw.cons) {
+               /* request was completed successfully */
+               cqr->status = DASD_CQR_SUCCESS;
                cqr->stopclk = now;
                /* Start first request on queue if possible -> fast_io. */
-               if (cqr->list.next != &device->ccw_queue) {
-                       next = list_entry(cqr->list.next,
-                                         struct dasd_ccw_req, list);
-                       if ((next->status == DASD_CQR_QUEUED) &&
-                           (!device->stopped)) {
-                               if (device->discipline->start_IO(next) == 0)
-                                       expires = next->expires;
-                               else
-                                       DEV_MESSAGE(KERN_DEBUG, device, "%s",
-                                                   "Interrupt fastpath "
-                                                   "failed!");
-                       }
+               if (cqr->devlist.next != &device->ccw_queue) {
+                       next = list_entry(cqr->devlist.next,
+                                         struct dasd_ccw_req, devlist);
                }
-       } else {                /* error */
-               memcpy(&cqr->irb, irb, sizeof (struct irb));
+       } else {  /* error */
+               memcpy(&cqr->irb, irb, sizeof(struct irb));
                if (device->features & DASD_FEATURE_ERPLOG) {
-                       /* dump sense data */
                        dasd_log_sense(cqr, irb);
                }
-               switch (era) {
-               case dasd_era_fatal:
-                       cqr->status = DASD_CQR_FAILED;
-                       cqr->stopclk = now;
-                       break;
-               case dasd_era_recover:
+               /* If we have no sense data, or we just don't want complex ERP
+                * for this request, but if we have retries left, then just
+                * reset this request and retry it in the fastpath
+                */
+               if (!(cqr->irb.esw.esw0.erw.cons &&
+                     test_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags)) &&
+                   cqr->retries > 0) {
+                       DEV_MESSAGE(KERN_DEBUG, device,
+                                   "default ERP in fastpath (%i retries left)",
+                                   cqr->retries);
+                       cqr->lpm    = LPM_ANYPATH;
+                       cqr->status = DASD_CQR_QUEUED;
+                       next = cqr;
+               } else
                        cqr->status = DASD_CQR_ERROR;
-                       break;
-               default:
-                       BUG();
-               }
+       }
+       if (next && (next->status == DASD_CQR_QUEUED) &&
+           (!device->stopped)) {
+               if (device->discipline->start_IO(next) == 0)
+                       expires = next->expires;
+               else
+                       DEV_MESSAGE(KERN_DEBUG, device, "%s",
+                                   "Interrupt fastpath "
+                                   "failed!");
        }
        if (expires != 0)
-               dasd_set_timer(device, expires);
+               dasd_device_set_timer(device, expires);
        else
-               dasd_clear_timer(device);
-       dasd_schedule_bh(device);
+               dasd_device_clear_timer(device);
+       dasd_schedule_device_bh(device);
 }
 
 /*
- * posts the buffer_cache about a finalized request
+ * If we have an error on a dasd_block layer request then we cancel
+ * and return all further requests from the same dasd_block as well.
  */
-static inline void
-dasd_end_request(struct request *req, int uptodate)
+static void __dasd_device_recovery(struct dasd_device *device,
+                                  struct dasd_ccw_req *ref_cqr)
 {
-       if (end_that_request_first(req, uptodate, req->hard_nr_sectors))
-               BUG();
-       add_disk_randomness(req->rq_disk);
-       end_that_request_last(req, uptodate);
-}
+       struct list_head *l, *n;
+       struct dasd_ccw_req *cqr;
 
-/*
- * Process finished error recovery ccw.
- */
-static inline void
-__dasd_process_erp(struct dasd_device *device, struct dasd_ccw_req *cqr)
-{
-       dasd_erp_fn_t erp_fn;
+       /*
+        * only requeue request that came from the dasd_block layer
+        */
+       if (!ref_cqr->block)
+               return;
 
-       if (cqr->status == DASD_CQR_DONE)
-               DBF_DEV_EVENT(DBF_NOTICE, device, "%s", "ERP successful");
-       else
-               DEV_MESSAGE(KERN_ERR, device, "%s", "ERP unsuccessful");
-       erp_fn = device->discipline->erp_postaction(cqr);
-       erp_fn(cqr);
-}
+       list_for_each_safe(l, n, &device->ccw_queue) {
+               cqr = list_entry(l, struct dasd_ccw_req, devlist);
+               if (cqr->status == DASD_CQR_QUEUED &&
+                   ref_cqr->block == cqr->block) {
+                       cqr->status = DASD_CQR_CLEARED;
+               }
+       }
+};
 
 /*
- * Process ccw request queue.
+ * Remove those ccw requests from the queue that need to be returned
+ * to the upper layer.
  */
-static void
-__dasd_process_ccw_queue(struct dasd_device * device,
-                        struct list_head *final_queue)
+static void __dasd_device_process_ccw_queue(struct dasd_device *device,
+                                           struct list_head *final_queue)
 {
        struct list_head *l, *n;
        struct dasd_ccw_req *cqr;
-       dasd_erp_fn_t erp_fn;
 
-restart:
        /* Process request with final status. */
        list_for_each_safe(l, n, &device->ccw_queue) {
-               cqr = list_entry(l, struct dasd_ccw_req, list);
+               cqr = list_entry(l, struct dasd_ccw_req, devlist);
+
                /* Stop list processing at the first non-final request. */
-               if (cqr->status != DASD_CQR_DONE &&
-                   cqr->status != DASD_CQR_FAILED &&
-                   cqr->status != DASD_CQR_ERROR)
+               if (cqr->status == DASD_CQR_QUEUED ||
+                   cqr->status == DASD_CQR_IN_IO ||
+                   cqr->status == DASD_CQR_CLEAR_PENDING)
                        break;
-               /*  Process requests with DASD_CQR_ERROR */
                if (cqr->status == DASD_CQR_ERROR) {
-                       if (cqr->irb.scsw.fctl & SCSW_FCTL_HALT_FUNC) {
-                               cqr->status = DASD_CQR_FAILED;
-                               cqr->stopclk = get_clock();
-                       } else {
-                               if (cqr->irb.esw.esw0.erw.cons &&
-                                   test_bit(DASD_CQR_FLAGS_USE_ERP,
-                                            &cqr->flags)) {
-                                       erp_fn = device->discipline->
-                                               erp_action(cqr);
-                                       erp_fn(cqr);
-                               } else
-                                       dasd_default_erp_action(cqr);
-                       }
-                       goto restart;
-               }
-
-               /* First of all call extended error reporting. */
-               if (dasd_eer_enabled(device) &&
-                   cqr->status == DASD_CQR_FAILED) {
-                       dasd_eer_write(device, cqr, DASD_EER_FATALERROR);
-
-                       /* restart request  */
-                       cqr->status = DASD_CQR_QUEUED;
-                       cqr->retries = 255;
-                       device->stopped |= DASD_STOPPED_QUIESCE;
-                       goto restart;
+                       __dasd_device_recovery(device, cqr);
                }
-
-               /* Process finished ERP request. */
-               if (cqr->refers) {
-                       __dasd_process_erp(device, cqr);
-                       goto restart;
-               }
-
                /* Rechain finished requests to final queue */
-               cqr->endclk = get_clock();
-               list_move_tail(&cqr->list, final_queue);
+               list_move_tail(&cqr->devlist, final_queue);
        }
 }
 
-static void
-dasd_end_request_cb(struct dasd_ccw_req * cqr, void *data)
-{
-       struct request *req;
-       struct dasd_device *device;
-       int status;
-
-       req = (struct request *) data;
-       device = cqr->device;
-       dasd_profile_end(device, cqr, req);
-       status = cqr->device->discipline->free_cp(cqr,req);
-       spin_lock_irq(&device->request_queue_lock);
-       dasd_end_request(req, status);
-       spin_unlock_irq(&device->request_queue_lock);
-}
-
-
 /*
- * Fetch requests from the block device queue.
+ * the cqrs from the final queue are returned to the upper layer
+ * by setting a dasd_block state and calling the callback function
  */
-static void
-__dasd_process_blk_queue(struct dasd_device * device)
+static void __dasd_device_process_final_queue(struct dasd_device *device,
+                                             struct list_head *final_queue)
 {
-       struct request_queue *queue;
-       struct request *req;
+       struct list_head *l, *n;
        struct dasd_ccw_req *cqr;
-       int nr_queued;
-
-       queue = device->request_queue;
-       /* No queue ? Then there is nothing to do. */
-       if (queue == NULL)
-               return;
-
-       /*
-        * We requeue request from the block device queue to the ccw
-        * queue only in two states. In state DASD_STATE_READY the
-        * partition detection is done and we need to requeue requests
-        * for that. State DASD_STATE_ONLINE is normal block device
-        * operation.
-        */
-       if (device->state != DASD_STATE_READY &&
-           device->state != DASD_STATE_ONLINE)
-               return;
-       nr_queued = 0;
-       /* Now we try to fetch requests from the request queue */
-       list_for_each_entry(cqr, &device->ccw_queue, list)
-               if (cqr->status == DASD_CQR_QUEUED)
-                       nr_queued++;
-       while (!blk_queue_plugged(queue) &&
-              elv_next_request(queue) &&
-               nr_queued < DASD_CHANQ_MAX_SIZE) {
-               req = elv_next_request(queue);
 
-               if (device->features & DASD_FEATURE_READONLY &&
-                   rq_data_dir(req) == WRITE) {
-                       DBF_DEV_EVENT(DBF_ERR, device,
-                                     "Rejecting write request %p",
-                                     req);
-                       blkdev_dequeue_request(req);
-                       dasd_end_request(req, 0);
-                       continue;
-               }
-               if (device->stopped & DASD_STOPPED_DC_EIO) {
-                       blkdev_dequeue_request(req);
-                       dasd_end_request(req, 0);
-                       continue;
-               }
-               cqr = device->discipline->build_cp(device, req);
-               if (IS_ERR(cqr)) {
-                       if (PTR_ERR(cqr) == -ENOMEM)
-                               break;  /* terminate request queue loop */
-                       if (PTR_ERR(cqr) == -EAGAIN) {
-                               /*
-                                * The current request cannot be build right
-                                * now, we have to try later. If this request
-                                * is the head-of-queue we stop the device
-                                * for 1/2 second.
-                                */
-                               if (!list_empty(&device->ccw_queue))
-                                       break;
-                               device->stopped |= DASD_STOPPED_PENDING;
-                               dasd_set_timer(device, HZ/2);
-                               break;
-                       }
-                       DBF_DEV_EVENT(DBF_ERR, device,
-                                     "CCW creation failed (rc=%ld) "
-                                     "on request %p",
-                                     PTR_ERR(cqr), req);
-                       blkdev_dequeue_request(req);
-                       dasd_end_request(req, 0);
-                       continue;
+       list_for_each_safe(l, n, final_queue) {
+               cqr = list_entry(l, struct dasd_ccw_req, devlist);
+               list_del_init(&cqr->devlist);
+               if (cqr->block)
+                       spin_lock_bh(&cqr->block->queue_lock);
+               switch (cqr->status) {
+               case DASD_CQR_SUCCESS:
+                       cqr->status = DASD_CQR_DONE;
+                       break;
+               case DASD_CQR_ERROR:
+                       cqr->status = DASD_CQR_NEED_ERP;
+                       break;
+               case DASD_CQR_CLEARED:
+                       cqr->status = DASD_CQR_TERMINATED;
+                       break;
+               default:
+                       DEV_MESSAGE(KERN_ERR, device,
+                                   "wrong cqr status in __dasd_process_final_queue "
+                                   "for cqr %p, status %x",
+                                   cqr, cqr->status);
+                       BUG();
                }
-               cqr->callback = dasd_end_request_cb;
-               cqr->callback_data = (void *) req;
-               cqr->status = DASD_CQR_QUEUED;
-               blkdev_dequeue_request(req);
-               list_add_tail(&cqr->list, &device->ccw_queue);
-               dasd_profile_start(device, cqr, req);
-               nr_queued++;
+               if (cqr->block)
+                       spin_unlock_bh(&cqr->block->queue_lock);
+               if (cqr->callback != NULL)
+                       (cqr->callback)(cqr, cqr->callback_data);
        }
 }
 
+
+
 /*
  * Take a look at the first request on the ccw queue and check
  * if it reached its expire time. If so, terminate the IO.
  */
-static void
-__dasd_check_expire(struct dasd_device * device)
+static void __dasd_device_check_expire(struct dasd_device *device)
 {
        struct dasd_ccw_req *cqr;
 
        if (list_empty(&device->ccw_queue))
                return;
-       cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, list);
+       cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, devlist);
        if ((cqr->status == DASD_CQR_IN_IO && cqr->expires != 0) &&
            (time_after_eq(jiffies, cqr->expires + cqr->starttime))) {
                if (device->discipline->term_IO(cqr) != 0) {
                        /* Hmpf, try again in 5 sec */
-                       dasd_set_timer(device, 5*HZ);
+                       dasd_device_set_timer(device, 5*HZ);
                        DEV_MESSAGE(KERN_ERR, device,
                                    "internal error - timeout (%is) expired "
                                    "for cqr %p, termination failed, "
  * Take a look at the first request on the ccw queue and check
  * if it needs to be started.
  */
-static void
-__dasd_start_head(struct dasd_device * device)
+static void __dasd_device_start_head(struct dasd_device *device)
 {
        struct dasd_ccw_req *cqr;
        int rc;
 
        if (list_empty(&device->ccw_queue))
                return;
-       cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, list);
+       cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, devlist);
        if (cqr->status != DASD_CQR_QUEUED)
                return;
-       /* Non-temporary stop condition will trigger fail fast */
-       if (device->stopped & ~DASD_STOPPED_PENDING &&
-           test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) &&
-           (!dasd_eer_enabled(device))) {
-               cqr->status = DASD_CQR_FAILED;
-               dasd_schedule_bh(device);
+       /* when device is stopped, return request to previous layer */
+       if (device->stopped) {
+               cqr->status = DASD_CQR_CLEARED;
+               dasd_schedule_device_bh(device);
                return;
        }
-       /* Don't try to start requests if device is stopped */
-       if (device->stopped)
-               return;
 
        rc = device->discipline->start_IO(cqr);
        if (rc == 0)
-               dasd_set_timer(device, cqr->expires);
+               dasd_device_set_timer(device, cqr->expires);
        else if (rc == -EACCES) {
-               dasd_schedule_bh(device);
+               dasd_schedule_device_bh(device);
        } else
                /* Hmpf, try again in 1/2 sec */
-               dasd_set_timer(device, 50);
-}
-
-static inline int
-_wait_for_clear(struct dasd_ccw_req *cqr)
-{
-       return (cqr->status == DASD_CQR_QUEUED);
+               dasd_device_set_timer(device, 50);
 }
 
 /*
- * Remove all requests from the ccw queue (all = '1') or only block device
- * requests in case all = '0'.
- * Take care of the erp-chain (chained via cqr->refers) and remove either
- * the whole erp-chain or none of the erp-requests.
- * If a request is currently running, term_IO is called and the request
- * is re-queued. Prior to removing the terminated request we need to wait
- * for the clear-interrupt.
- * In case termination is not possible we stop processing and just finishing
- * the already moved requests.
+ * Go through all request on the dasd_device request queue,
+ * terminate them on the cdev if necessary, and return them to the
+ * submitting layer via callback.
+ * Note:
+ * Make sure that all 'submitting layers' still exist when
+ * this function is called!. In other words, when 'device' is a base
+ * device then all block layer requests must have been removed before
+ * via dasd_flush_block_queue.
  */
-static int
-dasd_flush_ccw_queue(struct dasd_device * device, int all)
+int dasd_flush_device_queue(struct dasd_device *device)
 {
-       struct dasd_ccw_req *cqr, *orig, *n;
-       int rc, i;
-
+       struct dasd_ccw_req *cqr, *n;
+       int rc;
        struct list_head flush_queue;
 
        INIT_LIST_HEAD(&flush_queue);
        spin_lock_irq(get_ccwdev_lock(device->cdev));
        rc = 0;
-restart:
-       list_for_each_entry_safe(cqr, n, &device->ccw_queue, list) {
-               /* get original request of erp request-chain */
-               for (orig = cqr; orig->refers != NULL; orig = orig->refers);
-
-               /* Flush all request or only block device requests? */
-               if (all == 0 && cqr->callback != dasd_end_request_cb &&
-                   orig->callback != dasd_end_request_cb) {
-                       continue;
-               }
+       list_for_each_entry_safe(cqr, n, &device->ccw_queue, devlist) {
                /* Check status and move request to flush_queue */
                switch (cqr->status) {
                case DASD_CQR_IN_IO:
                        }
                        break;
                case DASD_CQR_QUEUED:
-               case DASD_CQR_ERROR:
-                       /* set request to FAILED */
                        cqr->stopclk = get_clock();
-                       cqr->status = DASD_CQR_FAILED;
+                       cqr->status = DASD_CQR_CLEARED;
                        break;
-               default: /* do not touch the others */
+               default: /* no need to modify the others */
                        break;
                }
-               /* Rechain request (including erp chain) */
-               for (i = 0; cqr != NULL; cqr = cqr->refers, i++) {
-                       cqr->endclk = get_clock();
-                       list_move_tail(&cqr->list, &flush_queue);
-               }
-               if (i > 1)
-                       /* moved more than one request - need to restart */
-                       goto restart;
+               list_move_tail(&cqr->devlist, &flush_queue);
        }
-
 finished:
        spin_unlock_irq(get_ccwdev_lock(device->cdev));
-       /* Now call the callback function of flushed requests */
-restart_cb:
-       list_for_each_entry_safe(cqr, n, &flush_queue, list) {
-               if (cqr->status == DASD_CQR_CLEAR) {
-                       /* wait for clear interrupt! */
-                       wait_event(dasd_flush_wq, _wait_for_clear(cqr));
-                       cqr->status = DASD_CQR_FAILED;
-               }
-               /* Process finished ERP request. */
-               if (cqr->refers) {
-                       __dasd_process_erp(device, cqr);
-                       /* restart list_for_xx loop since dasd_process_erp
-                        * might remove multiple elements */
-                       goto restart_cb;
-               }
-               /* call the callback function */
-               cqr->endclk = get_clock();
-               if (cqr->callback != NULL)
-                       (cqr->callback)(cqr, cqr->callback_data);
-       }
+       /*
+        * After this point all requests must be in state CLEAR_PENDING,
+        * CLEARED, SUCCESS or ERROR. Now wait for CLEAR_PENDING to become
+        * one of the others.
+        */
+       list_for_each_entry_safe(cqr, n, &flush_queue, devlist)
+               wait_event(dasd_flush_wq,
+                          (cqr->status != DASD_CQR_CLEAR_PENDING));
+       /*
+        * Now set each request back to TERMINATED, DONE or NEED_ERP
+        * and call the callback function of flushed requests
+        */
+       __dasd_device_process_final_queue(device, &flush_queue);
        return rc;
 }
 
 /*
  * Acquire the device lock and process queues for the device.
  */
-static void
-dasd_tasklet(struct dasd_device * device)
+static void dasd_device_tasklet(struct dasd_device *device)
 {
        struct list_head final_queue;
-       struct list_head *l, *n;
-       struct dasd_ccw_req *cqr;
 
        atomic_set (&device->tasklet_scheduled, 0);
        INIT_LIST_HEAD(&final_queue);
        spin_lock_irq(get_ccwdev_lock(device->cdev));
        /* Check expire time of first request on the ccw queue. */
-       __dasd_check_expire(device);
-       /* Finish off requests on ccw queue */
-       __dasd_process_ccw_queue(device, &final_queue);
+       __dasd_device_check_expire(device);
+       /* find final requests on ccw queue */
+       __dasd_device_process_ccw_queue(device, &final_queue);
        spin_unlock_irq(get_ccwdev_lock(device->cdev));
        /* Now call the callback function of requests with final status */
-       list_for_each_safe(l, n, &final_queue) {
-               cqr = list_entry(l, struct dasd_ccw_req, list);
-               list_del_init(&cqr->list);
-               if (cqr->callback != NULL)
-                       (cqr->callback)(cqr, cqr->callback_data);
-       }
-       spin_lock_irq(&device->request_queue_lock);
-       spin_lock(get_ccwdev_lock(device->cdev));
-       /* Get new request from the block device request queue */
-       __dasd_process_blk_queue(device);
+       __dasd_device_process_final_queue(device, &final_queue);
+       spin_lock_irq(get_ccwdev_lock(device->cdev));
        /* Now check if the head of the ccw queue needs to be started. */
-       __dasd_start_head(device);
-       spin_unlock(get_ccwdev_lock(device->cdev));
-       spin_unlock_irq(&device->request_queue_lock);
+       __dasd_device_start_head(device);
+       spin_unlock_irq(get_ccwdev_lock(device->cdev));
        dasd_put_device(device);
 }
 
 /*
  * Schedules a call to dasd_tasklet over the device tasklet.
  */
-void
-dasd_schedule_bh(struct dasd_device * device)
+void dasd_schedule_device_bh(struct dasd_device *device)
 {
        /* Protect against rescheduling. */
        if (atomic_cmpxchg (&device->tasklet_scheduled, 0, 1) != 0)
 }
 
 /*
- * Queue a request to the head of the ccw_queue. Start the I/O if
- * possible.
+ * Queue a request to the head of the device ccw_queue.
+ * Start the I/O if possible.
  */
-void
-dasd_add_request_head(struct dasd_ccw_req *req)
+void dasd_add_request_head(struct dasd_ccw_req *cqr)
 {
        struct dasd_device *device;
        unsigned long flags;
 
-       device = req->device;
+       device = cqr->startdev;
        spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
-       req->status = DASD_CQR_QUEUED;
-       req->device = device;
-       list_add(&req->list, &device->ccw_queue);
+       cqr->status = DASD_CQR_QUEUED;
+       list_add(&cqr->devlist, &device->ccw_queue);
        /* let the bh start the request to keep them in order */
-       dasd_schedule_bh(device);
+       dasd_schedule_device_bh(device);
        spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
 }
 
 /*
- * Queue a request to the tail of the ccw_queue. Start the I/O if
- * possible.
+ * Queue a request to the tail of the device ccw_queue.
+ * Start the I/O if possible.
  */
-void
-dasd_add_request_tail(struct dasd_ccw_req *req)
+void dasd_add_request_tail(struct dasd_ccw_req *cqr)
 {
        struct dasd_device *device;
        unsigned long flags;
 
-       device = req->device;
+       device = cqr->startdev;
        spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
-       req->status = DASD_CQR_QUEUED;
-       req->device = device;
-       list_add_tail(&req->list, &device->ccw_queue);
+       cqr->status = DASD_CQR_QUEUED;
+       list_add_tail(&cqr->devlist, &device->ccw_queue);
        /* let the bh start the request to keep them in order */
-       dasd_schedule_bh(device);
+       dasd_schedule_device_bh(device);
        spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
 }
 
 /*
- * Wakeup callback.
+ * Wakeup helper for the 'sleep_on' functions.
  */
-static void
-dasd_wakeup_cb(struct dasd_ccw_req *cqr, void *data)
+static void dasd_wakeup_cb(struct dasd_ccw_req *cqr, void *data)
 {
        wake_up((wait_queue_head_t *) data);
 }
 
-static inline int
-_wait_for_wakeup(struct dasd_ccw_req *cqr)
+static inline int _wait_for_wakeup(struct dasd_ccw_req *cqr)
 {
        struct dasd_device *device;
        int rc;
 
-       device = cqr->device;
+       device = cqr->startdev;
        spin_lock_irq(get_ccwdev_lock(device->cdev));
        rc = ((cqr->status == DASD_CQR_DONE ||
-              cqr->status == DASD_CQR_FAILED) &&
-             list_empty(&cqr->list));
+              cqr->status == DASD_CQR_NEED_ERP ||
+              cqr->status == DASD_CQR_TERMINATED) &&
+             list_empty(&cqr->devlist));
        spin_unlock_irq(get_ccwdev_lock(device->cdev));
        return rc;
 }
 
 /*
- * Attempts to start a special ccw queue and waits for its completion.
+ * Queue a request to the tail of the device ccw_queue and wait for
+ * it's completion.
  */
-int
-dasd_sleep_on(struct dasd_ccw_req * cqr)
+int dasd_sleep_on(struct dasd_ccw_req *cqr)
 {
        wait_queue_head_t wait_q;
        struct dasd_device *device;
        int rc;
 
-       device = cqr->device;
-       spin_lock_irq(get_ccwdev_lock(device->cdev));
+       device = cqr->startdev;
 
        init_waitqueue_head (&wait_q);
        cqr->callback = dasd_wakeup_cb;
        cqr->callback_data = (void *) &wait_q;
-       cqr->status = DASD_CQR_QUEUED;
-       list_add_tail(&cqr->list, &device->ccw_queue);
-
-       /* let the bh start the request to keep them in order */
-       dasd_schedule_bh(device);
-
-       spin_unlock_irq(get_ccwdev_lock(device->cdev));
-
+       dasd_add_request_tail(cqr);
        wait_event(wait_q, _wait_for_wakeup(cqr));
 
        /* Request status is either done or failed. */
-       rc = (cqr->status == DASD_CQR_FAILED) ? -EIO : 0;
+       rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO;
        return rc;
 }
 
 /*
- * Attempts to start a special ccw queue and wait interruptible
- * for its completion.
+ * Queue a request to the tail of the device ccw_queue and wait
+ * interruptible for it's completion.
  */
-int
-dasd_sleep_on_interruptible(struct dasd_ccw_req * cqr)
+int dasd_sleep_on_interruptible(struct dasd_ccw_req *cqr)
 {
        wait_queue_head_t wait_q;
        struct dasd_device *device;
-       int rc, finished;
-
-       device = cqr->device;
-       spin_lock_irq(get_ccwdev_lock(device->cdev));
+       int rc;
 
+       device = cqr->startdev;
        init_waitqueue_head (&wait_q);
        cqr->callback = dasd_wakeup_cb;
        cqr->callback_data = (void *) &wait_q;
-       cqr->status = DASD_CQR_QUEUED;
-       list_add_tail(&cqr->list, &device->ccw_queue);
-
-       /* let the bh start the request to keep them in order */
-       dasd_schedule_bh(device);
-       spin_unlock_irq(get_ccwdev_lock(device->cdev));
-
-       finished = 0;
-       while (!finished) {
-               rc = wait_event_interruptible(wait_q, _wait_for_wakeup(cqr));
-               if (rc != -ERESTARTSYS) {
-                       /* Request is final (done or failed) */
-                       rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO;
-                       break;
-               }
-               spin_lock_irq(get_ccwdev_lock(device->cdev));
-               switch (cqr->status) {
-               case DASD_CQR_IN_IO:
-                        /* terminate runnig cqr */
-                       if (device->discipline->term_IO) {
-                               cqr->retries = -1;
-                               device->discipline->term_IO(cqr);
-                               /* wait (non-interruptible) for final status
-                                * because signal ist still pending */
-                               spin_unlock_irq(get_ccwdev_lock(device->cdev));
-                               wait_event(wait_q, _wait_for_wakeup(cqr));
-                               spin_lock_irq(get_ccwdev_lock(device->cdev));
-                               rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO;
-                               finished = 1;
-                       }
-                       break;
-               case DASD_CQR_QUEUED:
-                       /* request  */
-                       list_del_init(&cqr->list);
-                       rc = -EIO;
-                       finished = 1;
-                       break;
-               default:
-                       /* cqr with 'non-interruptable' status - just wait */
-                       break;
-               }
-               spin_unlock_irq(get_ccwdev_lock(device->cdev));
+       dasd_add_request_tail(cqr);
+       rc = wait_event_interruptible(wait_q, _wait_for_wakeup(cqr));
+       if (rc == -ERESTARTSYS) {
+               dasd_cancel_req(cqr);
+               /* wait (non-interruptible) for final status */
+               wait_event(wait_q, _wait_for_wakeup(cqr));
        }
+       rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO;
        return rc;
 }
 
  * and be put back to status queued, before the special request is added
  * to the head of the queue. Then the special request is waited on normally.
  */
-static inline int
-_dasd_term_running_cqr(struct dasd_device *device)
+static inline int _dasd_term_running_cqr(struct dasd_device *device)
 {
        struct dasd_ccw_req *cqr;
 
        if (list_empty(&device->ccw_queue))
                return 0;
-       cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, list);
+       cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, devlist);
        return device->discipline->term_IO(cqr);
 }
 
-int
-dasd_sleep_on_immediatly(struct dasd_ccw_req * cqr)
+int dasd_sleep_on_immediatly(struct dasd_ccw_req *cqr)
 {
        wait_queue_head_t wait_q;
        struct dasd_device *device;
        int rc;
 
-       device = cqr->device;
+       device = cqr->startdev;
        spin_lock_irq(get_ccwdev_lock(device->cdev));
        rc = _dasd_term_running_cqr(device);
        if (rc) {
        cqr->callback = dasd_wakeup_cb;
        cqr->callback_data = (void *) &wait_q;
        cqr->status = DASD_CQR_QUEUED;
-       list_add(&cqr->list, &device->ccw_queue);
+       list_add(&cqr->devlist, &device->ccw_queue);
 
        /* let the bh start the request to keep them in order */
-       dasd_schedule_bh(device);
+       dasd_schedule_device_bh(device);
 
        spin_unlock_irq(get_ccwdev_lock(device->cdev));
 
        wait_event(wait_q, _wait_for_wakeup(cqr));
 
        /* Request status is either done or failed. */
-       rc = (cqr->status == DASD_CQR_FAILED) ? -EIO : 0;
+       rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO;
        return rc;
 }
 
  * This is useful to timeout requests. The request will be
  * terminated if it is currently in i/o.
  * Returns 1 if the request has been terminated.
+ *        0 if there was no need to terminate the request (not started yet)
+ *        negative error code if termination failed
+ * Cancellation of a request is an asynchronous operation! The calling
+ * function has to wait until the request is properly returned via callback.
  */
-int
-dasd_cancel_req(struct dasd_ccw_req *cqr)
+int dasd_cancel_req(struct dasd_ccw_req *cqr)
 {
-       struct dasd_device *device = cqr->device;
+       struct dasd_device *device = cqr->startdev;
        unsigned long flags;
        int rc;
 
        spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
        switch (cqr->status) {
        case DASD_CQR_QUEUED:
-               /* request was not started - just set to failed */
-               cqr->status = DASD_CQR_FAILED;
+               /* request was not started - just set to cleared */
+               cqr->status = DASD_CQR_CLEARED;
                break;
        case DASD_CQR_IN_IO:
                /* request in IO - terminate IO and release again */
-               if (device->discipline->term_IO(cqr) != 0)
-                       /* what to do if unable to terminate ??????
-                          e.g. not _IN_IO */
-                       cqr->status = DASD_CQR_FAILED;
-               cqr->stopclk = get_clock();
-               rc = 1;
+               rc = device->discipline->term_IO(cqr);
+               if (rc) {
+                       DEV_MESSAGE(KERN_ERR, device,
+                                   "dasd_cancel_req is unable "
+                                   " to terminate request %p, rc = %d",
+                                   cqr, rc);
+               } else {
+                       cqr->stopclk = get_clock();
+                       rc = 1;
+               }
                break;
-       case DASD_CQR_DONE:
-       case DASD_CQR_FAILED:
-               /* already finished - do nothing */
+       default: /* already finished or clear pending - do nothing */
                break;
-       default:
-               DEV_MESSAGE(KERN_ALERT, device,
-                           "invalid status %02x in request",
-                           cqr->status);
+       }
+       spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
+       dasd_schedule_device_bh(device);
+       return rc;
+}
+
+
+/*
+ * SECTION: Operations of the dasd_block layer.
+ */
+
+/*
+ * Timeout function for dasd_block. This is used when the block layer
+ * is waiting for something that may not come reliably, (e.g. a state
+ * change interrupt)
+ */
+static void dasd_block_timeout(unsigned long ptr)
+{
+       unsigned long flags;
+       struct dasd_block *block;
+
+       block = (struct dasd_block *) ptr;
+       spin_lock_irqsave(get_ccwdev_lock(block->base->cdev), flags);
+       /* re-activate request queue */
+       block->base->stopped &= ~DASD_STOPPED_PENDING;
+       spin_unlock_irqrestore(get_ccwdev_lock(block->base->cdev), flags);
+       dasd_schedule_block_bh(block);
+}
+
+/*
+ * Setup timeout for a dasd_block in jiffies.
+ */
+void dasd_block_set_timer(struct dasd_block *block, int expires)
+{
+       if (expires == 0) {
+               if (timer_pending(&block->timer))
+                       del_timer(&block->timer);
+               return;
+       }
+       if (timer_pending(&block->timer)) {
+               if (mod_timer(&block->timer, jiffies + expires))
+                       return;
+       }
+       block->timer.function = dasd_block_timeout;
+       block->timer.data = (unsigned long) block;
+       block->timer.expires = jiffies + expires;
+       add_timer(&block->timer);
+}
+
+/*
+ * Clear timeout for a dasd_block.
+ */
+void dasd_block_clear_timer(struct dasd_block *block)
+{
+       if (timer_pending(&block->timer))
+               del_timer(&block->timer);
+}
+
+/*
+ * posts the buffer_cache about a finalized request
+ */
+static inline void dasd_end_request(struct request *req, int uptodate)
+{
+       if (end_that_request_first(req, uptodate, req->hard_nr_sectors))
                BUG();
+       add_disk_randomness(req->rq_disk);
+       end_that_request_last(req, uptodate);
+}
+
+/*
+ * Process finished error recovery ccw.
+ */
+static inline void __dasd_block_process_erp(struct dasd_block *block,
+                                           struct dasd_ccw_req *cqr)
+{
+       dasd_erp_fn_t erp_fn;
+       struct dasd_device *device = block->base;
+
+       if (cqr->status == DASD_CQR_DONE)
+               DBF_DEV_EVENT(DBF_NOTICE, device, "%s", "ERP successful");
+       else
+               DEV_MESSAGE(KERN_ERR, device, "%s", "ERP unsuccessful");
+       erp_fn = device->discipline->erp_postaction(cqr);
+       erp_fn(cqr);
+}
 
+/*
+ * Fetch requests from the block device queue.
+ */
+static void __dasd_process_request_queue(struct dasd_block *block)
+{
+       struct request_queue *queue;
+       struct request *req;
+       struct dasd_ccw_req *cqr;
+       struct dasd_device *basedev;
+       unsigned long flags;
+       queue = block->request_queue;
+       basedev = block->base;
+       /* No queue ? Then there is nothing to do. */
+       if (queue == NULL)
+               return;
+
+       /*
+        * We requeue request from the block device queue to the ccw
+        * queue only in two states. In state DASD_STATE_READY the
+        * partition detection is done and we need to requeue requests
+        * for that. State DASD_STATE_ONLINE is normal block device
+        * operation.
+        */
+       if (basedev->state < DASD_STATE_READY)
+               return;
+       /* Now we try to fetch requests from the request queue */
+       while (!blk_queue_plugged(queue) &&
+              elv_next_request(queue)) {
+
+               req = elv_next_request(queue);
+
+               if (basedev->features & DASD_FEATURE_READONLY &&
+                   rq_data_dir(req) == WRITE) {
+                       DBF_DEV_EVENT(DBF_ERR, basedev,
+                                     "Rejecting write request %p",
+                                     req);
+                       blkdev_dequeue_request(req);
+                       dasd_end_request(req, 0);
+                       continue;
+               }
+               cqr = basedev->discipline->build_cp(basedev, block, req);
+               if (IS_ERR(cqr)) {
+                       if (PTR_ERR(cqr) == -EBUSY)
+                               break;  /* normal end condition */
+                       if (PTR_ERR(cqr) == -ENOMEM)
+                               break;  /* terminate request queue loop */
+                       if (PTR_ERR(cqr) == -EAGAIN) {
+                               /*
+                                * The current request cannot be build right
+                                * now, we have to try later. If this request
+                                * is the head-of-queue we stop the device
+                                * for 1/2 second.
+                                */
+                               if (!list_empty(&block->ccw_queue))
+                                       break;
+                               spin_lock_irqsave(get_ccwdev_lock(basedev->cdev), flags);
+                               basedev->stopped |= DASD_STOPPED_PENDING;
+                               spin_unlock_irqrestore(get_ccwdev_lock(basedev->cdev), flags);
+                               dasd_block_set_timer(block, HZ/2);
+                               break;
+                       }
+                       DBF_DEV_EVENT(DBF_ERR, basedev,
+                                     "CCW creation failed (rc=%ld) "
+                                     "on request %p",
+                                     PTR_ERR(cqr), req);
+                       blkdev_dequeue_request(req);
+                       dasd_end_request(req, 0);
+                       continue;
+               }
+               /*
+                *  Note: callback is set to dasd_return_cqr_cb in
+                * __dasd_block_start_head to cover erp requests as well
+                */
+               cqr->callback_data = (void *) req;
+               cqr->status = DASD_CQR_FILLED;
+               blkdev_dequeue_request(req);
+               list_add_tail(&cqr->blocklist, &block->ccw_queue);
+               dasd_profile_start(block, cqr, req);
+       }
+}
+
+static void __dasd_cleanup_cqr(struct dasd_ccw_req *cqr)
+{
+       struct request *req;
+       int status;
+
+       req = (struct request *) cqr->callback_data;
+       dasd_profile_end(cqr->block, cqr, req);
+       status = cqr->memdev->discipline->free_cp(cqr, req);
+       dasd_end_request(req, status);
+}
+
+/*
+ * Process ccw request queue.
+ */
+static void __dasd_process_block_ccw_queue(struct dasd_block *block,
+                                          struct list_head *final_queue)
+{
+       struct list_head *l, *n;
+       struct dasd_ccw_req *cqr;
+       dasd_erp_fn_t erp_fn;
+       unsigned long flags;
+       struct dasd_device *base = block->base;
+
+restart:
+       /* Process request with final status. */
+       list_for_each_safe(l, n, &block->ccw_queue) {
+               cqr = list_entry(l, struct dasd_ccw_req, blocklist);
+               if (cqr->status != DASD_CQR_DONE &&
+                   cqr->status != DASD_CQR_FAILED &&
+                   cqr->status != DASD_CQR_NEED_ERP &&
+                   cqr->status != DASD_CQR_TERMINATED)
+                       continue;
+
+               if (cqr->status == DASD_CQR_TERMINATED) {
+                       base->discipline->handle_terminated_request(cqr);
+                       goto restart;
+               }
+
+               /*  Process requests that may be recovered */
+               if (cqr->status == DASD_CQR_NEED_ERP) {
+                       if (cqr->irb.esw.esw0.erw.cons &&
+                           test_bit(DASD_CQR_FLAGS_USE_ERP,
+                                    &cqr->flags)) {
+                               erp_fn = base->discipline->erp_action(cqr);
+                               erp_fn(cqr);
+                       }
+                       goto restart;
+               }
+
+               /* First of all call extended error reporting. */
+               if (dasd_eer_enabled(base) &&
+                   cqr->status == DASD_CQR_FAILED) {
+                       dasd_eer_write(base, cqr, DASD_EER_FATALERROR);
+
+                       /* restart request  */
+                       cqr->status = DASD_CQR_FILLED;
+                       cqr->retries = 255;
+                       spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags);
+                       base->stopped |= DASD_STOPPED_QUIESCE;
+                       spin_unlock_irqrestore(get_ccwdev_lock(base->cdev),
+                                              flags);
+                       goto restart;
+               }
+
+               /* Process finished ERP request. */
+               if (cqr->refers) {
+                       __dasd_block_process_erp(block, cqr);
+                       goto restart;
+               }
+
+               /* Rechain finished requests to final queue */
+               cqr->endclk = get_clock();
+               list_move_tail(&cqr->blocklist, final_queue);
+       }
+}
+
+static void dasd_return_cqr_cb(struct dasd_ccw_req *cqr, void *data)
+{
+       dasd_schedule_block_bh(cqr->block);
+}
+
+static void __dasd_block_start_head(struct dasd_block *block)
+{
+       struct dasd_ccw_req *cqr;
+
+       if (list_empty(&block->ccw_queue))
+               return;
+       /* We allways begin with the first requests on the queue, as some
+        * of previously started requests have to be enqueued on a
+        * dasd_device again for error recovery.
+        */
+       list_for_each_entry(cqr, &block->ccw_queue, blocklist) {
+               if (cqr->status != DASD_CQR_FILLED)
+                       continue;
+               /* Non-temporary stop condition will trigger fail fast */
+               if (block->base->stopped & ~DASD_STOPPED_PENDING &&
+                   test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) &&
+                   (!dasd_eer_enabled(block->base))) {
+                       cqr->status = DASD_CQR_FAILED;
+                       dasd_schedule_block_bh(block);
+                       continue;
+               }
+               /* Don't try to start requests if device is stopped */
+               if (block->base->stopped)
+                       return;
+
+               /* just a fail safe check, should not happen */
+               if (!cqr->startdev)
+                       cqr->startdev = block->base;
+
+               /* make sure that the requests we submit find their way back */
+               cqr->callback = dasd_return_cqr_cb;
+
+               dasd_add_request_tail(cqr);
+       }
+}
+
+/*
+ * Central dasd_block layer routine. Takes requests from the generic
+ * block layer request queue, creates ccw requests, enqueues them on
+ * a dasd_device and processes ccw requests that have been returned.
+ */
+static void dasd_block_tasklet(struct dasd_block *block)
+{
+       struct list_head final_queue;
+       struct list_head *l, *n;
+       struct dasd_ccw_req *cqr;
+
+       atomic_set(&block->tasklet_scheduled, 0);
+       INIT_LIST_HEAD(&final_queue);
+       spin_lock(&block->queue_lock);
+       /* Finish off requests on ccw queue */
+       __dasd_process_block_ccw_queue(block, &final_queue);
+       spin_unlock(&block->queue_lock);
+       /* Now call the callback function of requests with final status */
+       spin_lock_irq(&block->request_queue_lock);
+       list_for_each_safe(l, n, &final_queue) {
+               cqr = list_entry(l, struct dasd_ccw_req, blocklist);
+               list_del_init(&cqr->blocklist);
+               __dasd_cleanup_cqr(cqr);
+       }
+       spin_lock(&block->queue_lock);
+       /* Get new request from the block device request queue */
+       __dasd_process_request_queue(block);
+       /* Now check if the head of the ccw queue needs to be started. */
+       __dasd_block_start_head(block);
+       spin_unlock(&block->queue_lock);
+       spin_unlock_irq(&block->request_queue_lock);
+       dasd_put_device(block->base);
+}
+
+static void _dasd_wake_block_flush_cb(struct dasd_ccw_req *cqr, void *data)
+{
+       wake_up(&dasd_flush_wq);
+}
+
+/*
+ * Go through all request on the dasd_block request queue, cancel them
+ * on the respective dasd_device, and return them to the generic
+ * block layer.
+ */
+static int dasd_flush_block_queue(struct dasd_block *block)
+{
+       struct dasd_ccw_req *cqr, *n;
+       int rc, i;
+       struct list_head flush_queue;
+
+       INIT_LIST_HEAD(&flush_queue);
+       spin_lock_bh(&block->queue_lock);
+       rc = 0;
+restart:
+       list_for_each_entry_safe(cqr, n, &block->ccw_queue, blocklist) {
+               /* if this request currently owned by a dasd_device cancel it */
+               if (cqr->status >= DASD_CQR_QUEUED)
+                       rc = dasd_cancel_req(cqr);
+               if (rc < 0)
+                       break;
+               /* Rechain request (including erp chain) so it won't be
+                * touched by the dasd_block_tasklet anymore.
+                * Replace the callback so we notice when the request
+                * is returned from the dasd_device layer.
+                */
+               cqr->callback = _dasd_wake_block_flush_cb;
+               for (i = 0; cqr != NULL; cqr = cqr->refers, i++)
+                       list_move_tail(&cqr->blocklist, &flush_queue);
+               if (i > 1)
+                       /* moved more than one request - need to restart */
+                       goto restart;
+       }
+       spin_unlock_bh(&block->queue_lock);
+       /* Now call the callback function of flushed requests */
+restart_cb:
+       list_for_each_entry_safe(cqr, n, &flush_queue, blocklist) {
+               wait_event(dasd_flush_wq, (cqr->status < DASD_CQR_QUEUED));
+               /* Process finished ERP request. */
+               if (cqr->refers) {
+                       __dasd_block_process_erp(block, cqr);
+                       /* restart list_for_xx loop since dasd_process_erp
+                        * might remove multiple elements */
+                       goto restart_cb;
+               }
+               /* call the callback function */
+               cqr->endclk = get_clock();
+               list_del_init(&cqr->blocklist);
+               __dasd_cleanup_cqr(cqr);
        }
-       spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
-       dasd_schedule_bh(device);
        return rc;
 }
 
 /*
- * SECTION: Block device operations (request queue, partitions, open, release).
+ * Schedules a call to dasd_tasklet over the device tasklet.
+ */
+void dasd_schedule_block_bh(struct dasd_block *block)
+{
+       /* Protect against rescheduling. */
+       if (atomic_cmpxchg(&block->tasklet_scheduled, 0, 1) != 0)
+               return;
+       /* life cycle of block is bound to it's base device */
+       dasd_get_device(block->base);
+       tasklet_hi_schedule(&block->tasklet);
+}
+
+
+/*
+ * SECTION: external block device operations
+ * (request queue handling, open, release, etc.)
  */
 
 /*
  * Dasd request queue function. Called from ll_rw_blk.c
  */
-static void
-do_dasd_request(struct request_queue * queue)
+static void do_dasd_request(struct request_queue *queue)
 {
-       struct dasd_device *device;
+       struct dasd_block *block;
 
-       device = (struct dasd_device *) queue->queuedata;
-       spin_lock(get_ccwdev_lock(device->cdev));
+       block = queue->queuedata;
+       spin_lock(&block->queue_lock);
        /* Get new request from the block device request queue */
-       __dasd_process_blk_queue(device);
+       __dasd_process_request_queue(block);
        /* Now check if the head of the ccw queue needs to be started. */
-       __dasd_start_head(device);
-       spin_unlock(get_ccwdev_lock(device->cdev));
+       __dasd_block_start_head(block);
+       spin_unlock(&block->queue_lock);
 }
 
 /*
  * Allocate and initialize request queue and default I/O scheduler.
  */
-static int
-dasd_alloc_queue(struct dasd_device * device)
+static int dasd_alloc_queue(struct dasd_block *block)
 {
        int rc;
 
-       device->request_queue = blk_init_queue(do_dasd_request,
-                                              &device->request_queue_lock);
-       if (device->request_queue == NULL)
+       block->request_queue = blk_init_queue(do_dasd_request,
+                                              &block->request_queue_lock);
+       if (block->request_queue == NULL)
                return -ENOMEM;
 
-       device->request_queue->queuedata = device;
+       block->request_queue->queuedata = block;
 
-       elevator_exit(device->request_queue->elevator);
-       rc = elevator_init(device->request_queue, "deadline");
+       elevator_exit(block->request_queue->elevator);
+       rc = elevator_init(block->request_queue, "deadline");
        if (rc) {
-               blk_cleanup_queue(device->request_queue);
+               blk_cleanup_queue(block->request_queue);
                return rc;
        }
        return 0;
 /*
  * Allocate and initialize request queue.
  */
-static void
-dasd_setup_queue(struct dasd_device * device)
+static void dasd_setup_queue(struct dasd_block *block)
 {
        int max;
 
-       blk_queue_hardsect_size(device->request_queue, device->bp_block);
-       max = device->discipline->max_blocks << device->s2b_shift;
-       blk_queue_max_sectors(device->request_queue, max);
-       blk_queue_max_phys_segments(device->request_queue, -1L);
-       blk_queue_max_hw_segments(device->request_queue, -1L);
-       blk_queue_max_segment_size(device->request_queue, -1L);
-       blk_queue_segment_boundary(device->request_queue, -1L);
-       blk_queue_ordered(device->request_queue, QUEUE_ORDERED_TAG, NULL);
+       blk_queue_hardsect_size(block->request_queue, block->bp_block);
+       max = block->base->discipline->max_blocks << block->s2b_shift;
+       blk_queue_max_sectors(block->request_queue, max);
+       blk_queue_max_phys_segments(block->request_queue, -1L);
+       blk_queue_max_hw_segments(block->request_queue, -1L);
+       blk_queue_max_segment_size(block->request_queue, -1L);
+       blk_queue_segment_boundary(block->request_queue, -1L);
+       blk_queue_ordered(block->request_queue, QUEUE_ORDERED_DRAIN, NULL);
 }
 
 /*
  * Deactivate and free request queue.
  */
-static void
-dasd_free_queue(struct dasd_device * device)
+static void dasd_free_queue(struct dasd_block *block)
 {
-       if (device->request_queue) {
-               blk_cleanup_queue(device->request_queue);
-               device->request_queue = NULL;
+       if (block->request_queue) {
+               blk_cleanup_queue(block->request_queue);
+               block->request_queue = NULL;
        }
 }
 
 /*
  * Flush request on the request queue.
  */
-static void
-dasd_flush_request_queue(struct dasd_device * device)
+static void dasd_flush_request_queue(struct dasd_block *block)
 {
        struct request *req;
 
-       if (!device->request_queue)
+       if (!block->request_queue)
                return;
 
-       spin_lock_irq(&device->request_queue_lock);
-       while ((req = elv_next_request(device->request_queue))) {
+       spin_lock_irq(&block->request_queue_lock);
+       while ((req = elv_next_request(block->request_queue))) {
                blkdev_dequeue_request(req);
                dasd_end_request(req, 0);
        }
-       spin_unlock_irq(&device->request_queue_lock);
+       spin_unlock_irq(&block->request_queue_lock);
 }
 
-static int
-dasd_open(struct inode *inp, struct file *filp)
+static int dasd_open(struct inode *inp, struct file *filp)
 {
        struct gendisk *disk = inp->i_bdev->bd_disk;
-       struct dasd_device *device = disk->private_data;
+       struct dasd_block *block = disk->private_data;
+       struct dasd_device *base = block->base;
        int rc;
 
-        atomic_inc(&device->open_count);
-       if (test_bit(DASD_FLAG_OFFLINE, &device->flags)) {
+       atomic_inc(&block->open_count);
+       if (test_bit(DASD_FLAG_OFFLINE, &base->flags)) {
                rc = -ENODEV;
                goto unlock;
        }
 
-       if (!try_module_get(device->discipline->owner)) {
+       if (!try_module_get(base->discipline->owner)) {
                rc = -EINVAL;
                goto unlock;
        }
 
        if (dasd_probeonly) {
-               DEV_MESSAGE(KERN_INFO, device, "%s",
+               DEV_MESSAGE(KERN_INFO, base, "%s",
                            "No access to device due to probeonly mode");
                rc = -EPERM;
                goto out;
        }
 
-       if (device->state <= DASD_STATE_BASIC) {
-               DBF_DEV_EVENT(DBF_ERR, device, " %s",
+       if (base->state <= DASD_STATE_BASIC) {
+               DBF_DEV_EVENT(DBF_ERR, base, " %s",
                              " Cannot open unrecognized device");
                rc = -ENODEV;
                goto out;
        return 0;
 
 out:
-       module_put(device->discipline->owner);
+       module_put(base->discipline->owner);
 unlock:
-       atomic_dec(&device->open_count);
+       atomic_dec(&block->open_count);
        return rc;
 }
 
-static int
-dasd_release(struct inode *inp, struct file *filp)
+static int dasd_release(struct inode *inp, struct file *filp)
 {
        struct gendisk *disk = inp->i_bdev->bd_disk;
-       struct dasd_device *device = disk->private_data;
+       struct dasd_block *block = disk->private_data;
 
-       atomic_dec(&device->open_count);
-       module_put(device->discipline->owner);
+       atomic_dec(&block->open_count);
+       module_put(block->base->discipline->owner);
        return 0;
 }
 
 /*
  * Return disk geometry.
  */
-static int
-dasd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
+static int dasd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
 {
-       struct dasd_device *device;
+       struct dasd_block *block;
+       struct dasd_device *base;
 
-       device = bdev->bd_disk->private_data;
-       if (!device)
+       block = bdev->bd_disk->private_data;
+       base = block->base;
+       if (!block)
                return -ENODEV;
 
-       if (!device->discipline ||
-           !device->discipline->fill_geometry)
+       if (!base->discipline ||
+           !base->discipline->fill_geometry)
                return -EINVAL;
 
-       device->discipline->fill_geometry(device, geo);
-       geo->start = get_start_sect(bdev) >> device->s2b_shift;
+       base->discipline->fill_geometry(block, geo);
+       geo->start = get_start_sect(bdev) >> block->s2b_shift;
        return 0;
 }
 
        .getgeo         = dasd_getgeo,
 };
 
+/*******************************************************************************
+ * end of block device operations
+ */
 
 static void
 dasd_exit(void)
  * Initial attempt at a probe function. this can be simplified once
  * the other detection code is gone.
  */
-int
-dasd_generic_probe (struct ccw_device *cdev,
-                   struct dasd_discipline *discipline)
+int dasd_generic_probe(struct ccw_device *cdev,
+                      struct dasd_discipline *discipline)
 {
        int ret;
 
  * This will one day be called from a global not_oper handler.
  * It is also used by driver_unregister during module unload.
  */
-void
-dasd_generic_remove (struct ccw_device *cdev)
+void dasd_generic_remove(struct ccw_device *cdev)
 {
        struct dasd_device *device;
+       struct dasd_block *block;
 
        cdev->handler = NULL;
 
         */
        dasd_set_target_state(device, DASD_STATE_NEW);
        /* dasd_delete_device destroys the device reference. */
+       block = device->block;
+       device->block = NULL;
        dasd_delete_device(device);
+       /*
+        * life cycle of block is bound to device, so delete it after
+        * device was safely removed
+        */
+       if (block)
+               dasd_free_block(block);
 }
 
 /*
  * the device is detected for the first time and is supposed to be used
  * or the user has started activation through sysfs.
  */
-int
-dasd_generic_set_online (struct ccw_device *cdev,
-                        struct dasd_discipline *base_discipline)
-
+int dasd_generic_set_online(struct ccw_device *cdev,
+                           struct dasd_discipline *base_discipline)
 {
        struct dasd_discipline *discipline;
        struct dasd_device *device;
        device->base_discipline = base_discipline;
        device->discipline = discipline;
 
+       /* check_device will allocate block device if necessary */
        rc = discipline->check_device(device);
        if (rc) {
                printk (KERN_WARNING
                        cdev->dev.bus_id);
                rc = -ENODEV;
                dasd_set_target_state(device, DASD_STATE_NEW);
+               if (device->block)
+                       dasd_free_block(device->block);
                dasd_delete_device(device);
        } else
                pr_debug("dasd_generic device %s found\n",
        return rc;
 }
 
-int
-dasd_generic_set_offline (struct ccw_device *cdev)
+int dasd_generic_set_offline(struct ccw_device *cdev)
 {
        struct dasd_device *device;
+       struct dasd_block *block;
        int max_count, open_count;
 
        device = dasd_device_from_cdev(cdev);
         * the blkdev_get in dasd_scan_partitions. We are only interested
         * in the other openers.
         */
-       max_count = device->bdev ? 0 : -1;
-       open_count = (int) atomic_read(&device->open_count);
-       if (open_count > max_count) {
-               if (open_count > 0)
-                       printk (KERN_WARNING "Can't offline dasd device with "
-                               "open count = %i.\n",
-                               open_count);
-               else
-                       printk (KERN_WARNING "%s",
-                               "Can't offline dasd device due to internal "
-                               "use\n");
-               clear_bit(DASD_FLAG_OFFLINE, &device->flags);
-               dasd_put_device(device);
-               return -EBUSY;
+       if (device->block) {
+               struct dasd_block *block = device->block;
+               max_count = block->bdev ? 0 : -1;
+               open_count = (int) atomic_read(&block->open_count);
+               if (open_count > max_count) {
+                       if (open_count > 0)
+                               printk(KERN_WARNING "Can't offline dasd "
+                                      "device with open count = %i.\n",
+                                      open_count);
+                       else
+                               printk(KERN_WARNING "%s",
+                                      "Can't offline dasd device due "
+                                      "to internal use\n");
+                       clear_bit(DASD_FLAG_OFFLINE, &device->flags);
+                       dasd_put_device(device);
+                       return -EBUSY;
+               }
        }
        dasd_set_target_state(device, DASD_STATE_NEW);
        /* dasd_delete_device destroys the device reference. */
+       block = device->block;
+       device->block = NULL;
        dasd_delete_device(device);
-
+       /*
+        * life cycle of block is bound to device, so delete it after
+        * device was safely removed
+        */
+       if (block)
+               dasd_free_block(block);
        return 0;
 }
 
-int
-dasd_generic_notify(struct ccw_device *cdev, int event)
+int dasd_generic_notify(struct ccw_device *cdev, int event)
 {
        struct dasd_device *device;
        struct dasd_ccw_req *cqr;
                if (device->state < DASD_STATE_BASIC)
                        break;
                /* Device is active. We want to keep it. */
-               if (test_bit(DASD_FLAG_DSC_ERROR, &device->flags)) {
-                       list_for_each_entry(cqr, &device->ccw_queue, list)
-                               if (cqr->status == DASD_CQR_IN_IO)
-                                       cqr->status = DASD_CQR_FAILED;
-                       device->stopped |= DASD_STOPPED_DC_EIO;
-               } else {
-                       list_for_each_entry(cqr, &device->ccw_queue, list)
-                               if (cqr->status == DASD_CQR_IN_IO) {
-                                       cqr->status = DASD_CQR_QUEUED;
-                                       cqr->retries++;
-                               }
-                       device->stopped |= DASD_STOPPED_DC_WAIT;
-                       dasd_set_timer(device, 0);
-               }
-               dasd_schedule_bh(device);
+               list_for_each_entry(cqr, &device->ccw_queue, devlist)
+                       if (cqr->status == DASD_CQR_IN_IO) {
+                               cqr->status = DASD_CQR_QUEUED;
+                               cqr->retries++;
+                       }
+               device->stopped |= DASD_STOPPED_DC_WAIT;
+               dasd_device_clear_timer(device);
+               dasd_schedule_device_bh(device);
                ret = 1;
                break;
        case CIO_OPER:
                /* FIXME: add a sanity check. */
-               device->stopped &= ~(DASD_STOPPED_DC_WAIT|DASD_STOPPED_DC_EIO);
-               dasd_schedule_bh(device);
+               device->stopped &= ~DASD_STOPPED_DC_WAIT;
+               dasd_schedule_device_bh(device);
+               if (device->block)
+                       dasd_schedule_block_bh(device->block);
                ret = 1;
                break;
        }
        ccw->cda = (__u32)(addr_t)rdc_buffer;
        ccw->count = rdc_buffer_size;
 
-       cqr->device = device;
+       cqr->startdev = device;
+       cqr->memdev = device;
        cqr->expires = 10*HZ;
        clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
        cqr->retries = 2;
                return PTR_ERR(cqr);
 
        ret = dasd_sleep_on(cqr);
-       dasd_sfree_request(cqr, cqr->device);
+       dasd_sfree_request(cqr, cqr->memdev);
        return ret;
 }
 EXPORT_SYMBOL_GPL(dasd_generic_read_dev_chars);
 
-static int __init
-dasd_init(void)
+static int __init dasd_init(void)
 {
        int rc;
 
        init_waitqueue_head(&dasd_flush_wq);
 
        /* register 'common' DASD debug area, used for all DBF_XXX calls */
-       dasd_debug_area = debug_register("dasd", 1, 2, 8 * sizeof (long));
+       dasd_debug_area = debug_register("dasd", 1, 2, 8 * sizeof(long));
        if (dasd_debug_area == NULL) {
                rc = -ENOMEM;
                goto failed;
 EXPORT_SYMBOL(dasd_add_request_head);
 EXPORT_SYMBOL(dasd_add_request_tail);
 EXPORT_SYMBOL(dasd_cancel_req);
-EXPORT_SYMBOL(dasd_clear_timer);
+EXPORT_SYMBOL(dasd_device_clear_timer);
+EXPORT_SYMBOL(dasd_block_clear_timer);
 EXPORT_SYMBOL(dasd_enable_device);
 EXPORT_SYMBOL(dasd_int_handler);
 EXPORT_SYMBOL(dasd_kfree_request);
 EXPORT_SYMBOL(dasd_kick_device);
 EXPORT_SYMBOL(dasd_kmalloc_request);
-EXPORT_SYMBOL(dasd_schedule_bh);
+EXPORT_SYMBOL(dasd_schedule_device_bh);
+EXPORT_SYMBOL(dasd_schedule_block_bh);
 EXPORT_SYMBOL(dasd_set_target_state);
-EXPORT_SYMBOL(dasd_set_timer);
+EXPORT_SYMBOL(dasd_device_set_timer);
+EXPORT_SYMBOL(dasd_block_set_timer);
 EXPORT_SYMBOL(dasd_sfree_request);
 EXPORT_SYMBOL(dasd_sleep_on);
 EXPORT_SYMBOL(dasd_sleep_on_immediatly);
 EXPORT_SYMBOL_GPL(dasd_generic_notify);
 EXPORT_SYMBOL_GPL(dasd_generic_set_online);
 EXPORT_SYMBOL_GPL(dasd_generic_set_offline);
-
+EXPORT_SYMBOL_GPL(dasd_generic_handle_state_change);
+EXPORT_SYMBOL_GPL(dasd_flush_device_queue);
+EXPORT_SYMBOL_GPL(dasd_alloc_block);
+EXPORT_SYMBOL_GPL(dasd_free_block);
 
+++ /dev/null
-/*
- * File...........: linux/drivers/s390/block/dasd_3370_erp.c
- * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
- * Bugreports.to..: <Linux390@de.ibm.com>
- * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000
- *
- */
-
-#define PRINTK_HEADER "dasd_erp(3370)"
-
-#include "dasd_int.h"
-
-
-/*
- * DASD_3370_ERP_EXAMINE
- *
- * DESCRIPTION
- *   Checks only for fatal/no/recover error.
- *   A detailed examination of the sense data is done later outside
- *   the interrupt handler.
- *
- *   The logic is based on the 'IBM 3880 Storage Control Reference' manual
- *   'Chapter 7. 3370 Sense Data'.
- *
- * RETURN VALUES
- *   dasd_era_none     no error
- *   dasd_era_fatal    for all fatal (unrecoverable errors)
- *   dasd_era_recover  for all others.
- */
-dasd_era_t
-dasd_3370_erp_examine(struct dasd_ccw_req * cqr, struct irb * irb)
-{
-       char *sense = irb->ecw;
-
-       /* check for successful execution first */
-       if (irb->scsw.cstat == 0x00 &&
-           irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
-               return dasd_era_none;
-       if (sense[0] & 0x80) {  /* CMD reject */
-               return dasd_era_fatal;
-       }
-       if (sense[0] & 0x40) {  /* Drive offline */
-               return dasd_era_recover;
-       }
-       if (sense[0] & 0x20) {  /* Bus out parity */
-               return dasd_era_recover;
-       }
-       if (sense[0] & 0x10) {  /* equipment check */
-               if (sense[1] & 0x80) {
-                       return dasd_era_fatal;
-               }
-               return dasd_era_recover;
-       }
-       if (sense[0] & 0x08) {  /* data check */
-               if (sense[1] & 0x80) {
-                       return dasd_era_fatal;
-               }
-               return dasd_era_recover;
-       }
-       if (sense[0] & 0x04) {  /* overrun */
-               if (sense[1] & 0x80) {
-                       return dasd_era_fatal;
-               }
-               return dasd_era_recover;
-       }
-       if (sense[1] & 0x40) {  /* invalid blocksize */
-               return dasd_era_fatal;
-       }
-       if (sense[1] & 0x04) {  /* file protected */
-               return dasd_era_recover;
-       }
-       if (sense[1] & 0x01) {  /* operation incomplete */
-               return dasd_era_recover;
-       }
-       if (sense[2] & 0x80) {  /* check data erroor */
-               return dasd_era_recover;
-       }
-       if (sense[2] & 0x10) {  /* Env. data present */
-               return dasd_era_recover;
-       }
-       /* examine the 24 byte sense data */
-       return dasd_era_recover;
-
-}                              /* END dasd_3370_erp_examine */
 
        unsigned short res;        /* reserved */
 } __attribute__ ((packed));
 
-/*
- *****************************************************************************
- * SECTION ERP EXAMINATION
- *****************************************************************************
- */
-
-/*
- * DASD_3990_ERP_EXAMINE_24
- *
- * DESCRIPTION
- *   Checks only for fatal (unrecoverable) error.
- *   A detailed examination of the sense data is done later outside
- *   the interrupt handler.
- *
- *   Each bit configuration leading to an action code 2 (Exit with
- *   programming error or unusual condition indication)
- *   are handled as fatal errors.
- *
- *   All other configurations are handled as recoverable errors.
- *
- * RETURN VALUES
- *   dasd_era_fatal    for all fatal (unrecoverable errors)
- *   dasd_era_recover  for all others.
- */
-static dasd_era_t
-dasd_3990_erp_examine_24(struct dasd_ccw_req * cqr, char *sense)
-{
-
-       struct dasd_device *device = cqr->device;
-
-       /* check for 'Command Reject' */
-       if ((sense[0] & SNS0_CMD_REJECT) &&
-           (!(sense[2] & SNS2_ENV_DATA_PRESENT))) {
-
-               DEV_MESSAGE(KERN_ERR, device, "%s",
-                           "EXAMINE 24: Command Reject detected - "
-                           "fatal error");
-
-               return dasd_era_fatal;
-       }
-
-       /* check for 'Invalid Track Format' */
-       if ((sense[1] & SNS1_INV_TRACK_FORMAT) &&
-           (!(sense[2] & SNS2_ENV_DATA_PRESENT))) {
-
-               DEV_MESSAGE(KERN_ERR, device, "%s",
-                           "EXAMINE 24: Invalid Track Format detected "
-                           "- fatal error");
-
-               return dasd_era_fatal;
-       }
-
-       /* check for 'No Record Found' */
-       if (sense[1] & SNS1_NO_REC_FOUND) {
-
-                /* FIXME: fatal error ?!? */
-               DEV_MESSAGE(KERN_ERR, device,
-                           "EXAMINE 24: No Record Found detected %s",
-                            device->state <= DASD_STATE_BASIC ?
-                           " " : "- fatal error");
-
-               return dasd_era_fatal;
-       }
-
-       /* return recoverable for all others */
-       return dasd_era_recover;
-}                              /* END dasd_3990_erp_examine_24 */
-
-/*
- * DASD_3990_ERP_EXAMINE_32
- *
- * DESCRIPTION
- *   Checks only for fatal/no/recoverable error.
- *   A detailed examination of the sense data is done later outside
- *   the interrupt handler.
- *
- * RETURN VALUES
- *   dasd_era_none     no error
- *   dasd_era_fatal    for all fatal (unrecoverable errors)
- *   dasd_era_recover  for recoverable others.
- */
-static dasd_era_t
-dasd_3990_erp_examine_32(struct dasd_ccw_req * cqr, char *sense)
-{
-
-       struct dasd_device *device = cqr->device;
-
-       switch (sense[25]) {
-       case 0x00:
-               return dasd_era_none;
-
-       case 0x01:
-               DEV_MESSAGE(KERN_ERR, device, "%s", "EXAMINE 32: fatal error");
-
-               return dasd_era_fatal;
-
-       default:
-
-               return dasd_era_recover;
-       }
-
-}                              /* end dasd_3990_erp_examine_32 */
-
-/*
- * DASD_3990_ERP_EXAMINE
- *
- * DESCRIPTION
- *   Checks only for fatal/no/recover error.
- *   A detailed examination of the sense data is done later outside
- *   the interrupt handler.
- *
- *   The logic is based on the 'IBM 3990 Storage Control  Reference' manual
- *   'Chapter 7. Error Recovery Procedures'.
- *
- * RETURN VALUES
- *   dasd_era_none     no error
- *   dasd_era_fatal    for all fatal (unrecoverable errors)
- *   dasd_era_recover  for all others.
- */
-dasd_era_t
-dasd_3990_erp_examine(struct dasd_ccw_req * cqr, struct irb * irb)
-{
-
-       char *sense = irb->ecw;
-       dasd_era_t era = dasd_era_recover;
-       struct dasd_device *device = cqr->device;
-
-       /* check for successful execution first */
-       if (irb->scsw.cstat == 0x00 &&
-           irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
-               return dasd_era_none;
-
-       /* distinguish between 24 and 32 byte sense data */
-       if (sense[27] & DASD_SENSE_BIT_0) {
-
-               era = dasd_3990_erp_examine_24(cqr, sense);
-
-       } else {
-
-               era = dasd_3990_erp_examine_32(cqr, sense);
-
-       }
-
-       /* log the erp chain if fatal error occurred */
-       if ((era == dasd_era_fatal) && (device->state >= DASD_STATE_READY)) {
-               dasd_log_sense(cqr, irb);
-       }
-
-       return era;
-
-}                              /* END dasd_3990_erp_examine */
-
 /*
  *****************************************************************************
  * SECTION ERP HANDLING
 {
        struct dasd_ccw_req *cqr = erp->refers;
 
-       dasd_free_erp_request(erp, erp->device);
+       dasd_free_erp_request(erp, erp->memdev);
        cqr->status = final_status;
        return cqr;
 
 dasd_3990_erp_block_queue(struct dasd_ccw_req * erp, int expires)
 {
 
-       struct dasd_device *device = erp->device;
+       struct dasd_device *device = erp->startdev;
+       unsigned long flags;
 
        DEV_MESSAGE(KERN_INFO, device,
                    "blocking request queue for %is", expires/HZ);
 
+       spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
        device->stopped |= DASD_STOPPED_PENDING;
-       erp->status = DASD_CQR_QUEUED;
-
-       dasd_set_timer(device, expires);
+       spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
+       erp->status = DASD_CQR_FILLED;
+       dasd_block_set_timer(device->block, expires);
 }
 
 /*
 dasd_3990_erp_int_req(struct dasd_ccw_req * erp)
 {
 
-       struct dasd_device *device = erp->device;
+       struct dasd_device *device = erp->startdev;
 
        /* first time set initial retry counter and erp_function */
        /* and retry once without blocking queue                 */
 static void
 dasd_3990_erp_alternate_path(struct dasd_ccw_req * erp)
 {
-       struct dasd_device *device = erp->device;
+       struct dasd_device *device = erp->startdev;
        __u8 opm;
+       unsigned long flags;
 
        /* try alternate valid path */
+       spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
        opm = ccw_device_get_path_mask(device->cdev);
+       spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
        //FIXME: start with get_opm ?
        if (erp->lpm == 0)
                erp->lpm = LPM_ANYPATH & ~(erp->irb.esw.esw0.sublog.lpum);
                            "try alternate lpm=%x (lpum=%x / opm=%x)",
                            erp->lpm, erp->irb.esw.esw0.sublog.lpum, opm);
 
-               /* reset status to queued to handle the request again... */
-               if (erp->status > DASD_CQR_QUEUED)
-                       erp->status = DASD_CQR_QUEUED;
+               /* reset status to submit the request again... */
+               erp->status = DASD_CQR_FILLED;
                erp->retries = 1;
        } else {
                DEV_MESSAGE(KERN_ERR, device,
                            erp->irb.esw.esw0.sublog.lpum, opm);
 
                /* post request with permanent error */
-               if (erp->status > DASD_CQR_QUEUED)
-                       erp->status = DASD_CQR_FAILED;
+               erp->status = DASD_CQR_FAILED;
        }
 }                              /* end dasd_3990_erp_alternate_path */
 
 dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier)
 {
 
-       struct dasd_device *device = erp->device;
+       struct dasd_device *device = erp->startdev;
        struct DCTL_data *DCTL_data;
        struct ccw1 *ccw;
        struct dasd_ccw_req *dctl_cqr;
 
        dctl_cqr = dasd_alloc_erp_request((char *) &erp->magic, 1,
-                                         sizeof (struct DCTL_data),
-                                         erp->device);
+                                         sizeof(struct DCTL_data),
+                                         device);
        if (IS_ERR(dctl_cqr)) {
                DEV_MESSAGE(KERN_ERR, device, "%s",
                            "Unable to allocate DCTL-CQR");
        DCTL_data->modifier = modifier;
 
        ccw = dctl_cqr->cpaddr;
-       memset(ccw, 0, sizeof (struct ccw1));
+       memset(ccw, 0, sizeof(struct ccw1));
        ccw->cmd_code = CCW_CMD_DCTL;
        ccw->count = 4;
        ccw->cda = (__u32)(addr_t) DCTL_data;
        dctl_cqr->function = dasd_3990_erp_DCTL;
        dctl_cqr->refers = erp;
-       dctl_cqr->device = erp->device;
+       dctl_cqr->startdev = device;
+       dctl_cqr->memdev = device;
        dctl_cqr->magic = erp->magic;
        dctl_cqr->expires = 5 * 60 * HZ;
        dctl_cqr->retries = 2;
 dasd_3990_erp_action_4(struct dasd_ccw_req * erp, char *sense)
 {
 
-       struct dasd_device *device = erp->device;
+       struct dasd_device *device = erp->startdev;
 
        /* first time set initial retry counter and erp_function    */
        /* and retry once without waiting for state change pending  */
                                     "redriving request immediately, "
                                     "%d retries left",
                                     erp->retries);
-                       erp->status = DASD_CQR_QUEUED;
+                       erp->status = DASD_CQR_FILLED;
                }
        }
 
 dasd_3990_handle_env_data(struct dasd_ccw_req * erp, char *sense)
 {
 
-       struct dasd_device *device = erp->device;
+       struct dasd_device *device = erp->startdev;
        char msg_format = (sense[7] & 0xF0);
        char msg_no = (sense[7] & 0x0F);
 
 dasd_3990_erp_com_rej(struct dasd_ccw_req * erp, char *sense)
 {
 
-       struct dasd_device *device = erp->device;
+       struct dasd_device *device = erp->startdev;
 
        erp->function = dasd_3990_erp_com_rej;
 
 dasd_3990_erp_bus_out(struct dasd_ccw_req * erp)
 {
 
-       struct dasd_device *device = erp->device;
+       struct dasd_device *device = erp->startdev;
 
        /* first time set initial retry counter and erp_function */
        /* and retry once without blocking queue                 */
 dasd_3990_erp_equip_check(struct dasd_ccw_req * erp, char *sense)
 {
 
-       struct dasd_device *device = erp->device;
+       struct dasd_device *device = erp->startdev;
 
        erp->function = dasd_3990_erp_equip_check;
 
 
                erp = dasd_3990_erp_action_5(erp);
        }
-
        return erp;
 
 }                              /* end dasd_3990_erp_equip_check */
 dasd_3990_erp_data_check(struct dasd_ccw_req * erp, char *sense)
 {
 
-       struct dasd_device *device = erp->device;
+       struct dasd_device *device = erp->startdev;
 
        erp->function = dasd_3990_erp_data_check;
 
 dasd_3990_erp_overrun(struct dasd_ccw_req * erp, char *sense)
 {
 
-       struct dasd_device *device = erp->device;
+       struct dasd_device *device = erp->startdev;
 
        erp->function = dasd_3990_erp_overrun;
 
 dasd_3990_erp_inv_format(struct dasd_ccw_req * erp, char *sense)
 {
 
-       struct dasd_device *device = erp->device;
+       struct dasd_device *device = erp->startdev;
 
        erp->function = dasd_3990_erp_inv_format;
 
 
        } else {
                DEV_MESSAGE(KERN_ERR, device, "%s",
-                           "Invalid Track Format - Fatal error should have "
-                           "been handled within the interrupt handler");
+                           "Invalid Track Format - Fatal error");
 
                erp = dasd_3990_erp_cleanup(erp, DASD_CQR_FAILED);
        }
 dasd_3990_erp_EOC(struct dasd_ccw_req * default_erp, char *sense)
 {
 
-       struct dasd_device *device = default_erp->device;
+       struct dasd_device *device = default_erp->startdev;
 
        DEV_MESSAGE(KERN_ERR, device, "%s",
                    "End-of-Cylinder - must never happen");
 dasd_3990_erp_env_data(struct dasd_ccw_req * erp, char *sense)
 {
 
-       struct dasd_device *device = erp->device;
+       struct dasd_device *device = erp->startdev;
 
        erp->function = dasd_3990_erp_env_data;
 
 
        /* don't retry on disabled interface */
        if (sense[7] != 0x0F) {
-
                erp = dasd_3990_erp_action_4(erp, sense);
        } else {
-
-               erp = dasd_3990_erp_cleanup(erp, DASD_CQR_IN_IO);
+               erp->status = DASD_CQR_FILLED;
        }
 
        return erp;
 dasd_3990_erp_no_rec(struct dasd_ccw_req * default_erp, char *sense)
 {
 
-       struct dasd_device *device = default_erp->device;
+       struct dasd_device *device = default_erp->startdev;
 
        DEV_MESSAGE(KERN_ERR, device, "%s",
-                   "No Record Found - Fatal error should "
-                   "have been handled within the interrupt handler");
+                   "No Record Found - Fatal error ");
 
        return dasd_3990_erp_cleanup(default_erp, DASD_CQR_FAILED);
 
 dasd_3990_erp_file_prot(struct dasd_ccw_req * erp)
 {
 
-       struct dasd_device *device = erp->device;
+       struct dasd_device *device = erp->startdev;
 
        DEV_MESSAGE(KERN_ERR, device, "%s", "File Protected");
 
 
 }                              /* end dasd_3990_erp_file_prot */
 
+/*
+ * DASD_3990_ERP_INSPECT_ALIAS
+ *
+ * DESCRIPTION
+ *   Checks if the original request was started on an alias device.
+ *   If yes, it modifies the original and the erp request so that
+ *   the erp request can be started on a base device.
+ *
+ * PARAMETER
+ *   erp               pointer to the currently created default ERP
+ *
+ * RETURN VALUES
+ *   erp               pointer to the modified ERP, or NULL
+ */
+
+static struct dasd_ccw_req *dasd_3990_erp_inspect_alias(
+                                               struct dasd_ccw_req *erp)
+{
+       struct dasd_ccw_req *cqr = erp->refers;
+
+       if (cqr->block &&
+           (cqr->block->base != cqr->startdev)) {
+               if (cqr->startdev->features & DASD_FEATURE_ERPLOG) {
+                       DEV_MESSAGE(KERN_ERR, cqr->startdev,
+                                   "ERP on alias device for request %p,"
+                                   " recover on base device %s", cqr,
+                                   cqr->block->base->cdev->dev.bus_id);
+               }
+               dasd_eckd_reset_ccw_to_base_io(cqr);
+               erp->startdev = cqr->block->base;
+               erp->function = dasd_3990_erp_inspect_alias;
+               return erp;
+       } else
+               return NULL;
+}
+
+
 /*
  * DASD_3990_ERP_INSPECT_24
  *
 dasd_3990_erp_action_10_32(struct dasd_ccw_req * erp, char *sense)
 {
 
-       struct dasd_device *device = erp->device;
+       struct dasd_device *device = erp->startdev;
 
        erp->retries = 256;
        erp->function = dasd_3990_erp_action_10_32;
 dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense)
 {
 
-       struct dasd_device *device = default_erp->device;
+       struct dasd_device *device = default_erp->startdev;
        __u32 cpa = 0;
        struct dasd_ccw_req *cqr;
        struct dasd_ccw_req *erp;
        struct DE_eckd_data *DE_data;
+       struct PFX_eckd_data *PFX_data;
        char *LO_data;          /* LO_eckd_data_t */
-       struct ccw1 *ccw;
+       struct ccw1 *ccw, *oldccw;
 
        DEV_MESSAGE(KERN_DEBUG, device, "%s",
                    "Write not finished because of unexpected condition");
        /* Build new ERP request including DE/LO */
        erp = dasd_alloc_erp_request((char *) &cqr->magic,
                                     2 + 1,/* DE/LO + TIC */
-                                    sizeof (struct DE_eckd_data) +
-                                    sizeof (struct LO_eckd_data), device);
+                                    sizeof(struct DE_eckd_data) +
+                                    sizeof(struct LO_eckd_data), device);
 
        if (IS_ERR(erp)) {
                DEV_MESSAGE(KERN_ERR, device, "%s", "Unable to allocate ERP");
 
        /* use original DE */
        DE_data = erp->data;
-       memcpy(DE_data, cqr->data, sizeof (struct DE_eckd_data));
+       oldccw = cqr->cpaddr;
+       if (oldccw->cmd_code == DASD_ECKD_CCW_PFX) {
+               PFX_data = cqr->data;
+               memcpy(DE_data, &PFX_data->define_extend,
+                      sizeof(struct DE_eckd_data));
+       } else
+               memcpy(DE_data, cqr->data, sizeof(struct DE_eckd_data));
 
        /* create LO */
-       LO_data = erp->data + sizeof (struct DE_eckd_data);
+       LO_data = erp->data + sizeof(struct DE_eckd_data);
 
        if ((sense[3] == 0x01) && (LO_data[1] & 0x01)) {
 
 
        /* create DE ccw */
        ccw = erp->cpaddr;
-       memset(ccw, 0, sizeof (struct ccw1));
+       memset(ccw, 0, sizeof(struct ccw1));
        ccw->cmd_code = DASD_ECKD_CCW_DEFINE_EXTENT;
        ccw->flags = CCW_FLAG_CC;
        ccw->count = 16;
 
        /* create LO ccw */
        ccw++;
-       memset(ccw, 0, sizeof (struct ccw1));
+       memset(ccw, 0, sizeof(struct ccw1));
        ccw->cmd_code = DASD_ECKD_CCW_LOCATE_RECORD;
        ccw->flags = CCW_FLAG_CC;
        ccw->count = 16;
        /* fill erp related fields */
        erp->function = dasd_3990_erp_action_1B_32;
        erp->refers = default_erp->refers;
-       erp->device = device;
+       erp->startdev = device;
+       erp->memdev = device;
        erp->magic = default_erp->magic;
        erp->expires = 0;
        erp->retries = 256;
 dasd_3990_update_1B(struct dasd_ccw_req * previous_erp, char *sense)
 {
 
-       struct dasd_device *device = previous_erp->device;
+       struct dasd_device *device = previous_erp->startdev;
        __u32 cpa = 0;
        struct dasd_ccw_req *cqr;
        struct dasd_ccw_req *erp;
                DEV_MESSAGE(KERN_DEBUG, device, "%s",
                            "Imprecise ending is set - just retry");
 
-               previous_erp->status = DASD_CQR_QUEUED;
+               previous_erp->status = DASD_CQR_FILLED;
 
                return previous_erp;
        }
        erp = previous_erp;
 
        /* update the LO with the new returned sense data  */
-       LO_data = erp->data + sizeof (struct DE_eckd_data);
+       LO_data = erp->data + sizeof(struct DE_eckd_data);
 
        if ((sense[3] == 0x01) && (LO_data[1] & 0x01)) {
 
        ccw++;                  /* addr of TIC ccw */
        ccw->cda = cpa;
 
-       erp->status = DASD_CQR_QUEUED;
+       erp->status = DASD_CQR_FILLED;
 
        return erp;
 
                         * try further actions. */
 
                        erp->lpm = 0;
-
-                       erp->status = DASD_CQR_ERROR;
-
+                       erp->status = DASD_CQR_NEED_ERP;
                }
        }
 
        if ((sense[25] & DASD_SENSE_BIT_1) && (sense[26] & DASD_SENSE_BIT_2)) {
 
                /* set to suspended duplex state then restart */
-               struct dasd_device *device = erp->device;
+               struct dasd_device *device = erp->startdev;
 
                DEV_MESSAGE(KERN_ERR, device, "%s",
                            "Set device to suspended duplex state should be "
 {
 
        if ((erp->function == dasd_3990_erp_compound_retry) &&
-           (erp->status == DASD_CQR_ERROR)) {
+           (erp->status == DASD_CQR_NEED_ERP)) {
 
                dasd_3990_erp_compound_path(erp, sense);
        }
 
        if ((erp->function == dasd_3990_erp_compound_path) &&
-           (erp->status == DASD_CQR_ERROR)) {
+           (erp->status == DASD_CQR_NEED_ERP)) {
 
                erp = dasd_3990_erp_compound_code(erp, sense);
        }
 
        if ((erp->function == dasd_3990_erp_compound_code) &&
-           (erp->status == DASD_CQR_ERROR)) {
+           (erp->status == DASD_CQR_NEED_ERP)) {
 
                dasd_3990_erp_compound_config(erp, sense);
        }
 
        /* if no compound action ERP specified, the request failed */
-       if (erp->status == DASD_CQR_ERROR) {
-
+       if (erp->status == DASD_CQR_NEED_ERP)
                erp->status = DASD_CQR_FAILED;
-       }
 
        return erp;
 
 dasd_3990_erp_inspect_32(struct dasd_ccw_req * erp, char *sense)
 {
 
-       struct dasd_device *device = erp->device;
+       struct dasd_device *device = erp->startdev;
 
        erp->function = dasd_3990_erp_inspect_32;
 
 
                case 0x01:      /* fatal error */
                        DEV_MESSAGE(KERN_ERR, device, "%s",
-                                   "Fatal error should have been "
-                                   "handled within the interrupt handler");
+                                   "Retry not recommended - Fatal error");
 
                        erp = dasd_3990_erp_cleanup(erp, DASD_CQR_FAILED);
                        break;
        /* already set up new ERP !                           */
        char *sense = erp->refers->irb.ecw;
 
+       /* if this problem occured on an alias retry on base */
+       erp_new = dasd_3990_erp_inspect_alias(erp);
+       if (erp_new)
+               return erp_new;
+
        /* distinguish between 24 and 32 byte sense data */
        if (sense[27] & DASD_SENSE_BIT_0) {
 
 dasd_3990_erp_add_erp(struct dasd_ccw_req * cqr)
 {
 
-       struct dasd_device *device = cqr->device;
+       struct dasd_device *device = cqr->startdev;
        struct ccw1 *ccw;
 
        /* allocate additional request block */
        struct dasd_ccw_req *erp;
 
-       erp = dasd_alloc_erp_request((char *) &cqr->magic, 2, 0, cqr->device);
+       erp = dasd_alloc_erp_request((char *) &cqr->magic, 2, 0, device);
        if (IS_ERR(erp)) {
                 if (cqr->retries <= 0) {
                        DEV_MESSAGE(KERN_ERR, device, "%s",
                                      "Unable to allocate ERP request "
                                     "(%i retries left)",
                                      cqr->retries);
-                       dasd_set_timer(device, (HZ << 3));
+                       dasd_block_set_timer(device->block, (HZ << 3));
                 }
                return cqr;
        }
        ccw->cda      = (long)(cqr->cpaddr);
        erp->function = dasd_3990_erp_add_erp;
        erp->refers   = cqr;
-       erp->device   = cqr->device;
+       erp->startdev = device;
+       erp->memdev   = device;
+       erp->block    = cqr->block;
        erp->magic    = cqr->magic;
        erp->expires  = 0;
        erp->retries  = 256;
 dasd_3990_erp_further_erp(struct dasd_ccw_req *erp)
 {
 
-       struct dasd_device *device = erp->device;
+       struct dasd_device *device = erp->startdev;
        char *sense = erp->irb.ecw;
 
        /* check for 24 byte sense ERP */
                               struct dasd_ccw_req *erp)
 {
 
-       struct dasd_device *device = erp_head->device;
+       struct dasd_device *device = erp_head->startdev;
        struct dasd_ccw_req *erp_done = erp_head;       /* finished req */
        struct dasd_ccw_req *erp_free = NULL;   /* req to be freed */
 
                              "original request was lost\n");
 
                /* remove the request from the device queue */
-               list_del(&erp_done->list);
+               list_del(&erp_done->blocklist);
 
                erp_free = erp_done;
                erp_done = erp_done->refers;
 
                /* free the finished erp request */
-               dasd_free_erp_request(erp_free, erp_free->device);
+               dasd_free_erp_request(erp_free, erp_free->memdev);
 
        }                       /* end while */
 
                                    erp->retries, erp);
 
                        /* handle the request again... */
-                       erp->status = DASD_CQR_QUEUED;
+                       erp->status = DASD_CQR_FILLED;
                }
 
        } else {
 struct dasd_ccw_req *
 dasd_3990_erp_action(struct dasd_ccw_req * cqr)
 {
-
        struct dasd_ccw_req *erp = NULL;
-       struct dasd_device *device = cqr->device;
+       struct dasd_device *device = cqr->startdev;
        struct dasd_ccw_req *temp_erp = NULL;
 
        if (device->features & DASD_FEATURE_ERPLOG) {
                }
        }
 
-       /* enqueue added ERP request */
-       if (erp->status == DASD_CQR_FILLED) {
-               erp->status = DASD_CQR_QUEUED;
-               list_add(&erp->list, &device->ccw_queue);
+       /* enqueue ERP request if it's a new one */
+       if (list_empty(&erp->blocklist)) {
+               cqr->status = DASD_CQR_IN_ERP;
+               /* add erp request before the cqr */
+               list_add_tail(&erp->blocklist, &cqr->blocklist);
        }
 
        return erp;
 
+++ /dev/null
-/*
- * File...........: linux/drivers/s390/block/dasd_9336_erp.c
- * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
- * Bugreports.to..: <Linux390@de.ibm.com>
- * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000
- *
- */
-
-#define PRINTK_HEADER "dasd_erp(9336)"
-
-#include "dasd_int.h"
-
-
-/*
- * DASD_9336_ERP_EXAMINE
- *
- * DESCRIPTION
- *   Checks only for fatal/no/recover error.
- *   A detailed examination of the sense data is done later outside
- *   the interrupt handler.
- *
- *   The logic is based on the 'IBM 3880 Storage Control Reference' manual
- *   'Chapter 7. 9336 Sense Data'.
- *
- * RETURN VALUES
- *   dasd_era_none     no error
- *   dasd_era_fatal    for all fatal (unrecoverable errors)
- *   dasd_era_recover  for all others.
- */
-dasd_era_t
-dasd_9336_erp_examine(struct dasd_ccw_req * cqr, struct irb * irb)
-{
-       /* check for successful execution first */
-       if (irb->scsw.cstat == 0x00 &&
-           irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
-               return dasd_era_none;
-
-       /* examine the 24 byte sense data */
-       return dasd_era_recover;
-
-}                              /* END dasd_9336_erp_examine */
 
+++ /dev/null
-/*
- * File...........: linux/drivers/s390/block/dasd_9345_erp.c
- * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
- * Bugreports.to..: <Linux390@de.ibm.com>
- * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000
- *
- */
-
-#define PRINTK_HEADER "dasd_erp(9343)"
-
-#include "dasd_int.h"
-
-dasd_era_t
-dasd_9343_erp_examine(struct dasd_ccw_req * cqr, struct irb * irb)
-{
-       if (irb->scsw.cstat == 0x00 &&
-           irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
-               return dasd_era_none;
-
-       return dasd_era_recover;
-}
 
--- /dev/null
+/*
+ * PAV alias management for the DASD ECKD discipline
+ *
+ * Copyright IBM Corporation, 2007
+ * Author(s): Stefan Weinhuber <wein@de.ibm.com>
+ */
+
+#include <linux/list.h>
+#include <asm/ebcdic.h>
+#include "dasd_int.h"
+#include "dasd_eckd.h"
+
+#ifdef PRINTK_HEADER
+#undef PRINTK_HEADER
+#endif                         /* PRINTK_HEADER */
+#define PRINTK_HEADER "dasd(eckd):"
+
+
+/*
+ * General concept of alias management:
+ * - PAV and DASD alias management is specific to the eckd discipline.
+ * - A device is connected to an lcu as long as the device exists.
+ *   dasd_alias_make_device_known_to_lcu will be called wenn the
+ *   device is checked by the eckd discipline and
+ *   dasd_alias_disconnect_device_from_lcu will be called
+ *   before the device is deleted.
+ * - The dasd_alias_add_device / dasd_alias_remove_device
+ *   functions mark the point when a device is 'ready for service'.
+ * - A summary unit check is a rare occasion, but it is mandatory to
+ *   support it. It requires some complex recovery actions before the
+ *   devices can be used again (see dasd_alias_handle_summary_unit_check).
+ * - dasd_alias_get_start_dev will find an alias device that can be used
+ *   instead of the base device and does some (very simple) load balancing.
+ *   This is the function that gets called for each I/O, so when improving
+ *   something, this function should get faster or better, the rest has just
+ *   to be correct.
+ */
+
+
+static void summary_unit_check_handling_work(struct work_struct *);
+static void lcu_update_work(struct work_struct *);
+static int _schedule_lcu_update(struct alias_lcu *, struct dasd_device *);
+
+static struct alias_root aliastree = {
+       .serverlist = LIST_HEAD_INIT(aliastree.serverlist),
+       .lock = __SPIN_LOCK_UNLOCKED(aliastree.lock),
+};
+
+static struct alias_server *_find_server(struct dasd_uid *uid)
+{
+       struct alias_server *pos;
+       list_for_each_entry(pos, &aliastree.serverlist, server) {
+               if (!strncmp(pos->uid.vendor, uid->vendor,
+                            sizeof(uid->vendor))
+                   && !strncmp(pos->uid.serial, uid->serial,
+                               sizeof(uid->serial)))
+                       return pos;
+       };
+       return NULL;
+}
+
+static struct alias_lcu *_find_lcu(struct alias_server *server,
+                                  struct dasd_uid *uid)
+{
+       struct alias_lcu *pos;
+       list_for_each_entry(pos, &server->lculist, lcu) {
+               if (pos->uid.ssid == uid->ssid)
+                       return pos;
+       };
+       return NULL;
+}
+
+static struct alias_pav_group *_find_group(struct alias_lcu *lcu,
+                                          struct dasd_uid *uid)
+{
+       struct alias_pav_group *pos;
+       __u8 search_unit_addr;
+
+       /* for hyper pav there is only one group */
+       if (lcu->pav == HYPER_PAV) {
+               if (list_empty(&lcu->grouplist))
+                       return NULL;
+               else
+                       return list_first_entry(&lcu->grouplist,
+                                               struct alias_pav_group, group);
+       }
+
+       /* for base pav we have to find the group that matches the base */
+       if (uid->type == UA_BASE_DEVICE)
+               search_unit_addr = uid->real_unit_addr;
+       else
+               search_unit_addr = uid->base_unit_addr;
+       list_for_each_entry(pos, &lcu->grouplist, group) {
+               if (pos->uid.base_unit_addr == search_unit_addr)
+                       return pos;
+       };
+       return NULL;
+}
+
+static struct alias_server *_allocate_server(struct dasd_uid *uid)
+{
+       struct alias_server *server;
+
+       server = kzalloc(sizeof(*server), GFP_KERNEL);
+       if (!server)
+               return ERR_PTR(-ENOMEM);
+       memcpy(server->uid.vendor, uid->vendor, sizeof(uid->vendor));
+       memcpy(server->uid.serial, uid->serial, sizeof(uid->serial));
+       INIT_LIST_HEAD(&server->server);
+       INIT_LIST_HEAD(&server->lculist);
+       return server;
+}
+
+static void _free_server(struct alias_server *server)
+{
+       kfree(server);
+}
+
+static struct alias_lcu *_allocate_lcu(struct dasd_uid *uid)
+{
+       struct alias_lcu *lcu;
+
+       lcu = kzalloc(sizeof(*lcu), GFP_KERNEL);
+       if (!lcu)
+               return ERR_PTR(-ENOMEM);
+       lcu->uac = kzalloc(sizeof(*(lcu->uac)), GFP_KERNEL | GFP_DMA);
+       if (!lcu->uac)
+               goto out_err1;
+       lcu->rsu_cqr = kzalloc(sizeof(*lcu->rsu_cqr), GFP_KERNEL | GFP_DMA);
+       if (!lcu->rsu_cqr)
+               goto out_err2;
+       lcu->rsu_cqr->cpaddr = kzalloc(sizeof(struct ccw1),
+                                      GFP_KERNEL | GFP_DMA);
+       if (!lcu->rsu_cqr->cpaddr)
+               goto out_err3;
+       lcu->rsu_cqr->data = kzalloc(16, GFP_KERNEL | GFP_DMA);
+       if (!lcu->rsu_cqr->data)
+               goto out_err4;
+
+       memcpy(lcu->uid.vendor, uid->vendor, sizeof(uid->vendor));
+       memcpy(lcu->uid.serial, uid->serial, sizeof(uid->serial));
+       lcu->uid.ssid = uid->ssid;
+       lcu->pav = NO_PAV;
+       lcu->flags = NEED_UAC_UPDATE | UPDATE_PENDING;
+       INIT_LIST_HEAD(&lcu->lcu);
+       INIT_LIST_HEAD(&lcu->inactive_devices);
+       INIT_LIST_HEAD(&lcu->active_devices);
+       INIT_LIST_HEAD(&lcu->grouplist);
+       INIT_WORK(&lcu->suc_data.worker, summary_unit_check_handling_work);
+       INIT_DELAYED_WORK(&lcu->ruac_data.dwork, lcu_update_work);
+       spin_lock_init(&lcu->lock);
+       return lcu;
+
+out_err4:
+       kfree(lcu->rsu_cqr->cpaddr);
+out_err3:
+       kfree(lcu->rsu_cqr);
+out_err2:
+       kfree(lcu->uac);
+out_err1:
+       kfree(lcu);
+       return ERR_PTR(-ENOMEM);
+}
+
+static void _free_lcu(struct alias_lcu *lcu)
+{
+       kfree(lcu->rsu_cqr->data);
+       kfree(lcu->rsu_cqr->cpaddr);
+       kfree(lcu->rsu_cqr);
+       kfree(lcu->uac);
+       kfree(lcu);
+}
+
+/*
+ * This is the function that will allocate all the server and lcu data,
+ * so this function must be called first for a new device.
+ * If the return value is 1, the lcu was already known before, if it
+ * is 0, this is a new lcu.
+ * Negative return code indicates that something went wrong (e.g. -ENOMEM)
+ */
+int dasd_alias_make_device_known_to_lcu(struct dasd_device *device)
+{
+       struct dasd_eckd_private *private;
+       unsigned long flags;
+       struct alias_server *server, *newserver;
+       struct alias_lcu *lcu, *newlcu;
+       int is_lcu_known;
+       struct dasd_uid *uid;
+
+       private = (struct dasd_eckd_private *) device->private;
+       uid = &private->uid;
+       spin_lock_irqsave(&aliastree.lock, flags);
+       is_lcu_known = 1;
+       server = _find_server(uid);
+       if (!server) {
+               spin_unlock_irqrestore(&aliastree.lock, flags);
+               newserver = _allocate_server(uid);
+               if (IS_ERR(newserver))
+                       return PTR_ERR(newserver);
+               spin_lock_irqsave(&aliastree.lock, flags);
+               server = _find_server(uid);
+               if (!server) {
+                       list_add(&newserver->server, &aliastree.serverlist);
+                       server = newserver;
+                       is_lcu_known = 0;
+               } else {
+                       /* someone was faster */
+                       _free_server(newserver);
+               }
+       }
+
+       lcu = _find_lcu(server, uid);
+       if (!lcu) {
+               spin_unlock_irqrestore(&aliastree.lock, flags);
+               newlcu = _allocate_lcu(uid);
+               if (IS_ERR(newlcu))
+                       return PTR_ERR(lcu);
+               spin_lock_irqsave(&aliastree.lock, flags);
+               lcu = _find_lcu(server, uid);
+               if (!lcu) {
+                       list_add(&newlcu->lcu, &server->lculist);
+                       lcu = newlcu;
+                       is_lcu_known = 0;
+               } else {
+                       /* someone was faster */
+                       _free_lcu(newlcu);
+               }
+               is_lcu_known = 0;
+       }
+       spin_lock(&lcu->lock);
+       list_add(&device->alias_list, &lcu->inactive_devices);
+       private->lcu = lcu;
+       spin_unlock(&lcu->lock);
+       spin_unlock_irqrestore(&aliastree.lock, flags);
+
+       return is_lcu_known;
+}
+
+/*
+ * This function removes a device from the scope of alias management.
+ * The complicated part is to make sure that it is not in use by
+ * any of the workers. If necessary cancel the work.
+ */
+void dasd_alias_disconnect_device_from_lcu(struct dasd_device *device)
+{
+       struct dasd_eckd_private *private;
+       unsigned long flags;
+       struct alias_lcu *lcu;
+       struct alias_server *server;
+       int was_pending;
+
+       private = (struct dasd_eckd_private *) device->private;
+       lcu = private->lcu;
+       spin_lock_irqsave(&lcu->lock, flags);
+       list_del_init(&device->alias_list);
+       /* make sure that the workers don't use this device */
+       if (device == lcu->suc_data.device) {
+               spin_unlock_irqrestore(&lcu->lock, flags);
+               cancel_work_sync(&lcu->suc_data.worker);
+               spin_lock_irqsave(&lcu->lock, flags);
+               if (device == lcu->suc_data.device)
+                       lcu->suc_data.device = NULL;
+       }
+       was_pending = 0;
+       if (device == lcu->ruac_data.device) {
+               spin_unlock_irqrestore(&lcu->lock, flags);
+               was_pending = 1;
+               cancel_delayed_work_sync(&lcu->ruac_data.dwork);
+               spin_lock_irqsave(&lcu->lock, flags);
+               if (device == lcu->ruac_data.device)
+                       lcu->ruac_data.device = NULL;
+       }
+       private->lcu = NULL;
+       spin_unlock_irqrestore(&lcu->lock, flags);
+
+       spin_lock_irqsave(&aliastree.lock, flags);
+       spin_lock(&lcu->lock);
+       if (list_empty(&lcu->grouplist) &&
+           list_empty(&lcu->active_devices) &&
+           list_empty(&lcu->inactive_devices)) {
+               list_del(&lcu->lcu);
+               spin_unlock(&lcu->lock);
+               _free_lcu(lcu);
+               lcu = NULL;
+       } else {
+               if (was_pending)
+                       _schedule_lcu_update(lcu, NULL);
+               spin_unlock(&lcu->lock);
+       }
+       server = _find_server(&private->uid);
+       if (server && list_empty(&server->lculist)) {
+               list_del(&server->server);
+               _free_server(server);
+       }
+       spin_unlock_irqrestore(&aliastree.lock, flags);
+}
+
+/*
+ * This function assumes that the unit address configuration stored
+ * in the lcu is up to date and will update the device uid before
+ * adding it to a pav group.
+ */
+static int _add_device_to_lcu(struct alias_lcu *lcu,
+                             struct dasd_device *device)
+{
+
+       struct dasd_eckd_private *private;
+       struct alias_pav_group *group;
+       struct dasd_uid *uid;
+
+       private = (struct dasd_eckd_private *) device->private;
+       uid = &private->uid;
+       uid->type = lcu->uac->unit[uid->real_unit_addr].ua_type;
+       uid->base_unit_addr = lcu->uac->unit[uid->real_unit_addr].base_ua;
+       dasd_set_uid(device->cdev, &private->uid);
+
+       /* if we have no PAV anyway, we don't need to bother with PAV groups */
+       if (lcu->pav == NO_PAV) {
+               list_move(&device->alias_list, &lcu->active_devices);
+               return 0;
+       }
+
+       group = _find_group(lcu, uid);
+       if (!group) {
+               group = kzalloc(sizeof(*group), GFP_ATOMIC);
+               if (!group)
+                       return -ENOMEM;
+               memcpy(group->uid.vendor, uid->vendor, sizeof(uid->vendor));
+               memcpy(group->uid.serial, uid->serial, sizeof(uid->serial));
+               group->uid.ssid = uid->ssid;
+               if (uid->type == UA_BASE_DEVICE)
+                       group->uid.base_unit_addr = uid->real_unit_addr;
+               else
+                       group->uid.base_unit_addr = uid->base_unit_addr;
+               INIT_LIST_HEAD(&group->group);
+               INIT_LIST_HEAD(&group->baselist);
+               INIT_LIST_HEAD(&group->aliaslist);
+               list_add(&group->group, &lcu->grouplist);
+       }
+       if (uid->type == UA_BASE_DEVICE)
+               list_move(&device->alias_list, &group->baselist);
+       else
+               list_move(&device->alias_list, &group->aliaslist);
+       private->pavgroup = group;
+       return 0;
+};
+
+static void _remove_device_from_lcu(struct alias_lcu *lcu,
+                                   struct dasd_device *device)
+{
+       struct dasd_eckd_private *private;
+       struct alias_pav_group *group;
+
+       private = (struct dasd_eckd_private *) device->private;
+       list_move(&device->alias_list, &lcu->inactive_devices);
+       group = private->pavgroup;
+       if (!group)
+               return;
+       private->pavgroup = NULL;
+       if (list_empty(&group->baselist) && list_empty(&group->aliaslist)) {
+               list_del(&group->group);
+               kfree(group);
+               return;
+       }
+       if (group->next == device)
+               group->next = NULL;
+};
+
+static int read_unit_address_configuration(struct dasd_device *device,
+                                          struct alias_lcu *lcu)
+{
+       struct dasd_psf_prssd_data *prssdp;
+       struct dasd_ccw_req *cqr;
+       struct ccw1 *ccw;
+       int rc;
+       unsigned long flags;
+
+       cqr = dasd_kmalloc_request("ECKD",
+                                  1 /* PSF */  + 1 /* RSSD */ ,
+                                  (sizeof(struct dasd_psf_prssd_data)),
+                                  device);
+       if (IS_ERR(cqr))
+               return PTR_ERR(cqr);
+       cqr->startdev = device;
+       cqr->memdev = device;
+       clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
+       cqr->retries = 10;
+       cqr->expires = 20 * HZ;
+
+       /* Prepare for Read Subsystem Data */
+       prssdp = (struct dasd_psf_prssd_data *) cqr->data;
+       memset(prssdp, 0, sizeof(struct dasd_psf_prssd_data));
+       prssdp->order = PSF_ORDER_PRSSD;
+       prssdp->suborder = 0x0e;        /* Read unit address configuration */
+       /* all other bytes of prssdp must be zero */
+
+       ccw = cqr->cpaddr;
+       ccw->cmd_code = DASD_ECKD_CCW_PSF;
+       ccw->count = sizeof(struct dasd_psf_prssd_data);
+       ccw->flags |= CCW_FLAG_CC;
+       ccw->cda = (__u32)(addr_t) prssdp;
+
+       /* Read Subsystem Data - feature codes */
+       memset(lcu->uac, 0, sizeof(*(lcu->uac)));
+
+       ccw++;
+       ccw->cmd_code = DASD_ECKD_CCW_RSSD;
+       ccw->count = sizeof(*(lcu->uac));
+       ccw->cda = (__u32)(addr_t) lcu->uac;
+
+       cqr->buildclk = get_clock();
+       cqr->status = DASD_CQR_FILLED;
+
+       /* need to unset flag here to detect race with summary unit check */
+       spin_lock_irqsave(&lcu->lock, flags);
+       lcu->flags &= ~NEED_UAC_UPDATE;
+       spin_unlock_irqrestore(&lcu->lock, flags);
+
+       do {
+               rc = dasd_sleep_on(cqr);
+       } while (rc && (cqr->retries > 0));
+       if (rc) {
+               spin_lock_irqsave(&lcu->lock, flags);
+               lcu->flags |= NEED_UAC_UPDATE;
+               spin_unlock_irqrestore(&lcu->lock, flags);
+       }
+       dasd_kfree_request(cqr, cqr->memdev);
+       return rc;
+}
+
+static int _lcu_update(struct dasd_device *refdev, struct alias_lcu *lcu)
+{
+       unsigned long flags;
+       struct alias_pav_group *pavgroup, *tempgroup;
+       struct dasd_device *device, *tempdev;
+       int i, rc;
+       struct dasd_eckd_private *private;
+
+       spin_lock_irqsave(&lcu->lock, flags);
+       list_for_each_entry_safe(pavgroup, tempgroup, &lcu->grouplist, group) {
+               list_for_each_entry_safe(device, tempdev, &pavgroup->baselist,
+                                        alias_list) {
+                       list_move(&device->alias_list, &lcu->active_devices);
+                       private = (struct dasd_eckd_private *) device->private;
+                       private->pavgroup = NULL;
+               }
+               list_for_each_entry_safe(device, tempdev, &pavgroup->aliaslist,
+                                        alias_list) {
+                       list_move(&device->alias_list, &lcu->active_devices);
+                       private = (struct dasd_eckd_private *) device->private;
+                       private->pavgroup = NULL;
+               }
+               list_del(&pavgroup->group);
+               kfree(pavgroup);
+       }
+       spin_unlock_irqrestore(&lcu->lock, flags);
+
+       rc = read_unit_address_configuration(refdev, lcu);
+       if (rc)
+               return rc;
+
+       spin_lock_irqsave(&lcu->lock, flags);
+       lcu->pav = NO_PAV;
+       for (i = 0; i < MAX_DEVICES_PER_LCU; ++i) {
+               switch (lcu->uac->unit[i].ua_type) {
+               case UA_BASE_PAV_ALIAS:
+                       lcu->pav = BASE_PAV;
+                       break;
+               case UA_HYPER_PAV_ALIAS:
+                       lcu->pav = HYPER_PAV;
+                       break;
+               }
+               if (lcu->pav != NO_PAV)
+                       break;
+       }
+
+       list_for_each_entry_safe(device, tempdev, &lcu->active_devices,
+                                alias_list) {
+               _add_device_to_lcu(lcu, device);
+       }
+       spin_unlock_irqrestore(&lcu->lock, flags);
+       return 0;
+}
+
+static void lcu_update_work(struct work_struct *work)
+{
+       struct alias_lcu *lcu;
+       struct read_uac_work_data *ruac_data;
+       struct dasd_device *device;
+       unsigned long flags;
+       int rc;
+
+       ruac_data = container_of(work, struct read_uac_work_data, dwork.work);
+       lcu = container_of(ruac_data, struct alias_lcu, ruac_data);
+       device = ruac_data->device;
+       rc = _lcu_update(device, lcu);
+       /*
+        * Need to check flags again, as there could have been another
+        * prepare_update or a new device a new device while we were still
+        * processing the data
+        */
+       spin_lock_irqsave(&lcu->lock, flags);
+       if (rc || (lcu->flags & NEED_UAC_UPDATE)) {
+               DEV_MESSAGE(KERN_WARNING, device, "could not update"
+                           " alias data in lcu (rc = %d), retry later", rc);
+               schedule_delayed_work(&lcu->ruac_data.dwork, 30*HZ);
+       } else {
+               lcu->ruac_data.device = NULL;
+               lcu->flags &= ~UPDATE_PENDING;
+       }
+       spin_unlock_irqrestore(&lcu->lock, flags);
+}
+
+static int _schedule_lcu_update(struct alias_lcu *lcu,
+                               struct dasd_device *device)
+{
+       struct dasd_device *usedev = NULL;
+       struct alias_pav_group *group;
+
+       lcu->flags |= NEED_UAC_UPDATE;
+       if (lcu->ruac_data.device) {
+               /* already scheduled or running */
+               return 0;
+       }
+       if (device && !list_empty(&device->alias_list))
+               usedev = device;
+
+       if (!usedev && !list_empty(&lcu->grouplist)) {
+               group = list_first_entry(&lcu->grouplist,
+                                        struct alias_pav_group, group);
+               if (!list_empty(&group->baselist))
+                       usedev = list_first_entry(&group->baselist,
+                                                 struct dasd_device,
+                                                 alias_list);
+               else if (!list_empty(&group->aliaslist))
+                       usedev = list_first_entry(&group->aliaslist,
+                                                 struct dasd_device,
+                                                 alias_list);
+       }
+       if (!usedev && !list_empty(&lcu->active_devices)) {
+               usedev = list_first_entry(&lcu->active_devices,
+                                         struct dasd_device, alias_list);
+       }
+       /*
+        * if we haven't found a proper device yet, give up for now, the next
+        * device that will be set active will trigger an lcu update
+        */
+       if (!usedev)
+               return -EINVAL;
+       lcu->ruac_data.device = usedev;
+       schedule_delayed_work(&lcu->ruac_data.dwork, 0);
+       return 0;
+}
+
+int dasd_alias_add_device(struct dasd_device *device)
+{
+       struct dasd_eckd_private *private;
+       struct alias_lcu *lcu;
+       unsigned long flags;
+       int rc;
+
+       private = (struct dasd_eckd_private *) device->private;
+       lcu = private->lcu;
+       rc = 0;
+       spin_lock_irqsave(&lcu->lock, flags);
+       if (!(lcu->flags & UPDATE_PENDING)) {
+               rc = _add_device_to_lcu(lcu, device);
+               if (rc)
+                       lcu->flags |= UPDATE_PENDING;
+       }
+       if (lcu->flags & UPDATE_PENDING) {
+               list_move(&device->alias_list, &lcu->active_devices);
+               _schedule_lcu_update(lcu, device);
+       }
+       spin_unlock_irqrestore(&lcu->lock, flags);
+       return rc;
+}
+
+int dasd_alias_remove_device(struct dasd_device *device)
+{
+       struct dasd_eckd_private *private;
+       struct alias_lcu *lcu;
+       unsigned long flags;
+
+       private = (struct dasd_eckd_private *) device->private;
+       lcu = private->lcu;
+       spin_lock_irqsave(&lcu->lock, flags);
+       _remove_device_from_lcu(lcu, device);
+       spin_unlock_irqrestore(&lcu->lock, flags);
+       return 0;
+}
+
+struct dasd_device *dasd_alias_get_start_dev(struct dasd_device *base_device)
+{
+
+       struct dasd_device *alias_device;
+       struct alias_pav_group *group;
+       struct alias_lcu *lcu;
+       struct dasd_eckd_private *private, *alias_priv;
+       unsigned long flags;
+
+       private = (struct dasd_eckd_private *) base_device->private;
+       group = private->pavgroup;
+       lcu = private->lcu;
+       if (!group || !lcu)
+               return NULL;
+       if (lcu->pav == NO_PAV ||
+           lcu->flags & (NEED_UAC_UPDATE | UPDATE_PENDING))
+               return NULL;
+
+       spin_lock_irqsave(&lcu->lock, flags);
+       alias_device = group->next;
+       if (!alias_device) {
+               if (list_empty(&group->aliaslist)) {
+                       spin_unlock_irqrestore(&lcu->lock, flags);
+                       return NULL;
+               } else {
+                       alias_device = list_first_entry(&group->aliaslist,
+                                                       struct dasd_device,
+                                                       alias_list);
+               }
+       }
+       if (list_is_last(&alias_device->alias_list, &group->aliaslist))
+               group->next = list_first_entry(&group->aliaslist,
+                                              struct dasd_device, alias_list);
+       else
+               group->next = list_first_entry(&alias_device->alias_list,
+                                              struct dasd_device, alias_list);
+       spin_unlock_irqrestore(&lcu->lock, flags);
+       alias_priv = (struct dasd_eckd_private *) alias_device->private;
+       if ((alias_priv->count < private->count) && !alias_device->stopped)
+               return alias_device;
+       else
+               return NULL;
+}
+
+/*
+ * Summary unit check handling depends on the way alias devices
+ * are handled so it is done here rather then in dasd_eckd.c
+ */
+static int reset_summary_unit_check(struct alias_lcu *lcu,
+                                   struct dasd_device *device,
+                                   char reason)
+{
+       struct dasd_ccw_req *cqr;
+       int rc = 0;
+
+       cqr = lcu->rsu_cqr;
+       strncpy((char *) &cqr->magic, "ECKD", 4);
+       ASCEBC((char *) &cqr->magic, 4);
+       cqr->cpaddr->cmd_code = DASD_ECKD_CCW_RSCK;
+       cqr->cpaddr->flags = 0 ;
+       cqr->cpaddr->count = 16;
+       cqr->cpaddr->cda = (__u32)(addr_t) cqr->data;
+       ((char *)cqr->data)[0] = reason;
+
+       clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
+       cqr->retries = 255;     /* set retry counter to enable basic ERP */
+       cqr->startdev = device;
+       cqr->memdev = device;
+       cqr->block = NULL;
+       cqr->expires = 5 * HZ;
+       cqr->buildclk = get_clock();
+       cqr->status = DASD_CQR_FILLED;
+
+       rc = dasd_sleep_on_immediatly(cqr);
+       return rc;
+}
+
+static void _restart_all_base_devices_on_lcu(struct alias_lcu *lcu)
+{
+       struct alias_pav_group *pavgroup;
+       struct dasd_device *device;
+       struct dasd_eckd_private *private;
+
+       /* active and inactive list can contain alias as well as base devices */
+       list_for_each_entry(device, &lcu->active_devices, alias_list) {
+               private = (struct dasd_eckd_private *) device->private;
+               if (private->uid.type != UA_BASE_DEVICE)
+                       continue;
+               dasd_schedule_block_bh(device->block);
+               dasd_schedule_device_bh(device);
+       }
+       list_for_each_entry(device, &lcu->inactive_devices, alias_list) {
+               private = (struct dasd_eckd_private *) device->private;
+               if (private->uid.type != UA_BASE_DEVICE)
+                       continue;
+               dasd_schedule_block_bh(device->block);
+               dasd_schedule_device_bh(device);
+       }
+       list_for_each_entry(pavgroup, &lcu->grouplist, group) {
+               list_for_each_entry(device, &pavgroup->baselist, alias_list) {
+                       dasd_schedule_block_bh(device->block);
+                       dasd_schedule_device_bh(device);
+               }
+       }
+}
+
+static void flush_all_alias_devices_on_lcu(struct alias_lcu *lcu)
+{
+       struct alias_pav_group *pavgroup;
+       struct dasd_device *device, *temp;
+       struct dasd_eckd_private *private;
+       int rc;
+       unsigned long flags;
+       LIST_HEAD(active);
+
+       /*
+        * Problem here ist that dasd_flush_device_queue may wait
+        * for termination of a request to complete. We can't keep
+        * the lcu lock during that time, so we must assume that
+        * the lists may have changed.
+        * Idea: first gather all active alias devices in a separate list,
+        * then flush the first element of this list unlocked, and afterwards
+        * check if it is still on the list before moving it to the
+        * active_devices list.
+        */
+
+       spin_lock_irqsave(&lcu->lock, flags);
+       list_for_each_entry_safe(device, temp, &lcu->active_devices,
+                                alias_list) {
+               private = (struct dasd_eckd_private *) device->private;
+               if (private->uid.type == UA_BASE_DEVICE)
+                       continue;
+               list_move(&device->alias_list, &active);
+       }
+
+       list_for_each_entry(pavgroup, &lcu->grouplist, group) {
+               list_splice_init(&pavgroup->aliaslist, &active);
+       }
+       while (!list_empty(&active)) {
+               device = list_first_entry(&active, struct dasd_device,
+                                         alias_list);
+               spin_unlock_irqrestore(&lcu->lock, flags);
+               rc = dasd_flush_device_queue(device);
+               spin_lock_irqsave(&lcu->lock, flags);
+               /*
+                * only move device around if it wasn't moved away while we
+                * were waiting for the flush
+                */
+               if (device == list_first_entry(&active,
+                                              struct dasd_device, alias_list))
+                       list_move(&device->alias_list, &lcu->active_devices);
+       }
+       spin_unlock_irqrestore(&lcu->lock, flags);
+}
+
+/*
+ * This function is called in interrupt context, so the
+ * cdev lock for device is already locked!
+ */
+static void _stop_all_devices_on_lcu(struct alias_lcu *lcu,
+                                    struct dasd_device *device)
+{
+       struct alias_pav_group *pavgroup;
+       struct dasd_device *pos;
+
+       list_for_each_entry(pos, &lcu->active_devices, alias_list) {
+               if (pos != device)
+                       spin_lock(get_ccwdev_lock(pos->cdev));
+               pos->stopped |= DASD_STOPPED_SU;
+               if (pos != device)
+                       spin_unlock(get_ccwdev_lock(pos->cdev));
+       }
+       list_for_each_entry(pos, &lcu->inactive_devices, alias_list) {
+               if (pos != device)
+                       spin_lock(get_ccwdev_lock(pos->cdev));
+               pos->stopped |= DASD_STOPPED_SU;
+               if (pos != device)
+                       spin_unlock(get_ccwdev_lock(pos->cdev));
+       }
+       list_for_each_entry(pavgroup, &lcu->grouplist, group) {
+               list_for_each_entry(pos, &pavgroup->baselist, alias_list) {
+                       if (pos != device)
+                               spin_lock(get_ccwdev_lock(pos->cdev));
+                       pos->stopped |= DASD_STOPPED_SU;
+                       if (pos != device)
+                               spin_unlock(get_ccwdev_lock(pos->cdev));
+               }
+               list_for_each_entry(pos, &pavgroup->aliaslist, alias_list) {
+                       if (pos != device)
+                               spin_lock(get_ccwdev_lock(pos->cdev));
+                       pos->stopped |= DASD_STOPPED_SU;
+                       if (pos != device)
+                               spin_unlock(get_ccwdev_lock(pos->cdev));
+               }
+       }
+}
+
+static void _unstop_all_devices_on_lcu(struct alias_lcu *lcu)
+{
+       struct alias_pav_group *pavgroup;
+       struct dasd_device *device;
+       unsigned long flags;
+
+       list_for_each_entry(device, &lcu->active_devices, alias_list) {
+               spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
+               device->stopped &= ~DASD_STOPPED_SU;
+               spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
+       }
+
+       list_for_each_entry(device, &lcu->inactive_devices, alias_list) {
+               spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
+               device->stopped &= ~DASD_STOPPED_SU;
+               spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
+       }
+
+       list_for_each_entry(pavgroup, &lcu->grouplist, group) {
+               list_for_each_entry(device, &pavgroup->baselist, alias_list) {
+                       spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
+                       device->stopped &= ~DASD_STOPPED_SU;
+                       spin_unlock_irqrestore(get_ccwdev_lock(device->cdev),
+                                              flags);
+               }
+               list_for_each_entry(device, &pavgroup->aliaslist, alias_list) {
+                       spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
+                       device->stopped &= ~DASD_STOPPED_SU;
+                       spin_unlock_irqrestore(get_ccwdev_lock(device->cdev),
+                                              flags);
+               }
+       }
+}
+
+static void summary_unit_check_handling_work(struct work_struct *work)
+{
+       struct alias_lcu *lcu;
+       struct summary_unit_check_work_data *suc_data;
+       unsigned long flags;
+       struct dasd_device *device;
+
+       suc_data = container_of(work, struct summary_unit_check_work_data,
+                               worker);
+       lcu = container_of(suc_data, struct alias_lcu, suc_data);
+       device = suc_data->device;
+
+       /* 1. flush alias devices */
+       flush_all_alias_devices_on_lcu(lcu);
+
+       /* 2. reset summary unit check */
+       spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
+       device->stopped &= ~(DASD_STOPPED_SU | DASD_STOPPED_PENDING);
+       spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
+       reset_summary_unit_check(lcu, device, suc_data->reason);
+
+       spin_lock_irqsave(&lcu->lock, flags);
+       _unstop_all_devices_on_lcu(lcu);
+       _restart_all_base_devices_on_lcu(lcu);
+       /* 3. read new alias configuration */
+       _schedule_lcu_update(lcu, device);
+       lcu->suc_data.device = NULL;
+       spin_unlock_irqrestore(&lcu->lock, flags);
+}
+
+/*
+ * note: this will be called from int handler context (cdev locked)
+ */
+void dasd_alias_handle_summary_unit_check(struct dasd_device *device,
+                                         struct irb *irb)
+{
+       struct alias_lcu *lcu;
+       char reason;
+       struct dasd_eckd_private *private;
+
+       private = (struct dasd_eckd_private *) device->private;
+
+       reason = irb->ecw[8];
+       DEV_MESSAGE(KERN_WARNING, device, "%s %x",
+                   "eckd handle summary unit check: reason", reason);
+
+       lcu = private->lcu;
+       if (!lcu) {
+               DEV_MESSAGE(KERN_WARNING, device, "%s",
+                           "device not ready to handle summary"
+                           " unit check (no lcu structure)");
+               return;
+       }
+       spin_lock(&lcu->lock);
+       _stop_all_devices_on_lcu(lcu, device);
+       /* prepare for lcu_update */
+       private->lcu->flags |= NEED_UAC_UPDATE | UPDATE_PENDING;
+       /* If this device is about to be removed just return and wait for
+        * the next interrupt on a different device
+        */
+       if (list_empty(&device->alias_list)) {
+               DEV_MESSAGE(KERN_WARNING, device, "%s",
+                           "device is in offline processing,"
+                           " don't do summary unit check handling");
+               spin_unlock(&lcu->lock);
+               return;
+       }
+       if (lcu->suc_data.device) {
+               /* already scheduled or running */
+               DEV_MESSAGE(KERN_WARNING, device, "%s",
+                           "previous instance of summary unit check worker"
+                           " still pending");
+               spin_unlock(&lcu->lock);
+               return ;
+       }
+       lcu->suc_data.reason = reason;
+       lcu->suc_data.device = device;
+       spin_unlock(&lcu->lock);
+       schedule_work(&lcu->suc_data.worker);
+};
 
        struct dasd_uid uid;
 };
 
-/*
- * dasd_server_ssid_map contains a globally unique storage server subsystem ID.
- * dasd_server_ssid_list contains the list of all subsystem IDs accessed by
- * the DASD device driver.
- */
-struct dasd_server_ssid_map {
-       struct list_head list;
-       struct system_id {
-               char vendor[4];
-               char serial[15];
-               __u16 ssid;
-       } sid;
-};
-
-static struct list_head dasd_server_ssid_list;
-
 /*
  * Parameter parsing functions for dasd= parameter. The syntax is:
  *   <devno>           : (0x)?[0-9a-fA-F]+
                devmap->features &= ~DASD_FEATURE_READONLY;
        if (devmap->device)
                devmap->device->features = devmap->features;
-       if (devmap->device && devmap->device->gdp)
-               set_disk_ro(devmap->device->gdp, val);
+       if (devmap->device && devmap->device->block
+           && devmap->device->block->gdp)
+               set_disk_ro(devmap->device->block->gdp, val);
        spin_unlock(&dasd_devmap_lock);
        return count;
 }
 
        devmap = dasd_find_busid(dev->bus_id);
        spin_lock(&dasd_devmap_lock);
-       if (!IS_ERR(devmap))
-               alias = devmap->uid.alias;
+       if (IS_ERR(devmap) || strlen(devmap->uid.vendor) == 0) {
+               spin_unlock(&dasd_devmap_lock);
+               return sprintf(buf, "0\n");
+       }
+       if (devmap->uid.type == UA_BASE_PAV_ALIAS ||
+           devmap->uid.type == UA_HYPER_PAV_ALIAS)
+               alias = 1;
        else
                alias = 0;
        spin_unlock(&dasd_devmap_lock);
-
        return sprintf(buf, alias ? "1\n" : "0\n");
 }
 
 dasd_uid_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
        struct dasd_devmap *devmap;
-       char uid[UID_STRLEN];
+       char uid_string[UID_STRLEN];
+       char ua_string[3];
+       struct dasd_uid *uid;
 
        devmap = dasd_find_busid(dev->bus_id);
        spin_lock(&dasd_devmap_lock);
-       if (!IS_ERR(devmap) && strlen(devmap->uid.vendor) > 0)
-               snprintf(uid, sizeof(uid), "%s.%s.%04x.%02x",
-                        devmap->uid.vendor, devmap->uid.serial,
-                        devmap->uid.ssid, devmap->uid.unit_addr);
-       else
-               uid[0] = 0;
+       if (IS_ERR(devmap) || strlen(devmap->uid.vendor) == 0) {
+               spin_unlock(&dasd_devmap_lock);
+               return sprintf(buf, "\n");
+       }
+       uid = &devmap->uid;
+       switch (uid->type) {
+       case UA_BASE_DEVICE:
+               sprintf(ua_string, "%02x", uid->real_unit_addr);
+               break;
+       case UA_BASE_PAV_ALIAS:
+               sprintf(ua_string, "%02x", uid->base_unit_addr);
+               break;
+       case UA_HYPER_PAV_ALIAS:
+               sprintf(ua_string, "xx");
+               break;
+       default:
+               /* should not happen, treat like base device */
+               sprintf(ua_string, "%02x", uid->real_unit_addr);
+               break;
+       }
+       snprintf(uid_string, sizeof(uid_string), "%s.%s.%04x.%s",
+                uid->vendor, uid->serial, uid->ssid, ua_string);
        spin_unlock(&dasd_devmap_lock);
-
-       return snprintf(buf, PAGE_SIZE, "%s\n", uid);
+       return snprintf(buf, PAGE_SIZE, "%s\n", uid_string);
 }
 
 static DEVICE_ATTR(uid, 0444, dasd_uid_show, NULL);
 dasd_set_uid(struct ccw_device *cdev, struct dasd_uid *uid)
 {
        struct dasd_devmap *devmap;
-       struct dasd_server_ssid_map *srv, *tmp;
 
        devmap = dasd_find_busid(cdev->dev.bus_id);
        if (IS_ERR(devmap))
                return PTR_ERR(devmap);
 
-       /* generate entry for server_ssid_map */
-       srv = (struct dasd_server_ssid_map *)
-               kzalloc(sizeof(struct dasd_server_ssid_map), GFP_KERNEL);
-       if (!srv)
-               return -ENOMEM;
-       strncpy(srv->sid.vendor, uid->vendor, sizeof(srv->sid.vendor) - 1);
-       strncpy(srv->sid.serial, uid->serial, sizeof(srv->sid.serial) - 1);
-       srv->sid.ssid = uid->ssid;
-
-       /* server is already contained ? */
        spin_lock(&dasd_devmap_lock);
        devmap->uid = *uid;
-       list_for_each_entry(tmp, &dasd_server_ssid_list, list) {
-               if (!memcmp(&srv->sid, &tmp->sid,
-                           sizeof(struct system_id))) {
-                       kfree(srv);
-                       srv = NULL;
-                       break;
-               }
-       }
-
-       /* add servermap to serverlist */
-       if (srv)
-               list_add(&srv->list, &dasd_server_ssid_list);
        spin_unlock(&dasd_devmap_lock);
 
-       return (srv ? 1 : 0);
+       return 0;
 }
 EXPORT_SYMBOL_GPL(dasd_set_uid);
 
        dasd_max_devindex = 0;
        for (i = 0; i < 256; i++)
                INIT_LIST_HEAD(&dasd_hashlists[i]);
-
-       /* Initialize servermap structure. */
-       INIT_LIST_HEAD(&dasd_server_ssid_list);
        return 0;
 }
 
 
        int rc;
 
        mdsk_term_io(device);
-       rc = mdsk_init_io(device, device->bp_block, 0, NULL);
+       rc = mdsk_init_io(device, device->block->bp_block, 0, NULL);
        if (rc)
                DEV_MESSAGE(KERN_WARNING, device, "DIAG ERP unsuccessful, "
                            "rc=%d", rc);
        struct dasd_diag_req *dreq;
        int rc;
 
-       device = cqr->device;
+       device = cqr->startdev;
        if (cqr->retries < 0) {
                DEV_MESSAGE(KERN_WARNING, device, "DIAG start_IO: request %p "
                            "- no retry left)", cqr);
-               cqr->status = DASD_CQR_FAILED;
+               cqr->status = DASD_CQR_ERROR;
                return -EIO;
        }
        private = (struct dasd_diag_private *) device->private;
        switch (rc) {
        case 0: /* Synchronous I/O finished successfully */
                cqr->stopclk = get_clock();
-               cqr->status = DASD_CQR_DONE;
+               cqr->status = DASD_CQR_SUCCESS;
                /* Indicate to calling function that only a dasd_schedule_bh()
                   and no timer is needed */
                 rc = -EACCES;
 {
        struct dasd_device *device;
 
-       device = cqr->device;
+       device = cqr->startdev;
        mdsk_term_io(device);
-       mdsk_init_io(device, device->bp_block, 0, NULL);
-       cqr->status = DASD_CQR_CLEAR;
+       mdsk_init_io(device, device->block->bp_block, 0, NULL);
+       cqr->status = DASD_CQR_CLEAR_PENDING;
        cqr->stopclk = get_clock();
-       dasd_schedule_bh(device);
+       dasd_schedule_device_bh(device);
        return 0;
 }
 
                return;
        }
        cqr = (struct dasd_ccw_req *) ip;
-       device = (struct dasd_device *) cqr->device;
+       device = (struct dasd_device *) cqr->startdev;
        if (strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) {
                DEV_MESSAGE(KERN_WARNING, device,
                            " magic number of dasd_ccw_req 0x%08X doesn't"
        spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
 
        /* Check for a pending clear operation */
-       if (cqr->status == DASD_CQR_CLEAR) {
-               cqr->status = DASD_CQR_QUEUED;
-               dasd_clear_timer(device);
-               dasd_schedule_bh(device);
+       if (cqr->status == DASD_CQR_CLEAR_PENDING) {
+               cqr->status = DASD_CQR_CLEARED;
+               dasd_device_clear_timer(device);
+               dasd_schedule_device_bh(device);
                spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
                return;
        }
 
        expires = 0;
        if (status == 0) {
-               cqr->status = DASD_CQR_DONE;
+               cqr->status = DASD_CQR_SUCCESS;
                /* Start first request on queue if possible -> fast_io. */
                if (!list_empty(&device->ccw_queue)) {
                        next = list_entry(device->ccw_queue.next,
-                                         struct dasd_ccw_req, list);
+                                         struct dasd_ccw_req, devlist);
                        if (next->status == DASD_CQR_QUEUED) {
                                rc = dasd_start_diag(next);
                                if (rc == 0)
        }
 
        if (expires != 0)
-               dasd_set_timer(device, expires);
+               dasd_device_set_timer(device, expires);
        else
-               dasd_clear_timer(device);
-       dasd_schedule_bh(device);
+               dasd_device_clear_timer(device);
+       dasd_schedule_device_bh(device);
 
        spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
 }
 static int
 dasd_diag_check_device(struct dasd_device *device)
 {
+       struct dasd_block *block;
        struct dasd_diag_private *private;
        struct dasd_diag_characteristics *rdc_data;
        struct dasd_diag_bio bio;
                ccw_device_get_id(device->cdev, &private->dev_id);
                device->private = (void *) private;
        }
+       block = dasd_alloc_block();
+       if (IS_ERR(block)) {
+               DEV_MESSAGE(KERN_WARNING, device, "%s",
+                           "could not allocate dasd block structure");
+               kfree(device->private);
+               return PTR_ERR(block);
+       }
+       device->block = block;
+       block->base = device;
+
        /* Read Device Characteristics */
        rdc_data = (void *) &(private->rdc_data);
        rdc_data->dev_nr = private->dev_id.devno;
                  sizeof(DASD_DIAG_CMS1)) == 0) {
                /* get formatted blocksize from label block */
                bsize = (unsigned int) label->block_size;
-               device->blocks = (unsigned long) label->block_count;
+               block->blocks = (unsigned long) label->block_count;
        } else
-               device->blocks = end_block;
-       device->bp_block = bsize;
-       device->s2b_shift = 0;  /* bits to shift 512 to get a block */
+               block->blocks = end_block;
+       block->bp_block = bsize;
+       block->s2b_shift = 0;   /* bits to shift 512 to get a block */
        for (sb = 512; sb < bsize; sb = sb << 1)
-               device->s2b_shift++;
-       rc = mdsk_init_io(device, device->bp_block, 0, NULL);
+               block->s2b_shift++;
+       rc = mdsk_init_io(device, block->bp_block, 0, NULL);
        if (rc) {
                DEV_MESSAGE(KERN_WARNING, device, "DIAG initialization "
                        "failed (rc=%d)", rc);
        } else {
                DEV_MESSAGE(KERN_INFO, device,
                            "(%ld B/blk): %ldkB",
-                           (unsigned long) device->bp_block,
-                           (unsigned long) (device->blocks <<
-                               device->s2b_shift) >> 1);
+                           (unsigned long) block->bp_block,
+                           (unsigned long) (block->blocks <<
+                               block->s2b_shift) >> 1);
        }
 out:
        free_page((long) label);
 /* Fill in virtual disk geometry for device. Return zero on success, non-zero
  * otherwise. */
 static int
-dasd_diag_fill_geometry(struct dasd_device *device, struct hd_geometry *geo)
+dasd_diag_fill_geometry(struct dasd_block *block, struct hd_geometry *geo)
 {
-       if (dasd_check_blocksize(device->bp_block) != 0)
+       if (dasd_check_blocksize(block->bp_block) != 0)
                return -EINVAL;
-       geo->cylinders = (device->blocks << device->s2b_shift) >> 10;
+       geo->cylinders = (block->blocks << block->s2b_shift) >> 10;
        geo->heads = 16;
-       geo->sectors = 128 >> device->s2b_shift;
+       geo->sectors = 128 >> block->s2b_shift;
        return 0;
 }
 
-static dasd_era_t
-dasd_diag_examine_error(struct dasd_ccw_req * cqr, struct irb * stat)
-{
-       return dasd_era_fatal;
-}
-
 static dasd_erp_fn_t
 dasd_diag_erp_action(struct dasd_ccw_req * cqr)
 {
 
 /* Create DASD request from block device request. Return pointer to new
  * request on success, ERR_PTR otherwise. */
-static struct dasd_ccw_req *
-dasd_diag_build_cp(struct dasd_device * device, struct request *req)
+static struct dasd_ccw_req *dasd_diag_build_cp(struct dasd_device *memdev,
+                                              struct dasd_block *block,
+                                              struct request *req)
 {
        struct dasd_ccw_req *cqr;
        struct dasd_diag_req *dreq;
                rw_cmd = MDSK_WRITE_REQ;
        else
                return ERR_PTR(-EINVAL);
-       blksize = device->bp_block;
+       blksize = block->bp_block;
        /* Calculate record id of first and last block. */
-       first_rec = req->sector >> device->s2b_shift;
-       last_rec = (req->sector + req->nr_sectors - 1) >> device->s2b_shift;
+       first_rec = req->sector >> block->s2b_shift;
+       last_rec = (req->sector + req->nr_sectors - 1) >> block->s2b_shift;
        /* Check struct bio and count the number of blocks for the request. */
        count = 0;
        rq_for_each_segment(bv, req, iter) {
                if (bv->bv_len & (blksize - 1))
                        /* Fba can only do full blocks. */
                        return ERR_PTR(-EINVAL);
-               count += bv->bv_len >> (device->s2b_shift + 9);
+               count += bv->bv_len >> (block->s2b_shift + 9);
        }
        /* Paranoia. */
        if (count != last_rec - first_rec + 1)
        datasize = sizeof(struct dasd_diag_req) +
                count*sizeof(struct dasd_diag_bio);
        cqr = dasd_smalloc_request(dasd_diag_discipline.name, 0,
-                                  datasize, device);
+                                  datasize, memdev);
        if (IS_ERR(cqr))
                return cqr;
 
        cqr->buildclk = get_clock();
        if (req->cmd_flags & REQ_FAILFAST)
                set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
-       cqr->device = device;
+       cqr->startdev = memdev;
+       cqr->memdev = memdev;
+       cqr->block = block;
        cqr->expires = DIAG_TIMEOUT;
        cqr->status = DASD_CQR_FILLED;
        return cqr;
        int status;
 
        status = cqr->status == DASD_CQR_DONE;
-       dasd_sfree_request(cqr, cqr->device);
+       dasd_sfree_request(cqr, cqr->memdev);
        return status;
 }
 
+static void dasd_diag_handle_terminated_request(struct dasd_ccw_req *cqr)
+{
+       cqr->status = DASD_CQR_FILLED;
+};
+
 /* Fill in IOCTL data for device. */
 static int
 dasd_diag_fill_info(struct dasd_device * device,
        .fill_geometry = dasd_diag_fill_geometry,
        .start_IO = dasd_start_diag,
        .term_IO = dasd_diag_term_IO,
-       .examine_error = dasd_diag_examine_error,
+       .handle_terminated_request = dasd_diag_handle_terminated_request,
        .erp_action = dasd_diag_erp_action,
        .erp_postaction = dasd_diag_erp_postaction,
        .build_cp = dasd_diag_build_cp,
 
 
 static struct dasd_discipline dasd_eckd_discipline;
 
-struct dasd_eckd_private {
-       struct dasd_eckd_characteristics rdc_data;
-       struct dasd_eckd_confdata conf_data;
-       struct dasd_eckd_path path_data;
-       struct eckd_count count_area[5];
-       int init_cqr_status;
-       int uses_cdl;
-       struct attrib_data_t attrib;    /* e.g. cache operations */
-};
-
 /* The ccw bus type uses this table to find devices that it sends to
  * dasd_eckd_probe */
 static struct ccw_device_id dasd_eckd_ids[] = {
        if (rc == -ENOSYS || rc == -EACCES)
                rc = 0;
 
-       de_ccw->count = sizeof (struct DE_eckd_data);
+       de_ccw->count = sizeof(struct DE_eckd_data);
        de_ccw->flags |= CCW_FLAG_SLI;
        return rc;
 }
        ccw->count = 16;
        ccw->cda = (__u32) __pa(data);
 
-       memset(data, 0, sizeof (struct DE_eckd_data));
+       memset(data, 0, sizeof(struct DE_eckd_data));
        switch (cmd) {
        case DASD_ECKD_CCW_READ_HOME_ADDRESS:
        case DASD_ECKD_CCW_READ_RECORD_ZERO:
        return rc;
 }
 
+static int check_XRC_on_prefix(struct PFX_eckd_data *pfxdata,
+                              struct dasd_device  *device)
+{
+       struct dasd_eckd_private *private;
+       int rc;
+
+       private = (struct dasd_eckd_private *) device->private;
+       if (!private->rdc_data.facilities.XRC_supported)
+               return 0;
+
+       /* switch on System Time Stamp - needed for XRC Support */
+       pfxdata->define_extend.ga_extended |= 0x08; /* 'Time Stamp Valid'   */
+       pfxdata->define_extend.ga_extended |= 0x02; /* 'Extended Parameter' */
+       pfxdata->validity.time_stamp = 1;           /* 'Time Stamp Valid'   */
+
+       rc = get_sync_clock(&pfxdata->define_extend.ep_sys_time);
+       /* Ignore return code if sync clock is switched off. */
+       if (rc == -ENOSYS || rc == -EACCES)
+               rc = 0;
+       return rc;
+}
+
+static int prefix(struct ccw1 *ccw, struct PFX_eckd_data *pfxdata, int trk,
+                 int totrk, int cmd, struct dasd_device *basedev,
+                 struct dasd_device *startdev)
+{
+       struct dasd_eckd_private *basepriv, *startpriv;
+       struct DE_eckd_data *data;
+       struct ch_t geo, beg, end;
+       int rc = 0;
+
+       basepriv = (struct dasd_eckd_private *) basedev->private;
+       startpriv = (struct dasd_eckd_private *) startdev->private;
+       data = &pfxdata->define_extend;
+
+       ccw->cmd_code = DASD_ECKD_CCW_PFX;
+       ccw->flags = 0;
+       ccw->count = sizeof(*pfxdata);
+       ccw->cda = (__u32) __pa(pfxdata);
+
+       memset(pfxdata, 0, sizeof(*pfxdata));
+       /* prefix data */
+       pfxdata->format = 0;
+       pfxdata->base_address = basepriv->conf_data.ned1.unit_addr;
+       pfxdata->base_lss = basepriv->conf_data.ned1.ID;
+       pfxdata->validity.define_extend = 1;
+
+       /* private uid is kept up to date, conf_data may be outdated */
+       if (startpriv->uid.type != UA_BASE_DEVICE) {
+               pfxdata->validity.verify_base = 1;
+               if (startpriv->uid.type == UA_HYPER_PAV_ALIAS)
+                       pfxdata->validity.hyper_pav = 1;
+       }
+
+       /* define extend data (mostly)*/
+       switch (cmd) {
+       case DASD_ECKD_CCW_READ_HOME_ADDRESS:
+       case DASD_ECKD_CCW_READ_RECORD_ZERO:
+       case DASD_ECKD_CCW_READ:
+       case DASD_ECKD_CCW_READ_MT:
+       case DASD_ECKD_CCW_READ_CKD:
+       case DASD_ECKD_CCW_READ_CKD_MT:
+       case DASD_ECKD_CCW_READ_KD:
+       case DASD_ECKD_CCW_READ_KD_MT:
+       case DASD_ECKD_CCW_READ_COUNT:
+               data->mask.perm = 0x1;
+               data->attributes.operation = basepriv->attrib.operation;
+               break;
+       case DASD_ECKD_CCW_WRITE:
+       case DASD_ECKD_CCW_WRITE_MT:
+       case DASD_ECKD_CCW_WRITE_KD:
+       case DASD_ECKD_CCW_WRITE_KD_MT:
+               data->mask.perm = 0x02;
+               data->attributes.operation = basepriv->attrib.operation;
+               rc = check_XRC_on_prefix(pfxdata, basedev);
+               break;
+       case DASD_ECKD_CCW_WRITE_CKD:
+       case DASD_ECKD_CCW_WRITE_CKD_MT:
+               data->attributes.operation = DASD_BYPASS_CACHE;
+               rc = check_XRC_on_prefix(pfxdata, basedev);
+               break;
+       case DASD_ECKD_CCW_ERASE:
+       case DASD_ECKD_CCW_WRITE_HOME_ADDRESS:
+       case DASD_ECKD_CCW_WRITE_RECORD_ZERO:
+               data->mask.perm = 0x3;
+               data->mask.auth = 0x1;
+               data->attributes.operation = DASD_BYPASS_CACHE;
+               rc = check_XRC_on_prefix(pfxdata, basedev);
+               break;
+       default:
+               DEV_MESSAGE(KERN_ERR, basedev, "unknown opcode 0x%x", cmd);
+               break;
+       }
+
+       data->attributes.mode = 0x3;    /* ECKD */
+
+       if ((basepriv->rdc_data.cu_type == 0x2105 ||
+            basepriv->rdc_data.cu_type == 0x2107 ||
+            basepriv->rdc_data.cu_type == 0x1750)
+           && !(basepriv->uses_cdl && trk < 2))
+               data->ga_extended |= 0x40; /* Regular Data Format Mode */
+
+       geo.cyl = basepriv->rdc_data.no_cyl;
+       geo.head = basepriv->rdc_data.trk_per_cyl;
+       beg.cyl = trk / geo.head;
+       beg.head = trk % geo.head;
+       end.cyl = totrk / geo.head;
+       end.head = totrk % geo.head;
+
+       /* check for sequential prestage - enhance cylinder range */
+       if (data->attributes.operation == DASD_SEQ_PRESTAGE ||
+           data->attributes.operation == DASD_SEQ_ACCESS) {
+
+               if (end.cyl + basepriv->attrib.nr_cyl < geo.cyl)
+                       end.cyl += basepriv->attrib.nr_cyl;
+               else
+                       end.cyl = (geo.cyl - 1);
+       }
+
+       data->beg_ext.cyl = beg.cyl;
+       data->beg_ext.head = beg.head;
+       data->end_ext.cyl = end.cyl;
+       data->end_ext.head = end.head;
+       return rc;
+}
+
 static void
 locate_record(struct ccw1 *ccw, struct LO_eckd_data *data, int trk,
              int rec_on_trk, int no_rec, int cmd,
        ccw->count = 16;
        ccw->cda = (__u32) __pa(data);
 
-       memset(data, 0, sizeof (struct LO_eckd_data));
+       memset(data, 0, sizeof(struct LO_eckd_data));
        sector = 0;
        if (rec_on_trk) {
                switch (private->rdc_data.dev_type) {
               sizeof(uid->serial) - 1);
        EBCASC(uid->serial, sizeof(uid->serial) - 1);
        uid->ssid = confdata->neq.subsystemID;
-       if (confdata->ned2.sneq.flags == 0x40) {
-               uid->alias = 1;
-               uid->unit_addr = confdata->ned2.sneq.base_unit_addr;
-       } else
-               uid->unit_addr = confdata->ned1.unit_addr;
-
+       uid->real_unit_addr = confdata->ned1.unit_addr;
+       if (confdata->ned2.sneq.flags == 0x40 &&
+           confdata->ned2.sneq.format == 0x0001) {
+               uid->type = confdata->ned2.sneq.sua_flags;
+               if (uid->type == UA_BASE_PAV_ALIAS)
+                       uid->base_unit_addr = confdata->ned2.sneq.base_unit_addr;
+       } else {
+               uid->type = UA_BASE_DEVICE;
+       }
        return 0;
 }
 
        ccw->cda = (__u32)(addr_t)rcd_buffer;
        ccw->count = ciw->count;
 
-       cqr->device = device;
+       cqr->startdev = device;
+       cqr->memdev = device;
+       cqr->block = NULL;
        cqr->expires = 10*HZ;
        cqr->lpm = lpm;
        clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
        /*
         * on success we update the user input parms
         */
-       dasd_sfree_request(cqr, cqr->device);
+       dasd_sfree_request(cqr, cqr->memdev);
        if (ret)
                goto out_error;
 
                                        "data retrieved");
                                continue;       /* no error */
                        }
-                       if (conf_len != sizeof (struct dasd_eckd_confdata)) {
+                       if (conf_len != sizeof(struct dasd_eckd_confdata)) {
                                MESSAGE(KERN_WARNING,
                                        "sizes of configuration data mismatch"
                                        "%d (read) vs %ld (expected)",
                                        conf_len,
-                                       sizeof (struct dasd_eckd_confdata));
+                                       sizeof(struct dasd_eckd_confdata));
                                kfree(conf_data);
                                continue;       /* no error */
                        }
                        /* save first valid configuration data */
                        if (!conf_data_saved){
                                memcpy(&private->conf_data, conf_data,
-                                      sizeof (struct dasd_eckd_confdata));
+                                      sizeof(struct dasd_eckd_confdata));
                                conf_data_saved++;
                        }
                        switch (((char *)conf_data)[242] & 0x07){
        return 0;
 }
 
+static int dasd_eckd_read_features(struct dasd_device *device)
+{
+       struct dasd_psf_prssd_data *prssdp;
+       struct dasd_rssd_features *features;
+       struct dasd_ccw_req *cqr;
+       struct ccw1 *ccw;
+       int rc;
+       struct dasd_eckd_private *private;
+
+       private = (struct dasd_eckd_private *) device->private;
+       cqr = dasd_smalloc_request(dasd_eckd_discipline.name,
+                                  1 /* PSF */  + 1 /* RSSD */ ,
+                                  (sizeof(struct dasd_psf_prssd_data) +
+                                   sizeof(struct dasd_rssd_features)),
+                                  device);
+       if (IS_ERR(cqr)) {
+               DEV_MESSAGE(KERN_WARNING, device, "%s",
+                           "Could not allocate initialization request");
+               return PTR_ERR(cqr);
+       }
+       cqr->startdev = device;
+       cqr->memdev = device;
+       cqr->block = NULL;
+       clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
+       cqr->retries = 5;
+       cqr->expires = 10 * HZ;
+
+       /* Prepare for Read Subsystem Data */
+       prssdp = (struct dasd_psf_prssd_data *) cqr->data;
+       memset(prssdp, 0, sizeof(struct dasd_psf_prssd_data));
+       prssdp->order = PSF_ORDER_PRSSD;
+       prssdp->suborder = 0x41;        /* Read Feature Codes */
+       /* all other bytes of prssdp must be zero */
+
+       ccw = cqr->cpaddr;
+       ccw->cmd_code = DASD_ECKD_CCW_PSF;
+       ccw->count = sizeof(struct dasd_psf_prssd_data);
+       ccw->flags |= CCW_FLAG_CC;
+       ccw->cda = (__u32)(addr_t) prssdp;
+
+       /* Read Subsystem Data - feature codes */
+       features = (struct dasd_rssd_features *) (prssdp + 1);
+       memset(features, 0, sizeof(struct dasd_rssd_features));
+
+       ccw++;
+       ccw->cmd_code = DASD_ECKD_CCW_RSSD;
+       ccw->count = sizeof(struct dasd_rssd_features);
+       ccw->cda = (__u32)(addr_t) features;
+
+       cqr->buildclk = get_clock();
+       cqr->status = DASD_CQR_FILLED;
+       rc = dasd_sleep_on(cqr);
+       if (rc == 0) {
+               prssdp = (struct dasd_psf_prssd_data *) cqr->data;
+               features = (struct dasd_rssd_features *) (prssdp + 1);
+               memcpy(&private->features, features,
+                      sizeof(struct dasd_rssd_features));
+       }
+       dasd_sfree_request(cqr, cqr->memdev);
+       return rc;
+}
+
+
 /*
  * Build CP for Perform Subsystem Function - SSC.
  */
-static struct dasd_ccw_req *
-dasd_eckd_build_psf_ssc(struct dasd_device *device)
+static struct dasd_ccw_req *dasd_eckd_build_psf_ssc(struct dasd_device *device)
 {
-       struct dasd_ccw_req *cqr;
-       struct dasd_psf_ssc_data *psf_ssc_data;
-       struct ccw1 *ccw;
+       struct dasd_ccw_req *cqr;
+       struct dasd_psf_ssc_data *psf_ssc_data;
+       struct ccw1 *ccw;
 
-       cqr = dasd_smalloc_request("ECKD", 1 /* PSF */ ,
+       cqr = dasd_smalloc_request("ECKD", 1 /* PSF */ ,
                                  sizeof(struct dasd_psf_ssc_data),
                                  device);
 
-       if (IS_ERR(cqr)) {
-              DEV_MESSAGE(KERN_WARNING, device, "%s",
+       if (IS_ERR(cqr)) {
+               DEV_MESSAGE(KERN_WARNING, device, "%s",
                           "Could not allocate PSF-SSC request");
-              return cqr;
-       }
-       psf_ssc_data = (struct dasd_psf_ssc_data *)cqr->data;
-       psf_ssc_data->order = PSF_ORDER_SSC;
-       psf_ssc_data->suborder = 0x08;
-
-       ccw = cqr->cpaddr;
-       ccw->cmd_code = DASD_ECKD_CCW_PSF;
-       ccw->cda = (__u32)(addr_t)psf_ssc_data;
-       ccw->count = 66;
-
-       cqr->device = device;
-       cqr->expires = 10*HZ;
-       cqr->buildclk = get_clock();
-       cqr->status = DASD_CQR_FILLED;
-       return cqr;
+               return cqr;
+       }
+       psf_ssc_data = (struct dasd_psf_ssc_data *)cqr->data;
+       psf_ssc_data->order = PSF_ORDER_SSC;
+       psf_ssc_data->suborder = 0x88;
+       psf_ssc_data->reserved[0] = 0x88;
+
+       ccw = cqr->cpaddr;
+       ccw->cmd_code = DASD_ECKD_CCW_PSF;
+       ccw->cda = (__u32)(addr_t)psf_ssc_data;
+       ccw->count = 66;
+
+       cqr->startdev = device;
+       cqr->memdev = device;
+       cqr->block = NULL;
+       cqr->expires = 10*HZ;
+       cqr->buildclk = get_clock();
+       cqr->status = DASD_CQR_FILLED;
+       return cqr;
 }
 
 /*
 static int
 dasd_eckd_psf_ssc(struct dasd_device *device)
 {
-       struct dasd_ccw_req *cqr;
-       int rc;
-
-       cqr = dasd_eckd_build_psf_ssc(device);
-       if (IS_ERR(cqr))
-              return PTR_ERR(cqr);
-
-       rc = dasd_sleep_on(cqr);
-       if (!rc)
-              /* trigger CIO to reprobe devices */
-              css_schedule_reprobe();
-       dasd_sfree_request(cqr, cqr->device);
-       return rc;
+       struct dasd_ccw_req *cqr;
+       int rc;
+
+       cqr = dasd_eckd_build_psf_ssc(device);
+       if (IS_ERR(cqr))
+               return PTR_ERR(cqr);
+
+       rc = dasd_sleep_on(cqr);
+       if (!rc)
+               /* trigger CIO to reprobe devices */
+               css_schedule_reprobe();
+       dasd_sfree_request(cqr, cqr->memdev);
+       return rc;
 }
 
 /*
  * Valide storage server of current device.
  */
-static int
-dasd_eckd_validate_server(struct dasd_device *device, struct dasd_uid *uid)
+static int dasd_eckd_validate_server(struct dasd_device *device)
 {
        int rc;
+       struct dasd_eckd_private *private;
 
        /* Currently PAV is the only reason to 'validate' server on LPAR */
        if (dasd_nopav || MACHINE_IS_VM)
        rc = dasd_eckd_psf_ssc(device);
        /* may be requested feature is not available on server,
         * therefore just report error and go ahead */
+       private = (struct dasd_eckd_private *) device->private;
        DEV_MESSAGE(KERN_INFO, device,
                    "PSF-SSC on storage subsystem %s.%s.%04x returned rc=%d",
-                   uid->vendor, uid->serial, uid->ssid, rc);
+                   private->uid.vendor, private->uid.serial,
+                   private->uid.ssid, rc);
        /* RE-Read Configuration Data */
        return dasd_eckd_read_conf(device);
 }
 dasd_eckd_check_characteristics(struct dasd_device *device)
 {
        struct dasd_eckd_private *private;
-       struct dasd_uid uid;
+       struct dasd_block *block;
        void *rdc_data;
-       int rc;
+       int is_known, rc;
 
        private = (struct dasd_eckd_private *) device->private;
        if (private == NULL) {
        /* Read Configuration Data */
        rc = dasd_eckd_read_conf(device);
        if (rc)
-               return rc;
+               goto out_err1;
 
        /* Generate device unique id and register in devmap */
-       rc = dasd_eckd_generate_uid(device, &uid);
+       rc = dasd_eckd_generate_uid(device, &private->uid);
        if (rc)
-               return rc;
-       rc = dasd_set_uid(device->cdev, &uid);
-       if (rc == 1)    /* new server found */
-               rc = dasd_eckd_validate_server(device, &uid);
+               goto out_err1;
+       dasd_set_uid(device->cdev, &private->uid);
+
+       if (private->uid.type == UA_BASE_DEVICE) {
+               block = dasd_alloc_block();
+               if (IS_ERR(block)) {
+                       DEV_MESSAGE(KERN_WARNING, device, "%s",
+                                   "could not allocate dasd block structure");
+                       rc = PTR_ERR(block);
+                       goto out_err1;
+               }
+               device->block = block;
+               block->base = device;
+       }
+
+       /* register lcu with alias handling, enable PAV if this is a new lcu */
+       is_known = dasd_alias_make_device_known_to_lcu(device);
+       if (is_known < 0) {
+               rc = is_known;
+               goto out_err2;
+       }
+       if (!is_known) {
+               /* new lcu found */
+               rc = dasd_eckd_validate_server(device); /* will switch pav on */
+               if (rc)
+                       goto out_err3;
+       }
+
+       /* Read Feature Codes */
+       rc = dasd_eckd_read_features(device);
        if (rc)
-               return rc;
+               goto out_err3;
 
        /* Read Device Characteristics */
        rdc_data = (void *) &(private->rdc_data);
        memset(rdc_data, 0, sizeof(rdc_data));
        rc = dasd_generic_read_dev_chars(device, "ECKD", &rdc_data, 64);
-       if (rc)
+       if (rc) {
                DEV_MESSAGE(KERN_WARNING, device,
                            "Read device characteristics returned "
                            "rc=%d", rc);
-
+               goto out_err3;
+       }
        DEV_MESSAGE(KERN_INFO, device,
                    "%04X/%02X(CU:%04X/%02X) Cyl:%d Head:%d Sec:%d",
                    private->rdc_data.dev_type,
                    private->rdc_data.no_cyl,
                    private->rdc_data.trk_per_cyl,
                    private->rdc_data.sec_per_trk);
+       return 0;
+
+out_err3:
+       dasd_alias_disconnect_device_from_lcu(device);
+out_err2:
+       dasd_free_block(device->block);
+       device->block = NULL;
+out_err1:
+       kfree(device->private);
+       device->private = NULL;
        return rc;
 }
 
+static void dasd_eckd_uncheck_device(struct dasd_device *device)
+{
+       dasd_alias_disconnect_device_from_lcu(device);
+}
+
 static struct dasd_ccw_req *
 dasd_eckd_analysis_ccw(struct dasd_device *device)
 {
        /* Define extent for the first 3 tracks. */
        define_extent(ccw++, cqr->data, 0, 2,
                      DASD_ECKD_CCW_READ_COUNT, device);
-       LO_data = cqr->data + sizeof (struct DE_eckd_data);
+       LO_data = cqr->data + sizeof(struct DE_eckd_data);
        /* Locate record for the first 4 records on track 0. */
        ccw[-1].flags |= CCW_FLAG_CC;
        locate_record(ccw++, LO_data++, 0, 0, 4,
        ccw->count = 8;
        ccw->cda = (__u32)(addr_t) count_data;
 
-       cqr->device = device;
+       cqr->block = NULL;
+       cqr->startdev = device;
+       cqr->memdev = device;
        cqr->retries = 0;
        cqr->buildclk = get_clock();
        cqr->status = DASD_CQR_FILLED;
        struct dasd_eckd_private *private;
        struct dasd_device *device;
 
-       device = init_cqr->device;
+       device = init_cqr->startdev;
        private = (struct dasd_eckd_private *) device->private;
        private->init_cqr_status = init_cqr->status;
        dasd_sfree_request(init_cqr, device);
 }
 
 static int
-dasd_eckd_start_analysis(struct dasd_device *device)
+dasd_eckd_start_analysis(struct dasd_block *block)
 {
        struct dasd_eckd_private *private;
        struct dasd_ccw_req *init_cqr;
 
-       private = (struct dasd_eckd_private *) device->private;
-       init_cqr = dasd_eckd_analysis_ccw(device);
+       private = (struct dasd_eckd_private *) block->base->private;
+       init_cqr = dasd_eckd_analysis_ccw(block->base);
        if (IS_ERR(init_cqr))
                return PTR_ERR(init_cqr);
        init_cqr->callback = dasd_eckd_analysis_callback;
 }
 
 static int
-dasd_eckd_end_analysis(struct dasd_device *device)
+dasd_eckd_end_analysis(struct dasd_block *block)
 {
+       struct dasd_device *device;
        struct dasd_eckd_private *private;
        struct eckd_count *count_area;
        unsigned int sb, blk_per_trk;
        int status, i;
 
+       device = block->base;
        private = (struct dasd_eckd_private *) device->private;
        status = private->init_cqr_status;
        private->init_cqr_status = -1;
 
        private->uses_cdl = 1;
        /* Calculate number of blocks/records per track. */
-       blk_per_trk = recs_per_track(&private->rdc_data, 0, device->bp_block);
+       blk_per_trk = recs_per_track(&private->rdc_data, 0, block->bp_block);
        /* Check Track 0 for Compatible Disk Layout */
        count_area = NULL;
        for (i = 0; i < 3; i++) {
        if (count_area != NULL && count_area->kl == 0) {
                /* we found notthing violating our disk layout */
                if (dasd_check_blocksize(count_area->dl) == 0)
-                       device->bp_block = count_area->dl;
+                       block->bp_block = count_area->dl;
        }
-       if (device->bp_block == 0) {
+       if (block->bp_block == 0) {
                DEV_MESSAGE(KERN_WARNING, device, "%s",
                            "Volume has incompatible disk layout");
                return -EMEDIUMTYPE;
        }
-       device->s2b_shift = 0;  /* bits to shift 512 to get a block */
-       for (sb = 512; sb < device->bp_block; sb = sb << 1)
-               device->s2b_shift++;
+       block->s2b_shift = 0;   /* bits to shift 512 to get a block */
+       for (sb = 512; sb < block->bp_block; sb = sb << 1)
+               block->s2b_shift++;
 
-       blk_per_trk = recs_per_track(&private->rdc_data, 0, device->bp_block);
-       device->blocks = (private->rdc_data.no_cyl *
+       blk_per_trk = recs_per_track(&private->rdc_data, 0, block->bp_block);
+       block->blocks = (private->rdc_data.no_cyl *
                          private->rdc_data.trk_per_cyl *
                          blk_per_trk);
 
        DEV_MESSAGE(KERN_INFO, device,
                    "(%dkB blks): %dkB at %dkB/trk %s",
-                   (device->bp_block >> 10),
+                   (block->bp_block >> 10),
                    ((private->rdc_data.no_cyl *
                      private->rdc_data.trk_per_cyl *
-                     blk_per_trk * (device->bp_block >> 9)) >> 1),
-                   ((blk_per_trk * device->bp_block) >> 10),
+                     blk_per_trk * (block->bp_block >> 9)) >> 1),
+                   ((blk_per_trk * block->bp_block) >> 10),
                    private->uses_cdl ?
                    "compatible disk layout" : "linux disk layout");
 
        return 0;
 }
 
-static int
-dasd_eckd_do_analysis(struct dasd_device *device)
+static int dasd_eckd_do_analysis(struct dasd_block *block)
 {
        struct dasd_eckd_private *private;
 
-       private = (struct dasd_eckd_private *) device->private;
+       private = (struct dasd_eckd_private *) block->base->private;
        if (private->init_cqr_status < 0)
-               return dasd_eckd_start_analysis(device);
+               return dasd_eckd_start_analysis(block);
        else
-               return dasd_eckd_end_analysis(device);
+               return dasd_eckd_end_analysis(block);
 }
 
+static int dasd_eckd_ready_to_online(struct dasd_device *device)
+{
+       return dasd_alias_add_device(device);
+};
+
+static int dasd_eckd_online_to_ready(struct dasd_device *device)
+{
+       return dasd_alias_remove_device(device);
+};
+
 static int
-dasd_eckd_fill_geometry(struct dasd_device *device, struct hd_geometry *geo)
+dasd_eckd_fill_geometry(struct dasd_block *block, struct hd_geometry *geo)
 {
        struct dasd_eckd_private *private;
 
-       private = (struct dasd_eckd_private *) device->private;
-       if (dasd_check_blocksize(device->bp_block) == 0) {
+       private = (struct dasd_eckd_private *) block->base->private;
+       if (dasd_check_blocksize(block->bp_block) == 0) {
                geo->sectors = recs_per_track(&private->rdc_data,
-                                             0, device->bp_block);
+                                             0, block->bp_block);
        }
        geo->cylinders = private->rdc_data.no_cyl;
        geo->heads = private->rdc_data.trk_per_cyl;
                locate_record(ccw++, (struct LO_eckd_data *) data,
                              fdata->start_unit, 0, rpt + 1,
                              DASD_ECKD_CCW_WRITE_RECORD_ZERO, device,
-                             device->bp_block);
+                             device->block->bp_block);
                data += sizeof(struct LO_eckd_data);
                break;
        case 0x04: /* Invalidate track. */
                        ccw++;
                }
        }
-       fcp->device = device;
-       fcp->retries = 2;       /* set retry counter to enable ERP */
+       fcp->startdev = device;
+       fcp->memdev = device;
+       clear_bit(DASD_CQR_FLAGS_USE_ERP, &fcp->flags);
+       fcp->retries = 5;       /* set retry counter to enable default ERP */
        fcp->buildclk = get_clock();
        fcp->status = DASD_CQR_FILLED;
        return fcp;
 }
 
-static dasd_era_t
-dasd_eckd_examine_error(struct dasd_ccw_req * cqr, struct irb * irb)
+static void dasd_eckd_handle_terminated_request(struct dasd_ccw_req *cqr)
 {
-       struct dasd_device *device = (struct dasd_device *) cqr->device;
-       struct ccw_device *cdev = device->cdev;
-
-       if (irb->scsw.cstat == 0x00 &&
-           irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
-               return dasd_era_none;
-
-       switch (cdev->id.cu_type) {
-       case 0x3990:
-       case 0x2105:
-       case 0x2107:
-       case 0x1750:
-               return dasd_3990_erp_examine(cqr, irb);
-       case 0x9343:
-               return dasd_9343_erp_examine(cqr, irb);
-       case 0x3880:
-       default:
-               DEV_MESSAGE(KERN_WARNING, device, "%s",
-                           "default (unknown CU type) - RECOVERABLE return");
-               return dasd_era_recover;
+       cqr->status = DASD_CQR_FILLED;
+       if (cqr->block && (cqr->startdev != cqr->block->base)) {
+               dasd_eckd_reset_ccw_to_base_io(cqr);
+               cqr->startdev = cqr->block->base;
        }
-}
+};
 
 static dasd_erp_fn_t
 dasd_eckd_erp_action(struct dasd_ccw_req * cqr)
 {
-       struct dasd_device *device = (struct dasd_device *) cqr->device;
+       struct dasd_device *device = (struct dasd_device *) cqr->startdev;
        struct ccw_device *cdev = device->cdev;
 
        switch (cdev->id.cu_type) {
        return dasd_default_erp_postaction;
 }
 
-static struct dasd_ccw_req *
-dasd_eckd_build_cp(struct dasd_device * device, struct request *req)
+
+static void dasd_eckd_handle_unsolicited_interrupt(struct dasd_device *device,
+                                                  struct irb *irb)
+{
+       char mask;
+
+       /* first of all check for state change pending interrupt */
+       mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP;
+       if ((irb->scsw.dstat & mask) == mask) {
+               dasd_generic_handle_state_change(device);
+               return;
+       }
+
+       /* summary unit check */
+       if ((irb->scsw.dstat & DEV_STAT_UNIT_CHECK) && irb->ecw[7] == 0x0D) {
+               dasd_alias_handle_summary_unit_check(device, irb);
+               return;
+       }
+
+       /* just report other unsolicited interrupts */
+       DEV_MESSAGE(KERN_DEBUG, device, "%s",
+                   "unsolicited interrupt received");
+       device->discipline->dump_sense(device, NULL, irb);
+       dasd_schedule_device_bh(device);
+
+       return;
+};
+
+static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev,
+                                              struct dasd_block *block,
+                                              struct request *req)
 {
        struct dasd_eckd_private *private;
        unsigned long *idaws;
        sector_t first_trk, last_trk;
        unsigned int first_offs, last_offs;
        unsigned char cmd, rcmd;
+       int use_prefix;
+       struct dasd_device *basedev;
 
-       private = (struct dasd_eckd_private *) device->private;
+       basedev = block->base;
+       private = (struct dasd_eckd_private *) basedev->private;
        if (rq_data_dir(req) == READ)
                cmd = DASD_ECKD_CCW_READ_MT;
        else if (rq_data_dir(req) == WRITE)
        else
                return ERR_PTR(-EINVAL);
        /* Calculate number of blocks/records per track. */
-       blksize = device->bp_block;
+       blksize = block->bp_block;
        blk_per_trk = recs_per_track(&private->rdc_data, 0, blksize);
        /* Calculate record id of first and last block. */
-       first_rec = first_trk = req->sector >> device->s2b_shift;
+       first_rec = first_trk = req->sector >> block->s2b_shift;
        first_offs = sector_div(first_trk, blk_per_trk);
        last_rec = last_trk =
-               (req->sector + req->nr_sectors - 1) >> device->s2b_shift;
+               (req->sector + req->nr_sectors - 1) >> block->s2b_shift;
        last_offs = sector_div(last_trk, blk_per_trk);
        /* Check struct bio and count the number of blocks for the request. */
        count = 0;
                if (bv->bv_len & (blksize - 1))
                        /* Eckd can only do full blocks. */
                        return ERR_PTR(-EINVAL);
-               count += bv->bv_len >> (device->s2b_shift + 9);
+               count += bv->bv_len >> (block->s2b_shift + 9);
 #if defined(CONFIG_64BIT)
                if (idal_is_needed (page_address(bv->bv_page), bv->bv_len))
-                       cidaw += bv->bv_len >> (device->s2b_shift + 9);
+                       cidaw += bv->bv_len >> (block->s2b_shift + 9);
 #endif
        }
        /* Paranoia. */
        if (count != last_rec - first_rec + 1)
                return ERR_PTR(-EINVAL);
-       /* 1x define extent + 1x locate record + number of blocks */
-       cplength = 2 + count;
-       /* 1x define extent + 1x locate record + cidaws*sizeof(long) */
-       datasize = sizeof(struct DE_eckd_data) + sizeof(struct LO_eckd_data) +
-               cidaw * sizeof(unsigned long);
+
+       /* use the prefix command if available */
+       use_prefix = private->features.feature[8] & 0x01;
+       if (use_prefix) {
+               /* 1x prefix + number of blocks */
+               cplength = 2 + count;
+               /* 1x prefix + cidaws*sizeof(long) */
+               datasize = sizeof(struct PFX_eckd_data) +
+                       sizeof(struct LO_eckd_data) +
+                       cidaw * sizeof(unsigned long);
+       } else {
+               /* 1x define extent + 1x locate record + number of blocks */
+               cplength = 2 + count;
+               /* 1x define extent + 1x locate record + cidaws*sizeof(long) */
+               datasize = sizeof(struct DE_eckd_data) +
+                       sizeof(struct LO_eckd_data) +
+                       cidaw * sizeof(unsigned long);
+       }
        /* Find out the number of additional locate record ccws for cdl. */
        if (private->uses_cdl && first_rec < 2*blk_per_trk) {
                if (last_rec >= 2*blk_per_trk)
        }
        /* Allocate the ccw request. */
        cqr = dasd_smalloc_request(dasd_eckd_discipline.name,
-                                  cplength, datasize, device);
+                                  cplength, datasize, startdev);
        if (IS_ERR(cqr))
                return cqr;
        ccw = cqr->cpaddr;
-       /* First ccw is define extent. */
-       if (define_extent(ccw++, cqr->data, first_trk,
-                         last_trk, cmd, device) == -EAGAIN) {
-               /* Clock not in sync and XRC is enabled. Try again later. */
-               dasd_sfree_request(cqr, device);
-               return ERR_PTR(-EAGAIN);
+       /* First ccw is define extent or prefix. */
+       if (use_prefix) {
+               if (prefix(ccw++, cqr->data, first_trk,
+                          last_trk, cmd, basedev, startdev) == -EAGAIN) {
+                       /* Clock not in sync and XRC is enabled.
+                        * Try again later.
+                        */
+                       dasd_sfree_request(cqr, startdev);
+                       return ERR_PTR(-EAGAIN);
+               }
+               idaws = (unsigned long *) (cqr->data +
+                                          sizeof(struct PFX_eckd_data));
+       } else {
+               if (define_extent(ccw++, cqr->data, first_trk,
+                                 last_trk, cmd, startdev) == -EAGAIN) {
+                       /* Clock not in sync and XRC is enabled.
+                        * Try again later.
+                        */
+                       dasd_sfree_request(cqr, startdev);
+                       return ERR_PTR(-EAGAIN);
+               }
+               idaws = (unsigned long *) (cqr->data +
+                                          sizeof(struct DE_eckd_data));
        }
        /* Build locate_record+read/write/ccws. */
-       idaws = (unsigned long *) (cqr->data + sizeof(struct DE_eckd_data));
        LO_data = (struct LO_eckd_data *) (idaws + cidaw);
        recid = first_rec;
        if (private->uses_cdl == 0 || recid > 2*blk_per_trk) {
                /* Only standard blocks so there is just one locate record. */
                ccw[-1].flags |= CCW_FLAG_CC;
                locate_record(ccw++, LO_data++, first_trk, first_offs + 1,
-                             last_rec - recid + 1, cmd, device, blksize);
+                             last_rec - recid + 1, cmd, basedev, blksize);
        }
        rq_for_each_segment(bv, req, iter) {
                dst = page_address(bv->bv_page) + bv->bv_offset;
                                ccw[-1].flags |= CCW_FLAG_CC;
                                locate_record(ccw++, LO_data++,
                                              trkid, recoffs + 1,
-                                             1, rcmd, device, count);
+                                             1, rcmd, basedev, count);
                        }
                        /* Locate record for standard blocks ? */
                        if (private->uses_cdl && recid == 2*blk_per_trk) {
                                locate_record(ccw++, LO_data++,
                                              trkid, recoffs + 1,
                                              last_rec - recid + 1,
-                                             cmd, device, count);
+                                             cmd, basedev, count);
                        }
                        /* Read/write ccw. */
                        ccw[-1].flags |= CCW_FLAG_CC;
        }
        if (req->cmd_flags & REQ_FAILFAST)
                set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
-       cqr->device = device;
+       cqr->startdev = startdev;
+       cqr->memdev = startdev;
+       cqr->block = block;
        cqr->expires = 5 * 60 * HZ;     /* 5 minutes */
        cqr->lpm = private->path_data.ppm;
        cqr->retries = 256;
 
        if (!dasd_page_cache)
                goto out;
-       private = (struct dasd_eckd_private *) cqr->device->private;
-       blksize = cqr->device->bp_block;
+       private = (struct dasd_eckd_private *) cqr->block->base->private;
+       blksize = cqr->block->bp_block;
        blk_per_trk = recs_per_track(&private->rdc_data, 0, blksize);
-       recid = req->sector >> cqr->device->s2b_shift;
+       recid = req->sector >> cqr->block->s2b_shift;
        ccw = cqr->cpaddr;
        /* Skip over define extent & locate record. */
        ccw++;
        }
 out:
        status = cqr->status == DASD_CQR_DONE;
-       dasd_sfree_request(cqr, cqr->device);
+       dasd_sfree_request(cqr, cqr->memdev);
        return status;
 }
 
+/*
+ * Modify ccw chain in cqr so it can be started on a base device.
+ *
+ * Note that this is not enough to restart the cqr!
+ * Either reset cqr->startdev as well (summary unit check handling)
+ * or restart via separate cqr (as in ERP handling).
+ */
+void dasd_eckd_reset_ccw_to_base_io(struct dasd_ccw_req *cqr)
+{
+       struct ccw1 *ccw;
+       struct PFX_eckd_data *pfxdata;
+
+       ccw = cqr->cpaddr;
+       pfxdata = cqr->data;
+
+       if (ccw->cmd_code == DASD_ECKD_CCW_PFX) {
+               pfxdata->validity.verify_base = 0;
+               pfxdata->validity.hyper_pav = 0;
+       }
+}
+
+#define DASD_ECKD_CHANQ_MAX_SIZE 4
+
+static struct dasd_ccw_req *dasd_eckd_build_alias_cp(struct dasd_device *base,
+                                                    struct dasd_block *block,
+                                                    struct request *req)
+{
+       struct dasd_eckd_private *private;
+       struct dasd_device *startdev;
+       unsigned long flags;
+       struct dasd_ccw_req *cqr;
+
+       startdev = dasd_alias_get_start_dev(base);
+       if (!startdev)
+               startdev = base;
+       private = (struct dasd_eckd_private *) startdev->private;
+       if (private->count >= DASD_ECKD_CHANQ_MAX_SIZE)
+               return ERR_PTR(-EBUSY);
+
+       spin_lock_irqsave(get_ccwdev_lock(startdev->cdev), flags);
+       private->count++;
+       cqr = dasd_eckd_build_cp(startdev, block, req);
+       if (IS_ERR(cqr))
+               private->count--;
+       spin_unlock_irqrestore(get_ccwdev_lock(startdev->cdev), flags);
+       return cqr;
+}
+
+static int dasd_eckd_free_alias_cp(struct dasd_ccw_req *cqr,
+                                  struct request *req)
+{
+       struct dasd_eckd_private *private;
+       unsigned long flags;
+
+       spin_lock_irqsave(get_ccwdev_lock(cqr->memdev->cdev), flags);
+       private = (struct dasd_eckd_private *) cqr->memdev->private;
+       private->count--;
+       spin_unlock_irqrestore(get_ccwdev_lock(cqr->memdev->cdev), flags);
+       return dasd_eckd_free_cp(cqr, req);
+}
+
 static int
 dasd_eckd_fill_info(struct dasd_device * device,
                    struct dasd_information2_t * info)
        info->characteristics_size = sizeof(struct dasd_eckd_characteristics);
        memcpy(info->characteristics, &private->rdc_data,
               sizeof(struct dasd_eckd_characteristics));
-       info->confdata_size = sizeof (struct dasd_eckd_confdata);
+       info->confdata_size = sizeof(struct dasd_eckd_confdata);
        memcpy(info->configuration_data, &private->conf_data,
-              sizeof (struct dasd_eckd_confdata));
+              sizeof(struct dasd_eckd_confdata));
        return 0;
 }
 
         cqr->cpaddr->flags |= CCW_FLAG_SLI;
         cqr->cpaddr->count = 32;
        cqr->cpaddr->cda = (__u32)(addr_t) cqr->data;
-       cqr->device = device;
+       cqr->startdev = device;
+       cqr->memdev = device;
        clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
        set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
        cqr->retries = 2;       /* set retry counter to enable basic ERP */
 
        rc = dasd_sleep_on_immediatly(cqr);
 
-       dasd_sfree_request(cqr, cqr->device);
+       dasd_sfree_request(cqr, cqr->memdev);
        return rc;
 }
 
         cqr->cpaddr->flags |= CCW_FLAG_SLI;
         cqr->cpaddr->count = 32;
        cqr->cpaddr->cda = (__u32)(addr_t) cqr->data;
-       cqr->device = device;
+       cqr->startdev = device;
+       cqr->memdev = device;
        clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
        set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
        cqr->retries = 2;       /* set retry counter to enable basic ERP */
 
        rc = dasd_sleep_on_immediatly(cqr);
 
-       dasd_sfree_request(cqr, cqr->device);
+       dasd_sfree_request(cqr, cqr->memdev);
        return rc;
 }
 
         cqr->cpaddr->flags |= CCW_FLAG_SLI;
         cqr->cpaddr->count = 32;
        cqr->cpaddr->cda = (__u32)(addr_t) cqr->data;
-       cqr->device = device;
+       cqr->startdev = device;
+       cqr->memdev = device;
        clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
        set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
        cqr->retries = 2;       /* set retry counter to enable basic ERP */
 
        rc = dasd_sleep_on_immediatly(cqr);
 
-       dasd_sfree_request(cqr, cqr->device);
+       dasd_sfree_request(cqr, cqr->memdev);
        return rc;
 }
 
 
        cqr = dasd_smalloc_request(dasd_eckd_discipline.name,
                                   1 /* PSF */  + 1 /* RSSD */ ,
-                                  (sizeof (struct dasd_psf_prssd_data) +
-                                   sizeof (struct dasd_rssd_perf_stats_t)),
+                                  (sizeof(struct dasd_psf_prssd_data) +
+                                   sizeof(struct dasd_rssd_perf_stats_t)),
                                   device);
        if (IS_ERR(cqr)) {
                DEV_MESSAGE(KERN_WARNING, device, "%s",
                            "Could not allocate initialization request");
                return PTR_ERR(cqr);
        }
-       cqr->device = device;
+       cqr->startdev = device;
+       cqr->memdev = device;
        cqr->retries = 0;
        cqr->expires = 10 * HZ;
 
        /* Prepare for Read Subsystem Data */
        prssdp = (struct dasd_psf_prssd_data *) cqr->data;
-       memset(prssdp, 0, sizeof (struct dasd_psf_prssd_data));
+       memset(prssdp, 0, sizeof(struct dasd_psf_prssd_data));
        prssdp->order = PSF_ORDER_PRSSD;
        prssdp->suborder = 0x01;        /* Performance Statistics */
        prssdp->varies[1] = 0x01;       /* Perf Statistics for the Subsystem */
 
        ccw = cqr->cpaddr;
        ccw->cmd_code = DASD_ECKD_CCW_PSF;
-       ccw->count = sizeof (struct dasd_psf_prssd_data);
+       ccw->count = sizeof(struct dasd_psf_prssd_data);
        ccw->flags |= CCW_FLAG_CC;
        ccw->cda = (__u32)(addr_t) prssdp;
 
        /* Read Subsystem Data - Performance Statistics */
        stats = (struct dasd_rssd_perf_stats_t *) (prssdp + 1);
-       memset(stats, 0, sizeof (struct dasd_rssd_perf_stats_t));
+       memset(stats, 0, sizeof(struct dasd_rssd_perf_stats_t));
 
        ccw++;
        ccw->cmd_code = DASD_ECKD_CCW_RSSD;
-       ccw->count = sizeof (struct dasd_rssd_perf_stats_t);
+       ccw->count = sizeof(struct dasd_rssd_perf_stats_t);
        ccw->cda = (__u32)(addr_t) stats;
 
        cqr->buildclk = get_clock();
        cqr->status = DASD_CQR_FILLED;
        rc = dasd_sleep_on(cqr);
        if (rc == 0) {
-               /* Prepare for Read Subsystem Data */
                prssdp = (struct dasd_psf_prssd_data *) cqr->data;
                stats = (struct dasd_rssd_perf_stats_t *) (prssdp + 1);
                if (copy_to_user(argp, stats,
                                 sizeof(struct dasd_rssd_perf_stats_t)))
                        rc = -EFAULT;
        }
-       dasd_sfree_request(cqr, cqr->device);
+       dasd_sfree_request(cqr, cqr->memdev);
        return rc;
 }
 
 
        rc = 0;
        if (copy_to_user(argp, (long *) &attrib,
-                        sizeof (struct attrib_data_t)))
+                        sizeof(struct attrib_data_t)))
                rc = -EFAULT;
 
        return rc;
 }
 
 static int
-dasd_eckd_ioctl(struct dasd_device *device, unsigned int cmd, void __user *argp)
+dasd_eckd_ioctl(struct dasd_block *block, unsigned int cmd, void __user *argp)
 {
+       struct dasd_device *device = block->base;
+
        switch (cmd) {
        case BIODASDGATTR:
                return dasd_eckd_get_attrib(device, argp);
  * Print sense data and related channel program.
  * Parts are printed because printk buffer is only 1024 bytes.
  */
-static void
-dasd_eckd_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req,
-                    struct irb *irb)
+static void dasd_eckd_dump_sense(struct dasd_device *device,
+                                struct dasd_ccw_req *req, struct irb *irb)
 {
        char *page;
        struct ccw1 *first, *last, *fail, *from, *to;
        }
        printk("%s", page);
 
-       /* dump the Channel Program (max 140 Bytes per line) */
-       /* Count CCW and print first CCWs (maximum 1024 % 140 = 7) */
-       first = req->cpaddr;
-       for (last = first; last->flags & (CCW_FLAG_CC | CCW_FLAG_DC); last++);
-       to = min(first + 6, last);
-       len = sprintf(page,  KERN_ERR PRINTK_HEADER
-                     " Related CP in req: %p\n", req);
-       dasd_eckd_dump_ccw_range(first, to, page + len);
-       printk("%s", page);
+       if (req) {
+               /* req == NULL for unsolicited interrupts */
+               /* dump the Channel Program (max 140 Bytes per line) */
+               /* Count CCW and print first CCWs (maximum 1024 % 140 = 7) */
+               first = req->cpaddr;
+               for (last = first; last->flags & (CCW_FLAG_CC | CCW_FLAG_DC); last++);
+               to = min(first + 6, last);
+               len = sprintf(page,  KERN_ERR PRINTK_HEADER
+                             " Related CP in req: %p\n", req);
+               dasd_eckd_dump_ccw_range(first, to, page + len);
+               printk("%s", page);
 
-       /* print failing CCW area (maximum 4) */
-       /* scsw->cda is either valid or zero  */
-       len = 0;
-       from = ++to;
-       fail = (struct ccw1 *)(addr_t) irb->scsw.cpa; /* failing CCW */
-       if (from <  fail - 2) {
-               from = fail - 2;     /* there is a gap - print header */
-               len += sprintf(page, KERN_ERR PRINTK_HEADER "......\n");
-       }
-       to = min(fail + 1, last);
-       len += dasd_eckd_dump_ccw_range(from, to, page + len);
-
-       /* print last CCWs (maximum 2) */
-       from = max(from, ++to);
-       if (from < last - 1) {
-               from = last - 1;     /* there is a gap - print header */
-               len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n");
+               /* print failing CCW area (maximum 4) */
+               /* scsw->cda is either valid or zero  */
+               len = 0;
+               from = ++to;
+               fail = (struct ccw1 *)(addr_t) irb->scsw.cpa; /* failing CCW */
+               if (from <  fail - 2) {
+                       from = fail - 2;     /* there is a gap - print header */
+                       len += sprintf(page, KERN_ERR PRINTK_HEADER "......\n");
+               }
+               to = min(fail + 1, last);
+               len += dasd_eckd_dump_ccw_range(from, to, page + len);
+
+               /* print last CCWs (maximum 2) */
+               from = max(from, ++to);
+               if (from < last - 1) {
+                       from = last - 1;     /* there is a gap - print header */
+                       len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n");
+               }
+               len += dasd_eckd_dump_ccw_range(from, last, page + len);
+               if (len > 0)
+                       printk("%s", page);
        }
-       len += dasd_eckd_dump_ccw_range(from, last, page + len);
-       if (len > 0)
-               printk("%s", page);
        free_page((unsigned long) page);
 }
 
        .ebcname = "ECKD",
        .max_blocks = 240,
        .check_device = dasd_eckd_check_characteristics,
+       .uncheck_device = dasd_eckd_uncheck_device,
        .do_analysis = dasd_eckd_do_analysis,
+       .ready_to_online = dasd_eckd_ready_to_online,
+       .online_to_ready = dasd_eckd_online_to_ready,
        .fill_geometry = dasd_eckd_fill_geometry,
        .start_IO = dasd_start_IO,
        .term_IO = dasd_term_IO,
+       .handle_terminated_request = dasd_eckd_handle_terminated_request,
        .format_device = dasd_eckd_format_device,
-       .examine_error = dasd_eckd_examine_error,
        .erp_action = dasd_eckd_erp_action,
        .erp_postaction = dasd_eckd_erp_postaction,
-       .build_cp = dasd_eckd_build_cp,
-       .free_cp = dasd_eckd_free_cp,
+       .handle_unsolicited_interrupt = dasd_eckd_handle_unsolicited_interrupt,
+       .build_cp = dasd_eckd_build_alias_cp,
+       .free_cp = dasd_eckd_free_alias_cp,
        .dump_sense = dasd_eckd_dump_sense,
        .fill_info = dasd_eckd_fill_info,
        .ioctl = dasd_eckd_ioctl,
 
 #define DASD_ECKD_CCW_READ_CKD_MT       0x9e
 #define DASD_ECKD_CCW_WRITE_CKD_MT      0x9d
 #define DASD_ECKD_CCW_RESERVE           0xB4
+#define DASD_ECKD_CCW_PFX               0xE7
+#define DASD_ECKD_CCW_RSCK              0xF9
 
 /*
  * Perform Subsystem Function / Sub-Orders
        __u16 length;
 } __attribute__ ((packed));
 
+/* Prefix data for format 0x00 and 0x01 */
+struct PFX_eckd_data {
+       unsigned char format;
+       struct {
+               unsigned char define_extend:1;
+               unsigned char time_stamp:1;
+               unsigned char verify_base:1;
+               unsigned char hyper_pav:1;
+               unsigned char reserved:4;
+       } __attribute__ ((packed)) validity;
+       __u8 base_address;
+       __u8 aux;
+       __u8 base_lss;
+       __u8 reserved[7];
+       struct DE_eckd_data define_extend;
+       struct LO_eckd_data locate_record;
+       __u8 LO_extended_data[4];
+} __attribute__ ((packed));
+
 struct dasd_eckd_characteristics {
        __u16 cu_type;
        struct {
                } __attribute__ ((packed)) ned;
                struct {
                        unsigned char flags;            /* byte  0    */
-                       unsigned char res2[7];          /* byte  1- 7 */
+                       unsigned char res1;             /* byte  1    */
+                       __u16 format;                   /* byte  2-3  */
+                       unsigned char res2[4];          /* byte  4-7  */
                        unsigned char sua_flags;        /* byte  8    */
                        __u8 base_unit_addr;            /* byte  9    */
                        unsigned char res3[22];         /* byte 10-31 */
        __u8 npm;
 };
 
+struct dasd_rssd_features {
+       char feature[256];
+} __attribute__((packed));
+
+
 /*
  * Perform Subsystem Function - Prepare for Read Subsystem Data
  */
        unsigned char reserved[59];
 } __attribute__((packed));
 
+
+/*
+ * some structures and definitions for alias handling
+ */
+struct dasd_unit_address_configuration {
+       struct {
+               char ua_type;
+               char base_ua;
+       } unit[256];
+} __attribute__((packed));
+
+
+#define MAX_DEVICES_PER_LCU 256
+
+/* flags on the LCU  */
+#define NEED_UAC_UPDATE  0x01
+#define UPDATE_PENDING 0x02
+
+enum pavtype {NO_PAV, BASE_PAV, HYPER_PAV};
+
+
+struct alias_root {
+       struct list_head serverlist;
+       spinlock_t lock;
+};
+
+struct alias_server {
+       struct list_head server;
+       struct dasd_uid uid;
+       struct list_head lculist;
+};
+
+struct summary_unit_check_work_data {
+       char reason;
+       struct dasd_device *device;
+       struct work_struct worker;
+};
+
+struct read_uac_work_data {
+       struct dasd_device *device;
+       struct delayed_work dwork;
+};
+
+struct alias_lcu {
+       struct list_head lcu;
+       struct dasd_uid uid;
+       enum pavtype pav;
+       char flags;
+       spinlock_t lock;
+       struct list_head grouplist;
+       struct list_head active_devices;
+       struct list_head inactive_devices;
+       struct dasd_unit_address_configuration *uac;
+       struct summary_unit_check_work_data suc_data;
+       struct read_uac_work_data ruac_data;
+       struct dasd_ccw_req *rsu_cqr;
+};
+
+struct alias_pav_group {
+       struct list_head group;
+       struct dasd_uid uid;
+       struct alias_lcu *lcu;
+       struct list_head baselist;
+       struct list_head aliaslist;
+       struct dasd_device *next;
+};
+
+
+struct dasd_eckd_private {
+       struct dasd_eckd_characteristics rdc_data;
+       struct dasd_eckd_confdata conf_data;
+       struct dasd_eckd_path path_data;
+       struct eckd_count count_area[5];
+       int init_cqr_status;
+       int uses_cdl;
+       struct attrib_data_t attrib;    /* e.g. cache operations */
+       struct dasd_rssd_features features;
+
+       /* alias managemnet */
+       struct dasd_uid uid;
+       struct alias_pav_group *pavgroup;
+       struct alias_lcu *lcu;
+       int count;
+};
+
+
+
+int dasd_alias_make_device_known_to_lcu(struct dasd_device *);
+void dasd_alias_disconnect_device_from_lcu(struct dasd_device *);
+int dasd_alias_add_device(struct dasd_device *);
+int dasd_alias_remove_device(struct dasd_device *);
+struct dasd_device *dasd_alias_get_start_dev(struct dasd_device *);
+void dasd_alias_handle_summary_unit_check(struct dasd_device *, struct irb *);
+void dasd_eckd_reset_ccw_to_base_io(struct dasd_ccw_req *);
+
 #endif                         /* DASD_ECKD_H */
 
        unsigned long flags;
        struct eerbuffer *eerb;
 
-       snss_rc = (cqr->status == DASD_CQR_FAILED) ? -EIO : 0;
+       snss_rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO;
        if (snss_rc)
                data_size = 0;
        else
                set_bit(DASD_FLAG_EER_SNSS, &device->flags);
                return;
        }
+       /* cdev is already locked, can't use dasd_add_request_head */
        clear_bit(DASD_FLAG_EER_SNSS, &device->flags);
        cqr->status = DASD_CQR_QUEUED;
-       list_add(&cqr->list, &device->ccw_queue);
-       dasd_schedule_bh(device);
+       list_add(&cqr->devlist, &device->ccw_queue);
+       dasd_schedule_device_bh(device);
 }
 
 /*
  */
 static void dasd_eer_snss_cb(struct dasd_ccw_req *cqr, void *data)
 {
-        struct dasd_device *device = cqr->device;
+       struct dasd_device *device = cqr->startdev;
        unsigned long flags;
 
        dasd_eer_write(device, cqr, DASD_EER_STATECHANGE);
        if (!cqr)
                return -ENOMEM;
 
-       cqr->device = device;
+       cqr->startdev = device;
        cqr->retries = 255;
        cqr->expires = 10 * HZ;
        clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
 
        if (cqr == NULL)
                return ERR_PTR(-ENOMEM);
        memset(cqr, 0, sizeof(struct dasd_ccw_req));
+       INIT_LIST_HEAD(&cqr->devlist);
+       INIT_LIST_HEAD(&cqr->blocklist);
        data = (char *) cqr + ((sizeof(struct dasd_ccw_req) + 7L) & -8L);
        cqr->cpaddr = NULL;
        if (cplength > 0) {
 }
 
 void
-dasd_free_erp_request(struct dasd_ccw_req * cqr, struct dasd_device * device)
+dasd_free_erp_request(struct dasd_ccw_req *cqr, struct dasd_device * device)
 {
        unsigned long flags;
 
  * dasd_default_erp_action just retries the current cqr
  */
 struct dasd_ccw_req *
-dasd_default_erp_action(struct dasd_ccw_req * cqr)
+dasd_default_erp_action(struct dasd_ccw_req *cqr)
 {
        struct dasd_device *device;
 
-       device = cqr->device;
+       device = cqr->startdev;
 
         /* just retry - there is nothing to save ... I got no sense data.... */
         if (cqr->retries > 0) {
                              "default ERP called (%i retries left)",
                              cqr->retries);
                cqr->lpm    = LPM_ANYPATH;
-               cqr->status = DASD_CQR_QUEUED;
+               cqr->status = DASD_CQR_FILLED;
         } else {
                 DEV_MESSAGE (KERN_WARNING, device, "%s",
                             "default ERP called (NO retry left)");
                cqr->status = DASD_CQR_FAILED;
-               cqr->stopclk = get_clock ();
+               cqr->stopclk = get_clock();
         }
         return cqr;
 }                              /* end dasd_default_erp_action */
  * RETURN VALUES
  *   cqr               pointer to the original CQR
  */
-struct dasd_ccw_req *
-dasd_default_erp_postaction(struct dasd_ccw_req * cqr)
+struct dasd_ccw_req *dasd_default_erp_postaction(struct dasd_ccw_req *cqr)
 {
-       struct dasd_device *device;
        int success;
 
        BUG_ON(cqr->refers == NULL || cqr->function == NULL);
 
-       device = cqr->device;
        success = cqr->status == DASD_CQR_DONE;
 
        /* free all ERPs - but NOT the original cqr */
                struct dasd_ccw_req *refers;
 
                refers = cqr->refers;
-               /* remove the request from the device queue */
-               list_del(&cqr->list);
+               /* remove the request from the block queue */
+               list_del(&cqr->blocklist);
                /* free the finished erp request */
-               dasd_free_erp_request(cqr, device);
+               dasd_free_erp_request(cqr, cqr->memdev);
                cqr = refers;
        }
 
 {
        struct dasd_device *device;
 
-       device = cqr->device;
+       device = cqr->startdev;
        /* dump sense data */
        if (device->discipline && device->discipline->dump_sense)
                device->discipline->dump_sense(device, cqr, irb);
 
 static int
 dasd_fba_check_characteristics(struct dasd_device *device)
 {
+       struct dasd_block *block;
        struct dasd_fba_private *private;
        struct ccw_device *cdev = device->cdev;
        void *rdc_data;
                }
                device->private = (void *) private;
        }
+       block = dasd_alloc_block();
+       if (IS_ERR(block)) {
+               DEV_MESSAGE(KERN_WARNING, device, "%s",
+                           "could not allocate dasd block structure");
+               kfree(device->private);
+               return PTR_ERR(block);
+       }
+       device->block = block;
+       block->base = device;
+
        /* Read Device Characteristics */
        rdc_data = (void *) &(private->rdc_data);
        rc = dasd_generic_read_dev_chars(device, "FBA ", &rdc_data, 32);
        return 0;
 }
 
-static int
-dasd_fba_do_analysis(struct dasd_device *device)
+static int dasd_fba_do_analysis(struct dasd_block *block)
 {
        struct dasd_fba_private *private;
        int sb, rc;
 
-       private = (struct dasd_fba_private *) device->private;
+       private = (struct dasd_fba_private *) block->base->private;
        rc = dasd_check_blocksize(private->rdc_data.blk_size);
        if (rc) {
-               DEV_MESSAGE(KERN_INFO, device, "unknown blocksize %d",
+               DEV_MESSAGE(KERN_INFO, block->base, "unknown blocksize %d",
                            private->rdc_data.blk_size);
                return rc;
        }
-       device->blocks = private->rdc_data.blk_bdsa;
-       device->bp_block = private->rdc_data.blk_size;
-       device->s2b_shift = 0;  /* bits to shift 512 to get a block */
+       block->blocks = private->rdc_data.blk_bdsa;
+       block->bp_block = private->rdc_data.blk_size;
+       block->s2b_shift = 0;   /* bits to shift 512 to get a block */
        for (sb = 512; sb < private->rdc_data.blk_size; sb = sb << 1)
-               device->s2b_shift++;
+               block->s2b_shift++;
        return 0;
 }
 
-static int
-dasd_fba_fill_geometry(struct dasd_device *device, struct hd_geometry *geo)
+static int dasd_fba_fill_geometry(struct dasd_block *block,
+                                 struct hd_geometry *geo)
 {
-       if (dasd_check_blocksize(device->bp_block) != 0)
+       if (dasd_check_blocksize(block->bp_block) != 0)
                return -EINVAL;
-       geo->cylinders = (device->blocks << device->s2b_shift) >> 10;
+       geo->cylinders = (block->blocks << block->s2b_shift) >> 10;
        geo->heads = 16;
-       geo->sectors = 128 >> device->s2b_shift;
+       geo->sectors = 128 >> block->s2b_shift;
        return 0;
 }
 
-static dasd_era_t
-dasd_fba_examine_error(struct dasd_ccw_req * cqr, struct irb * irb)
-{
-       struct dasd_device *device;
-       struct ccw_device *cdev;
-
-       device = (struct dasd_device *) cqr->device;
-       if (irb->scsw.cstat == 0x00 &&
-           irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
-               return dasd_era_none;
-
-       cdev = device->cdev;
-       switch (cdev->id.dev_type) {
-       case 0x3370:
-               return dasd_3370_erp_examine(cqr, irb);
-       case 0x9336:
-               return dasd_9336_erp_examine(cqr, irb);
-       default:
-               return dasd_era_recover;
-       }
-}
-
 static dasd_erp_fn_t
 dasd_fba_erp_action(struct dasd_ccw_req * cqr)
 {
        if (cqr->function == dasd_default_erp_action)
                return dasd_default_erp_postaction;
 
-       DEV_MESSAGE(KERN_WARNING, cqr->device, "unknown ERP action %p",
+       DEV_MESSAGE(KERN_WARNING, cqr->startdev, "unknown ERP action %p",
                    cqr->function);
        return NULL;
 }
 
-static struct dasd_ccw_req *
-dasd_fba_build_cp(struct dasd_device * device, struct request *req)
+static void dasd_fba_handle_unsolicited_interrupt(struct dasd_device *device,
+                                                  struct irb *irb)
+{
+       char mask;
+
+       /* first of all check for state change pending interrupt */
+       mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP;
+       if ((irb->scsw.dstat & mask) == mask) {
+               dasd_generic_handle_state_change(device);
+               return;
+       }
+
+       /* check for unsolicited interrupts */
+       DEV_MESSAGE(KERN_DEBUG, device, "%s",
+                   "unsolicited interrupt received");
+       device->discipline->dump_sense(device, NULL, irb);
+       dasd_schedule_device_bh(device);
+       return;
+};
+
+static struct dasd_ccw_req *dasd_fba_build_cp(struct dasd_device * memdev,
+                                             struct dasd_block *block,
+                                             struct request *req)
 {
        struct dasd_fba_private *private;
        unsigned long *idaws;
        unsigned int blksize, off;
        unsigned char cmd;
 
-       private = (struct dasd_fba_private *) device->private;
+       private = (struct dasd_fba_private *) block->base->private;
        if (rq_data_dir(req) == READ) {
                cmd = DASD_FBA_CCW_READ;
        } else if (rq_data_dir(req) == WRITE) {
                cmd = DASD_FBA_CCW_WRITE;
        } else
                return ERR_PTR(-EINVAL);
-       blksize = device->bp_block;
+       blksize = block->bp_block;
        /* Calculate record id of first and last block. */
-       first_rec = req->sector >> device->s2b_shift;
-       last_rec = (req->sector + req->nr_sectors - 1) >> device->s2b_shift;
+       first_rec = req->sector >> block->s2b_shift;
+       last_rec = (req->sector + req->nr_sectors - 1) >> block->s2b_shift;
        /* Check struct bio and count the number of blocks for the request. */
        count = 0;
        cidaw = 0;
                if (bv->bv_len & (blksize - 1))
                        /* Fba can only do full blocks. */
                        return ERR_PTR(-EINVAL);
-               count += bv->bv_len >> (device->s2b_shift + 9);
+               count += bv->bv_len >> (block->s2b_shift + 9);
 #if defined(CONFIG_64BIT)
                if (idal_is_needed (page_address(bv->bv_page), bv->bv_len))
                        cidaw += bv->bv_len / blksize;
        }
        /* Allocate the ccw request. */
        cqr = dasd_smalloc_request(dasd_fba_discipline.name,
-                                  cplength, datasize, device);
+                                  cplength, datasize, memdev);
        if (IS_ERR(cqr))
                return cqr;
        ccw = cqr->cpaddr;
        /* First ccw is define extent. */
        define_extent(ccw++, cqr->data, rq_data_dir(req),
-                     device->bp_block, req->sector, req->nr_sectors);
+                     block->bp_block, req->sector, req->nr_sectors);
        /* Build locate_record + read/write ccws. */
        idaws = (unsigned long *) (cqr->data + sizeof(struct DE_fba_data));
        LO_data = (struct LO_fba_data *) (idaws + cidaw);
                                        ccw[-1].flags |= CCW_FLAG_CC;
                        }
                        ccw->cmd_code = cmd;
-                       ccw->count = device->bp_block;
+                       ccw->count = block->bp_block;
                        if (idal_is_needed(dst, blksize)) {
                                ccw->cda = (__u32)(addr_t) idaws;
                                ccw->flags = CCW_FLAG_IDA;
        }
        if (req->cmd_flags & REQ_FAILFAST)
                set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
-       cqr->device = device;
+       cqr->startdev = memdev;
+       cqr->memdev = memdev;
+       cqr->block = block;
        cqr->expires = 5 * 60 * HZ;     /* 5 minutes */
        cqr->retries = 32;
        cqr->buildclk = get_clock();
 
        if (!dasd_page_cache)
                goto out;
-       private = (struct dasd_fba_private *) cqr->device->private;
-       blksize = cqr->device->bp_block;
+       private = (struct dasd_fba_private *) cqr->block->base->private;
+       blksize = cqr->block->bp_block;
        ccw = cqr->cpaddr;
        /* Skip over define extent & locate record. */
        ccw++;
        }
 out:
        status = cqr->status == DASD_CQR_DONE;
-       dasd_sfree_request(cqr, cqr->device);
+       dasd_sfree_request(cqr, cqr->memdev);
        return status;
 }
 
+static void dasd_fba_handle_terminated_request(struct dasd_ccw_req *cqr)
+{
+       cqr->status = DASD_CQR_FILLED;
+};
+
 static int
 dasd_fba_fill_info(struct dasd_device * device,
                   struct dasd_information2_t * info)
        .fill_geometry = dasd_fba_fill_geometry,
        .start_IO = dasd_start_IO,
        .term_IO = dasd_term_IO,
-       .examine_error = dasd_fba_examine_error,
+       .handle_terminated_request = dasd_fba_handle_terminated_request,
        .erp_action = dasd_fba_erp_action,
        .erp_postaction = dasd_fba_erp_postaction,
+       .handle_unsolicited_interrupt = dasd_fba_handle_unsolicited_interrupt,
        .build_cp = dasd_fba_build_cp,
        .free_cp = dasd_fba_free_cp,
        .dump_sense = dasd_fba_dump_sense,
 
 /*
  * Allocate and register gendisk structure for device.
  */
-int
-dasd_gendisk_alloc(struct dasd_device *device)
+int dasd_gendisk_alloc(struct dasd_block *block)
 {
        struct gendisk *gdp;
+       struct dasd_device *base;
        int len;
 
        /* Make sure the minor for this device exists. */
-       if (device->devindex >= DASD_PER_MAJOR)
+       base = block->base;
+       if (base->devindex >= DASD_PER_MAJOR)
                return -EBUSY;
 
        gdp = alloc_disk(1 << DASD_PARTN_BITS);
 
        /* Initialize gendisk structure. */
        gdp->major = DASD_MAJOR;
-       gdp->first_minor = device->devindex << DASD_PARTN_BITS;
+       gdp->first_minor = base->devindex << DASD_PARTN_BITS;
        gdp->fops = &dasd_device_operations;
-       gdp->driverfs_dev = &device->cdev->dev;
+       gdp->driverfs_dev = &base->cdev->dev;
 
        /*
         * Set device name.
         *   dasdaaaa - dasdzzzz : 456976 devices, added up = 475252
         */
        len = sprintf(gdp->disk_name, "dasd");
-       if (device->devindex > 25) {
-               if (device->devindex > 701) {
-                       if (device->devindex > 18277)
+       if (base->devindex > 25) {
+               if (base->devindex > 701) {
+                       if (base->devindex > 18277)
                                len += sprintf(gdp->disk_name + len, "%c",
-                                              'a'+(((device->devindex-18278)
+                                              'a'+(((base->devindex-18278)
                                                     /17576)%26));
                        len += sprintf(gdp->disk_name + len, "%c",
-                                      'a'+(((device->devindex-702)/676)%26));
+                                      'a'+(((base->devindex-702)/676)%26));
                }
                len += sprintf(gdp->disk_name + len, "%c",
-                              'a'+(((device->devindex-26)/26)%26));
+                              'a'+(((base->devindex-26)/26)%26));
        }
-       len += sprintf(gdp->disk_name + len, "%c", 'a'+(device->devindex%26));
+       len += sprintf(gdp->disk_name + len, "%c", 'a'+(base->devindex%26));
 
-       if (device->features & DASD_FEATURE_READONLY)
+       if (block->base->features & DASD_FEATURE_READONLY)
                set_disk_ro(gdp, 1);
-       gdp->private_data = device;
-       gdp->queue = device->request_queue;
-       device->gdp = gdp;
-       set_capacity(device->gdp, 0);
-       add_disk(device->gdp);
+       gdp->private_data = block;
+       gdp->queue = block->request_queue;
+       block->gdp = gdp;
+       set_capacity(block->gdp, 0);
+       add_disk(block->gdp);
        return 0;
 }
 
 /*
  * Unregister and free gendisk structure for device.
  */
-void
-dasd_gendisk_free(struct dasd_device *device)
+void dasd_gendisk_free(struct dasd_block *block)
 {
-       if (device->gdp) {
-               del_gendisk(device->gdp);
-               device->gdp->queue = NULL;
-               put_disk(device->gdp);
-               device->gdp = NULL;
+       if (block->gdp) {
+               del_gendisk(block->gdp);
+               block->gdp->queue = NULL;
+               put_disk(block->gdp);
+               block->gdp = NULL;
        }
 }
 
 /*
  * Trigger a partition detection.
  */
-int
-dasd_scan_partitions(struct dasd_device * device)
+int dasd_scan_partitions(struct dasd_block *block)
 {
        struct block_device *bdev;
 
-       bdev = bdget_disk(device->gdp, 0);
+       bdev = bdget_disk(block->gdp, 0);
        if (!bdev || blkdev_get(bdev, FMODE_READ, 1) < 0)
                return -ENODEV;
        /*
         * is why the assignment to device->bdev is done AFTER
         * the BLKRRPART ioctl.
         */
-       device->bdev = bdev;
+       block->bdev = bdev;
        return 0;
 }
 
  * Remove all inodes in the system for a device, delete the
  * partitions and make device unusable by setting its size to zero.
  */
-void
-dasd_destroy_partitions(struct dasd_device * device)
+void dasd_destroy_partitions(struct dasd_block *block)
 {
        /* The two structs have 168/176 byte on 31/64 bit. */
        struct blkpg_partition bpart;
         * Get the bdev pointer from the device structure and clear
         * device->bdev to lower the offline open_count limit again.
         */
-       bdev = device->bdev;
-       device->bdev = NULL;
+       bdev = block->bdev;
+       block->bdev = NULL;
 
        /*
         * See fs/partition/check.c:delete_partition
        memset(&barg, 0, sizeof(struct blkpg_ioctl_arg));
        barg.data = (void __force __user *) &bpart;
        barg.op = BLKPG_DEL_PARTITION;
-       for (bpart.pno = device->gdp->minors - 1; bpart.pno > 0; bpart.pno--)
+       for (bpart.pno = block->gdp->minors - 1; bpart.pno > 0; bpart.pno--)
                ioctl_by_bdev(bdev, BLKPG, (unsigned long) &barg);
 
-       invalidate_partition(device->gdp, 0);
+       invalidate_partition(block->gdp, 0);
        /* Matching blkdev_put to the blkdev_get in dasd_scan_partitions. */
        blkdev_put(bdev);
-       set_capacity(device->gdp, 0);
+       set_capacity(block->gdp, 0);
 }
 
-int
-dasd_gendisk_init(void)
+int dasd_gendisk_init(void)
 {
        int rc;
 
        return 0;
 }
 
-void
-dasd_gendisk_exit(void)
+void dasd_gendisk_exit(void)
 {
        unregister_blkdev(DASD_MAJOR, "dasd");
 }
 
  * SECTION: Type definitions
  */
 struct dasd_device;
-
-typedef enum {
-       dasd_era_fatal = -1,    /* no chance to recover              */
-       dasd_era_none = 0,      /* don't recover, everything alright */
-       dasd_era_msg = 1,       /* don't recover, just report...     */
-       dasd_era_recover = 2    /* recovery action recommended       */
-} dasd_era_t;
+struct dasd_block;
 
 /* BIT DEFINITIONS FOR SENSE DATA */
 #define DASD_SENSE_BIT_0 0x80
 
 struct dasd_ccw_req {
        unsigned int magic;             /* Eye catcher */
-        struct list_head list;         /* list_head for request queueing. */
+       struct list_head devlist;       /* for dasd_device request queue */
+       struct list_head blocklist;     /* for dasd_block request queue */
 
        /* Where to execute what... */
-       struct dasd_device *device;     /* device the request is for */
+       struct dasd_block *block;       /* the originating block device */
+       struct dasd_device *memdev;     /* the device used to allocate this */
+       struct dasd_device *startdev;   /* device the request is started on */
        struct ccw1 *cpaddr;            /* address of channel program */
-       char status;                    /* status of this request */
+       char status;                    /* status of this request */
        short retries;                  /* A retry counter */
        unsigned long flags;            /* flags of this request */
 
        /* ... and how */
        unsigned long starttime;        /* jiffies time of request start */
        int expires;                    /* expiration period in jiffies */
-       char lpm;                       /* logical path mask */
+       char lpm;                       /* logical path mask */
        void *data;                     /* pointer to data area */
 
        /* these are important for recovering erroneous requests          */
        unsigned long long endclk;      /* TOD-clock of request termination */
 
         /* Callback that is called after reaching final status. */
-        void (*callback)(struct dasd_ccw_req *, void *data);
-        void *callback_data;
+       void (*callback)(struct dasd_ccw_req *, void *data);
+       void *callback_data;
 };
 
 /*
  * dasd_ccw_req -> status can be:
  */
-#define DASD_CQR_FILLED   0x00 /* request is ready to be processed */
-#define DASD_CQR_QUEUED   0x01 /* request is queued to be processed */
-#define DASD_CQR_IN_IO    0x02 /* request is currently in IO */
-#define DASD_CQR_DONE     0x03 /* request is completed successfully */
-#define DASD_CQR_ERROR    0x04 /* request is completed with error */
-#define DASD_CQR_FAILED   0x05 /* request is finally failed */
-#define DASD_CQR_CLEAR    0x06 /* request is clear pending */
+#define DASD_CQR_FILLED        0x00    /* request is ready to be processed */
+#define DASD_CQR_DONE          0x01    /* request is completed successfully */
+#define DASD_CQR_NEED_ERP      0x02    /* request needs recovery action */
+#define DASD_CQR_IN_ERP        0x03    /* request is in recovery */
+#define DASD_CQR_FAILED        0x04    /* request is finally failed */
+#define DASD_CQR_TERMINATED    0x05    /* request was stopped by driver */
+
+#define DASD_CQR_QUEUED        0x80    /* request is queued to be processed */
+#define DASD_CQR_IN_IO         0x81    /* request is currently in IO */
+#define DASD_CQR_ERROR         0x82    /* request is completed with error */
+#define DASD_CQR_CLEAR_PENDING 0x83    /* request is clear pending */
+#define DASD_CQR_CLEARED       0x84    /* request was cleared */
+#define DASD_CQR_SUCCESS       0x85    /* request was successfull */
+
 
 /* per dasd_ccw_req flags */
 #define DASD_CQR_FLAGS_USE_ERP   0     /* use ERP for this request */
 
        struct list_head list;  /* used for list of disciplines */
 
-        /*
-         * Device recognition functions. check_device is used to verify
-         * the sense data and the information returned by read device
-         * characteristics. It returns 0 if the discipline can be used
-         * for the device in question.
-         * do_analysis is used in the step from device state "basic" to
-         * state "accept". It returns 0 if the device can be made ready,
-         * it returns -EMEDIUMTYPE if the device can't be made ready or
-         * -EAGAIN if do_analysis started a ccw that needs to complete
-         * before the analysis may be repeated.
-         */
-        int (*check_device)(struct dasd_device *);
-       int (*do_analysis) (struct dasd_device *);
-
-        /*
-         * Device operation functions. build_cp creates a ccw chain for
-         * a block device request, start_io starts the request and
-         * term_IO cancels it (e.g. in case of a timeout). format_device
-         * returns a ccw chain to be used to format the device.
-         */
+       /*
+        * Device recognition functions. check_device is used to verify
+        * the sense data and the information returned by read device
+        * characteristics. It returns 0 if the discipline can be used
+        * for the device in question. uncheck_device is called during
+        * device shutdown to deregister a device from its discipline.
+        */
+       int (*check_device) (struct dasd_device *);
+       void (*uncheck_device) (struct dasd_device *);
+
+       /*
+        * do_analysis is used in the step from device state "basic" to
+        * state "accept". It returns 0 if the device can be made ready,
+        * it returns -EMEDIUMTYPE if the device can't be made ready or
+        * -EAGAIN if do_analysis started a ccw that needs to complete
+        * before the analysis may be repeated.
+        */
+       int (*do_analysis) (struct dasd_block *);
+
+       /*
+        * Last things to do when a device is set online, and first things
+        * when it is set offline.
+        */
+       int (*ready_to_online) (struct dasd_device *);
+       int (*online_to_ready) (struct dasd_device *);
+
+       /*
+        * Device operation functions. build_cp creates a ccw chain for
+        * a block device request, start_io starts the request and
+        * term_IO cancels it (e.g. in case of a timeout). format_device
+        * returns a ccw chain to be used to format the device.
+        * handle_terminated_request allows to examine a cqr and prepare
+        * it for retry.
+        */
        struct dasd_ccw_req *(*build_cp) (struct dasd_device *,
+                                         struct dasd_block *,
                                          struct request *);
        int (*start_IO) (struct dasd_ccw_req *);
        int (*term_IO) (struct dasd_ccw_req *);
+       void (*handle_terminated_request) (struct dasd_ccw_req *);
        struct dasd_ccw_req *(*format_device) (struct dasd_device *,
                                               struct format_data_t *);
        int (*free_cp) (struct dasd_ccw_req *, struct request *);
-        /*
-         * Error recovery functions. examine_error() returns a value that
-         * indicates what to do for an error condition. If examine_error()
+
+       /*
+        * Error recovery functions. examine_error() returns a value that
+        * indicates what to do for an error condition. If examine_error()
         * returns 'dasd_era_recover' erp_action() is called to create a
-         * special error recovery ccw. erp_postaction() is called after
-         * an error recovery ccw has finished its execution. dump_sense
-         * is called for every error condition to print the sense data
-         * to the console.
-         */
-       dasd_era_t(*examine_error) (struct dasd_ccw_req *, struct irb *);
+        * special error recovery ccw. erp_postaction() is called after
+        * an error recovery ccw has finished its execution. dump_sense
+        * is called for every error condition to print the sense data
+        * to the console.
+        */
        dasd_erp_fn_t(*erp_action) (struct dasd_ccw_req *);
        dasd_erp_fn_t(*erp_postaction) (struct dasd_ccw_req *);
        void (*dump_sense) (struct dasd_device *, struct dasd_ccw_req *,
                            struct irb *);
 
+       void (*handle_unsolicited_interrupt) (struct dasd_device *,
+                                             struct irb *);
+
         /* i/o control functions. */
-       int (*fill_geometry) (struct dasd_device *, struct hd_geometry *);
+       int (*fill_geometry) (struct dasd_block *, struct hd_geometry *);
        int (*fill_info) (struct dasd_device *, struct dasd_information2_t *);
-       int (*ioctl) (struct dasd_device *, unsigned int, void __user *);
+       int (*ioctl) (struct dasd_block *, unsigned int, void __user *);
 };
 
 extern struct dasd_discipline *dasd_diag_discipline_pointer;
 /*
  * Unique identifier for dasd device.
  */
+#define UA_NOT_CONFIGURED  0x00
+#define UA_BASE_DEVICE    0x01
+#define UA_BASE_PAV_ALIAS  0x02
+#define UA_HYPER_PAV_ALIAS 0x03
+
 struct dasd_uid {
-       __u8 alias;
+       __u8 type;
        char vendor[4];
        char serial[15];
        __u16 ssid;
-       __u8 unit_addr;
+       __u8 real_unit_addr;
+       __u8 base_unit_addr;
 };
 
 /*
 
 struct dasd_device {
        /* Block device stuff. */
-       struct gendisk *gdp;
-       struct request_queue *request_queue;
-       spinlock_t request_queue_lock;
-       struct block_device *bdev;
+       struct dasd_block *block;
+
         unsigned int devindex;
-       unsigned long blocks;      /* size of volume in blocks */
-       unsigned int bp_block;     /* bytes per block */
-       unsigned int s2b_shift;    /* log2 (bp_block/512) */
        unsigned long flags;       /* per device flags */
        unsigned short features;   /* copy of devmap-features (read-only!) */
 
        int state, target;
        int stopped;            /* device (ccw_device_start) was stopped */
 
-       /* Open and reference count. */
+       /* reference count. */
         atomic_t ref_count;
-       atomic_t open_count;
 
        /* ccw queue and memory for static ccw/erp buffers. */
        struct list_head ccw_queue;
 
        struct ccw_device *cdev;
 
+       /* hook for alias management */
+       struct list_head alias_list;
+};
+
+struct dasd_block {
+       /* Block device stuff. */
+       struct gendisk *gdp;
+       struct request_queue *request_queue;
+       spinlock_t request_queue_lock;
+       struct block_device *bdev;
+       atomic_t open_count;
+
+       unsigned long blocks;      /* size of volume in blocks */
+       unsigned int bp_block;     /* bytes per block */
+       unsigned int s2b_shift;    /* log2 (bp_block/512) */
+
+       struct dasd_device *base;
+       struct list_head ccw_queue;
+       spinlock_t queue_lock;
+
+       atomic_t tasklet_scheduled;
+       struct tasklet_struct tasklet;
+       struct timer_list timer;
+
 #ifdef CONFIG_DASD_PROFILE
        struct dasd_profile_info_t profile;
 #endif
 };
 
+
+
 /* reasons why device (ccw_device_start) was stopped */
 #define DASD_STOPPED_NOT_ACC 1         /* not accessible */
 #define DASD_STOPPED_QUIESCE 2         /* Quiesced */
 #define DASD_STOPPED_PENDING 4         /* long busy */
 #define DASD_STOPPED_DC_WAIT 8         /* disconnected, wait */
-#define DASD_STOPPED_DC_EIO  16        /* disconnected, return -EIO */
+#define DASD_STOPPED_SU      16        /* summary unit check handling */
 
 /* per device flags */
-#define DASD_FLAG_DSC_ERROR    2       /* return -EIO when disconnected */
 #define DASD_FLAG_OFFLINE      3       /* device is in offline processing */
 #define DASD_FLAG_EER_SNSS     4       /* A SNSS is required */
 #define DASD_FLAG_EER_IN_USE   5       /* A SNSS request is running */
 struct dasd_device *dasd_alloc_device(void);
 void dasd_free_device(struct dasd_device *);
 
+struct dasd_block *dasd_alloc_block(void);
+void dasd_free_block(struct dasd_block *);
+
 void dasd_enable_device(struct dasd_device *);
 void dasd_set_target_state(struct dasd_device *, int);
 void dasd_kick_device(struct dasd_device *);
 void dasd_add_request_tail(struct dasd_ccw_req *);
 int  dasd_start_IO(struct dasd_ccw_req *);
 int  dasd_term_IO(struct dasd_ccw_req *);
-void dasd_schedule_bh(struct dasd_device *);
+void dasd_schedule_device_bh(struct dasd_device *);
+void dasd_schedule_block_bh(struct dasd_block *);
 int  dasd_sleep_on(struct dasd_ccw_req *);
 int  dasd_sleep_on_immediatly(struct dasd_ccw_req *);
 int  dasd_sleep_on_interruptible(struct dasd_ccw_req *);
-void dasd_set_timer(struct dasd_device *, int);
-void dasd_clear_timer(struct dasd_device *);
+void dasd_device_set_timer(struct dasd_device *, int);
+void dasd_device_clear_timer(struct dasd_device *);
+void dasd_block_set_timer(struct dasd_block *, int);
+void dasd_block_clear_timer(struct dasd_block *);
 int  dasd_cancel_req(struct dasd_ccw_req *);
+int dasd_flush_device_queue(struct dasd_device *);
 int dasd_generic_probe (struct ccw_device *, struct dasd_discipline *);
 void dasd_generic_remove (struct ccw_device *cdev);
 int dasd_generic_set_online(struct ccw_device *, struct dasd_discipline *);
 int dasd_generic_set_offline (struct ccw_device *cdev);
 int dasd_generic_notify(struct ccw_device *, int);
+void dasd_generic_handle_state_change(struct dasd_device *);
 
 int dasd_generic_read_dev_chars(struct dasd_device *, char *, void **, int);
 
 /* externals in dasd_gendisk.c */
 int  dasd_gendisk_init(void);
 void dasd_gendisk_exit(void);
-int dasd_gendisk_alloc(struct dasd_device *);
-void dasd_gendisk_free(struct dasd_device *);
-int dasd_scan_partitions(struct dasd_device *);
-void dasd_destroy_partitions(struct dasd_device *);
+int dasd_gendisk_alloc(struct dasd_block *);
+void dasd_gendisk_free(struct dasd_block *);
+int dasd_scan_partitions(struct dasd_block *);
+void dasd_destroy_partitions(struct dasd_block *);
 
 /* externals in dasd_ioctl.c */
 int  dasd_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
 void dasd_free_erp_request(struct dasd_ccw_req *, struct dasd_device *);
 void dasd_log_sense(struct dasd_ccw_req *, struct irb *);
 
-/* externals in dasd_3370_erp.c */
-dasd_era_t dasd_3370_erp_examine(struct dasd_ccw_req *, struct irb *);
-
 /* externals in dasd_3990_erp.c */
-dasd_era_t dasd_3990_erp_examine(struct dasd_ccw_req *, struct irb *);
 struct dasd_ccw_req *dasd_3990_erp_action(struct dasd_ccw_req *);
 
-/* externals in dasd_9336_erp.c */
-dasd_era_t dasd_9336_erp_examine(struct dasd_ccw_req *, struct irb *);
-
-/* externals in dasd_9336_erp.c */
-dasd_era_t dasd_9343_erp_examine(struct dasd_ccw_req *, struct irb *);
-struct dasd_ccw_req *dasd_9343_erp_action(struct dasd_ccw_req *);
-
 /* externals in dasd_eer.c */
 #ifdef CONFIG_DASD_EER
 int dasd_eer_init(void);
 
 static int
 dasd_ioctl_enable(struct block_device *bdev)
 {
-       struct dasd_device *device = bdev->bd_disk->private_data;
+       struct dasd_block *block = bdev->bd_disk->private_data;
 
        if (!capable(CAP_SYS_ADMIN))
                return -EACCES;
 
-       dasd_enable_device(device);
+       dasd_enable_device(block->base);
        /* Formatting the dasd device can change the capacity. */
        mutex_lock(&bdev->bd_mutex);
-       i_size_write(bdev->bd_inode, (loff_t)get_capacity(device->gdp) << 9);
+       i_size_write(bdev->bd_inode, (loff_t)get_capacity(block->gdp) << 9);
        mutex_unlock(&bdev->bd_mutex);
        return 0;
 }
 static int
 dasd_ioctl_disable(struct block_device *bdev)
 {
-       struct dasd_device *device = bdev->bd_disk->private_data;
+       struct dasd_block *block = bdev->bd_disk->private_data;
 
        if (!capable(CAP_SYS_ADMIN))
                return -EACCES;
         * using the BIODASDFMT ioctl. Therefore the correct state for the
         * device is DASD_STATE_BASIC that allows to do basic i/o.
         */
-       dasd_set_target_state(device, DASD_STATE_BASIC);
+       dasd_set_target_state(block->base, DASD_STATE_BASIC);
        /*
         * Set i_size to zero, since read, write, etc. check against this
         * value.
 /*
  * Quiesce device.
  */
-static int
-dasd_ioctl_quiesce(struct dasd_device *device)
+static int dasd_ioctl_quiesce(struct dasd_block *block)
 {
        unsigned long flags;
+       struct dasd_device *base;
 
+       base = block->base;
        if (!capable (CAP_SYS_ADMIN))
                return -EACCES;
 
-       DEV_MESSAGE (KERN_DEBUG, device, "%s",
-                    "Quiesce IO on device");
-       spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
-       device->stopped |= DASD_STOPPED_QUIESCE;
-       spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
+       DEV_MESSAGE(KERN_DEBUG, base, "%s", "Quiesce IO on device");
+       spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags);
+       base->stopped |= DASD_STOPPED_QUIESCE;
+       spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags);
        return 0;
 }
 
 /*
  * Quiesce device.
  */
-static int
-dasd_ioctl_resume(struct dasd_device *device)
+static int dasd_ioctl_resume(struct dasd_block *block)
 {
        unsigned long flags;
+       struct dasd_device *base;
 
+       base = block->base;
        if (!capable (CAP_SYS_ADMIN))
                return -EACCES;
 
-       DEV_MESSAGE (KERN_DEBUG, device, "%s",
-                    "resume IO on device");
-
-       spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
-       device->stopped &= ~DASD_STOPPED_QUIESCE;
-       spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
+       DEV_MESSAGE(KERN_DEBUG, base, "%s", "resume IO on device");
+       spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags);
+       base->stopped &= ~DASD_STOPPED_QUIESCE;
+       spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags);
 
-       dasd_schedule_bh (device);
+       dasd_schedule_block_bh(block);
        return 0;
 }
 
  * commands to format a single unit of the device. In terms of the ECKD
  * devices this means CCWs are generated to format a single track.
  */
-static int
-dasd_format(struct dasd_device * device, struct format_data_t * fdata)
+static int dasd_format(struct dasd_block *block, struct format_data_t *fdata)
 {
        struct dasd_ccw_req *cqr;
+       struct dasd_device *base;
        int rc;
 
-       if (device->discipline->format_device == NULL)
+       base = block->base;
+       if (base->discipline->format_device == NULL)
                return -EPERM;
 
-       if (device->state != DASD_STATE_BASIC) {
-               DEV_MESSAGE(KERN_WARNING, device, "%s",
+       if (base->state != DASD_STATE_BASIC) {
+               DEV_MESSAGE(KERN_WARNING, base, "%s",
                            "dasd_format: device is not disabled! ");
                return -EBUSY;
        }
 
-       DBF_DEV_EVENT(DBF_NOTICE, device,
+       DBF_DEV_EVENT(DBF_NOTICE, base,
                      "formatting units %d to %d (%d B blocks) flags %d",
                      fdata->start_unit,
                      fdata->stop_unit, fdata->blksize, fdata->intensity);
         * enabling the device later.
         */
        if (fdata->start_unit == 0) {
-               struct block_device *bdev = bdget_disk(device->gdp, 0);
+               struct block_device *bdev = bdget_disk(block->gdp, 0);
                bdev->bd_inode->i_blkbits = blksize_bits(fdata->blksize);
                bdput(bdev);
        }
 
        while (fdata->start_unit <= fdata->stop_unit) {
-               cqr = device->discipline->format_device(device, fdata);
+               cqr = base->discipline->format_device(base, fdata);
                if (IS_ERR(cqr))
                        return PTR_ERR(cqr);
                rc = dasd_sleep_on_interruptible(cqr);
-               dasd_sfree_request(cqr, cqr->device);
+               dasd_sfree_request(cqr, cqr->memdev);
                if (rc) {
                        if (rc != -ERESTARTSYS)
-                               DEV_MESSAGE(KERN_ERR, device,
+                               DEV_MESSAGE(KERN_ERR, base,
                                            " Formatting of unit %d failed "
                                            "with rc = %d",
                                            fdata->start_unit, rc);
 static int
 dasd_ioctl_format(struct block_device *bdev, void __user *argp)
 {
-       struct dasd_device *device = bdev->bd_disk->private_data;
+       struct dasd_block *block = bdev->bd_disk->private_data;
        struct format_data_t fdata;
 
        if (!capable(CAP_SYS_ADMIN))
        if (!argp)
                return -EINVAL;
 
-       if (device->features & DASD_FEATURE_READONLY)
+       if (block->base->features & DASD_FEATURE_READONLY)
                return -EROFS;
        if (copy_from_user(&fdata, argp, sizeof(struct format_data_t)))
                return -EFAULT;
        if (bdev != bdev->bd_contains) {
-               DEV_MESSAGE(KERN_WARNING, device, "%s",
+               DEV_MESSAGE(KERN_WARNING, block->base, "%s",
                            "Cannot low-level format a partition");
                return -EINVAL;
        }
-       return dasd_format(device, &fdata);
+       return dasd_format(block, &fdata);
 }
 
 #ifdef CONFIG_DASD_PROFILE
 /*
  * Reset device profile information
  */
-static int
-dasd_ioctl_reset_profile(struct dasd_device *device)
+static int dasd_ioctl_reset_profile(struct dasd_block *block)
 {
-       memset(&device->profile, 0, sizeof (struct dasd_profile_info_t));
+       memset(&block->profile, 0, sizeof(struct dasd_profile_info_t));
        return 0;
 }
 
 /*
  * Return device profile information
  */
-static int
-dasd_ioctl_read_profile(struct dasd_device *device, void __user *argp)
+static int dasd_ioctl_read_profile(struct dasd_block *block, void __user *argp)
 {
        if (dasd_profile_level == DASD_PROFILE_OFF)
                return -EIO;
-       if (copy_to_user(argp, &device->profile,
-                        sizeof (struct dasd_profile_info_t)))
+       if (copy_to_user(argp, &block->profile,
+                        sizeof(struct dasd_profile_info_t)))
                return -EFAULT;
        return 0;
 }
 #else
-static int
-dasd_ioctl_reset_profile(struct dasd_device *device)
+static int dasd_ioctl_reset_profile(struct dasd_block *block)
 {
        return -ENOSYS;
 }
 
-static int
-dasd_ioctl_read_profile(struct dasd_device *device, void __user *argp)
+static int dasd_ioctl_read_profile(struct dasd_block *block, void __user *argp)
 {
        return -ENOSYS;
 }
 /*
  * Return dasd information. Used for BIODASDINFO and BIODASDINFO2.
  */
-static int
-dasd_ioctl_information(struct dasd_device *device,
-               unsigned int cmd, void __user *argp)
+static int dasd_ioctl_information(struct dasd_block *block,
+                                 unsigned int cmd, void __user *argp)
 {
        struct dasd_information2_t *dasd_info;
        unsigned long flags;
        int rc;
+       struct dasd_device *base;
        struct ccw_device *cdev;
        struct ccw_dev_id dev_id;
 
-       if (!device->discipline->fill_info)
+       base = block->base;
+       if (!base->discipline->fill_info)
                return -EINVAL;
 
        dasd_info = kzalloc(sizeof(struct dasd_information2_t), GFP_KERNEL);
        if (dasd_info == NULL)
                return -ENOMEM;
 
-       rc = device->discipline->fill_info(device, dasd_info);
+       rc = base->discipline->fill_info(base, dasd_info);
        if (rc) {
                kfree(dasd_info);
                return rc;
        }
 
-       cdev = device->cdev;
+       cdev = base->cdev;
        ccw_device_get_id(cdev, &dev_id);
 
        dasd_info->devno = dev_id.devno;
-       dasd_info->schid = _ccw_device_get_subchannel_number(device->cdev);
+       dasd_info->schid = _ccw_device_get_subchannel_number(base->cdev);
        dasd_info->cu_type = cdev->id.cu_type;
        dasd_info->cu_model = cdev->id.cu_model;
        dasd_info->dev_type = cdev->id.dev_type;
        dasd_info->dev_model = cdev->id.dev_model;
-       dasd_info->status = device->state;
+       dasd_info->status = base->state;
        /*
         * The open_count is increased for every opener, that includes
         * the blkdev_get in dasd_scan_partitions.
         * This must be hidden from user-space.
         */
-       dasd_info->open_count = atomic_read(&device->open_count);
-       if (!device->bdev)
+       dasd_info->open_count = atomic_read(&block->open_count);
+       if (!block->bdev)
                dasd_info->open_count++;
 
        /*
         * check if device is really formatted
         * LDL / CDL was returned by 'fill_info'
         */
-       if ((device->state < DASD_STATE_READY) ||
-           (dasd_check_blocksize(device->bp_block)))
+       if ((base->state < DASD_STATE_READY) ||
+           (dasd_check_blocksize(block->bp_block)))
                dasd_info->format = DASD_FORMAT_NONE;
 
        dasd_info->features |=
-               ((device->features & DASD_FEATURE_READONLY) != 0);
+               ((base->features & DASD_FEATURE_READONLY) != 0);
 
-       if (device->discipline)
-               memcpy(dasd_info->type, device->discipline->name, 4);
+       if (base->discipline)
+               memcpy(dasd_info->type, base->discipline->name, 4);
        else
                memcpy(dasd_info->type, "none", 4);
 
-       if (device->request_queue->request_fn) {
+       if (block->request_queue->request_fn) {
                struct list_head *l;
 #ifdef DASD_EXTENDED_PROFILING
                {
                        struct list_head *l;
-                       spin_lock_irqsave(&device->lock, flags);
-                       list_for_each(l, &device->request_queue->queue_head)
+                       spin_lock_irqsave(&block->lock, flags);
+                       list_for_each(l, &block->request_queue->queue_head)
                                dasd_info->req_queue_len++;
-                       spin_unlock_irqrestore(&device->lock, flags);
+                       spin_unlock_irqrestore(&block->lock, flags);
                }
 #endif                         /* DASD_EXTENDED_PROFILING */
-               spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
-               list_for_each(l, &device->ccw_queue)
+               spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags);
+               list_for_each(l, &base->ccw_queue)
                        dasd_info->chanq_len++;
-               spin_unlock_irqrestore(get_ccwdev_lock(device->cdev),
+               spin_unlock_irqrestore(get_ccwdev_lock(base->cdev),
                                       flags);
        }
 
        rc = 0;
        if (copy_to_user(argp, dasd_info,
                         ((cmd == (unsigned int) BIODASDINFO2) ?
-                         sizeof (struct dasd_information2_t) :
-                         sizeof (struct dasd_information_t))))
+                         sizeof(struct dasd_information2_t) :
+                         sizeof(struct dasd_information_t))))
                rc = -EFAULT;
        kfree(dasd_info);
        return rc;
 static int
 dasd_ioctl_set_ro(struct block_device *bdev, void __user *argp)
 {
-       struct dasd_device *device =  bdev->bd_disk->private_data;
+       struct dasd_block *block =  bdev->bd_disk->private_data;
        int intval;
 
        if (!capable(CAP_SYS_ADMIN))
                return -EFAULT;
 
        set_disk_ro(bdev->bd_disk, intval);
-       return dasd_set_feature(device->cdev, DASD_FEATURE_READONLY, intval);
+       return dasd_set_feature(block->base->cdev, DASD_FEATURE_READONLY, intval);
 }
 
-static int
-dasd_ioctl_readall_cmb(struct dasd_device *device, unsigned int cmd,
+static int dasd_ioctl_readall_cmb(struct dasd_block *block, unsigned int cmd,
                unsigned long arg)
 {
        struct cmbdata __user *argp = (void __user *) arg;
        struct cmbdata data;
        int ret;
 
-       ret = cmf_readall(device->cdev, &data);
+       ret = cmf_readall(block->base->cdev, &data);
        if (!ret && copy_to_user(argp, &data, min(size, sizeof(*argp))))
                return -EFAULT;
        return ret;
           unsigned int cmd, unsigned long arg)
 {
        struct block_device *bdev = inode->i_bdev;
-       struct dasd_device *device = bdev->bd_disk->private_data;
+       struct dasd_block *block = bdev->bd_disk->private_data;
        void __user *argp = (void __user *)arg;
 
-       if (!device)
+       if (!block)
                 return -ENODEV;
 
        if ((_IOC_DIR(cmd) != _IOC_NONE) && !arg) {
        case BIODASDENABLE:
                return dasd_ioctl_enable(bdev);
        case BIODASDQUIESCE:
-               return dasd_ioctl_quiesce(device);
+               return dasd_ioctl_quiesce(block);
        case BIODASDRESUME:
-               return dasd_ioctl_resume(device);
+               return dasd_ioctl_resume(block);
        case BIODASDFMT:
                return dasd_ioctl_format(bdev, argp);
        case BIODASDINFO:
-               return dasd_ioctl_information(device, cmd, argp);
+               return dasd_ioctl_information(block, cmd, argp);
        case BIODASDINFO2:
-               return dasd_ioctl_information(device, cmd, argp);
+               return dasd_ioctl_information(block, cmd, argp);
        case BIODASDPRRD:
-               return dasd_ioctl_read_profile(device, argp);
+               return dasd_ioctl_read_profile(block, argp);
        case BIODASDPRRST:
-               return dasd_ioctl_reset_profile(device);
+               return dasd_ioctl_reset_profile(block);
        case BLKROSET:
                return dasd_ioctl_set_ro(bdev, argp);
        case DASDAPIVER:
                return dasd_ioctl_api_version(argp);
        case BIODASDCMFENABLE:
-               return enable_cmf(device->cdev);
+               return enable_cmf(block->base->cdev);
        case BIODASDCMFDISABLE:
-               return disable_cmf(device->cdev);
+               return disable_cmf(block->base->cdev);
        case BIODASDREADALLCMB:
-               return dasd_ioctl_readall_cmb(device, cmd, arg);
+               return dasd_ioctl_readall_cmb(block, cmd, arg);
        default:
                /* if the discipline has an ioctl method try it. */
-               if (device->discipline->ioctl) {
-                       int rval = device->discipline->ioctl(device, cmd, argp);
+               if (block->base->discipline->ioctl) {
+                       int rval = block->base->discipline->ioctl(block, cmd, argp);
                        if (rval != -ENOIOCTLCMD)
                                return rval;
                }
 
 dasd_devices_show(struct seq_file *m, void *v)
 {
        struct dasd_device *device;
+       struct dasd_block *block;
        char *substr;
 
        device = dasd_device_from_devindex((unsigned long) v - 1);
        if (IS_ERR(device))
                return 0;
+       if (device->block)
+               block = device->block;
+       else
+               return 0;
        /* Print device number. */
        seq_printf(m, "%s", device->cdev->dev.bus_id);
        /* Print discipline string. */
        else
                seq_printf(m, "(none)");
        /* Print kdev. */
-       if (device->gdp)
+       if (block->gdp)
                seq_printf(m, " at (%3d:%6d)",
-                          device->gdp->major, device->gdp->first_minor);
+                          block->gdp->major, block->gdp->first_minor);
        else
                seq_printf(m, "  at (???:??????)");
        /* Print device name. */
-       if (device->gdp)
-               seq_printf(m, " is %-8s", device->gdp->disk_name);
+       if (block->gdp)
+               seq_printf(m, " is %-8s", block->gdp->disk_name);
        else
                seq_printf(m, " is ????????");
        /* Print devices features. */
        case DASD_STATE_READY:
        case DASD_STATE_ONLINE:
                seq_printf(m, "active ");
-               if (dasd_check_blocksize(device->bp_block))
+               if (dasd_check_blocksize(block->bp_block))
                        seq_printf(m, "n/f       ");
                else
                        seq_printf(m,
                                   "at blocksize: %d, %ld blocks, %ld MB",
-                                  device->bp_block, device->blocks,
-                                  ((device->bp_block >> 9) *
-                                   device->blocks) >> 11);
+                                  block->bp_block, block->blocks,
+                                  ((block->bp_block >> 9) *
+                                   block->blocks) >> 11);
                break;
        default:
                seq_printf(m, "no stat");