diff options
Diffstat (limited to 'drivers/s390')
75 files changed, 4950 insertions, 2971 deletions
diff --git a/drivers/s390/block/Makefile b/drivers/s390/block/Makefile index be9f22d52fd..0a89e080b38 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 e6bfce690ca..d640427c74c 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_coun |