diff options
Diffstat (limited to 'drivers/s390/block')
-rw-r--r-- | drivers/s390/block/Makefile | 4 | ||||
-rw-r--r-- | drivers/s390/block/dasd.c | 1693 | ||||
-rw-r--r-- | drivers/s390/block/dasd_3370_erp.c | 84 | ||||
-rw-r--r-- | drivers/s390/block/dasd_3990_erp.c | 360 | ||||
-rw-r--r-- | drivers/s390/block/dasd_9336_erp.c | 41 | ||||
-rw-r--r-- | drivers/s390/block/dasd_9343_erp.c | 21 | ||||
-rw-r--r-- | drivers/s390/block/dasd_alias.c | 903 | ||||
-rw-r--r-- | drivers/s390/block/dasd_devmap.c | 94 | ||||
-rw-r--r-- | drivers/s390/block/dasd_diag.c | 107 | ||||
-rw-r--r-- | drivers/s390/block/dasd_eckd.c | 789 | ||||
-rw-r--r-- | drivers/s390/block/dasd_eckd.h | 125 | ||||
-rw-r--r-- | drivers/s390/block/dasd_eer.c | 11 | ||||
-rw-r--r-- | drivers/s390/block/dasd_erp.c | 25 | ||||
-rw-r--r-- | drivers/s390/block/dasd_fba.c | 119 | ||||
-rw-r--r-- | drivers/s390/block/dasd_genhd.c | 76 | ||||
-rw-r--r-- | drivers/s390/block/dasd_int.h | 209 | ||||
-rw-r--r-- | drivers/s390/block/dasd_ioctl.c | 172 | ||||
-rw-r--r-- | drivers/s390/block/dasd_proc.c | 23 | ||||
-rw-r--r-- | drivers/s390/block/dcssblk.c | 2 |
19 files changed, 3129 insertions, 1729 deletions
diff --git a/drivers/s390/block/Makefile b/drivers/s390/block/Makefile index be9f22d52fd8..0a89e080b389 100644 --- a/drivers/s390/block/Makefile +++ b/drivers/s390/block/Makefile @@ -2,8 +2,8 @@ # 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 diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index e6bfce690ca3..d640427c74c8 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -48,13 +48,15 @@ MODULE_LICENSE("GPL"); /* * 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. @@ -65,26 +67,23 @@ static wait_queue_head_t dasd_flush_wq; /* * 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); @@ -93,10 +92,9 @@ dasd_alloc_device(void) 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); @@ -110,8 +108,7 @@ dasd_alloc_device(void) /* * 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); @@ -120,10 +117,42 @@ dasd_free_device(struct dasd_device *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; @@ -133,12 +162,13 @@ dasd_state_new_to_known(struct dasd_device *device) */ 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; } @@ -146,21 +176,24 @@ dasd_state_new_to_known(struct dasd_device *device) /* * 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); @@ -170,19 +203,19 @@ dasd_state_known_to_new(struct dasd_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)); + device->debug_area = debug_register(device->cdev->dev.bus_id, 1, 1, + 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"); @@ -194,16 +227,17 @@ dasd_state_known_to_basic(struct dasd_device * device) /* * 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) { @@ -228,26 +262,32 @@ dasd_state_basic_to_known(struct dasd_device * device) * 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; } @@ -256,28 +296,31 @@ dasd_state_basic_to_ready(struct dasd_device * device) * 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; @@ -291,17 +334,31 @@ dasd_state_unfmt_to_basic(struct dasd_device * device) 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; } @@ -309,8 +366,7 @@ dasd_state_online_to_ready(struct dasd_device * device) /* * Device startup state changes. */ -static int -dasd_increase_state(struct dasd_device *device) +static int dasd_increase_state(struct dasd_device *device) { int rc; @@ -345,8 +401,7 @@ dasd_increase_state(struct dasd_device *device) /* * Device shutdown state changes. */ -static int -dasd_decrease_state(struct dasd_device *device) +static int dasd_decrease_state(struct dasd_device *device) { int rc; @@ -381,8 +436,7 @@ dasd_decrease_state(struct dasd_device *device) /* * 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; @@ -409,17 +463,15 @@ dasd_change_state(struct dasd_device *device) * 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. */ @@ -429,8 +481,7 @@ dasd_kick_device(struct dasd_device *device) /* * 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) @@ -447,14 +498,12 @@ dasd_set_target_state(struct dasd_device *device, int target) /* * 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) @@ -475,20 +524,20 @@ unsigned int dasd_profile_level = DASD_PROFILE_OFF; /* * 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; @@ -498,19 +547,19 @@ dasd_profile_start(struct dasd_device *device, struct dasd_ccw_req * cqr, /* 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; @@ -532,27 +581,27 @@ dasd_profile_end(struct dasd_device *device, struct dasd_ccw_req * cqr, 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 */ /* @@ -562,9 +611,9 @@ dasd_profile_end(struct dasd_device *device, struct dasd_ccw_req * cqr, * 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; @@ -600,9 +649,9 @@ dasd_kmalloc_request(char *magic, int cplength, int datasize, 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; @@ -649,8 +698,7 @@ dasd_smalloc_request(char *magic, int cplength, int datasize, * 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; @@ -667,8 +715,7 @@ dasd_kfree_request(struct dasd_ccw_req * cqr, struct dasd_device * device) 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; @@ -681,14 +728,13 @@ dasd_sfree_request(struct dasd_ccw_req * cqr, struct dasd_device * device) /* * 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" @@ -706,8 +752,7 @@ dasd_check_cqr(struct dasd_ccw_req *cqr) * 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; @@ -717,13 +762,13 @@ dasd_term_IO(struct dasd_ccw_req * cqr) 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, @@ -753,7 +798,7 @@ dasd_term_IO(struct dasd_ccw_req * cqr) } retries++; } - dasd_schedule_bh(device); + dasd_schedule_device_bh(device); return rc; } @@ -761,8 +806,7 @@ dasd_term_IO(struct dasd_ccw_req * cqr) * 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; @@ -771,12 +815,12 @@ dasd_start_IO(struct dasd_ccw_req * cqr) 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(); @@ -833,8 +877,7 @@ dasd_start_IO(struct dasd_ccw_req * cqr) * 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; @@ -844,14 +887,13 @@ dasd_timeout_device(unsigned long ptr) /* 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)) @@ -862,7 +904,7 @@ dasd_set_timer(struct dasd_device *device, int expires) 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); @@ -871,15 +913,14 @@ dasd_set_timer(struct dasd_device *device, int expires) /* * 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; @@ -893,7 +934,7 @@ dasd_handle_killed_request(struct ccw_device *cdev, unsigned long intparm) 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)) { @@ -905,46 +946,32 @@ dasd_handle_killed_request(struct ccw_device *cdev, unsigned long intparm) /* 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)) { @@ -969,29 +996,25 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, 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); @@ -999,12 +1022,12 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, } /* 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; } @@ -1017,277 +1040,170 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, } 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); DEV_MESSAGE(KERN_ERR, device, "internal error - timeout (%is) expired " "for cqr %p, termination failed, " "retrying in 5s", (cqr->expires/HZ), cqr); + cqr->expires += 5*HZ; + dasd_device_set_timer(device, 5*HZ); } else { DEV_MESSAGE(KERN_ERR, device, "internal error - timeout (%is) expired " @@ -1301,77 +1217,53 @@ __dasd_check_expire(struct dasd_device * device) * 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: @@ -1387,90 +1279,60 @@ restart: } 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) @@ -1480,160 +1342,109 @@ dasd_schedule_bh(struct dasd_device * device) } /* - * 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; } @@ -1643,25 +1454,23 @@ dasd_sleep_on_interruptible(struct dasd_ccw_req * cqr) * 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) { @@ -1673,17 +1482,17 @@ dasd_sleep_on_immediatly(struct dasd_ccw_req * cqr) 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; } @@ -1692,11 +1501,14 @@ dasd_sleep_on_immediatly(struct dasd_ccw_req * cqr) * 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; @@ -1704,74 +1516,454 @@ dasd_cancel_req(struct dasd_ccw_req *cqr) 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 error) +{ + if (__blk_end_request(req, error, blk_rq_bytes(req))) BUG(); +} + +/* + * 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, -EIO); + 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, -EIO); + 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; + int error = 0; + + req = (struct request *) cqr->callback_data; + dasd_profile_end(cqr->block, cqr, req); + status = cqr->memdev->discipline->free_cp(cqr, req); + if (status <= 0) + error = status ? status : -EIO; + dasd_end_request(req, error); +} + +/* + * 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; @@ -1780,79 +1972,76 @@ dasd_alloc_queue(struct dasd_device * device) /* * 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); + dasd_end_request(req, -EIO); } - 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; @@ -1861,41 +2050,41 @@ dasd_open(struct inode *inp, struct file *filp) 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; } @@ -1909,6 +2098,9 @@ dasd_device_operations = { .getgeo = dasd_getgeo, }; +/******************************************************************************* + * end of block device operations + */ static void dasd_exit(void) @@ -1937,9 +2129,8 @@ 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; @@ -1969,19 +2160,20 @@ dasd_generic_probe (struct ccw_device *cdev, ret = ccw_device_set_online(cdev); if (ret) printk(KERN_WARNING - "dasd_generic_probe: could not initially online " - "ccw-device %s\n", cdev->dev.bus_id); - return ret; + "dasd_generic_probe: could not initially " + "online ccw-device %s; return code: %d\n", + cdev->dev.bus_id, ret); + return 0; } /* * 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; @@ -2001,7 +2193,15 @@ dasd_generic_remove (struct ccw_device *cdev) */ 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); } /* @@ -2009,10 +2209,8 @@ dasd_generic_remove (struct ccw_device *cdev) * 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; @@ -2048,6 +2246,7 @@ dasd_generic_set_online (struct ccw_device *cdev, 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 @@ -2067,6 +2266,8 @@ dasd_generic_set_online (struct ccw_device *cdev, 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", @@ -2081,10 +2282,10 @@ dasd_generic_set_online (struct ccw_device *cdev, 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); @@ -2101,30 +2302,39 @@ dasd_generic_set_offline (struct ccw_device *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; @@ -2145,27 +2355,22 @@ dasd_generic_notify(struct ccw_device *cdev, int event) 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; } @@ -2195,7 +2400,8 @@ static struct dasd_ccw_req *dasd_generic_build_rdc(struct dasd_device *device, 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; @@ -2217,13 +2423,12 @@ int dasd_generic_read_dev_chars(struct dasd_device *device, char *magic, 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; @@ -2231,7 +2436,7 @@ dasd_init(void) 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, 1, 8 * sizeof(long)); if (dasd_debug_area == NULL) { rc = -ENOMEM; goto failed; @@ -2277,15 +2482,18 @@ EXPORT_SYMBOL(dasd_diag_discipline_pointer); 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); @@ -2299,4 +2507,7 @@ EXPORT_SYMBOL_GPL(dasd_generic_remove); 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); diff --git a/drivers/s390/block/dasd_3370_erp.c b/drivers/s390/block/dasd_3370_erp.c deleted file mode 100644 index 1ddab8991d92..000000000000 --- a/drivers/s390/block/dasd_3370_erp.c +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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 */ diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c index 5b7385e430ea..c361ab69ec00 100644 --- a/drivers/s390/block/dasd_3990_erp.c +++ b/drivers/s390/block/dasd_3990_erp.c @@ -26,158 +26,6 @@ struct DCTL_data { /* ***************************************************************************** - * 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 ***************************************************************************** */ @@ -206,7 +54,7 @@ dasd_3990_erp_cleanup(struct dasd_ccw_req * erp, char final_status) { 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; @@ -224,15 +72,17 @@ static void 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); } /* @@ -251,7 +101,7 @@ static struct dasd_ccw_req * 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 */ @@ -292,11 +142,14 @@ dasd_3990_erp_int_req(struct dasd_ccw_req * erp) 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); @@ -309,9 +162,8 @@ dasd_3990_erp_alternate_path(struct dasd_ccw_req * erp) "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, @@ -320,8 +172,7 @@ dasd_3990_erp_alternate_path(struct dasd_ccw_req * erp) 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 */ @@ -344,14 +195,14 @@ static struct dasd_ccw_req * 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"); @@ -365,13 +216,14 @@ dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier) 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; @@ -435,7 +287,7 @@ static struct dasd_ccw_req * 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 */ @@ -472,7 +324,7 @@ dasd_3990_erp_action_4(struct dasd_ccw_req * erp, char *sense) "redriving request immediately, " "%d retries left", erp->retries); - erp->status = DASD_CQR_QUEUED; + erp->status = DASD_CQR_FILLED; } } @@ -530,7 +382,7 @@ static void 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); @@ -1157,7 +1009,7 @@ static struct dasd_ccw_req * 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; @@ -1198,7 +1050,7 @@ static struct dasd_ccw_req * 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 */ @@ -1237,7 +1089,7 @@ static struct dasd_ccw_req * 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; @@ -1279,7 +1131,6 @@ dasd_3990_erp_equip_check(struct dasd_ccw_req * erp, char *sense) erp = dasd_3990_erp_action_5(erp); } - return erp; } /* end dasd_3990_erp_equip_check */ @@ -1299,7 +1150,7 @@ static struct dasd_ccw_req * 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; @@ -1358,7 +1209,7 @@ static struct dasd_ccw_req * 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; @@ -1387,7 +1238,7 @@ static struct dasd_ccw_req * 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; @@ -1403,8 +1254,7 @@ dasd_3990_erp_inv_format(struct dasd_ccw_req * erp, char *sense) } 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); } @@ -1428,7 +1278,7 @@ static struct dasd_ccw_req * 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"); @@ -1453,7 +1303,7 @@ static struct dasd_ccw_req * 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; @@ -1463,11 +1313,9 @@ dasd_3990_erp_env_data(struct dasd_ccw_req * erp, char *sense) /* 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; @@ -1490,11 +1338,10 @@ static struct dasd_ccw_req * 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); @@ -1517,7 +1364,7 @@ static struct dasd_ccw_req * 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"); @@ -1526,6 +1373,43 @@ dasd_3990_erp_file_prot(struct dasd_ccw_req * erp) } /* 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 * * DESCRIPTION @@ -1623,7 +1507,7 @@ static struct dasd_ccw_req * 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; @@ -1657,13 +1541,14 @@ static struct dasd_ccw_req * 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"); @@ -1702,8 +1587,8 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense) /* 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"); @@ -1712,10 +1597,16 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense) /* 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)) { @@ -1748,7 +1639,7 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense) /* 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; @@ -1756,7 +1647,7 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense) /* 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; @@ -1770,7 +1661,8 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense) /* 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; @@ -1803,7 +1695,7 @@ static struct dasd_ccw_req * 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; @@ -1827,7 +1719,7 @@ dasd_3990_update_1B(struct dasd_ccw_req * previous_erp, char *sense) 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; } @@ -1850,7 +1742,7 @@ dasd_3990_update_1B(struct dasd_ccw_req * previous_erp, char *sense) 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)) { @@ -1889,7 +1781,7 @@ dasd_3990_update_1B(struct dasd_ccw_req * previous_erp, char *sense) ccw++; /* addr of TIC ccw */ ccw->cda = cpa; - erp->status = DASD_CQR_QUEUED; + erp->status = DASD_CQR_FILLED; return erp; @@ -1968,9 +1860,7 @@ dasd_3990_erp_compound_path(struct dasd_ccw_req * erp, char *sense) * try further actions. */ erp->lpm = 0; - - erp->status = DASD_CQR_ERROR; - + erp->status = DASD_CQR_NEED_ERP; } } @@ -2047,7 +1937,7 @@ dasd_3990_erp_compound_config(struct dasd_ccw_req * erp, char *sense) 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 " @@ -2081,28 +1971,26 @@ dasd_3990_erp_compound(struct dasd_ccw_req * erp, char *sense) { 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; @@ -2127,7 +2015,7 @@ static struct dasd_ccw_req * 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; @@ -2149,8 +2037,7 @@ dasd_3990_erp_inspect_32(struct dasd_ccw_req * erp, char *sense) 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; @@ -2253,6 +2140,11 @@ dasd_3990_erp_inspect(struct dasd_ccw_req * erp) /* 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) { @@ -2287,13 +2179,13 @@ static struct dasd_ccw_req * 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", @@ -2305,7 +2197,7 @@ dasd_3990_erp_add_erp(struct dasd_ccw_req * cqr) "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; } @@ -2319,7 +2211,9 @@ dasd_3990_erp_add_erp(struct dasd_ccw_req * 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; @@ -2466,7 +2360,7 @@ static struct dasd_ccw_req * 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 */ @@ -2557,7 +2451,7 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head, 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 */ @@ -2569,13 +2463,13 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head, "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 */ @@ -2603,7 +2497,7 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head, erp->retries, erp); /* handle the request again... */ - erp->status = DASD_CQR_QUEUED; + erp->status = DASD_CQR_FILLED; } } else { @@ -2620,7 +2514,7 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head, * DASD_3990_ERP_ACTION * * DESCRIPTION - * controll routine for 3990 erp actions. + * control routine for 3990 erp actions. * Has to be called with the queue lock (namely the s390_irq_lock) acquired. * * PARAMETER @@ -2636,9 +2530,8 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head, 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) { @@ -2704,10 +2597,11 @@ dasd_3990_erp_action(struct dasd_ccw_req * cqr) } } - /* 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; diff --git a/drivers/s390/block/dasd_9336_erp.c b/drivers/s390/block/dasd_9336_erp.c deleted file mode 100644 index 6e082688475a..000000000000 --- a/drivers/s390/block/dasd_9336_erp.c +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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 */ diff --git a/drivers/s390/block/dasd_9343_erp.c b/drivers/s390/block/dasd_9343_erp.c deleted file mode 100644 index ddecb9808ed4..000000000000 --- a/drivers/s390/block/dasd_9343_erp.c +++ /dev/null @@ -1,21 +0,0 @@ -/* - * 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; -} diff --git a/drivers/s390/block/dasd_alias.c b/drivers/s390/block/dasd_alias.c new file mode 100644 index 000000000000..3a40bee9d358 --- /dev/null +++ b/drivers/s390/block/dasd_alias.c @@ -0,0 +1,903 @@ +/* + * 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); +}; diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index 0c67258fb9ec..f4fb40257348 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c @@ -49,22 +49,6 @@ struct dasd_devmap { }; /* - * 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]+ * <busid> : [0-0a-f]\.[0-9a-f]\.(0x)?[0-9a-fA-F]+ @@ -721,8 +705,9 @@ dasd_ro_store(struct device *dev, struct device_attribute *attr, 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; } @@ -893,12 +878,16 @@ dasd_alias_show(struct device *dev, struct device_attribute *attr, char *buf) 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"); } @@ -930,19 +919,36 @@ static ssize_t 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); @@ -1040,39 +1046,16 @@ int 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); @@ -1138,9 +1121,6 @@ dasd_devmap_init(void) 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; } diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c index 571320ab9e1a..d91df38ee4f7 100644 --- a/drivers/s390/block/dasd_diag.c +++ b/drivers/s390/block/dasd_diag.c @@ -142,7 +142,7 @@ dasd_diag_erp(struct dasd_device *device) 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); @@ -158,11 +158,11 @@ dasd_start_diag(struct dasd_ccw_req * cqr) 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; @@ -184,7 +184,7 @@ dasd_start_diag(struct dasd_ccw_req * cqr) 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; @@ -209,12 +209,12 @@ dasd_diag_term_IO(struct dasd_ccw_req * cqr) { 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; } @@ -247,7 +247,7 @@ dasd_ext_handler(__u16 code) 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" @@ -260,10 +260,10 @@ dasd_ext_handler(__u16 code) 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; } @@ -272,11 +272,11 @@ dasd_ext_handler(__u16 code) 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) @@ -296,10 +296,10 @@ dasd_ext_handler(__u16 code) } 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); } @@ -309,6 +309,7 @@ dasd_ext_handler(__u16 code) 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; @@ -328,6 +329,16 @@ dasd_diag_check_device(struct dasd_device *device) 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; @@ -409,14 +420,14 @@ dasd_diag_check_device(struct dasd_device *device) 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); @@ -424,9 +435,9 @@ dasd_diag_check_device(struct dasd_device *device) } 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); @@ -436,22 +447,16 @@ out: /* 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) { @@ -466,8 +471,9 @@ dasd_diag_erp_postaction(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; @@ -486,17 +492,17 @@ dasd_diag_build_cp(struct dasd_device * device, struct request *req) 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) @@ -505,7 +511,7 @@ dasd_diag_build_cp(struct dasd_device * device, struct request *req) 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; @@ -529,7 +535,9 @@ dasd_diag_build_cp(struct dasd_device * device, struct request *req) 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; @@ -543,10 +551,15 @@ dasd_diag_free_cp(struct dasd_ccw_req *cqr, struct request *req) 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, @@ -583,7 +596,7 @@ static struct dasd_discipline dasd_diag_discipline = { .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, diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 44adf8496bda..61f16937c1e0 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -52,16 +52,6 @@ MODULE_LICENSE("GPL"); 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[] = { @@ -188,7 +178,7 @@ check_XRC (struct ccw1 *de_ccw, 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; } @@ -208,7 +198,7 @@ define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk, 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: @@ -280,6 +270,132 @@ define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk, 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, @@ -300,7 +416,7 @@ locate_record(struct ccw1 *ccw, struct LO_eckd_data *data, int trk, 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) { @@ -441,12 +557,15 @@ dasd_eckd_generate_uid(struct dasd_device *device, struct dasd_uid *uid) 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; } @@ -470,7 +589,9 @@ static struct dasd_ccw_req *dasd_eckd_build_rcd_lpm(struct dasd_device *device, 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); @@ -511,7 +632,7 @@ static int dasd_eckd_read_conf_lpm(struct dasd_device *device, /* * 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; @@ -557,19 +678,19 @@ dasd_eckd_read_conf(struct dasd_device *device) "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){ @@ -586,39 +707,104 @@ dasd_eckd_read_conf(struct dasd_device *device) 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; } /* @@ -629,28 +815,28 @@ dasd_eckd_build_psf_ssc(struct dasd_device *device) 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) @@ -659,9 +845,11 @@ dasd_eckd_validate_server(struct dasd_device *device, struct dasd_uid *uid) 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); } @@ -674,9 +862,9 @@ static int 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) { @@ -699,27 +887,54 @@ dasd_eckd_check_characteristics(struct dasd_device *device) /* 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, @@ -729,9 +944,24 @@ dasd_eckd_check_characteristics(struct dasd_device *device) 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) { @@ -755,7 +985,7 @@ 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, @@ -783,7 +1013,9 @@ dasd_eckd_analysis_ccw(struct dasd_device *device) 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; @@ -803,7 +1035,7 @@ dasd_eckd_analysis_callback(struct dasd_ccw_req *init_cqr, void *data) 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); @@ -811,13 +1043,13 @@ dasd_eckd_analysis_callback(struct dasd_ccw_req *init_cqr, void *data) } 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; @@ -828,13 +1060,15 @@ dasd_eckd_start_analysis(struct dasd_device *device) } 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; @@ -846,7 +1080,7 @@ dasd_eckd_end_analysis(struct dasd_device *device) 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++) { @@ -876,56 +1110,65 @@ dasd_eckd_end_analysis(struct dasd_device *device) 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; @@ -1037,7 +1280,7 @@ dasd_eckd_format_device(struct dasd_device * device, 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. */ @@ -1110,43 +1353,28 @@ dasd_eckd_format_device(struct dasd_device * device, 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) { @@ -1168,8 +1396,37 @@ dasd_eckd_erp_postaction(struct dasd_ccw_req * cqr) 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; @@ -1185,8 +1442,11 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req) 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) @@ -1194,13 +1454,13 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req) 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; @@ -1209,20 +1469,33 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req) 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) @@ -1232,26 +1505,42 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req) } /* 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; @@ -1281,7 +1570,7 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req) 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) { @@ -1289,7 +1578,7 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req) 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; @@ -1310,7 +1599,9 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req) } 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; @@ -1333,10 +1624,10 @@ dasd_eckd_free_cp(struct dasd_ccw_req *cqr, struct request *req) 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++; @@ -1367,10 +1658,71 @@ dasd_eckd_free_cp(struct dasd_ccw_req *cqr, struct request *req) } 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) @@ -1384,9 +1736,9 @@ dasd_eckd_fill_info(struct dasd_device * device, 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; } @@ -1419,7 +1771,8 @@ dasd_eckd_release(struct dasd_device *device) 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 */ @@ -1429,7 +1782,7 @@ dasd_eckd_release(struct dasd_device *device) rc = dasd_sleep_on_immediatly(cqr); - dasd_sfree_request(cqr, cqr->device); + dasd_sfree_request(cqr, cqr->memdev); return rc; } @@ -1459,7 +1812,8 @@ dasd_eckd_reserve(struct dasd_device *device) 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 */ @@ -1469,7 +1823,7 @@ dasd_eckd_reserve(struct dasd_device *device) rc = dasd_sleep_on_immediatly(cqr); - dasd_sfree_request(cqr, cqr->device); + dasd_sfree_request(cqr, cqr->memdev); return rc; } @@ -1498,7 +1852,8 @@ dasd_eckd_steal_lock(struct dasd_device *device) 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 */ @@ -1508,7 +1863,7 @@ dasd_eckd_steal_lock(struct dasd_device *device) rc = dasd_sleep_on_immediatly(cqr); - dasd_sfree_request(cqr, cqr->device); + dasd_sfree_request(cqr, cqr->memdev); return rc; } @@ -1526,52 +1881,52 @@ dasd_eckd_performance(struct dasd_device *device, void __user *argp) 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; /* Perfomance Statistics */ + 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; } @@ -1594,7 +1949,7 @@ dasd_eckd_get_attrib(struct dasd_device *device, void __user *argp) rc = 0; if (copy_to_user(argp, (long *) &attrib, - sizeof (struct attrib_data_t))) + sizeof(struct attrib_data_t))) rc = -EFAULT; return rc; @@ -1627,8 +1982,10 @@ dasd_eckd_set_attrib(struct dasd_device *device, void __user *argp) } 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); @@ -1685,9 +2042,8 @@ dasd_eckd_dump_ccw_range(struct ccw1 *from, struct ccw1 *to, char *page) * 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; @@ -1743,37 +2099,40 @@ dasd_eckd_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req, } 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); } @@ -1796,16 +2155,20 @@ static struct dasd_discipline dasd_eckd_discipline = { .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, diff --git a/drivers/s390/block/dasd_eckd.h b/drivers/s390/block/dasd_eckd.h index 712ff1650134..fc2509c939bc 100644 --- a/drivers/s390/block/dasd_eckd.h +++ b/drivers/s390/block/dasd_eckd.h @@ -39,6 +39,8 @@ #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 @@ -137,6 +139,25 @@ struct LO_eckd_data { __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 { @@ -254,7 +275,9 @@ struct dasd_eckd_confdata { } __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 */ @@ -343,6 +366,11 @@ struct dasd_eckd_path { __u8 npm; }; +struct dasd_rssd_features { + char feature[256]; +} __attribute__((packed)); + + /* * Perform Subsystem Function - Prepare for Read Subsystem Data */ @@ -365,4 +393,99 @@ struct dasd_psf_ssc_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 */ diff --git a/drivers/s390/block/dasd_eer.c b/drivers/s390/block/dasd_eer.c index 0c081a664ee8..6e53ab606e97 100644 --- a/drivers/s390/block/dasd_eer.c +++ b/drivers/s390/block/dasd_eer.c @@ -336,7 +336,7 @@ static void dasd_eer_write_snss_trigger(struct dasd_device *device, 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 @@ -404,10 +404,11 @@ void dasd_eer_snss(struct dasd_device *device) 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); } /* @@ -415,7 +416,7 @@ void dasd_eer_snss(struct dasd_device *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); @@ -458,7 +459,7 @@ int dasd_eer_enable(struct dasd_device *device) 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); diff --git a/drivers/s390/block/dasd_erp.c b/drivers/s390/block/dasd_erp.c index caa5d91420f8..8f10000851a3 100644 --- a/drivers/s390/block/dasd_erp.c +++ b/drivers/s390/block/dasd_erp.c @@ -46,6 +46,8 @@ dasd_alloc_erp_request(char *magic, int cplength, int datasize, 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) { @@ -66,7 +68,7 @@ dasd_alloc_erp_request(char *magic, int cplength, int datasize, } 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; @@ -81,11 +83,11 @@ dasd_free_erp_request(struct dasd_ccw_req * cqr, struct dasd_device * device) * 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) { @@ -93,12 +95,12 @@ dasd_default_erp_action(struct dasd_ccw_req * cqr) "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 */ @@ -117,15 +119,12 @@ dasd_default_erp_action(struct dasd_ccw_req * cqr) * 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 */ @@ -133,10 +132,10 @@ dasd_default_erp_postaction(struct dasd_ccw_req * 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; } @@ -157,7 +156,7 @@ dasd_log_sense(struct dasd_ccw_req *cqr, struct irb *irb) { 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); diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c index 1d95822e0b8e..d13ea05089a7 100644 --- a/drivers/s390/block/dasd_fba.c +++ b/drivers/s390/block/dasd_fba.c @@ -117,6 +117,7 @@ locate_record(struct ccw1 * ccw, struct LO_fba_data *data, int rw, 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; @@ -133,6 +134,16 @@ dasd_fba_check_characteristics(struct dasd_device *device) } 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); @@ -155,60 +166,37 @@ dasd_fba_check_characteristics(struct dasd_device *device) 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) { @@ -221,13 +209,34 @@ dasd_fba_erp_postaction(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; @@ -242,17 +251,17 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req) 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; @@ -260,7 +269,7 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req) 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; @@ -284,13 +293,13 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req) } /* 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); @@ -326,7 +335,7 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req) 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; @@ -342,7 +351,9 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req) } 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(); @@ -363,8 +374,8 @@ dasd_fba_free_cp(struct dasd_ccw_req *cqr, struct request *req) 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++; @@ -394,10 +405,15 @@ dasd_fba_free_cp(struct dasd_ccw_req *cqr, struct request *req) } 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) @@ -546,9 +562,10 @@ static struct dasd_discipline dasd_fba_discipline = { .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, diff --git a/drivers/s390/block/dasd_genhd.c b/drivers/s390/block/dasd_genhd.c index 47ba4462708d..aee6565aaf98 100644 --- a/drivers/s390/block/dasd_genhd.c +++ b/drivers/s390/block/dasd_genhd.c @@ -25,14 +25,15 @@ /* * 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); @@ -41,9 +42,9 @@ dasd_gendisk_alloc(struct dasd_device *device) /* 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. @@ -53,53 +54,51 @@ dasd_gendisk_alloc(struct dasd_device *device) * 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; /* @@ -117,7 +116,7 @@ dasd_scan_partitions(struct dasd_device * device) * is why the assignment to device->bdev is done AFTER * the BLKRRPART ioctl. */ - device->bdev = bdev; + block->bdev = bdev; return 0; } @@ -125,8 +124,7 @@ dasd_scan_partitions(struct dasd_device * device) * 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; @@ -137,8 +135,8 @@ dasd_destroy_partitions(struct dasd_device * device) * 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 @@ -149,17 +147,16 @@ dasd_destroy_partitions(struct dasd_device * device) 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; @@ -174,8 +171,7 @@ dasd_gendisk_init(void) return 0; } -void -dasd_gendisk_exit(void) +void dasd_gendisk_exit(void) { unregister_blkdev(DASD_MAJOR, "dasd"); } diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index d427daeef511..44b2984dfbee 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -64,13 +64,7 @@ * 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 @@ -151,19 +145,22 @@ do { \ 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 */ @@ -178,20 +175,27 @@ struct dasd_ccw_req { 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 */ @@ -214,52 +218,71 @@ struct dasd_discipline { 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; @@ -267,12 +290,18 @@ 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; }; /* @@ -293,14 +322,9 @@ struct dasd_uid { 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!) */ @@ -316,9 +340,8 @@ struct dasd_device { 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; @@ -337,20 +360,45 @@ struct dasd_device { 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 */ @@ -489,6 +537,9 @@ dasd_kmalloc_set_cda(struct ccw1 *ccw, void *cda, struct dasd_device *device) 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 *); @@ -497,18 +548,23 @@ void dasd_add_request_head(struct dasd_ccw_req *); 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); @@ -542,10 +598,10 @@ int dasd_busid_known(char *); /* 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); @@ -563,20 +619,9 @@ struct dasd_ccw_req *dasd_alloc_erp_request(char *, int, int, 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); diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c index 672eb0a3dd0b..91a64630cb0f 100644 --- a/drivers/s390/block/dasd_ioctl.c +++ b/drivers/s390/block/dasd_ioctl.c @@ -38,15 +38,15 @@ dasd_ioctl_api_version(void __user *argp) 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; } @@ -58,7 +58,7 @@ dasd_ioctl_enable(struct block_device *bdev) 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; @@ -71,7 +71,7 @@ dasd_ioctl_disable(struct block_device *bdev) * 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. @@ -85,19 +85,19 @@ dasd_ioctl_disable(struct block_device *bdev) /* * 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; } @@ -105,22 +105,21 @@ dasd_ioctl_quiesce(struct dasd_device *device) /* * 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; } @@ -130,22 +129,23 @@ dasd_ioctl_resume(struct dasd_device *device) * 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); @@ -156,20 +156,20 @@ dasd_format(struct dasd_device * device, struct format_data_t * fdata) * 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); @@ -186,7 +186,7 @@ dasd_format(struct dasd_device * device, struct format_data_t * fdata) 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)) @@ -194,51 +194,47 @@ dasd_ioctl_format(struct block_device *bdev, void __user *argp) 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; } @@ -247,87 +243,88 @@ dasd_ioctl_read_profile(struct dasd_device *device, void __user *argp) /* * 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; @@ -339,7 +336,7 @@ dasd_ioctl_information(struct dasd_device *device, 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)) @@ -351,11 +348,10 @@ dasd_ioctl_set_ro(struct block_device *bdev, void __user *argp) 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; @@ -363,7 +359,7 @@ dasd_ioctl_readall_cmb(struct dasd_device *device, unsigned int cmd, 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; @@ -374,10 +370,10 @@ dasd_ioctl(struct inode *inode, struct file *file, 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) { @@ -391,33 +387,33 @@ dasd_ioctl(struct inode *inode, struct file *file, 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; } diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c index ac7e8ef504cb..28a86f070048 100644 --- a/drivers/s390/block/dasd_proc.c +++ b/drivers/s390/block/dasd_proc.c @@ -54,11 +54,16 @@ static int 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. */ @@ -67,14 +72,14 @@ dasd_devices_show(struct seq_file *m, void *v) 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. */ @@ -100,14 +105,14 @@ dasd_devices_show(struct seq_file *m, void *v) 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"); @@ -137,7 +142,7 @@ static void dasd_devices_stop(struct seq_file *m, void *v) { } -static struct seq_operations dasd_devices_seq_ops = { +static const struct seq_operations dasd_devices_seq_ops = { .start = dasd_devices_start, .next = dasd_devices_next, .stop = dasd_devices_stop, diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c index 15a5789b7734..7779bfce1c31 100644 --- a/drivers/s390/block/dcssblk.c +++ b/drivers/s390/block/dcssblk.c @@ -82,7 +82,7 @@ struct dcssblk_dev_info { struct request_queue *dcssblk_queue; }; -static struct list_head dcssblk_devices = LIST_HEAD_INIT(dcssblk_devices); +static LIST_HEAD(dcssblk_devices); static struct rw_semaphore dcssblk_devices_sem; /* |