Commit 8e09f215 authored by Stefan Weinhuber's avatar Stefan Weinhuber Committed by Martin Schwidefsky

[S390] dasd: add hyper PAV support to DASD device driver, part 1

Parallel access volumes (PAV) is a storage server feature, that allows
to start multiple channel programs on the same DASD in parallel. It
defines alias devices which can be used as alternative paths to the
same disk. With the old base PAV support we only needed rudimentary
functionality in the DASD device driver. As the mapping between base
and alias devices was static, we just had to export an identifier
(uid) and could leave the combining of devices to external layers
like a device mapper multipath.
Now hyper PAV removes the requirement to dedicate alias devices to
specific base devices. Instead each alias devices can be combined with
multiple base device on a per request basis. This requires full
support by the DASD device driver as now each channel program itself
has to identify the target base device.
The changes to the dasd device driver and the ECKD discipline are:
- Separate subchannel device representation (dasd_device) from block
  device representation (dasd_block). Only base devices are block
  devices.
- Gather information about base and alias devices and possible
  combinations.
- For each request decide which dasd_device should be used (base or
  alias) and build specific channel program.
- Support summary unit checks, which allow the storage server to
  upgrade / downgrade between base and hyper PAV at runtime (support
  is mandatory).
Signed-off-by: default avatarStefan Weinhuber <wein@de.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 0ac30be4
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
# S/390 block devices # S/390 block devices
# #
dasd_eckd_mod-objs := dasd_eckd.o dasd_3990_erp.o dasd_9343_erp.o dasd_eckd_mod-objs := dasd_eckd.o dasd_3990_erp.o dasd_alias.o
dasd_fba_mod-objs := dasd_fba.o dasd_3370_erp.o dasd_9336_erp.o dasd_fba_mod-objs := dasd_fba.o
dasd_diag_mod-objs := dasd_diag.o dasd_diag_mod-objs := dasd_diag.o
dasd_mod-objs := dasd.o dasd_ioctl.o dasd_proc.o dasd_devmap.o \ dasd_mod-objs := dasd.o dasd_ioctl.o dasd_proc.o dasd_devmap.o \
dasd_genhd.o dasd_erp.o dasd_genhd.o dasd_erp.o
......
...@@ -48,13 +48,15 @@ MODULE_LICENSE("GPL"); ...@@ -48,13 +48,15 @@ MODULE_LICENSE("GPL");
/* /*
* SECTION: prototypes for static functions of dasd.c * SECTION: prototypes for static functions of dasd.c
*/ */
static int dasd_alloc_queue(struct dasd_device * device); static int dasd_alloc_queue(struct dasd_block *);
static void dasd_setup_queue(struct dasd_device * device); static void dasd_setup_queue(struct dasd_block *);
static void dasd_free_queue(struct dasd_device * device); static void dasd_free_queue(struct dasd_block *);
static void dasd_flush_request_queue(struct dasd_device *); static void dasd_flush_request_queue(struct dasd_block *);
static int dasd_flush_ccw_queue(struct dasd_device *, int); static int dasd_flush_block_queue(struct dasd_block *);
static void dasd_tasklet(struct dasd_device *); 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 do_kick_device(struct work_struct *);
static void dasd_return_cqr_cb(struct dasd_ccw_req *, void *);
/* /*
* SECTION: Operations on the device structure. * SECTION: Operations on the device structure.
...@@ -65,26 +67,23 @@ static wait_queue_head_t dasd_flush_wq; ...@@ -65,26 +67,23 @@ static wait_queue_head_t dasd_flush_wq;
/* /*
* Allocate memory for a new device structure. * Allocate memory for a new device structure.
*/ */
struct dasd_device * struct dasd_device *dasd_alloc_device(void)
dasd_alloc_device(void)
{ {
struct dasd_device *device; struct dasd_device *device;
device = kzalloc(sizeof (struct dasd_device), GFP_ATOMIC); device = kzalloc(sizeof(struct dasd_device), GFP_ATOMIC);
if (device == NULL) if (!device)
return ERR_PTR(-ENOMEM); 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. */ /* Get two pages for normal block device operations. */
device->ccw_mem = (void *) __get_free_pages(GFP_ATOMIC | GFP_DMA, 1); device->ccw_mem = (void *) __get_free_pages(GFP_ATOMIC | GFP_DMA, 1);
if (device->ccw_mem == NULL) { if (!device->ccw_mem) {
kfree(device); kfree(device);
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
} }
/* Get one page for error recovery. */ /* Get one page for error recovery. */
device->erp_mem = (void *) get_zeroed_page(GFP_ATOMIC | GFP_DMA); 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); free_pages((unsigned long) device->ccw_mem, 1);
kfree(device); kfree(device);
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
...@@ -93,10 +92,9 @@ dasd_alloc_device(void) ...@@ -93,10 +92,9 @@ dasd_alloc_device(void)
dasd_init_chunklist(&device->ccw_chunks, device->ccw_mem, PAGE_SIZE*2); dasd_init_chunklist(&device->ccw_chunks, device->ccw_mem, PAGE_SIZE*2);
dasd_init_chunklist(&device->erp_chunks, device->erp_mem, PAGE_SIZE); dasd_init_chunklist(&device->erp_chunks, device->erp_mem, PAGE_SIZE);
spin_lock_init(&device->mem_lock); 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, tasklet_init(&device->tasklet,
(void (*)(unsigned long)) dasd_tasklet, (void (*)(unsigned long)) dasd_device_tasklet,
(unsigned long) device); (unsigned long) device);
INIT_LIST_HEAD(&device->ccw_queue); INIT_LIST_HEAD(&device->ccw_queue);
init_timer(&device->timer); init_timer(&device->timer);
...@@ -110,8 +108,7 @@ dasd_alloc_device(void) ...@@ -110,8 +108,7 @@ dasd_alloc_device(void)
/* /*
* Free memory of a device structure. * Free memory of a device structure.
*/ */
void void dasd_free_device(struct dasd_device *device)
dasd_free_device(struct dasd_device *device)
{ {
kfree(device->private); kfree(device->private);
free_page((unsigned long) device->erp_mem); free_page((unsigned long) device->erp_mem);
...@@ -119,11 +116,43 @@ dasd_free_device(struct dasd_device *device) ...@@ -119,11 +116,43 @@ dasd_free_device(struct dasd_device *device)
kfree(device); kfree(device);
} }
/*
* Allocate memory for a new device structure.
*/
struct dasd_block *dasd_alloc_block(void)
{
struct dasd_block *block;
block = kzalloc(sizeof(*block), GFP_ATOMIC);
if (!block)
return ERR_PTR(-ENOMEM);
/* open_count = 0 means device online but not in use */
atomic_set(&block->open_count, -1);
spin_lock_init(&block->request_queue_lock);
atomic_set(&block->tasklet_scheduled, 0);
tasklet_init(&block->tasklet,
(void (*)(unsigned long)) dasd_block_tasklet,
(unsigned long) block);
INIT_LIST_HEAD(&block->ccw_queue);
spin_lock_init(&block->queue_lock);
init_timer(&block->timer);
return block;
}
/*
* Free memory of a device structure.
*/
void dasd_free_block(struct dasd_block *block)
{
kfree(block);
}
/* /*
* Make a new device known to the system. * Make a new device known to the system.
*/ */
static int static int dasd_state_new_to_known(struct dasd_device *device)
dasd_state_new_to_known(struct dasd_device *device)
{ {
int rc; int rc;
...@@ -133,12 +162,13 @@ dasd_state_new_to_known(struct dasd_device *device) ...@@ -133,12 +162,13 @@ dasd_state_new_to_known(struct dasd_device *device)
*/ */
dasd_get_device(device); dasd_get_device(device);
rc = dasd_alloc_queue(device); if (device->block) {
if (rc) { rc = dasd_alloc_queue(device->block);
dasd_put_device(device); if (rc) {
return rc; dasd_put_device(device);
return rc;
}
} }
device->state = DASD_STATE_KNOWN; device->state = DASD_STATE_KNOWN;
return 0; return 0;
} }
...@@ -146,21 +176,24 @@ dasd_state_new_to_known(struct dasd_device *device) ...@@ -146,21 +176,24 @@ dasd_state_new_to_known(struct dasd_device *device)
/* /*
* Let the system forget about a device. * Let the system forget about a device.
*/ */
static int static int dasd_state_known_to_new(struct dasd_device *device)
dasd_state_known_to_new(struct dasd_device * device)
{ {
/* Disable extended error reporting for this device. */ /* Disable extended error reporting for this device. */
dasd_eer_disable(device); dasd_eer_disable(device);
/* Forget the discipline information. */ /* 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); module_put(device->discipline->owner);
}
device->discipline = NULL; device->discipline = NULL;
if (device->base_discipline) if (device->base_discipline)
module_put(device->base_discipline->owner); module_put(device->base_discipline->owner);
device->base_discipline = NULL; device->base_discipline = NULL;
device->state = DASD_STATE_NEW; 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. */ /* Give up reference we took in dasd_state_new_to_known. */
dasd_put_device(device); dasd_put_device(device);
...@@ -170,19 +203,19 @@ dasd_state_known_to_new(struct dasd_device * device) ...@@ -170,19 +203,19 @@ dasd_state_known_to_new(struct dasd_device * device)
/* /*
* Request the irq line for the device. * Request the irq line for the device.
*/ */
static int static int dasd_state_known_to_basic(struct dasd_device *device)
dasd_state_known_to_basic(struct dasd_device * device)
{ {
int rc; int rc;
/* Allocate and register gendisk structure. */ /* Allocate and register gendisk structure. */
rc = dasd_gendisk_alloc(device); if (device->block) {
if (rc) rc = dasd_gendisk_alloc(device->block);
return rc; if (rc)
return rc;
}
/* register 'device' debug area, used for all DBF_DEV_XXX calls */ /* register 'device' debug area, used for all DBF_DEV_XXX calls */
device->debug_area = debug_register(device->cdev->dev.bus_id, 1, 2, device->debug_area = debug_register(device->cdev->dev.bus_id, 1, 2,
8 * sizeof (long)); 8 * sizeof(long));
debug_register_view(device->debug_area, &debug_sprintf_view); debug_register_view(device->debug_area, &debug_sprintf_view);
debug_set_level(device->debug_area, DBF_WARNING); debug_set_level(device->debug_area, DBF_WARNING);
DBF_DEV_EVENT(DBF_EMERG, device, "%s", "debug area created"); DBF_DEV_EVENT(DBF_EMERG, device, "%s", "debug area created");
...@@ -194,16 +227,17 @@ dasd_state_known_to_basic(struct dasd_device * device) ...@@ -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. * Release the irq line for the device. Terminate any running i/o.
*/ */
static int static int dasd_state_basic_to_known(struct dasd_device *device)
dasd_state_basic_to_known(struct dasd_device * device)
{ {
int rc; int rc;
if (device->block) {
dasd_gendisk_free(device); dasd_gendisk_free(device->block);
rc = dasd_flush_ccw_queue(device, 1); dasd_block_clear_timer(device->block);
}
rc = dasd_flush_device_queue(device);
if (rc) if (rc)
return rc; return rc;
dasd_clear_timer(device); dasd_device_clear_timer(device);
DBF_DEV_EVENT(DBF_EMERG, device, "%p debug area deleted", device); DBF_DEV_EVENT(DBF_EMERG, device, "%p debug area deleted", device);
if (device->debug_area != NULL) { if (device->debug_area != NULL) {
...@@ -228,26 +262,32 @@ dasd_state_basic_to_known(struct dasd_device * device) ...@@ -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 * In case the analysis returns an error, the device setup is stopped
* (a fake disk was already added to allow formatting). * (a fake disk was already added to allow formatting).
*/ */
static int static int dasd_state_basic_to_ready(struct dasd_device *device)
dasd_state_basic_to_ready(struct dasd_device * device)
{ {
int rc; int rc;
struct dasd_block *block;
rc = 0; rc = 0;
if (device->discipline->do_analysis != NULL) block = device->block;
rc = device->discipline->do_analysis(device);
if (rc) {
if (rc != -EAGAIN)
device->state = DASD_STATE_UNFMT;
return rc;
}
/* make disk known with correct capacity */ /* make disk known with correct capacity */
dasd_setup_queue(device); if (block) {
set_capacity(device->gdp, device->blocks << device->s2b_shift); if (block->base->discipline->do_analysis != NULL)
device->state = DASD_STATE_READY; rc = block->base->discipline->do_analysis(block);
rc = dasd_scan_partitions(device); if (rc) {
if (rc) if (rc != -EAGAIN)
device->state = DASD_STATE_BASIC; 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; return rc;
} }
...@@ -256,28 +296,31 @@ dasd_state_basic_to_ready(struct dasd_device * device) ...@@ -256,28 +296,31 @@ dasd_state_basic_to_ready(struct dasd_device * device)
* Forget format information. Check if the target level is basic * Forget format information. Check if the target level is basic
* and if it is create fake disk for formatting. * and if it is create fake disk for formatting.
*/ */
static int static int dasd_state_ready_to_basic(struct dasd_device *device)
dasd_state_ready_to_basic(struct dasd_device * device)
{ {
int rc; 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; 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; return 0;
} }
/* /*
* Back to basic. * Back to basic.
*/ */
static int static int dasd_state_unfmt_to_basic(struct dasd_device *device)
dasd_state_unfmt_to_basic(struct dasd_device * device)
{ {
device->state = DASD_STATE_BASIC; device->state = DASD_STATE_BASIC;
return 0; return 0;
...@@ -291,17 +334,31 @@ dasd_state_unfmt_to_basic(struct dasd_device * device) ...@@ -291,17 +334,31 @@ dasd_state_unfmt_to_basic(struct dasd_device * device)
static int static int
dasd_state_ready_to_online(struct dasd_device * device) 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; device->state = DASD_STATE_ONLINE;
dasd_schedule_bh(device); if (device->block)
dasd_schedule_block_bh(device->block);
return 0; return 0;
} }
/* /*
* Stop the requeueing of requests again. * Stop the requeueing of requests again.
*/ */
static int static int dasd_state_online_to_ready(struct dasd_device *device)
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; device->state = DASD_STATE_READY;
return 0; return 0;
} }
...@@ -309,8 +366,7 @@ dasd_state_online_to_ready(struct dasd_device * device) ...@@ -309,8 +366,7 @@ dasd_state_online_to_ready(struct dasd_device * device)
/* /*
* Device startup state changes. * Device startup state changes.
*/ */
static int static int dasd_increase_state(struct dasd_device *device)
dasd_increase_state(struct dasd_device *device)
{ {
int rc; int rc;
...@@ -345,8 +401,7 @@ dasd_increase_state(struct dasd_device *device) ...@@ -345,8 +401,7 @@ dasd_increase_state(struct dasd_device *device)
/* /*
* Device shutdown state changes. * Device shutdown state changes.
*/ */
static int static int dasd_decrease_state(struct dasd_device *device)
dasd_decrease_state(struct dasd_device *device)
{ {
int rc; int rc;
...@@ -381,8 +436,7 @@ dasd_decrease_state(struct dasd_device *device) ...@@ -381,8 +436,7 @@ dasd_decrease_state(struct dasd_device *device)
/* /*
* This is the main startup/shutdown routine. * This is the main startup/shutdown routine.
*/ */
static void static void dasd_change_state(struct dasd_device *device)
dasd_change_state(struct dasd_device *device)
{ {
int rc; int rc;
...@@ -409,17 +463,15 @@ dasd_change_state(struct dasd_device *device) ...@@ -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 * dasd_kick_device will schedule a call do do_kick_device to the kernel
* event daemon. * event daemon.
*/ */
static void static void do_kick_device(struct work_struct *work)
do_kick_device(struct work_struct *work)
{ {
struct dasd_device *device = container_of(work, struct dasd_device, kick_work); struct dasd_device *device = container_of(work, struct dasd_device, kick_work);
dasd_change_state(device); dasd_change_state(device);
dasd_schedule_bh(device); dasd_schedule_device_bh(device);
dasd_put_device(device); dasd_put_device(device);
} }
void void dasd_kick_device(struct dasd_device *device)
dasd_kick_device(struct dasd_device *device)
{ {
dasd_get_device(device); dasd_get_device(device);
/* queue call to dasd_kick_device to the kernel event daemon. */ /* queue call to dasd_kick_device to the kernel event daemon. */
...@@ -429,8 +481,7 @@ dasd_kick_device(struct dasd_device *device) ...@@ -429,8 +481,7 @@ dasd_kick_device(struct dasd_device *device)
/* /*
* Set the target state for a device and starts the state change. * Set the target state for a device and starts the state change.
*/ */
void void dasd_set_target_state(struct dasd_device *device, int target)
dasd_set_target_state(struct dasd_device *device, int target)
{ {
/* If we are in probeonly mode stop at DASD_STATE_READY. */ /* If we are in probeonly mode stop at DASD_STATE_READY. */
if (dasd_probeonly && target > DASD_STATE_READY) if (dasd_probeonly && target > DASD_STATE_READY)
...@@ -447,14 +498,12 @@ dasd_set_target_state(struct dasd_device *device, int target) ...@@ -447,14 +498,12 @@ dasd_set_target_state(struct dasd_device *device, int target)
/* /*
* Enable devices with device numbers in [from..to]. * Enable devices with device numbers in [from..to].
*/ */
static inline int static inline int _wait_for_device(struct dasd_device *device)
_wait_for_device(struct dasd_device *device)
{ {
return (device->state == device->target); return (device->state == device->target);
} }
void void dasd_enable_device(struct dasd_device *device)
dasd_enable_device(struct dasd_device *device)
{ {
dasd_set_target_state(device, DASD_STATE_ONLINE); dasd_set_target_state(device, DASD_STATE_ONLINE);
if (device->state <= DASD_STATE_KNOWN) if (device->state <= DASD_STATE_KNOWN)
...@@ -475,20 +524,20 @@ unsigned int dasd_profile_level = DASD_PROFILE_OFF; ...@@ -475,20 +524,20 @@ unsigned int dasd_profile_level = DASD_PROFILE_OFF;
/* /*
* Increments counter in global and local profiling structures. * Increments counter in global and local profiling structures.
*/ */
#define dasd_profile_counter(value, counter, device) \ #define dasd_profile_counter(value, counter, block) \
{ \ { \
int index; \ int index; \
for (index = 0; index < 31 && value >> (2+index); index++); \ for (index = 0; index < 31 && value >> (2+index); index++); \
dasd_global_profile.counter[index]++; \ dasd_global_profile.counter[index]++; \
device->profile.counter[index]++; \ block->profile.counter[index]++; \
} }
/* /*
* Add profiling information for cqr before execution. * Add profiling information for cqr before execution.
*/ */
static void static void dasd_profile_start(struct dasd_block *block,
dasd_profile_start(struct dasd_device *device, struct dasd_ccw_req * cqr, struct dasd_ccw_req *cqr,
struct request *req) struct request *req)
{ {
struct list_head *l; struct list_head *l;
unsigned int counter; unsigned int counter;
...@@ -498,19 +547,19 @@ dasd_profile_start(struct dasd_device *device, struct dasd_ccw_req * cqr, ...@@ -498,19 +547,19 @@ dasd_profile_start(struct dasd_device *device, struct dasd_ccw_req * cqr,
/* count the length of the chanq for statistics */ /* count the length of the chanq for statistics */
counter = 0; counter = 0;
list_for_each(l, &device->ccw_queue) list_for_each(l, &block->ccw_queue)
if (++counter >= 31) if (++counter >= 31)
break; break;
dasd_global_profile.dasd_io_nr_req[counter]++; 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. * Add profiling information for cqr after execution.
*/ */
static void static void dasd_profile_end(struct dasd_block *block,
dasd_profile_end(struct dasd_device *device, struct dasd_ccw_req * cqr, struct dasd_ccw_req *cqr,
struct request *req) struct request *req)
{ {
long strtime, irqtime, endtime, tottime; /* in microseconds */ long strtime, irqtime, endtime, tottime; /* in microseconds */
long tottimeps, sectors; long tottimeps, sectors;
...@@ -532,27 +581,27 @@ dasd_profile_end(struct dasd_device *device, struct dasd_ccw_req * cqr, ...@@ -532,27 +581,27 @@ dasd_profile_end(struct dasd_device *device, struct dasd_ccw_req * cqr,
if (!dasd_global_profile.dasd_io_reqs) if (!dasd_global_profile.dasd_io_reqs)
memset(&dasd_global_profile, 0, 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_reqs++;
dasd_global_profile.dasd_io_sects += sectors; dasd_global_profile.dasd_io_sects += sectors;
if (!device->profile.dasd_io_reqs) if (!block->profile.dasd_io_reqs)
memset(&device->profile, 0, memset(&block->profile, 0,
sizeof (struct dasd_profile_info_t)); sizeof(struct dasd_profile_info_t));
device->profile.dasd_io_reqs++; block->profile.dasd_io_reqs++;
device->profile.dasd_io_sects += sectors; block->profile.dasd_io_sects += sectors;
dasd_profile_counter(sectors, dasd_io_secs, device); dasd_profile_counter(sectors, dasd_io_secs, block);
dasd_profile_counter(tottime, dasd_io_times, device); dasd_profile_counter(tottime, dasd_io_times, block);
dasd_profile_counter(tottimeps, dasd_io_timps, device); dasd_profile_counter(tottimeps, dasd_io_timps, block);
dasd_profile_counter(strtime, dasd_io_time1, device); dasd_profile_counter(strtime, dasd_io_time1, block);
dasd_profile_counter(irqtime, dasd_io_time2, device); dasd_profile_counter(irqtime, dasd_io_time2, block);
dasd_profile_counter(irqtime / sectors, dasd_io_time2ps, device); dasd_profile_counter(irqtime / sectors, dasd_io_time2ps, block);
dasd_profile_counter(endtime, dasd_io_time3, device); dasd_profile_counter(endtime, dasd_io_time3, block);
} }
#else #else
#define dasd_profile_start(device, cqr, req) do {} while (0) #define dasd_profile_start(block, cqr, req) do {} while (0)
#define dasd_profile_end(device, cqr, req) do {} while (0) #define dasd_profile_end(block, cqr, req) do {} while (0)
#endif /* CONFIG_DASD_PROFILE */ #endif /* CONFIG_DASD_PROFILE */
/* /*
...@@ -562,9 +611,9 @@ dasd_profile_end(struct dasd_device *device, struct dasd_ccw_req * cqr, ...@@ -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 * memory and 2) dasd_smalloc_request uses the static ccw memory
* that gets allocated for each device. * that gets allocated for each device.
*/ */
struct dasd_ccw_req * struct dasd_ccw_req *dasd_kmalloc_request(char *magic, int cplength,
dasd_kmalloc_request(char *magic, int cplength, int datasize, int datasize,
struct dasd_device * device) struct dasd_device *device)
{ {
struct dasd_ccw_req *cqr; struct dasd_ccw_req *cqr;
...@@ -600,9 +649,9 @@ dasd_kmalloc_request(char *magic, int cplength, int datasize, ...@@ -600,9 +649,9 @@ dasd_kmalloc_request(char *magic, int cplength, int datasize,
return cqr; return cqr;
} }
struct dasd_ccw_req * struct dasd_ccw_req *dasd_smalloc_request(char *magic, int cplength,
dasd_smalloc_request(char *magic, int cplength, int datasize, int datasize,
struct dasd_device * device) struct dasd_device *device)
{ {
unsigned long flags; unsigned long flags;
struct dasd_ccw_req *cqr; struct dasd_ccw_req *cqr;
...@@ -649,8 +698,7 @@ dasd_smalloc_request(char *magic, int cplength, int datasize, ...@@ -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 * idal lists that might have been created by dasd_set_cda and the
* struct dasd_ccw_req itself. * struct dasd_ccw_req itself.
*/ */
void void dasd_kfree_request(struct dasd_ccw_req *cqr, struct dasd_device *device)
dasd_kfree_request(struct dasd_ccw_req * cqr, struct dasd_device * device)
{ {
#ifdef CONFIG_64BIT #ifdef CONFIG_64BIT
struct ccw1 *ccw; struct ccw1 *ccw;
...@@ -667,8 +715,7 @@ dasd_kfree_request(struct dasd_ccw_req * cqr, struct dasd_device * device) ...@@ -667,8 +715,7 @@ dasd_kfree_request(struct dasd_ccw_req * cqr, struct dasd_device * device)
dasd_put_device(device); dasd_put_device(device);
} }
void void dasd_sfree_request(struct dasd_ccw_req *cqr, struct dasd_device *device)
dasd_sfree_request(struct dasd_ccw_req * cqr, struct dasd_device * device)
{ {
unsigned long flags; unsigned long flags;
...@@ -681,14 +728,13 @@ dasd_sfree_request(struct dasd_ccw_req * cqr, struct dasd_device * device) ...@@ -681,14 +728,13 @@ dasd_sfree_request(struct dasd_ccw_req * cqr, struct dasd_device * device)
/* /*
* Check discipline magic in cqr. * Check discipline magic in cqr.
*/ */
static inline int static inline int dasd_check_cqr(struct dasd_ccw_req *cqr)
dasd_check_cqr(struct dasd_ccw_req *cqr)
{ {
struct dasd_device *device; struct dasd_device *device;
if (cqr == NULL) if (cqr == NULL)
return -EINVAL; return -EINVAL;
device = cqr->device; device = cqr->startdev;
if (strncmp((char *) &cqr->magic, device->discipline->ebcname, 4)) { if (strncmp((char *) &cqr->magic, device->discipline->ebcname, 4)) {
DEV_MESSAGE(KERN_WARNING, device, DEV_MESSAGE(KERN_WARNING, device,
" dasd_ccw_req 0x%08x magic doesn't match" " dasd_ccw_req 0x%08x magic doesn't match"
...@@ -706,8 +752,7 @@ dasd_check_cqr(struct dasd_ccw_req *cqr) ...@@ -706,8 +752,7 @@ dasd_check_cqr(struct dasd_ccw_req *cqr)
* ccw_device_clear can fail if the i/o subsystem * ccw_device_clear can fail if the i/o subsystem
* is in a bad mood. * is in a bad mood.
*/ */
int int dasd_term_IO(struct dasd_ccw_req *cqr)
dasd_term_IO(struct dasd_ccw_req * cqr)
{ {
struct dasd_device *device; struct dasd_device *device;
int retries, rc; int retries, rc;
...@@ -717,13 +762,13 @@ dasd_term_IO(struct dasd_ccw_req * cqr) ...@@ -717,13 +762,13 @@ dasd_term_IO(struct dasd_ccw_req * cqr)
if (rc) if (rc)
return rc; return rc;
retries = 0; retries = 0;
device = (struct dasd_device *) cqr->device; device = (struct dasd_device *) cqr->startdev;
while ((retries < 5) && (cqr->status == DASD_CQR_IN_IO)) { while ((retries < 5) && (cqr->status == DASD_CQR_IN_IO)) {
rc = ccw_device_clear(device->cdev, (long) cqr); rc = ccw_device_clear(device->cdev, (long) cqr);
switch (rc) { switch (rc) {
case 0: /* termination successful */ case 0: /* termination successful */
cqr->retries--; cqr->retries--;
cqr->status = DASD_CQR_CLEAR; cqr->status = DASD_CQR_CLEAR_PENDING;
cqr->stopclk = get_clock(); cqr->stopclk = get_clock();
cqr->starttime = 0; cqr->starttime = 0;
DBF_DEV_EVENT(DBF_DEBUG, device, DBF_DEV_EVENT(DBF_DEBUG, device,
...@@ -753,7 +798,7 @@ dasd_term_IO(struct dasd_ccw_req * cqr) ...@@ -753,7 +798,7 @@ dasd_term_IO(struct dasd_ccw_req * cqr)
} }
retries++; retries++;
} }
dasd_schedule_bh(device); dasd_schedule_device_bh(device);
return rc; return rc;
} }
...@@ -761,8 +806,7 @@ dasd_term_IO(struct dasd_ccw_req * cqr) ...@@ -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. * 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. * In that case set up a timer to start the request later.
*/ */
int int dasd_start_IO(struct dasd_ccw_req *cqr)
dasd_start_IO(struct dasd_ccw_req * cqr)
{ {
struct dasd_device *device; struct dasd_device *device;
int rc; int rc;
...@@ -771,12 +815,12 @@ dasd_start_IO(struct dasd_ccw_req * cqr) ...@@ -771,12 +815,12 @@ dasd_start_IO(struct dasd_ccw_req * cqr)
rc = dasd_check_cqr(cqr); rc = dasd_check_cqr(cqr);
if (rc) if (rc)
return rc; return rc;
device = (struct dasd_device *) cqr->device; device = (struct dasd_device *) cqr->startdev;
if (cqr->retries < 0) { if (cqr->retries < 0) {
DEV_MESSAGE(KERN_DEBUG, device, DEV_MESSAGE(KERN_DEBUG, device,
"start_IO: request %p (%02x/%i) - no retry left.", "start_IO: request %p (%02x/%i) - no retry left.",
cqr, cqr->status, cqr->retries); cqr, cqr->status, cqr->retries);
cqr->status = DASD_CQR_FAILED; cqr->status = DASD_CQR_ERROR;
return -EIO; return -EIO;
} }
cqr->startclk = get_clock(); cqr->startclk = get_clock();
...@@ -833,8 +877,7 @@ dasd_start_IO(struct dasd_ccw_req * cqr) ...@@ -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), * The head of the ccw queue will have status DASD_CQR_IN_IO for 1),
* DASD_CQR_QUEUED for 2) and 3). * DASD_CQR_QUEUED for 2) and 3).
*/ */
static void static void dasd_device_timeout(unsigned long ptr)
dasd_timeout_device(unsigned long ptr)
{ {
unsigned long flags; unsigned long flags;
struct dasd_device *device; struct dasd_device *device;
...@@ -844,14 +887,13 @@ dasd_timeout_device(unsigned long ptr) ...@@ -844,14 +887,13 @@ dasd_timeout_device(unsigned long ptr)
/* re-activate request queue */ /* re-activate request queue */
device->stopped &= ~DASD_STOPPED_PENDING; device->stopped &= ~DASD_STOPPED_PENDING;
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); 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. * Setup timeout for a device in jiffies.
*/ */
void void dasd_device_set_timer(struct dasd_device *device, int expires)
dasd_set_timer(struct dasd_device *device, int expires)
{ {
if (expires == 0) { if (expires == 0) {
if (timer_pending(&device->timer)) if (timer_pending(&device->timer))
...@@ -862,7 +904,7 @@ dasd_set_timer(struct dasd_device *device, int expires) ...@@ -862,7 +904,7 @@ dasd_set_timer(struct dasd_device *device, int expires)
if (mod_timer(&device->timer, jiffies + expires)) if (mod_timer(&device->timer, jiffies + expires))
return; return;
} }
device->timer.function = dasd_timeout_device; device->timer.function = dasd_device_timeout;
device->timer.data = (unsigned long) device; device->timer.data = (unsigned long) device;
device->timer.expires = jiffies + expires; device->timer.expires = jiffies + expires;
add_timer(&device->timer); add_timer(&device->timer);
...@@ -871,15 +913,14 @@ dasd_set_timer(struct dasd_device *device, int expires) ...@@ -871,15 +913,14 @@ dasd_set_timer(struct dasd_device *device, int expires)
/* /*
* Clear timeout for a device. * Clear timeout for a device.
*/ */
void void dasd_device_clear_timer(struct dasd_device *device)
dasd_clear_timer(struct dasd_device *device)
{ {
if (timer_pending(&device->timer)) if (timer_pending(&device->timer))
del_timer(&device->timer); del_timer(&device->timer);
} }
static void static void dasd_handle_killed_request(struct ccw_device *cdev,
dasd_handle_killed_request(struct ccw_device *cdev, unsigned long intparm) unsigned long intparm)
{ {
struct dasd_ccw_req *cqr; struct dasd_ccw_req *cqr;
struct dasd_device *device; struct dasd_device *device;
...@@ -893,7 +934,7 @@ dasd_handle_killed_request(struct ccw_device *cdev, unsigned long intparm) ...@@ -893,7 +934,7 @@ dasd_handle_killed_request(struct ccw_device *cdev, unsigned long intparm)
return; return;
} }
device = (struct dasd_device *) cqr->device; device = (struct dasd_device *) cqr->startdev;
if (device == NULL || if (device == NULL ||
device != dasd_device_from_cdev_locked(cdev) || device != dasd_device_from_cdev_locked(cdev) ||
strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) { strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) {
...@@ -905,46 +946,32 @@ dasd_handle_killed_request(struct ccw_device *cdev, unsigned long intparm) ...@@ -905,46 +946,32 @@ dasd_handle_killed_request(struct ccw_device *cdev, unsigned long intparm)
/* Schedule request to be retried. */ /* Schedule request to be retried. */
cqr->status = DASD_CQR_QUEUED; cqr->status = DASD_CQR_QUEUED;
dasd_clear_timer(device); dasd_device_clear_timer(device);
dasd_schedule_bh(device); dasd_schedule_device_bh(device);
dasd_put_device(device); dasd_put_device(device);
} }
static void void dasd_generic_handle_state_change(struct dasd_device *device)
dasd_handle_state_change_pending(struct dasd_device *device)
{ {
struct dasd_ccw_req *cqr;
struct list_head *l, *n;
/* First of all start sense subsystem status request. */ /* First of all start sense subsystem status request. */
dasd_eer_snss(device); dasd_eer_snss(device);
device->stopped &= ~DASD_STOPPED_PENDING; device->stopped &= ~DASD_STOPPED_PENDING;
dasd_schedule_device_bh(device);
/* restart all 'running' IO on queue */ if (device->block)
list_for_each_safe(l, n, &device->ccw_queue) { dasd_schedule_block_bh(device->block);
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);
} }
/* /*
* Interrupt handler for "normal" ssch-io based dasd devices. * Interrupt handler for "normal" ssch-io based dasd devices.
*/ */
void void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
struct irb *irb)
{ {
struct dasd_ccw_req *cqr, *next; struct dasd_ccw_req *cqr, *next;
struct dasd_device *device; struct dasd_device *device;
unsigned long long now; unsigned long long now;
int expires; int expires;
dasd_era_t era;
char mask;
if (IS_ERR(irb)) { if (IS_ERR(irb)) {
switch (PTR_ERR(irb)) { switch (PTR_ERR(irb)) {
...@@ -969,29 +996,25 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, ...@@ -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), cdev->dev.bus_id, ((irb->scsw.cstat<<8)|irb->scsw.dstat),
(unsigned int) intparm); (unsigned int) intparm);
/* first of all check for state change pending interrupt */ /* check for unsolicited interrupts */
mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP; cqr = (struct dasd_ccw_req *) intparm;
if ((irb->scsw.dstat & mask) == mask) { 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); device = dasd_device_from_cdev_locked(cdev);
if (!IS_ERR(device)) { 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); dasd_put_device(device);
} }
return; return;
} }
cqr = (struct dasd_ccw_req *) intparm; device = (struct dasd_device *) cqr->startdev;
if (!device ||
/* 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 ||
strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) { strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) {
MESSAGE(KERN_DEBUG, "invalid device in request: bus_id %s", MESSAGE(KERN_DEBUG, "invalid device in request: bus_id %s",
cdev->dev.bus_id); cdev->dev.bus_id);
...@@ -999,12 +1022,12 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, ...@@ -999,12 +1022,12 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
} }
/* Check for clear pending */ /* Check for clear pending */
if (cqr->status == DASD_CQR_CLEAR && if (cqr->status == DASD_CQR_CLEAR_PENDING &&
irb->scsw.fctl & SCSW_FCTL_CLEAR_FUNC) { irb->scsw.fctl & SCSW_FCTL_CLEAR_FUNC) {
cqr->status = DASD_CQR_QUEUED; cqr->status = DASD_CQR_CLEARED;
dasd_clear_timer(device); dasd_device_clear_timer(device);
wake_up(&dasd_flush_wq); wake_up(&dasd_flush_wq);
dasd_schedule_bh(device); dasd_schedule_device_bh(device);
return; return;
} }
...@@ -1017,272 +1040,164 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, ...@@ -1017,272 +1040,164 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
} }
DBF_DEV_EVENT(DBF_DEBUG, device, "Int: CS/DS 0x%04x for cqr %p", DBF_DEV_EVENT(DBF_DEBUG, device, "Int: CS/DS 0x%04x for cqr %p",
((irb->scsw.cstat << 8) | irb->scsw.dstat), cqr); ((irb->scsw.cstat << 8) | irb->scsw.dstat), cqr);
next = NULL;
/* 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);
expires = 0; expires = 0;
if (era == dasd_era_none) { if (irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END) &&
cqr->status = DASD_CQR_DONE; irb->scsw.cstat == 0 && !irb->esw.esw0.erw.cons) {
/* request was completed successfully */
cqr->status = DASD_CQR_SUCCESS;
cqr->stopclk = now; cqr->stopclk = now;
/* Start first request on queue if possible -> fast_io. */ /* Start first request on queue if possible -> fast_io. */
if (cqr->list.next != &device->ccw_queue) { if (cqr->devlist.next != &device->ccw_queue) {
next = list_entry(cqr->list.next, next = list_entry(cqr->devlist.next,
struct dasd_ccw_req, list); struct dasd_ccw_req, devlist);
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!");
}
} }
} else { /* error */ } else { /* error */
memcpy(&cqr->irb, irb, sizeof (struct irb)); memcpy(&cqr->irb, irb, sizeof(struct irb));
if (device->features & DASD_FEATURE_ERPLOG) { if (device->features & DASD_FEATURE_ERPLOG) {
/* dump sense data */
dasd_log_sense(cqr, irb); dasd_log_sense(cqr, irb);
} }
switch (era) { /* If we have no sense data, or we just don't want complex ERP
case dasd_era_fatal: * for this request, but if we have retries left, then just
cqr->status = DASD_CQR_FAILED; * reset this request and retry it in the fastpath
cqr->stopclk = now; */
break; if (!(cqr->irb.esw.esw0.erw.cons &&
case dasd_era_recover: 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; cqr->status = DASD_CQR_ERROR;
break; }
default: if (next && (next->status == DASD_CQR_QUEUED) &&
BUG(); (!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) if (expires != 0)
dasd_set_timer(device, expires); dasd_device_set_timer(device, expires);
else else
dasd_clear_timer(device); dasd_device_clear_timer(device);
dasd_schedule_bh(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 static void __dasd_device_recovery(struct dasd_device *device,
dasd_end_request(struct request *req, int uptodate) struct dasd_ccw_req *ref_cqr)
{ {
if (end_that_request_first(req, uptodate, req->hard_nr_sectors)) struct list_head *l, *n;
BUG(); struct dasd_ccw_req *cqr;
add_disk_randomness(req->rq_disk);
end_that_request_last(req, uptodate);
}
/* /*
* Process finished error recovery ccw. * only requeue request that came from the dasd_block layer
*/ */
static inline void if (!ref_cqr->block)
__dasd_process_erp(struct dasd_device *device, struct dasd_ccw_req *cqr) return;
{
dasd_erp_fn_t erp_fn;
if (cqr->status == DASD_CQR_DONE) list_for_each_safe(l, n, &device->ccw_queue) {
DBF_DEV_EVENT(DBF_NOTICE, device, "%s", "ERP successful"); cqr = list_entry(l, struct dasd_ccw_req, devlist);
else if (cqr->status == DASD_CQR_QUEUED &&
DEV_MESSAGE(KERN_ERR, device, "%s", "ERP unsuccessful"); ref_cqr->block == cqr->block) {
erp_fn = device->discipline->erp_postaction(cqr); cqr->status = DASD_CQR_CLEARED;
erp_fn(cqr); }
} }
};
/* /*
* Process ccw request queue. * Remove those ccw requests from the queue that need to be returned
* to the upper layer.
*/ */
static void static void __dasd_device_process_ccw_queue(struct dasd_device *device,
__dasd_process_ccw_queue(struct dasd_device * device, struct list_head *final_queue)
struct list_head *final_queue)
{ {
struct list_head *l, *n; struct list_head *l, *n;
struct dasd_ccw_req *cqr; struct dasd_ccw_req *cqr;
dasd_erp_fn_t erp_fn;
restart:
/* Process request with final status. */ /* Process request with final status. */
list_for_each_safe(l, n, &device->ccw_queue) { 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. */ /* Stop list processing at the first non-final request. */
if (cqr->status != DASD_CQR_DONE && if (cqr->status == DASD_CQR_QUEUED ||
cqr->status != DASD_CQR_FAILED && cqr->status == DASD_CQR_IN_IO ||
cqr->status != DASD_CQR_ERROR) cqr->status == DASD_CQR_CLEAR_PENDING)
break; break;
/* Process requests with DASD_CQR_ERROR */
if (cqr->status == DASD_CQR_ERROR) { if (cqr->status == DASD_CQR_ERROR) {
if (cqr->irb.scsw.fctl & SCSW_FCTL_HALT_FUNC) { __dasd_device_recovery(device, cqr);
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;
} }
/* Process finished ERP request. */
if (cqr->refers) {
__dasd_process_erp(device, cqr);
goto restart;
}
/* Rechain finished requests to final queue */ /* Rechain finished requests to final queue */
cqr->endclk = get_clock(); list_move_tail(&cqr->devlist, final_queue);
list_move_tail(&cqr->list, 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 static void __dasd_device_process_final_queue(struct dasd_device *device,
__dasd_process_blk_queue(struct dasd_device * device) struct list_head *final_queue)
{ {
struct request_queue *queue; struct list_head *l, *n;
struct request *req;
struct dasd_ccw_req *cqr; 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 && list_for_each_safe(l, n, final_queue) {
rq_data_dir(req) == WRITE) { cqr = list_entry(l, struct dasd_ccw_req, devlist);
DBF_DEV_EVENT(DBF_ERR, device, list_del_init(&cqr->devlist);
"Rejecting write request %p", if (cqr->block)
req); spin_lock_bh(&cqr->block->queue_lock);
blkdev_dequeue_request(req); switch (cqr->status) {
dasd_end_request(req, 0); case DASD_CQR_SUCCESS:
continue; cqr->status = DASD_CQR_DONE;
} break;
if (device->stopped & DASD_STOPPED_DC_EIO) { case DASD_CQR_ERROR:
blkdev_dequeue_request(req); cqr->status = DASD_CQR_NEED_ERP;
dasd_end_request(req, 0); break;
continue; case DASD_CQR_CLEARED:
} cqr->status = DASD_CQR_TERMINATED;
cqr = device->discipline->build_cp(device, req); break;
if (IS_ERR(cqr)) { default:
if (PTR_ERR(cqr) == -ENOMEM) DEV_MESSAGE(KERN_ERR, device,
break; /* terminate request queue loop */ "wrong cqr status in __dasd_process_final_queue "
if (PTR_ERR(cqr) == -EAGAIN) { "for cqr %p, status %x",
/* cqr, cqr->status);
* The current request cannot be build right BUG();
* 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;
} }
cqr->callback = dasd_end_request_cb; if (cqr->block)
cqr->callback_data = (void *) req; spin_unlock_bh(&cqr->block->queue_lock);
cqr->status = DASD_CQR_QUEUED; if (cqr->callback != NULL)
blkdev_dequeue_request(req); (cqr->callback)(cqr, cqr->callback_data);
list_add_tail(&cqr->list, &device->ccw_queue);
dasd_profile_start(device, cqr, req);
nr_queued++;
} }
} }
/* /*
* Take a look at the first request on the ccw queue and check * Take a look at the first request on the ccw queue and check
* if it reached its expire time. If so, terminate the IO. * if it reached its expire time. If so, terminate the IO.
*/ */
static void static void __dasd_device_check_expire(struct dasd_device *device)
__dasd_check_expire(struct dasd_device * device)
{ {
struct dasd_ccw_req *cqr; struct dasd_ccw_req *cqr;
if (list_empty(&device->ccw_queue)) if (list_empty(&device->ccw_queue))
return; 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) && if ((cqr->status == DASD_CQR_IN_IO && cqr->expires != 0) &&
(time_after_eq(jiffies, cqr->expires + cqr->starttime))) { (time_after_eq(jiffies, cqr->expires + cqr->starttime))) {
if (device->discipline->term_IO(cqr) != 0) { if (device->discipline->term_IO(cqr) != 0) {
/* Hmpf, try again in 5 sec */ /* Hmpf, try again in 5 sec */
dasd_set_timer(device, 5*HZ); dasd_device_set_timer(device, 5*HZ);
DEV_MESSAGE(KERN_ERR, device, DEV_MESSAGE(KERN_ERR, device,
"internal error - timeout (%is) expired " "internal error - timeout (%is) expired "
"for cqr %p, termination failed, " "for cqr %p, termination failed, "
...@@ -1301,77 +1216,53 @@ __dasd_check_expire(struct dasd_device * device) ...@@ -1301,77 +1216,53 @@ __dasd_check_expire(struct dasd_device * device)
* Take a look at the first request on the ccw queue and check * Take a look at the first request on the ccw queue and check
* if it needs to be started. * if it needs to be started.
*/ */
static void static void __dasd_device_start_head(struct dasd_device *device)
__dasd_start_head(struct dasd_device * device)
{ {
struct dasd_ccw_req *cqr; struct dasd_ccw_req *cqr;
int rc; int rc;
if (list_empty(&device->ccw_queue)) if (list_empty(&device->ccw_queue))
return; 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) if (cqr->status != DASD_CQR_QUEUED)
return; return;
/* Non-temporary stop condition will trigger fail fast */ /* when device is stopped, return request to previous layer */
if (device->stopped & ~DASD_STOPPED_PENDING && if (device->stopped) {
test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) && cqr->status = DASD_CQR_CLEARED;
(!dasd_eer_enabled(device))) { dasd_schedule_device_bh(device);
cqr->status = DASD_CQR_FAILED;
dasd_schedule_bh(device);
return; return;
} }
/* Don't try to start requests if device is stopped */
if (device->stopped)
return;
rc = device->discipline->start_IO(cqr); rc = device->discipline->start_IO(cqr);
if (rc == 0) if (rc == 0)
dasd_set_timer(device, cqr->expires); dasd_device_set_timer(device, cqr->expires);
else if (rc == -EACCES) { else if (rc == -EACCES) {
dasd_schedule_bh(device); dasd_schedule_device_bh(device);
} else } else
/* Hmpf, try again in 1/2 sec */ /* Hmpf, try again in 1/2 sec */
dasd_set_timer(device, 50); dasd_device_set_timer(device, 50);
}
static inline int
_wait_for_clear(struct dasd_ccw_req *cqr)
{
return (cqr->status == DASD_CQR_QUEUED);
} }
/* /*
* Remove all requests from the ccw queue (all = '1') or only block device * Go through all request on the dasd_device request queue,
* requests in case all = '0'. * terminate them on the cdev if necessary, and return them to the
* Take care of the erp-chain (chained via cqr->refers) and remove either * submitting layer via callback.
* the whole erp-chain or none of the erp-requests. * Note:
* If a request is currently running, term_IO is called and the request * Make sure that all 'submitting layers' still exist when
* is re-queued. Prior to removing the terminated request we need to wait * this function is called!. In other words, when 'device' is a base
* for the clear-interrupt. * device then all block layer requests must have been removed before
* In case termination is not possible we stop processing and just finishing * via dasd_flush_block_queue.
* the already moved requests.
*/ */
static int int dasd_flush_device_queue(struct dasd_device *device)
dasd_flush_ccw_queue(struct dasd_device * device, int all)
{ {
struct dasd_ccw_req *cqr, *orig, *n; struct dasd_ccw_req *cqr, *n;
int rc, i; int rc;
struct list_head flush_queue; struct list_head flush_queue;
INIT_LIST_HEAD(&flush_queue); INIT_LIST_HEAD(&flush_queue);
spin_lock_irq(get_ccwdev_lock(device->cdev)); spin_lock_irq(get_ccwdev_lock(device->cdev));
rc = 0; rc = 0;
restart: list_for_each_entry_safe(cqr, n, &device->ccw_queue, devlist) {
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;
}
/* Check status and move request to flush_queue */ /* Check status and move request to flush_queue */
switch (cqr->status) { switch (cqr->status) {
case DASD_CQR_IN_IO: case DASD_CQR_IN_IO:
...@@ -1387,90 +1278,60 @@ dasd_flush_ccw_queue(struct dasd_device * device, int all) ...@@ -1387,90 +1278,60 @@ dasd_flush_ccw_queue(struct dasd_device * device, int all)
} }
break; break;
case DASD_CQR_QUEUED: case DASD_CQR_QUEUED:
case DASD_CQR_ERROR:
/* set request to FAILED */
cqr->stopclk = get_clock(); cqr->stopclk = get_clock();
cqr->status = DASD_CQR_FAILED; cqr->status = DASD_CQR_CLEARED;
break; break;
default: /* do not touch the others */ default: /* no need to modify the others */
break; break;
} }
/* Rechain request (including erp chain) */ list_move_tail(&cqr->devlist, &flush_queue);
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;
} }
finished: finished:
spin_unlock_irq(get_ccwdev_lock(device->cdev)); spin_unlock_irq(get_ccwdev_lock(device->cdev));
/* Now call the callback function of flushed requests */ /*
restart_cb: * After this point all requests must be in state CLEAR_PENDING,
list_for_each_entry_safe(cqr, n, &flush_queue, list) { * CLEARED, SUCCESS or ERROR. Now wait for CLEAR_PENDING to become
if (cqr->status == DASD_CQR_CLEAR) { * one of the others.
/* wait for clear interrupt! */ */
wait_event(dasd_flush_wq, _wait_for_clear(cqr)); list_for_each_entry_safe(cqr, n, &flush_queue, devlist)
cqr->status = DASD_CQR_FAILED; wait_event(dasd_flush_wq,
} (cqr->status != DASD_CQR_CLEAR_PENDING));
/* Process finished ERP request. */ /*
if (cqr->refers) { * Now set each request back to TERMINATED, DONE or NEED_ERP
__dasd_process_erp(device, cqr); * and call the callback function of flushed requests
/* restart list_for_xx loop since dasd_process_erp */
* might remove multiple elements */ __dasd_device_process_final_queue(device, &flush_queue);
goto restart_cb;
}
/* call the callback function */
cqr->endclk = get_clock();
if (cqr->callback != NULL)
(cqr->callback)(cqr, cqr->callback_data);
}
return rc; return rc;
} }
/* /*
* Acquire the device lock and process queues for the device. * Acquire the device lock and process queues for the device.
*/ */
static void static void dasd_device_tasklet(struct dasd_device *device)
dasd_tasklet(struct dasd_device * device)
{ {
struct list_head final_queue; struct list_head final_queue;
struct list_head *l, *n;
struct dasd_ccw_req *cqr;
atomic_set (&device->tasklet_scheduled, 0); atomic_set (&device->tasklet_scheduled, 0);
INIT_LIST_HEAD(&final_queue); INIT_LIST_HEAD(&final_queue);
spin_lock_irq(get_ccwdev_lock(device->cdev)); spin_lock_irq(get_ccwdev_lock(device->cdev));
/* Check expire time of first request on the ccw queue. */ /* Check expire time of first request on the ccw queue. */
__dasd_check_expire(device); __dasd_device_check_expire(device);
/* Finish off requests on ccw queue */ /* find final requests on ccw queue */
__dasd_process_ccw_queue(device, &final_queue); __dasd_device_process_ccw_queue(device, &final_queue);
spin_unlock_irq(get_ccwdev_lock(device->cdev)); spin_unlock_irq(get_ccwdev_lock(device->cdev));
/* Now call the callback function of requests with final status */ /* Now call the callback function of requests with final status */
list_for_each_safe(l, n, &final_queue) { __dasd_device_process_final_queue(device, &final_queue);
cqr = list_entry(l, struct dasd_ccw_req, list); spin_lock_irq(get_ccwdev_lock(device->cdev));
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);
/* Now check if the head of the ccw queue needs to be started. */ /* Now check if the head of the ccw queue needs to be started. */
__dasd_start_head(device); __dasd_device_start_head(device);
spin_unlock(get_ccwdev_lock(device->cdev)); spin_unlock_irq(get_ccwdev_lock(device->cdev));
spin_unlock_irq(&device->request_queue_lock);
dasd_put_device(device); dasd_put_device(device);
} }
/* /*
* Schedules a call to dasd_tasklet over the device tasklet. * Schedules a call to dasd_tasklet over the device tasklet.
*/ */
void void dasd_schedule_device_bh(struct dasd_device *device)
dasd_schedule_bh(struct dasd_device * device)
{ {
/* Protect against rescheduling. */ /* Protect against rescheduling. */
if (atomic_cmpxchg (&device->tasklet_scheduled, 0, 1) != 0) if (atomic_cmpxchg (&device->tasklet_scheduled, 0, 1) != 0)
...@@ -1480,160 +1341,109 @@ dasd_schedule_bh(struct dasd_device * device) ...@@ -1480,160 +1341,109 @@ dasd_schedule_bh(struct dasd_device * device)
} }
/* /*
* Queue a request to the head of the ccw_queue. Start the I/O if * Queue a request to the head of the device ccw_queue.
* possible. * Start the I/O if possible.
*/ */
void void dasd_add_request_head(struct dasd_ccw_req *cqr)
dasd_add_request_head(struct dasd_ccw_req *req)
{ {
struct dasd_device *device; struct dasd_device *device;
unsigned long flags; unsigned long flags;
device = req->device; device = cqr->startdev;
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
req->status = DASD_CQR_QUEUED; cqr->status = DASD_CQR_QUEUED;
req->device = device; list_add(&cqr->devlist, &device->ccw_queue);
list_add(&req->list, &device->ccw_queue);
/* let the bh start the request to keep them in order */ /* 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); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
} }
/* /*
* Queue a request to the tail of the ccw_queue. Start the I/O if * Queue a request to the tail of the device ccw_queue.
* possible. * Start the I/O if possible.
*/ */
void void dasd_add_request_tail(struct dasd_ccw_req *cqr)
dasd_add_request_tail(struct dasd_ccw_req *req)
{ {
struct dasd_device *device; struct dasd_device *device;
unsigned long flags; unsigned long flags;
device = req->device; device = cqr->startdev;
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
req->status = DASD_CQR_QUEUED; cqr->status = DASD_CQR_QUEUED;
req->device = device; list_add_tail(&cqr->devlist, &device->ccw_queue);
list_add_tail(&req->list, &device->ccw_queue);
/* let the bh start the request to keep them in order */ /* 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); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
} }
/* /*
* Wakeup callback. * Wakeup helper for the 'sleep_on' functions.
*/ */
static void static void dasd_wakeup_cb(struct dasd_ccw_req *cqr, void *data)
dasd_wakeup_cb(struct dasd_ccw_req *cqr, void *data)
{ {
wake_up((wait_queue_head_t *) data); wake_up((wait_queue_head_t *) data);
} }
static inline int static inline int _wait_for_wakeup(struct dasd_ccw_req *cqr)
_wait_for_wakeup(struct dasd_ccw_req *cqr)
{ {
struct dasd_device *device; struct dasd_device *device;
int rc; int rc;
device = cqr->device; device = cqr->startdev;
spin_lock_irq(get_ccwdev_lock(device->cdev)); spin_lock_irq(get_ccwdev_lock(device->cdev));
rc = ((cqr->status == DASD_CQR_DONE || rc = ((cqr->status == DASD_CQR_DONE ||
cqr->status == DASD_CQR_FAILED) && cqr->status == DASD_CQR_NEED_ERP ||
list_empty(&cqr->list)); cqr->status == DASD_CQR_TERMINATED) &&
list_empty(&cqr->devlist));
spin_unlock_irq(get_ccwdev_lock(device->cdev)); spin_unlock_irq(get_ccwdev_lock(device->cdev));
return rc; 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 int dasd_sleep_on(struct dasd_ccw_req *cqr)
dasd_sleep_on(struct dasd_ccw_req * cqr)
{ {
wait_queue_head_t wait_q; wait_queue_head_t wait_q;
struct dasd_device *device; struct dasd_device *device;
int rc; int rc;
device = cqr->device; device = cqr->startdev;
spin_lock_irq(get_ccwdev_lock(device->cdev));
init_waitqueue_head (&wait_q); init_waitqueue_head (&wait_q);
cqr->callback = dasd_wakeup_cb; cqr->callback = dasd_wakeup_cb;
cqr->callback_data = (void *) &wait_q; cqr->callback_data = (void *) &wait_q;
cqr->status = DASD_CQR_QUEUED; dasd_add_request_tail(cqr);
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));
wait_event(wait_q, _wait_for_wakeup(cqr)); wait_event(wait_q, _wait_for_wakeup(cqr));
/* Request status is either done or failed. */ /* 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; return rc;
} }
/* /*
* Attempts to start a special ccw queue and wait interruptible * Queue a request to the tail of the device ccw_queue and wait
* for its completion. * interruptible for it's completion.
*/ */
int int dasd_sleep_on_interruptible(struct dasd_ccw_req *cqr)
dasd_sleep_on_interruptible(struct dasd_ccw_req * cqr)
{ {
wait_queue_head_t wait_q; wait_queue_head_t wait_q;
struct dasd_device *device; struct dasd_device *device;
int rc, finished; int rc;
device = cqr->device;
spin_lock_irq(get_ccwdev_lock(device->cdev));
device = cqr->startdev;
init_waitqueue_head (&wait_q); init_waitqueue_head (&wait_q);
cqr->callback = dasd_wakeup_cb; cqr->callback = dasd_wakeup_cb;
cqr->callback_data = (void *) &wait_q; cqr->callback_data = (void *) &wait_q;
cqr->status = DASD_CQR_QUEUED; dasd_add_request_tail(cqr);
list_add_tail(&cqr->list, &device->ccw_queue); rc = wait_event_interruptible(wait_q, _wait_for_wakeup(cqr));
if (rc == -ERESTARTSYS) {
/* let the bh start the request to keep them in order */ dasd_cancel_req(cqr);
dasd_schedule_bh(device); /* wait (non-interruptible) for final status */
spin_unlock_irq(get_ccwdev_lock(device->cdev)); wait_event(wait_q, _wait_for_wakeup(cqr));
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));
} }
rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO;
return rc; return rc;
} }
...@@ -1643,25 +1453,23 @@ dasd_sleep_on_interruptible(struct dasd_ccw_req * cqr) ...@@ -1643,25 +1453,23 @@ dasd_sleep_on_interruptible(struct dasd_ccw_req * cqr)
* and be put back to status queued, before the special request is added * 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. * to the head of the queue. Then the special request is waited on normally.
*/ */
static inline int static inline int _dasd_term_running_cqr(struct dasd_device *device)
_dasd_term_running_cqr(struct dasd_device *device)
{ {
struct dasd_ccw_req *cqr; struct dasd_ccw_req *cqr;
if (list_empty(&device->ccw_queue)) if (list_empty(&device->ccw_queue))
return 0; 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); return device->discipline->term_IO(cqr);
} }
int int dasd_sleep_on_immediatly(struct dasd_ccw_req *cqr)
dasd_sleep_on_immediatly(struct dasd_ccw_req * cqr)
{ {
wait_queue_head_t wait_q; wait_queue_head_t wait_q;
struct dasd_device *device; struct dasd_device *device;
int rc; int rc;
device = cqr->device; device = cqr->startdev;
spin_lock_irq(get_ccwdev_lock(device->cdev)); spin_lock_irq(get_ccwdev_lock(device->cdev));
rc = _dasd_term_running_cqr(device); rc = _dasd_term_running_cqr(device);
if (rc) { if (rc) {
...@@ -1673,17 +1481,17 @@ dasd_sleep_on_immediatly(struct dasd_ccw_req * cqr) ...@@ -1673,17 +1481,17 @@ dasd_sleep_on_immediatly(struct dasd_ccw_req * cqr)
cqr->callback = dasd_wakeup_cb; cqr->callback = dasd_wakeup_cb;
cqr->callback_data = (void *) &wait_q; cqr->callback_data = (void *) &wait_q;
cqr->status = DASD_CQR_QUEUED; 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 */ /* 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)); spin_unlock_irq(get_ccwdev_lock(device->cdev));
wait_event(wait_q, _wait_for_wakeup(cqr)); wait_event(wait_q, _wait_for_wakeup(cqr));
/* Request status is either done or failed. */ /* 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; return rc;
} }
...@@ -1692,11 +1500,14 @@ dasd_sleep_on_immediatly(struct dasd_ccw_req * cqr) ...@@ -1692,11 +1500,14 @@ dasd_sleep_on_immediatly(struct dasd_ccw_req * cqr)
* This is useful to timeout requests. The request will be * This is useful to timeout requests. The request will be
* terminated if it is currently in i/o. * terminated if it is currently in i/o.
* Returns 1 if the request has been terminated. * 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 int dasd_cancel_req(struct dasd_ccw_req *cqr)
dasd_cancel_req(struct dasd_ccw_req *cqr)
{ {
struct dasd_device *device = cqr->device; struct dasd_device *device = cqr->startdev;
unsigned long flags; unsigned long flags;
int rc; int rc;
...@@ -1704,74 +1515,453 @@ dasd_cancel_req(struct dasd_ccw_req *cqr) ...@@ -1704,74 +1515,453 @@ dasd_cancel_req(struct dasd_ccw_req *cqr)
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
switch (cqr->status) { switch (cqr->status) {
case DASD_CQR_QUEUED: case DASD_CQR_QUEUED:
/* request was not started - just set to failed */ /* request was not started - just set to cleared */
cqr->status = DASD_CQR_FAILED; cqr->status = DASD_CQR_CLEARED;
break; break;
case DASD_CQR_IN_IO: case DASD_CQR_IN_IO:
/* request in IO - terminate IO and release again */ /* request in IO - terminate IO and release again */
if (device->discipline->term_IO(cqr) != 0) rc = device->discipline->term_IO(cqr);
/* what to do if unable to terminate ?????? if (rc) {
e.g. not _IN_IO */ DEV_MESSAGE(KERN_ERR, device,
cqr->status = DASD_CQR_FAILED; "dasd_cancel_req is unable "
cqr->stopclk = get_clock(); " to terminate request %p, rc = %d",
rc = 1; cqr, rc);
} else {
cqr->stopclk = get_clock();
rc = 1;
}
break; break;
case DASD_CQR_DONE: default: /* already finished or clear pending - do nothing */
case DASD_CQR_FAILED:
/* already finished - do nothing */
break; break;
default: }
DEV_MESSAGE(KERN_ALERT, device, spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
"invalid status %02x in request", dasd_schedule_device_bh(device);
cqr->status); return rc;
}
/*
* SECTION: Operations of the dasd_block layer.
*/
/*
* Timeout function for dasd_block. This is used when the block layer
* is waiting for something that may not come reliably, (e.g. a state
* change interrupt)
*/
static void dasd_block_timeout(unsigned long ptr)
{
unsigned long flags;
struct dasd_block *block;
block = (struct dasd_block *) ptr;
spin_lock_irqsave(get_ccwdev_lock(block->base->cdev), flags);
/* re-activate request queue */
block->base->stopped &= ~DASD_STOPPED_PENDING;
spin_unlock_irqrestore(get_ccwdev_lock(block->base->cdev), flags);
dasd_schedule_block_bh(block);
}
/*
* Setup timeout for a dasd_block in jiffies.
*/
void dasd_block_set_timer(struct dasd_block *block, int expires)
{
if (expires == 0) {
if (timer_pending(&block->timer))
del_timer(&block->timer);
return;
}
if (timer_pending(&block->timer)) {
if (mod_timer(&block->timer, jiffies + expires))
return;
}
block->timer.function = dasd_block_timeout;
block->timer.data = (unsigned long) block;
block->timer.expires = jiffies + expires;
add_timer(&block->timer);
}
/*
* Clear timeout for a dasd_block.
*/
void dasd_block_clear_timer(struct dasd_block *block)
{
if (timer_pending(&block->timer))
del_timer(&block->timer);
}
/*
* posts the buffer_cache about a finalized request
*/
static inline void dasd_end_request(struct request *req, int uptodate)
{
if (end_that_request_first(req, uptodate, req->hard_nr_sectors))
BUG(); BUG();
add_disk_randomness(req->rq_disk);
end_that_request_last(req, uptodate);
}
/*
* Process finished error recovery ccw.
*/
static inline void __dasd_block_process_erp(struct dasd_block *block,
struct dasd_ccw_req *cqr)
{
dasd_erp_fn_t erp_fn;
struct dasd_device *device = block->base;
if (cqr->status == DASD_CQR_DONE)
DBF_DEV_EVENT(DBF_NOTICE, device, "%s", "ERP successful");
else
DEV_MESSAGE(KERN_ERR, device, "%s", "ERP unsuccessful");
erp_fn = device->discipline->erp_postaction(cqr);
erp_fn(cqr);
}
/*
* Fetch requests from the block device queue.
*/
static void __dasd_process_request_queue(struct dasd_block *block)
{
struct request_queue *queue;
struct request *req;
struct dasd_ccw_req *cqr;
struct dasd_device *basedev;
unsigned long flags;
queue = block->request_queue;
basedev = block->base;
/* No queue ? Then there is nothing to do. */
if (queue == NULL)
return;
/*
* We requeue request from the block device queue to the ccw
* queue only in two states. In state DASD_STATE_READY the
* partition detection is done and we need to requeue requests
* for that. State DASD_STATE_ONLINE is normal block device
* operation.
*/
if (basedev->state < DASD_STATE_READY)
return;
/* Now we try to fetch requests from the request queue */
while (!blk_queue_plugged(queue) &&
elv_next_request(queue)) {
req = elv_next_request(queue);
if (basedev->features & DASD_FEATURE_READONLY &&
rq_data_dir(req) == WRITE) {
DBF_DEV_EVENT(DBF_ERR, basedev,
"Rejecting write request %p",
req);
blkdev_dequeue_request(req);
dasd_end_request(req, 0);
continue;
}
cqr = basedev->discipline->build_cp(basedev, block, req);
if (IS_ERR(cqr)) {
if (PTR_ERR(cqr) == -EBUSY)
break; /* normal end condition */
if (PTR_ERR(cqr) == -ENOMEM)
break; /* terminate request queue loop */
if (PTR_ERR(cqr) == -EAGAIN) {
/*
* The current request cannot be build right
* now, we have to try later. If this request
* is the head-of-queue we stop the device
* for 1/2 second.
*/
if (!list_empty(&block->ccw_queue))
break;
spin_lock_irqsave(get_ccwdev_lock(basedev->cdev), flags);
basedev->stopped |= DASD_STOPPED_PENDING;
spin_unlock_irqrestore(get_ccwdev_lock(basedev->cdev), flags);
dasd_block_set_timer(block, HZ/2);
break;
}
DBF_DEV_EVENT(DBF_ERR, basedev,
"CCW creation failed (rc=%ld) "
"on request %p",
PTR_ERR(cqr), req);
blkdev_dequeue_request(req);
dasd_end_request(req, 0);
continue;
}
/*
* Note: callback is set to dasd_return_cqr_cb in
* __dasd_block_start_head to cover erp requests as well
*/
cqr->callback_data = (void *) req;
cqr->status = DASD_CQR_FILLED;
blkdev_dequeue_request(req);
list_add_tail(&cqr->blocklist, &block->ccw_queue);
dasd_profile_start(block, cqr, req);
}
}
static void __dasd_cleanup_cqr(struct dasd_ccw_req *cqr)
{
struct request *req;
int status;
req = (struct request *) cqr->callback_data;
dasd_profile_end(cqr->block, cqr, req);
status = cqr->memdev->discipline->free_cp(cqr, req);
dasd_end_request(req, status);
}
/*
* Process ccw request queue.
*/
static void __dasd_process_block_ccw_queue(struct dasd_block *block,
struct list_head *final_queue)
{
struct list_head *l, *n;
struct dasd_ccw_req *cqr;
dasd_erp_fn_t erp_fn;
unsigned long flags;
struct dasd_device *base = block->base;
restart:
/* Process request with final status. */
list_for_each_safe(l, n, &block->ccw_queue) {
cqr = list_entry(l, struct dasd_ccw_req, blocklist);
if (cqr->status != DASD_CQR_DONE &&
cqr->status != DASD_CQR_FAILED &&
cqr->status != DASD_CQR_NEED_ERP &&
cqr->status != DASD_CQR_TERMINATED)
continue;
if (cqr->status == DASD_CQR_TERMINATED) {
base->discipline->handle_terminated_request(cqr);
goto restart;
}
/* Process requests that may be recovered */
if (cqr->status == DASD_CQR_NEED_ERP) {
if (cqr->irb.esw.esw0.erw.cons &&
test_bit(DASD_CQR_FLAGS_USE_ERP,
&cqr->flags)) {
erp_fn = base->discipline->erp_action(cqr);
erp_fn(cqr);
}
goto restart;
}
/* First of all call extended error reporting. */
if (dasd_eer_enabled(base) &&
cqr->status == DASD_CQR_FAILED) {
dasd_eer_write(base, cqr, DASD_EER_FATALERROR);
/* restart request */
cqr->status = DASD_CQR_FILLED;
cqr->retries = 255;
spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags);
base->stopped |= DASD_STOPPED_QUIESCE;
spin_unlock_irqrestore(get_ccwdev_lock(base->cdev),
flags);
goto restart;
}
/* Process finished ERP request. */
if (cqr->refers) {
__dasd_block_process_erp(block, cqr);
goto restart;
}
/* Rechain finished requests to final queue */
cqr->endclk = get_clock();
list_move_tail(&cqr->blocklist, final_queue);
}
}
static void dasd_return_cqr_cb(struct dasd_ccw_req *cqr, void *data)
{
dasd_schedule_block_bh(cqr->block);
}
static void __dasd_block_start_head(struct dasd_block *block)
{
struct dasd_ccw_req *cqr;
if (list_empty(&block->ccw_queue))
return;
/* We allways begin with the first requests on the queue, as some
* of previously started requests have to be enqueued on a
* dasd_device again for error recovery.
*/
list_for_each_entry(cqr, &block->ccw_queue, blocklist) {
if (cqr->status != DASD_CQR_FILLED)
continue;
/* Non-temporary stop condition will trigger fail fast */
if (block->base->stopped & ~DASD_STOPPED_PENDING &&
test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) &&
(!dasd_eer_enabled(block->base))) {
cqr->status = DASD_CQR_FAILED;
dasd_schedule_block_bh(block);
continue;
}
/* Don't try to start requests if device is stopped */
if (block->base->stopped)
return;
/* just a fail safe check, should not happen */
if (!cqr->startdev)
cqr->startdev = block->base;
/* make sure that the requests we submit find their way back */
cqr->callback = dasd_return_cqr_cb;
dasd_add_request_tail(cqr);
}
}
/*
* Central dasd_block layer routine. Takes requests from the generic
* block layer request queue, creates ccw requests, enqueues them on
* a dasd_device and processes ccw requests that have been returned.
*/
static void dasd_block_tasklet(struct dasd_block *block)
{
struct list_head final_queue;
struct list_head *l, *n;
struct dasd_ccw_req *cqr;
atomic_set(&block->tasklet_scheduled, 0);
INIT_LIST_HEAD(&final_queue);
spin_lock(&block->queue_lock);
/* Finish off requests on ccw queue */
__dasd_process_block_ccw_queue(block, &final_queue);
spin_unlock(&block->queue_lock);
/* Now call the callback function of requests with final status */
spin_lock_irq(&block->request_queue_lock);
list_for_each_safe(l, n, &final_queue) {
cqr = list_entry(l, struct dasd_ccw_req, blocklist);
list_del_init(&cqr->blocklist);
__dasd_cleanup_cqr(cqr);
}
spin_lock(&block->queue_lock);
/* Get new request from the block device request queue */
__dasd_process_request_queue(block);
/* Now check if the head of the ccw queue needs to be started. */
__dasd_block_start_head(block);
spin_unlock(&block->queue_lock);
spin_unlock_irq(&block->request_queue_lock);
dasd_put_device(block->base);
}
static void _dasd_wake_block_flush_cb(struct dasd_ccw_req *cqr, void *data)
{
wake_up(&dasd_flush_wq);
}
/*
* Go through all request on the dasd_block request queue, cancel them
* on the respective dasd_device, and return them to the generic
* block layer.
*/
static int dasd_flush_block_queue(struct dasd_block *block)
{
struct dasd_ccw_req *cqr, *n;
int rc, i;
struct list_head flush_queue;
INIT_LIST_HEAD(&flush_queue);
spin_lock_bh(&block->queue_lock);
rc = 0;
restart:
list_for_each_entry_safe(cqr, n, &block->ccw_queue, blocklist) {
/* if this request currently owned by a dasd_device cancel it */
if (cqr->status >= DASD_CQR_QUEUED)
rc = dasd_cancel_req(cqr);
if (rc < 0)
break;
/* Rechain request (including erp chain) so it won't be
* touched by the dasd_block_tasklet anymore.
* Replace the callback so we notice when the request
* is returned from the dasd_device layer.
*/
cqr->callback = _dasd_wake_block_flush_cb;
for (i = 0; cqr != NULL; cqr = cqr->refers, i++)
list_move_tail(&cqr->blocklist, &flush_queue);
if (i > 1)
/* moved more than one request - need to restart */
goto restart;
}
spin_unlock_bh(&block->queue_lock);
/* Now call the callback function of flushed requests */
restart_cb:
list_for_each_entry_safe(cqr, n, &flush_queue, blocklist) {
wait_event(dasd_flush_wq, (cqr->status < DASD_CQR_QUEUED));
/* Process finished ERP request. */
if (cqr->refers) {
__dasd_block_process_erp(block, cqr);
/* restart list_for_xx loop since dasd_process_erp
* might remove multiple elements */
goto restart_cb;
}
/* call the callback function */
cqr->endclk = get_clock();
list_del_init(&cqr->blocklist);
__dasd_cleanup_cqr(cqr);
} }
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
dasd_schedule_bh(device);
return rc; 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 * Dasd request queue function. Called from ll_rw_blk.c
*/ */
static void static void do_dasd_request(struct request_queue *queue)
do_dasd_request(struct request_queue * queue)
{ {
struct dasd_device *device; struct dasd_block *block;
device = (struct dasd_device *) queue->queuedata; block = queue->queuedata;
spin_lock(get_ccwdev_lock(device->cdev)); spin_lock(&block->queue_lock);
/* Get new request from the block device request queue */ /* 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. */ /* Now check if the head of the ccw queue needs to be started. */
__dasd_start_head(device); __dasd_block_start_head(block);
spin_unlock(get_ccwdev_lock(device->cdev)); spin_unlock(&block->queue_lock);
} }
/* /*
* Allocate and initialize request queue and default I/O scheduler. * Allocate and initialize request queue and default I/O scheduler.
*/ */
static int static int dasd_alloc_queue(struct dasd_block *block)
dasd_alloc_queue(struct dasd_device * device)
{ {
int rc; int rc;
device->request_queue = blk_init_queue(do_dasd_request, block->request_queue = blk_init_queue(do_dasd_request,
&device->request_queue_lock); &block->request_queue_lock);
if (device->request_queue == NULL) if (block->request_queue == NULL)
return -ENOMEM; return -ENOMEM;
device->request_queue->queuedata = device; block->request_queue->queuedata = block;
elevator_exit(device->request_queue->elevator); elevator_exit(block->request_queue->elevator);
rc = elevator_init(device->request_queue, "deadline"); rc = elevator_init(block->request_queue, "deadline");
if (rc) { if (rc) {
blk_cleanup_queue(device->request_queue); blk_cleanup_queue(block->request_queue);
return rc; return rc;
} }
return 0; return 0;
...@@ -1780,79 +1970,76 @@ dasd_alloc_queue(struct dasd_device * device) ...@@ -1780,79 +1970,76 @@ dasd_alloc_queue(struct dasd_device * device)
/* /*
* Allocate and initialize request queue. * Allocate and initialize request queue.
*/ */
static void static void dasd_setup_queue(struct dasd_block *block)
dasd_setup_queue(struct dasd_device * device)
{ {
int max; int max;
blk_queue_hardsect_size(device->request_queue, device->bp_block); blk_queue_hardsect_size(block->request_queue, block->bp_block);
max = device->discipline->max_blocks << device->s2b_shift; max = block->base->discipline->max_blocks << block->s2b_shift;
blk_queue_max_sectors(device->request_queue, max); blk_queue_max_sectors(block->request_queue, max);
blk_queue_max_phys_segments(device->request_queue, -1L); blk_queue_max_phys_segments(block->request_queue, -1L);
blk_queue_max_hw_segments(device->request_queue, -1L); blk_queue_max_hw_segments(block->request_queue, -1L);
blk_queue_max_segment_size(device->request_queue, -1L); blk_queue_max_segment_size(block->request_queue, -1L);
blk_queue_segment_boundary(device->request_queue, -1L); blk_queue_segment_boundary(block->request_queue, -1L);
blk_queue_ordered(device->request_queue, QUEUE_ORDERED_TAG, NULL); blk_queue_ordered(block->request_queue, QUEUE_ORDERED_DRAIN, NULL);
} }
/* /*
* Deactivate and free request queue. * Deactivate and free request queue.
*/ */
static void static void dasd_free_queue(struct dasd_block *block)
dasd_free_queue(struct dasd_device * device)
{ {
if (device->request_queue) { if (block->request_queue) {
blk_cleanup_queue(device->request_queue); blk_cleanup_queue(block->request_queue);
device->request_queue = NULL; block->request_queue = NULL;
} }
} }
/* /*
* Flush request on the request queue. * Flush request on the request queue.
*/ */
static void static void dasd_flush_request_queue(struct dasd_block *block)
dasd_flush_request_queue(struct dasd_device * device)
{ {
struct request *req; struct request *req;
if (!device->request_queue) if (!block->request_queue)
return; return;
spin_lock_irq(&device->request_queue_lock); spin_lock_irq(&block->request_queue_lock);
while ((req = elv_next_request(device->request_queue))) { while ((req = elv_next_request(block->request_queue))) {
blkdev_dequeue_request(req); blkdev_dequeue_request(req);
dasd_end_request(req, 0); dasd_end_request(req, 0);
} }
spin_unlock_irq(&device->request_queue_lock); spin_unlock_irq(&block->request_queue_lock);
} }
static int static int dasd_open(struct inode *inp, struct file *filp)
dasd_open(struct inode *inp, struct file *filp)
{ {
struct gendisk *disk = inp->i_bdev->bd_disk; 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; int rc;
atomic_inc(&device->open_count); atomic_inc(&block->open_count);
if (test_bit(DASD_FLAG_OFFLINE, &device->flags)) { if (test_bit(DASD_FLAG_OFFLINE, &base->flags)) {
rc = -ENODEV; rc = -ENODEV;
goto unlock; goto unlock;
} }
if (!try_module_get(device->discipline->owner)) { if (!try_module_get(base->discipline->owner)) {
rc = -EINVAL; rc = -EINVAL;
goto unlock; goto unlock;
} }
if (dasd_probeonly) { if (dasd_probeonly) {
DEV_MESSAGE(KERN_INFO, device, "%s", DEV_MESSAGE(KERN_INFO, base, "%s",
"No access to device due to probeonly mode"); "No access to device due to probeonly mode");
rc = -EPERM; rc = -EPERM;
goto out; goto out;
} }
if (device->state <= DASD_STATE_BASIC) { if (base->state <= DASD_STATE_BASIC) {
DBF_DEV_EVENT(DBF_ERR, device, " %s", DBF_DEV_EVENT(DBF_ERR, base, " %s",
" Cannot open unrecognized device"); " Cannot open unrecognized device");
rc = -ENODEV; rc = -ENODEV;
goto out; goto out;
...@@ -1861,41 +2048,41 @@ dasd_open(struct inode *inp, struct file *filp) ...@@ -1861,41 +2048,41 @@ dasd_open(struct inode *inp, struct file *filp)
return 0; return 0;
out: out:
module_put(device->discipline->owner); module_put(base->discipline->owner);
unlock: unlock:
atomic_dec(&device->open_count); atomic_dec(&block->open_count);
return rc; return rc;
} }
static int static int dasd_release(struct inode *inp, struct file *filp)
dasd_release(struct inode *inp, struct file *filp)
{ {
struct gendisk *disk = inp->i_bdev->bd_disk; 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); atomic_dec(&block->open_count);
module_put(device->discipline->owner); module_put(block->base->discipline->owner);
return 0; return 0;
} }
/* /*
* Return disk geometry. * Return disk geometry.
*/ */
static int static int dasd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
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; block = bdev->bd_disk->private_data;
if (!device) base = block->base;
if (!block)
return -ENODEV; return -ENODEV;
if (!device->discipline || if (!base->discipline ||
!device->discipline->fill_geometry) !base->discipline->fill_geometry)
return -EINVAL; return -EINVAL;
device->discipline->fill_geometry(device, geo); base->discipline->fill_geometry(block, geo);
geo->start = get_start_sect(bdev) >> device->s2b_shift; geo->start = get_start_sect(bdev) >> block->s2b_shift;
return 0; return 0;
} }
...@@ -1909,6 +2096,9 @@ dasd_device_operations = { ...@@ -1909,6 +2096,9 @@ dasd_device_operations = {
.getgeo = dasd_getgeo, .getgeo = dasd_getgeo,
}; };
/*******************************************************************************
* end of block device operations
*/
static void static void
dasd_exit(void) dasd_exit(void)
...@@ -1937,9 +2127,8 @@ dasd_exit(void) ...@@ -1937,9 +2127,8 @@ dasd_exit(void)
* Initial attempt at a probe function. this can be simplified once * Initial attempt at a probe function. this can be simplified once
* the other detection code is gone. * the other detection code is gone.
*/ */
int int dasd_generic_probe(struct ccw_device *cdev,
dasd_generic_probe (struct ccw_device *cdev, struct dasd_discipline *discipline)
struct dasd_discipline *discipline)
{ {
int ret; int ret;
...@@ -1979,10 +2168,10 @@ dasd_generic_probe (struct ccw_device *cdev, ...@@ -1979,10 +2168,10 @@ dasd_generic_probe (struct ccw_device *cdev,
* This will one day be called from a global not_oper handler. * This will one day be called from a global not_oper handler.
* It is also used by driver_unregister during module unload. * It is also used by driver_unregister during module unload.
*/ */
void void dasd_generic_remove(struct ccw_device *cdev)
dasd_generic_remove (struct ccw_device *cdev)
{ {
struct dasd_device *device; struct dasd_device *device;
struct dasd_block *block;
cdev->handler = NULL; cdev->handler = NULL;
...@@ -2002,7 +2191,15 @@ dasd_generic_remove (struct ccw_device *cdev) ...@@ -2002,7 +2191,15 @@ dasd_generic_remove (struct ccw_device *cdev)
*/ */
dasd_set_target_state(device, DASD_STATE_NEW); dasd_set_target_state(device, DASD_STATE_NEW);
/* dasd_delete_device destroys the device reference. */ /* dasd_delete_device destroys the device reference. */
block = device->block;
device->block = NULL;
dasd_delete_device(device); 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);
} }
/* /*
...@@ -2010,10 +2207,8 @@ dasd_generic_remove (struct ccw_device *cdev) ...@@ -2010,10 +2207,8 @@ dasd_generic_remove (struct ccw_device *cdev)
* the device is detected for the first time and is supposed to be used * the device is detected for the first time and is supposed to be used
* or the user has started activation through sysfs. * or the user has started activation through sysfs.
*/ */
int int dasd_generic_set_online(struct ccw_device *cdev,
dasd_generic_set_online (struct ccw_device *cdev, struct dasd_discipline *base_discipline)
struct dasd_discipline *base_discipline)
{ {
struct dasd_discipline *discipline; struct dasd_discipline *discipline;
struct dasd_device *device; struct dasd_device *device;
...@@ -2049,6 +2244,7 @@ dasd_generic_set_online (struct ccw_device *cdev, ...@@ -2049,6 +2244,7 @@ dasd_generic_set_online (struct ccw_device *cdev,
device->base_discipline = base_discipline; device->base_discipline = base_discipline;
device->discipline = discipline; device->discipline = discipline;
/* check_device will allocate block device if necessary */
rc = discipline->check_device(device); rc = discipline->check_device(device);
if (rc) { if (rc) {
printk (KERN_WARNING printk (KERN_WARNING
...@@ -2068,6 +2264,8 @@ dasd_generic_set_online (struct ccw_device *cdev, ...@@ -2068,6 +2264,8 @@ dasd_generic_set_online (struct ccw_device *cdev,
cdev->dev.bus_id); cdev->dev.bus_id);
rc = -ENODEV; rc = -ENODEV;
dasd_set_target_state(device, DASD_STATE_NEW); dasd_set_target_state(device, DASD_STATE_NEW);
if (device->block)
dasd_free_block(device->block);
dasd_delete_device(device); dasd_delete_device(device);
} else } else
pr_debug("dasd_generic device %s found\n", pr_debug("dasd_generic device %s found\n",
...@@ -2082,10 +2280,10 @@ dasd_generic_set_online (struct ccw_device *cdev, ...@@ -2082,10 +2280,10 @@ dasd_generic_set_online (struct ccw_device *cdev,
return rc; return rc;
} }
int int dasd_generic_set_offline(struct ccw_device *cdev)
dasd_generic_set_offline (struct ccw_device *cdev)
{ {
struct dasd_device *device; struct dasd_device *device;
struct dasd_block *block;
int max_count, open_count; int max_count, open_count;
device = dasd_device_from_cdev(cdev); device = dasd_device_from_cdev(cdev);
...@@ -2102,30 +2300,39 @@ dasd_generic_set_offline (struct ccw_device *cdev) ...@@ -2102,30 +2300,39 @@ dasd_generic_set_offline (struct ccw_device *cdev)
* the blkdev_get in dasd_scan_partitions. We are only interested * the blkdev_get in dasd_scan_partitions. We are only interested
* in the other openers. * in the other openers.
*/ */
max_count = device->bdev ? 0 : -1; if (device->block) {
open_count = (int) atomic_read(&device->open_count); struct dasd_block *block = device->block;
if (open_count > max_count) { max_count = block->bdev ? 0 : -1;
if (open_count > 0) open_count = (int) atomic_read(&block->open_count);
printk (KERN_WARNING "Can't offline dasd device with " if (open_count > max_count) {
"open count = %i.\n", if (open_count > 0)
open_count); printk(KERN_WARNING "Can't offline dasd "
else "device with open count = %i.\n",
printk (KERN_WARNING "%s", open_count);
"Can't offline dasd device due to internal " else
"use\n"); printk(KERN_WARNING "%s",
clear_bit(DASD_FLAG_OFFLINE, &device->flags); "Can't offline dasd device due "
dasd_put_device(device); "to internal use\n");
return -EBUSY; clear_bit(DASD_FLAG_OFFLINE, &device->flags);
dasd_put_device(device);
return -EBUSY;
}
} }
dasd_set_target_state(device, DASD_STATE_NEW); dasd_set_target_state(device, DASD_STATE_NEW);
/* dasd_delete_device destroys the device reference. */ /* dasd_delete_device destroys the device reference. */
block = device->block;
device->block = NULL;
dasd_delete_device(device); 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; return 0;
} }
int int dasd_generic_notify(struct ccw_device *cdev, int event)
dasd_generic_notify(struct ccw_device *cdev, int event)
{ {
struct dasd_device *device; struct dasd_device *device;
struct dasd_ccw_req *cqr; struct dasd_ccw_req *cqr;
...@@ -2146,27 +2353,22 @@ dasd_generic_notify(struct ccw_device *cdev, int event) ...@@ -2146,27 +2353,22 @@ dasd_generic_notify(struct ccw_device *cdev, int event)
if (device->state < DASD_STATE_BASIC) if (device->state < DASD_STATE_BASIC)
break; break;
/* Device is active. We want to keep it. */ /* 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, devlist)
list_for_each_entry(cqr, &device->ccw_queue, list) if (cqr->status == DASD_CQR_IN_IO) {
if (cqr->status == DASD_CQR_IN_IO) cqr->status = DASD_CQR_QUEUED;
cqr->status = DASD_CQR_FAILED; cqr->retries++;
device->stopped |= DASD_STOPPED_DC_EIO; }
} else { device->stopped |= DASD_STOPPED_DC_WAIT;
list_for_each_entry(cqr, &device->ccw_queue, list) dasd_device_clear_timer(device);
if (cqr->status == DASD_CQR_IN_IO) { dasd_schedule_device_bh(device);
cqr->status = DASD_CQR_QUEUED;
cqr->retries++;
}
device->stopped |= DASD_STOPPED_DC_WAIT;
dasd_set_timer(device, 0);
}
dasd_schedule_bh(device);
ret = 1; ret = 1;
break; break;
case CIO_OPER: case CIO_OPER:
/* FIXME: add a sanity check. */ /* FIXME: add a sanity check. */
device->stopped &= ~(DASD_STOPPED_DC_WAIT|DASD_STOPPED_DC_EIO); device->stopped &= ~DASD_STOPPED_DC_WAIT;
dasd_schedule_bh(device); dasd_schedule_device_bh(device);
if (device->block)
dasd_schedule_block_bh(device->block);
ret = 1; ret = 1;
break; break;
} }
...@@ -2196,7 +2398,8 @@ static struct dasd_ccw_req *dasd_generic_build_rdc(struct dasd_device *device, ...@@ -2196,7 +2398,8 @@ static struct dasd_ccw_req *dasd_generic_build_rdc(struct dasd_device *device,
ccw->cda = (__u32)(addr_t)rdc_buffer; ccw->cda = (__u32)(addr_t)rdc_buffer;
ccw->count = rdc_buffer_size; ccw->count = rdc_buffer_size;
cqr->device = device; cqr->startdev = device;
cqr->memdev = device;
cqr->expires = 10*HZ; cqr->expires = 10*HZ;
clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
cqr->retries = 2; cqr->retries = 2;
...@@ -2218,13 +2421,12 @@ int dasd_generic_read_dev_chars(struct dasd_device *device, char *magic, ...@@ -2218,13 +2421,12 @@ int dasd_generic_read_dev_chars(struct dasd_device *device, char *magic,
return PTR_ERR(cqr); return PTR_ERR(cqr);
ret = dasd_sleep_on(cqr); ret = dasd_sleep_on(cqr);
dasd_sfree_request(cqr, cqr->device); dasd_sfree_request(cqr, cqr->memdev);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(dasd_generic_read_dev_chars); EXPORT_SYMBOL_GPL(dasd_generic_read_dev_chars);
static int __init static int __init dasd_init(void)
dasd_init(void)
{ {
int rc; int rc;
...@@ -2232,7 +2434,7 @@ dasd_init(void) ...@@ -2232,7 +2434,7 @@ dasd_init(void)
init_waitqueue_head(&dasd_flush_wq); init_waitqueue_head(&dasd_flush_wq);
/* register 'common' DASD debug area, used for all DBF_XXX calls */ /* register 'common' DASD debug area, used for all DBF_XXX calls */
dasd_debug_area = debug_register("dasd", 1, 2, 8 * sizeof (long)); dasd_debug_area = debug_register("dasd", 1, 2, 8 * sizeof(long));
if (dasd_debug_area == NULL) { if (dasd_debug_area == NULL) {
rc = -ENOMEM; rc = -ENOMEM;
goto failed; goto failed;
...@@ -2278,15 +2480,18 @@ EXPORT_SYMBOL(dasd_diag_discipline_pointer); ...@@ -2278,15 +2480,18 @@ EXPORT_SYMBOL(dasd_diag_discipline_pointer);
EXPORT_SYMBOL(dasd_add_request_head); EXPORT_SYMBOL(dasd_add_request_head);
EXPORT_SYMBOL(dasd_add_request_tail); EXPORT_SYMBOL(dasd_add_request_tail);
EXPORT_SYMBOL(dasd_cancel_req); 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_enable_device);
EXPORT_SYMBOL(dasd_int_handler); EXPORT_SYMBOL(dasd_int_handler);
EXPORT_SYMBOL(dasd_kfree_request); EXPORT_SYMBOL(dasd_kfree_request);
EXPORT_SYMBOL(dasd_kick_device); EXPORT_SYMBOL(dasd_kick_device);
EXPORT_SYMBOL(dasd_kmalloc_request); 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_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_sfree_request);
EXPORT_SYMBOL(dasd_sleep_on); EXPORT_SYMBOL(dasd_sleep_on);
EXPORT_SYMBOL(dasd_sleep_on_immediatly); EXPORT_SYMBOL(dasd_sleep_on_immediatly);
...@@ -2300,4 +2505,7 @@ EXPORT_SYMBOL_GPL(dasd_generic_remove); ...@@ -2300,4 +2505,7 @@ EXPORT_SYMBOL_GPL(dasd_generic_remove);
EXPORT_SYMBOL_GPL(dasd_generic_notify); EXPORT_SYMBOL_GPL(dasd_generic_notify);
EXPORT_SYMBOL_GPL(dasd_generic_set_online); EXPORT_SYMBOL_GPL(dasd_generic_set_online);
EXPORT_SYMBOL_GPL(dasd_generic_set_offline); 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);
/*
* 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 */
...@@ -24,158 +24,6 @@ struct DCTL_data { ...@@ -24,158 +24,6 @@ struct DCTL_data {
unsigned short res; /* reserved */ unsigned short res; /* reserved */
} __attribute__ ((packed)); } __attribute__ ((packed));
/*
*****************************************************************************
* SECTION ERP EXAMINATION
*****************************************************************************
*/
/*
* DASD_3990_ERP_EXAMINE_24
*
* DESCRIPTION
* Checks only for fatal (unrecoverable) error.
* A detailed examination of the sense data is done later outside
* the interrupt handler.
*
* Each bit configuration leading to an action code 2 (Exit with
* programming error or unusual condition indication)
* are handled as fatal errors.
*
* All other configurations are handled as recoverable errors.
*
* RETURN VALUES
* dasd_era_fatal for all fatal (unrecoverable errors)
* dasd_era_recover for all others.
*/
static dasd_era_t
dasd_3990_erp_examine_24(struct dasd_ccw_req * cqr, char *sense)
{
struct dasd_device *device = cqr->device;
/* check for 'Command Reject' */
if ((sense[0] & SNS0_CMD_REJECT) &&
(!(sense[2] & SNS2_ENV_DATA_PRESENT))) {
DEV_MESSAGE(KERN_ERR, device, "%s",
"EXAMINE 24: Command Reject detected - "
"fatal error");
return dasd_era_fatal;
}
/* check for 'Invalid Track Format' */
if ((sense[1] & SNS1_INV_TRACK_FORMAT) &&
(!(sense[2] & SNS2_ENV_DATA_PRESENT))) {
DEV_MESSAGE(KERN_ERR, device, "%s",
"EXAMINE 24: Invalid Track Format detected "
"- fatal error");
return dasd_era_fatal;
}
/* check for 'No Record Found' */
if (sense[1] & SNS1_NO_REC_FOUND) {
/* FIXME: fatal error ?!? */
DEV_MESSAGE(KERN_ERR, device,
"EXAMINE 24: No Record Found detected %s",
device->state <= DASD_STATE_BASIC ?
" " : "- fatal error");
return dasd_era_fatal;
}
/* return recoverable for all others */
return dasd_era_recover;
} /* END dasd_3990_erp_examine_24 */
/*
* DASD_3990_ERP_EXAMINE_32
*
* DESCRIPTION
* Checks only for fatal/no/recoverable error.
* A detailed examination of the sense data is done later outside
* the interrupt handler.
*
* RETURN VALUES
* dasd_era_none no error
* dasd_era_fatal for all fatal (unrecoverable errors)
* dasd_era_recover for recoverable others.
*/
static dasd_era_t
dasd_3990_erp_examine_32(struct dasd_ccw_req * cqr, char *sense)
{
struct dasd_device *device = cqr->device;
switch (sense[25]) {
case 0x00:
return dasd_era_none;
case 0x01:
DEV_MESSAGE(KERN_ERR, device, "%s", "EXAMINE 32: fatal error");
return dasd_era_fatal;
default:
return dasd_era_recover;
}
} /* end dasd_3990_erp_examine_32 */
/*
* DASD_3990_ERP_EXAMINE
*
* DESCRIPTION
* Checks only for fatal/no/recover error.
* A detailed examination of the sense data is done later outside
* the interrupt handler.
*
* The logic is based on the 'IBM 3990 Storage Control Reference' manual
* 'Chapter 7. Error Recovery Procedures'.
*
* RETURN VALUES
* dasd_era_none no error
* dasd_era_fatal for all fatal (unrecoverable errors)
* dasd_era_recover for all others.
*/
dasd_era_t
dasd_3990_erp_examine(struct dasd_ccw_req * cqr, struct irb * irb)
{
char *sense = irb->ecw;
dasd_era_t era = dasd_era_recover;
struct dasd_device *device = cqr->device;
/* check for successful execution first */
if (irb->scsw.cstat == 0x00 &&
irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
return dasd_era_none;
/* distinguish between 24 and 32 byte sense data */
if (sense[27] & DASD_SENSE_BIT_0) {
era = dasd_3990_erp_examine_24(cqr, sense);
} else {
era = dasd_3990_erp_examine_32(cqr, sense);
}
/* log the erp chain if fatal error occurred */
if ((era == dasd_era_fatal) && (device->state >= DASD_STATE_READY)) {
dasd_log_sense(cqr, irb);
}
return era;
} /* END dasd_3990_erp_examine */
/* /*
***************************************************************************** *****************************************************************************
* SECTION ERP HANDLING * SECTION ERP HANDLING
...@@ -206,7 +54,7 @@ dasd_3990_erp_cleanup(struct dasd_ccw_req * erp, char final_status) ...@@ -206,7 +54,7 @@ dasd_3990_erp_cleanup(struct dasd_ccw_req * erp, char final_status)
{ {
struct dasd_ccw_req *cqr = erp->refers; 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; cqr->status = final_status;
return cqr; return cqr;
...@@ -224,15 +72,17 @@ static void ...@@ -224,15 +72,17 @@ static void
dasd_3990_erp_block_queue(struct dasd_ccw_req * erp, int expires) 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, DEV_MESSAGE(KERN_INFO, device,
"blocking request queue for %is", expires/HZ); "blocking request queue for %is", expires/HZ);
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
device->stopped |= DASD_STOPPED_PENDING; device->stopped |= DASD_STOPPED_PENDING;
erp->status = DASD_CQR_QUEUED; spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
erp->status = DASD_CQR_FILLED;
dasd_set_timer(device, expires); dasd_block_set_timer(device->block, expires);
} }
/* /*
...@@ -251,7 +101,7 @@ static struct dasd_ccw_req * ...@@ -251,7 +101,7 @@ static struct dasd_ccw_req *
dasd_3990_erp_int_req(struct dasd_ccw_req * erp) 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 */ /* first time set initial retry counter and erp_function */
/* and retry once without blocking queue */ /* and retry once without blocking queue */
...@@ -292,11 +142,14 @@ dasd_3990_erp_int_req(struct dasd_ccw_req * erp) ...@@ -292,11 +142,14 @@ dasd_3990_erp_int_req(struct dasd_ccw_req * erp)
static void static void
dasd_3990_erp_alternate_path(struct dasd_ccw_req * erp) dasd_3990_erp_alternate_path(struct dasd_ccw_req * erp)
{ {
struct dasd_device *device = erp->device; struct dasd_device *device = erp->startdev;
__u8 opm; __u8 opm;
unsigned long flags;
/* try alternate valid path */ /* try alternate valid path */
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
opm = ccw_device_get_path_mask(device->cdev); opm = ccw_device_get_path_mask(device->cdev);
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
//FIXME: start with get_opm ? //FIXME: start with get_opm ?
if (erp->lpm == 0) if (erp->lpm == 0)
erp->lpm = LPM_ANYPATH & ~(erp->irb.esw.esw0.sublog.lpum); erp->lpm = LPM_ANYPATH & ~(erp->irb.esw.esw0.sublog.lpum);
...@@ -309,9 +162,8 @@ dasd_3990_erp_alternate_path(struct dasd_ccw_req * erp) ...@@ -309,9 +162,8 @@ dasd_3990_erp_alternate_path(struct dasd_ccw_req * erp)
"try alternate lpm=%x (lpum=%x / opm=%x)", "try alternate lpm=%x (lpum=%x / opm=%x)",
erp->lpm, erp->irb.esw.esw0.sublog.lpum, opm); erp->lpm, erp->irb.esw.esw0.sublog.lpum, opm);
/* reset status to queued to handle the request again... */ /* reset status to submit the request again... */
if (erp->status > DASD_CQR_QUEUED) erp->status = DASD_CQR_FILLED;
erp->status = DASD_CQR_QUEUED;
erp->retries = 1; erp->retries = 1;
} else { } else {
DEV_MESSAGE(KERN_ERR, device, DEV_MESSAGE(KERN_ERR, device,
...@@ -320,8 +172,7 @@ dasd_3990_erp_alternate_path(struct dasd_ccw_req * erp) ...@@ -320,8 +172,7 @@ dasd_3990_erp_alternate_path(struct dasd_ccw_req * erp)
erp->irb.esw.esw0.sublog.lpum, opm); erp->irb.esw.esw0.sublog.lpum, opm);
/* post request with permanent error */ /* 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 */ } /* end dasd_3990_erp_alternate_path */
...@@ -344,14 +195,14 @@ static struct dasd_ccw_req * ...@@ -344,14 +195,14 @@ static struct dasd_ccw_req *
dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier) 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 DCTL_data *DCTL_data;
struct ccw1 *ccw; struct ccw1 *ccw;
struct dasd_ccw_req *dctl_cqr; struct dasd_ccw_req *dctl_cqr;
dctl_cqr = dasd_alloc_erp_request((char *) &erp->magic, 1, dctl_cqr = dasd_alloc_erp_request((char *) &erp->magic, 1,
sizeof (struct DCTL_data), sizeof(struct DCTL_data),
erp->device); device);
if (IS_ERR(dctl_cqr)) { if (IS_ERR(dctl_cqr)) {
DEV_MESSAGE(KERN_ERR, device, "%s", DEV_MESSAGE(KERN_ERR, device, "%s",
"Unable to allocate DCTL-CQR"); "Unable to allocate DCTL-CQR");
...@@ -365,13 +216,14 @@ dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier) ...@@ -365,13 +216,14 @@ dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier)
DCTL_data->modifier = modifier; DCTL_data->modifier = modifier;
ccw = dctl_cqr->cpaddr; ccw = dctl_cqr->cpaddr;
memset(ccw, 0, sizeof (struct ccw1)); memset(ccw, 0, sizeof(struct ccw1));
ccw->cmd_code = CCW_CMD_DCTL; ccw->cmd_code = CCW_CMD_DCTL;
ccw->count = 4; ccw->count = 4;
ccw->cda = (__u32)(addr_t) DCTL_data; ccw->cda = (__u32)(addr_t) DCTL_data;
dctl_cqr->function = dasd_3990_erp_DCTL; dctl_cqr->function = dasd_3990_erp_DCTL;
dctl_cqr->refers = erp; dctl_cqr->refers = erp;
dctl_cqr->device = erp->device; dctl_cqr->startdev = device;
dctl_cqr->memdev = device;
dctl_cqr->magic = erp->magic; dctl_cqr->magic = erp->magic;
dctl_cqr->expires = 5 * 60 * HZ; dctl_cqr->expires = 5 * 60 * HZ;
dctl_cqr->retries = 2; dctl_cqr->retries = 2;
...@@ -435,7 +287,7 @@ static struct dasd_ccw_req * ...@@ -435,7 +287,7 @@ static struct dasd_ccw_req *
dasd_3990_erp_action_4(struct dasd_ccw_req * erp, char *sense) 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 */ /* first time set initial retry counter and erp_function */
/* and retry once without waiting for state change pending */ /* 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) ...@@ -472,7 +324,7 @@ dasd_3990_erp_action_4(struct dasd_ccw_req * erp, char *sense)
"redriving request immediately, " "redriving request immediately, "
"%d retries left", "%d retries left",
erp->retries); erp->retries);
erp->status = DASD_CQR_QUEUED; erp->status = DASD_CQR_FILLED;
} }
} }
...@@ -530,7 +382,7 @@ static void ...@@ -530,7 +382,7 @@ static void
dasd_3990_handle_env_data(struct dasd_ccw_req * erp, char *sense) 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_format = (sense[7] & 0xF0);
char msg_no = (sense[7] & 0x0F); char msg_no = (sense[7] & 0x0F);
...@@ -1157,7 +1009,7 @@ static struct dasd_ccw_req * ...@@ -1157,7 +1009,7 @@ static struct dasd_ccw_req *
dasd_3990_erp_com_rej(struct dasd_ccw_req * erp, char *sense) 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; erp->function = dasd_3990_erp_com_rej;
...@@ -1198,7 +1050,7 @@ static struct dasd_ccw_req * ...@@ -1198,7 +1050,7 @@ static struct dasd_ccw_req *
dasd_3990_erp_bus_out(struct dasd_ccw_req * erp) 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 */ /* first time set initial retry counter and erp_function */
/* and retry once without blocking queue */ /* and retry once without blocking queue */
...@@ -1237,7 +1089,7 @@ static struct dasd_ccw_req * ...@@ -1237,7 +1089,7 @@ static struct dasd_ccw_req *
dasd_3990_erp_equip_check(struct dasd_ccw_req * erp, char *sense) dasd_3990_erp_equip_check(struct dasd_ccw_req * erp, char *sense)
{ {
struct dasd_device *device = erp->device; struct dasd_device *device = erp->startdev;
erp->function = dasd_3990_erp_equip_check; erp->function = dasd_3990_erp_equip_check;
...@@ -1279,7 +1131,6 @@ dasd_3990_erp_equip_check(struct dasd_ccw_req * erp, char *sense) ...@@ -1279,7 +1131,6 @@ dasd_3990_erp_equip_check(struct dasd_ccw_req * erp, char *sense)
erp = dasd_3990_erp_action_5(erp); erp = dasd_3990_erp_action_5(erp);
} }
return erp; return erp;
} /* end dasd_3990_erp_equip_check */ } /* end dasd_3990_erp_equip_check */
...@@ -1299,7 +1150,7 @@ static struct dasd_ccw_req * ...@@ -1299,7 +1150,7 @@ static struct dasd_ccw_req *
dasd_3990_erp_data_check(struct dasd_ccw_req * erp, char *sense) 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; erp->function = dasd_3990_erp_data_check;
...@@ -1358,7 +1209,7 @@ static struct dasd_ccw_req * ...@@ -1358,7 +1209,7 @@ static struct dasd_ccw_req *
dasd_3990_erp_overrun(struct dasd_ccw_req * erp, char *sense) 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; erp->function = dasd_3990_erp_overrun;
...@@ -1387,7 +1238,7 @@ static struct dasd_ccw_req * ...@@ -1387,7 +1238,7 @@ static struct dasd_ccw_req *
dasd_3990_erp_inv_format(struct dasd_ccw_req * erp, char *sense) 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; erp->function = dasd_3990_erp_inv_format;
...@@ -1403,8 +1254,7 @@ dasd_3990_erp_inv_format(struct dasd_ccw_req * erp, char *sense) ...@@ -1403,8 +1254,7 @@ dasd_3990_erp_inv_format(struct dasd_ccw_req * erp, char *sense)
} else { } else {
DEV_MESSAGE(KERN_ERR, device, "%s", DEV_MESSAGE(KERN_ERR, device, "%s",
"Invalid Track Format - Fatal error should have " "Invalid Track Format - Fatal error");
"been handled within the interrupt handler");
erp = dasd_3990_erp_cleanup(erp, DASD_CQR_FAILED); erp = dasd_3990_erp_cleanup(erp, DASD_CQR_FAILED);
} }
...@@ -1428,7 +1278,7 @@ static struct dasd_ccw_req * ...@@ -1428,7 +1278,7 @@ static struct dasd_ccw_req *
dasd_3990_erp_EOC(struct dasd_ccw_req * default_erp, char *sense) 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", DEV_MESSAGE(KERN_ERR, device, "%s",
"End-of-Cylinder - must never happen"); "End-of-Cylinder - must never happen");
...@@ -1453,7 +1303,7 @@ static struct dasd_ccw_req * ...@@ -1453,7 +1303,7 @@ static struct dasd_ccw_req *
dasd_3990_erp_env_data(struct dasd_ccw_req * erp, char *sense) 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; erp->function = dasd_3990_erp_env_data;
...@@ -1463,11 +1313,9 @@ dasd_3990_erp_env_data(struct dasd_ccw_req * erp, char *sense) ...@@ -1463,11 +1313,9 @@ dasd_3990_erp_env_data(struct dasd_ccw_req * erp, char *sense)
/* don't retry on disabled interface */ /* don't retry on disabled interface */
if (sense[7] != 0x0F) { if (sense[7] != 0x0F) {
erp = dasd_3990_erp_action_4(erp, sense); erp = dasd_3990_erp_action_4(erp, sense);
} else { } else {
erp->status = DASD_CQR_FILLED;
erp = dasd_3990_erp_cleanup(erp, DASD_CQR_IN_IO);
} }
return erp; return erp;
...@@ -1490,11 +1338,10 @@ static struct dasd_ccw_req * ...@@ -1490,11 +1338,10 @@ static struct dasd_ccw_req *
dasd_3990_erp_no_rec(struct dasd_ccw_req * default_erp, char *sense) 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", DEV_MESSAGE(KERN_ERR, device, "%s",
"No Record Found - Fatal error should " "No Record Found - Fatal error ");
"have been handled within the interrupt handler");
return dasd_3990_erp_cleanup(default_erp, DASD_CQR_FAILED); return dasd_3990_erp_cleanup(default_erp, DASD_CQR_FAILED);
...@@ -1517,7 +1364,7 @@ static struct dasd_ccw_req * ...@@ -1517,7 +1364,7 @@ static struct dasd_ccw_req *
dasd_3990_erp_file_prot(struct dasd_ccw_req * erp) 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"); DEV_MESSAGE(KERN_ERR, device, "%s", "File Protected");
...@@ -1525,6 +1372,43 @@ dasd_3990_erp_file_prot(struct dasd_ccw_req * erp) ...@@ -1525,6 +1372,43 @@ dasd_3990_erp_file_prot(struct dasd_ccw_req * erp)
} /* end dasd_3990_erp_file_prot */ } /* end dasd_3990_erp_file_prot */
/*
* DASD_3990_ERP_INSPECT_ALIAS
*
* DESCRIPTION
* Checks if the original request was started on an alias device.
* If yes, it modifies the original and the erp request so that
* the erp request can be started on a base device.
*
* PARAMETER
* erp pointer to the currently created default ERP
*
* RETURN VALUES
* erp pointer to the modified ERP, or NULL
*/
static struct dasd_ccw_req *dasd_3990_erp_inspect_alias(
struct dasd_ccw_req *erp)
{
struct dasd_ccw_req *cqr = erp->refers;
if (cqr->block &&
(cqr->block->base != cqr->startdev)) {
if (cqr->startdev->features & DASD_FEATURE_ERPLOG) {
DEV_MESSAGE(KERN_ERR, cqr->startdev,
"ERP on alias device for request %p,"
" recover on base device %s", cqr,
cqr->block->base->cdev->dev.bus_id);
}
dasd_eckd_reset_ccw_to_base_io(cqr);
erp->startdev = cqr->block->base;
erp->function = dasd_3990_erp_inspect_alias;
return erp;
} else
return NULL;
}
/* /*
* DASD_3990_ERP_INSPECT_24 * DASD_3990_ERP_INSPECT_24
* *
...@@ -1623,7 +1507,7 @@ static struct dasd_ccw_req * ...@@ -1623,7 +1507,7 @@ static struct dasd_ccw_req *
dasd_3990_erp_action_10_32(struct dasd_ccw_req * erp, char *sense) 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->retries = 256;
erp->function = dasd_3990_erp_action_10_32; erp->function = dasd_3990_erp_action_10_32;
...@@ -1657,13 +1541,14 @@ static struct dasd_ccw_req * ...@@ -1657,13 +1541,14 @@ static struct dasd_ccw_req *
dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense) 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; __u32 cpa = 0;
struct dasd_ccw_req *cqr; struct dasd_ccw_req *cqr;
struct dasd_ccw_req *erp; struct dasd_ccw_req *erp;
struct DE_eckd_data *DE_data; struct DE_eckd_data *DE_data;
struct PFX_eckd_data *PFX_data;
char *LO_data; /* LO_eckd_data_t */ char *LO_data; /* LO_eckd_data_t */
struct ccw1 *ccw; struct ccw1 *ccw, *oldccw;
DEV_MESSAGE(KERN_DEBUG, device, "%s", DEV_MESSAGE(KERN_DEBUG, device, "%s",
"Write not finished because of unexpected condition"); "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) ...@@ -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 */ /* Build new ERP request including DE/LO */
erp = dasd_alloc_erp_request((char *) &cqr->magic, erp = dasd_alloc_erp_request((char *) &cqr->magic,
2 + 1,/* DE/LO + TIC */ 2 + 1,/* DE/LO + TIC */
sizeof (struct DE_eckd_data) + sizeof(struct DE_eckd_data) +
sizeof (struct LO_eckd_data), device); sizeof(struct LO_eckd_data), device);
if (IS_ERR(erp)) { if (IS_ERR(erp)) {
DEV_MESSAGE(KERN_ERR, device, "%s", "Unable to allocate 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) ...@@ -1712,10 +1597,16 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense)
/* use original DE */ /* use original DE */
DE_data = erp->data; 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 */ /* 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)) { 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) ...@@ -1748,7 +1639,7 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense)
/* create DE ccw */ /* create DE ccw */
ccw = erp->cpaddr; ccw = erp->cpaddr;
memset(ccw, 0, sizeof (struct ccw1)); memset(ccw, 0, sizeof(struct ccw1));
ccw->cmd_code = DASD_ECKD_CCW_DEFINE_EXTENT; ccw->cmd_code = DASD_ECKD_CCW_DEFINE_EXTENT;
ccw->flags = CCW_FLAG_CC; ccw->flags = CCW_FLAG_CC;
ccw->count = 16; ccw->count = 16;
...@@ -1756,7 +1647,7 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense) ...@@ -1756,7 +1647,7 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense)
/* create LO ccw */ /* create LO ccw */
ccw++; ccw++;
memset(ccw, 0, sizeof (struct ccw1)); memset(ccw, 0, sizeof(struct ccw1));
ccw->cmd_code = DASD_ECKD_CCW_LOCATE_RECORD; ccw->cmd_code = DASD_ECKD_CCW_LOCATE_RECORD;
ccw->flags = CCW_FLAG_CC; ccw->flags = CCW_FLAG_CC;
ccw->count = 16; ccw->count = 16;
...@@ -1770,7 +1661,8 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense) ...@@ -1770,7 +1661,8 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense)
/* fill erp related fields */ /* fill erp related fields */
erp->function = dasd_3990_erp_action_1B_32; erp->function = dasd_3990_erp_action_1B_32;
erp->refers = default_erp->refers; erp->refers = default_erp->refers;
erp->device = device; erp->startdev = device;
erp->memdev = device;
erp->magic = default_erp->magic; erp->magic = default_erp->magic;
erp->expires = 0; erp->expires = 0;
erp->retries = 256; erp->retries = 256;
...@@ -1803,7 +1695,7 @@ static struct dasd_ccw_req * ...@@ -1803,7 +1695,7 @@ static struct dasd_ccw_req *
dasd_3990_update_1B(struct dasd_ccw_req * previous_erp, char *sense) 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; __u32 cpa = 0;
struct dasd_ccw_req *cqr; struct dasd_ccw_req *cqr;
struct dasd_ccw_req *erp; struct dasd_ccw_req *erp;
...@@ -1827,7 +1719,7 @@ dasd_3990_update_1B(struct dasd_ccw_req * previous_erp, char *sense) ...@@ -1827,7 +1719,7 @@ dasd_3990_update_1B(struct dasd_ccw_req * previous_erp, char *sense)
DEV_MESSAGE(KERN_DEBUG, device, "%s", DEV_MESSAGE(KERN_DEBUG, device, "%s",
"Imprecise ending is set - just retry"); "Imprecise ending is set - just retry");
previous_erp->status = DASD_CQR_QUEUED; previous_erp->status = DASD_CQR_FILLED;
return previous_erp; return previous_erp;
} }
...@@ -1850,7 +1742,7 @@ dasd_3990_update_1B(struct dasd_ccw_req * previous_erp, char *sense) ...@@ -1850,7 +1742,7 @@ dasd_3990_update_1B(struct dasd_ccw_req * previous_erp, char *sense)
erp = previous_erp; erp = previous_erp;
/* update the LO with the new returned sense data */ /* 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)) { if ((sense[3] == 0x01) && (LO_data[1] & 0x01)) {
...@@ -1889,7 +1781,7 @@ dasd_3990_update_1B(struct dasd_ccw_req * previous_erp, char *sense) ...@@ -1889,7 +1781,7 @@ dasd_3990_update_1B(struct dasd_ccw_req * previous_erp, char *sense)
ccw++; /* addr of TIC ccw */ ccw++; /* addr of TIC ccw */
ccw->cda = cpa; ccw->cda = cpa;
erp->status = DASD_CQR_QUEUED; erp->status = DASD_CQR_FILLED;
return erp; return erp;
...@@ -1968,9 +1860,7 @@ dasd_3990_erp_compound_path(struct dasd_ccw_req * erp, char *sense) ...@@ -1968,9 +1860,7 @@ dasd_3990_erp_compound_path(struct dasd_ccw_req * erp, char *sense)
* try further actions. */ * try further actions. */
erp->lpm = 0; erp->lpm = 0;
erp->status = DASD_CQR_NEED_ERP;
erp->status = DASD_CQR_ERROR;
} }
} }
...@@ -2047,7 +1937,7 @@ dasd_3990_erp_compound_config(struct dasd_ccw_req * erp, char *sense) ...@@ -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)) { if ((sense[25] & DASD_SENSE_BIT_1) && (sense[26] & DASD_SENSE_BIT_2)) {
/* set to suspended duplex state then restart */ /* 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", DEV_MESSAGE(KERN_ERR, device, "%s",
"Set device to suspended duplex state should be " "Set device to suspended duplex state should be "
...@@ -2081,28 +1971,26 @@ dasd_3990_erp_compound(struct dasd_ccw_req * erp, char *sense) ...@@ -2081,28 +1971,26 @@ dasd_3990_erp_compound(struct dasd_ccw_req * erp, char *sense)
{ {
if ((erp->function == dasd_3990_erp_compound_retry) && 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); dasd_3990_erp_compound_path(erp, sense);
} }
if ((erp->function == dasd_3990_erp_compound_path) && 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); erp = dasd_3990_erp_compound_code(erp, sense);
} }
if ((erp->function == dasd_3990_erp_compound_code) && 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); dasd_3990_erp_compound_config(erp, sense);
} }
/* if no compound action ERP specified, the request failed */ /* 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; erp->status = DASD_CQR_FAILED;
}
return erp; return erp;
...@@ -2127,7 +2015,7 @@ static struct dasd_ccw_req * ...@@ -2127,7 +2015,7 @@ static struct dasd_ccw_req *
dasd_3990_erp_inspect_32(struct dasd_ccw_req * erp, char *sense) 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; erp->function = dasd_3990_erp_inspect_32;
...@@ -2149,8 +2037,7 @@ dasd_3990_erp_inspect_32(struct dasd_ccw_req * erp, char *sense) ...@@ -2149,8 +2037,7 @@ dasd_3990_erp_inspect_32(struct dasd_ccw_req * erp, char *sense)
case 0x01: /* fatal error */ case 0x01: /* fatal error */
DEV_MESSAGE(KERN_ERR, device, "%s", DEV_MESSAGE(KERN_ERR, device, "%s",
"Fatal error should have been " "Retry not recommended - Fatal error");
"handled within the interrupt handler");
erp = dasd_3990_erp_cleanup(erp, DASD_CQR_FAILED); erp = dasd_3990_erp_cleanup(erp, DASD_CQR_FAILED);
break; break;
...@@ -2253,6 +2140,11 @@ dasd_3990_erp_inspect(struct dasd_ccw_req * erp) ...@@ -2253,6 +2140,11 @@ dasd_3990_erp_inspect(struct dasd_ccw_req * erp)
/* already set up new ERP ! */ /* already set up new ERP ! */
char *sense = erp->refers->irb.ecw; 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 */ /* distinguish between 24 and 32 byte sense data */
if (sense[27] & DASD_SENSE_BIT_0) { if (sense[27] & DASD_SENSE_BIT_0) {
...@@ -2287,13 +2179,13 @@ static struct dasd_ccw_req * ...@@ -2287,13 +2179,13 @@ static struct dasd_ccw_req *
dasd_3990_erp_add_erp(struct dasd_ccw_req * cqr) 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; struct ccw1 *ccw;
/* allocate additional request block */ /* allocate additional request block */
struct dasd_ccw_req *erp; 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 (IS_ERR(erp)) {
if (cqr->retries <= 0) { if (cqr->retries <= 0) {
DEV_MESSAGE(KERN_ERR, device, "%s", DEV_MESSAGE(KERN_ERR, device, "%s",
...@@ -2305,7 +2197,7 @@ dasd_3990_erp_add_erp(struct dasd_ccw_req * cqr) ...@@ -2305,7 +2197,7 @@ dasd_3990_erp_add_erp(struct dasd_ccw_req * cqr)
"Unable to allocate ERP request " "Unable to allocate ERP request "
"(%i retries left)", "(%i retries left)",
cqr->retries); cqr->retries);
dasd_set_timer(device, (HZ << 3)); dasd_block_set_timer(device->block, (HZ << 3));
} }
return cqr; return cqr;
} }
...@@ -2319,7 +2211,9 @@ dasd_3990_erp_add_erp(struct dasd_ccw_req * cqr) ...@@ -2319,7 +2211,9 @@ dasd_3990_erp_add_erp(struct dasd_ccw_req * cqr)
ccw->cda = (long)(cqr->cpaddr); ccw->cda = (long)(cqr->cpaddr);
erp->function = dasd_3990_erp_add_erp; erp->function = dasd_3990_erp_add_erp;
erp->refers = cqr; erp->refers = cqr;
erp->device = cqr->device; erp->startdev = device;
erp->memdev = device;
erp->block = cqr->block;
erp->magic = cqr->magic; erp->magic = cqr->magic;
erp->expires = 0; erp->expires = 0;
erp->retries = 256; erp->retries = 256;
...@@ -2466,7 +2360,7 @@ static struct dasd_ccw_req * ...@@ -2466,7 +2360,7 @@ static struct dasd_ccw_req *
dasd_3990_erp_further_erp(struct dasd_ccw_req *erp) 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; char *sense = erp->irb.ecw;
/* check for 24 byte sense ERP */ /* check for 24 byte sense ERP */
...@@ -2557,7 +2451,7 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head, ...@@ -2557,7 +2451,7 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head,
struct dasd_ccw_req *erp) struct dasd_ccw_req *erp)
{ {
struct dasd_device *device = erp_head->device; struct dasd_device *device = erp_head->startdev;
struct dasd_ccw_req *erp_done = erp_head; /* finished req */ struct dasd_ccw_req *erp_done = erp_head; /* finished req */
struct dasd_ccw_req *erp_free = NULL; /* req to be freed */ 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, ...@@ -2569,13 +2463,13 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head,
"original request was lost\n"); "original request was lost\n");
/* remove the request from the device queue */ /* remove the request from the device queue */
list_del(&erp_done->list); list_del(&erp_done->blocklist);
erp_free = erp_done; erp_free = erp_done;
erp_done = erp_done->refers; erp_done = erp_done->refers;
/* free the finished erp request */ /* 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 */ } /* end while */
...@@ -2603,7 +2497,7 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head, ...@@ -2603,7 +2497,7 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head,
erp->retries, erp); erp->retries, erp);
/* handle the request again... */ /* handle the request again... */
erp->status = DASD_CQR_QUEUED; erp->status = DASD_CQR_FILLED;
} }
} else { } else {
...@@ -2636,9 +2530,8 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head, ...@@ -2636,9 +2530,8 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head,
struct dasd_ccw_req * struct dasd_ccw_req *
dasd_3990_erp_action(struct dasd_ccw_req * cqr) dasd_3990_erp_action(struct dasd_ccw_req * cqr)
{ {
struct dasd_ccw_req *erp = NULL; 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; struct dasd_ccw_req *temp_erp = NULL;
if (device->features & DASD_FEATURE_ERPLOG) { if (device->features & DASD_FEATURE_ERPLOG) {
...@@ -2704,10 +2597,11 @@ dasd_3990_erp_action(struct dasd_ccw_req * cqr) ...@@ -2704,10 +2597,11 @@ dasd_3990_erp_action(struct dasd_ccw_req * cqr)
} }
} }
/* enqueue added ERP request */ /* enqueue ERP request if it's a new one */
if (erp->status == DASD_CQR_FILLED) { if (list_empty(&erp->blocklist)) {
erp->status = DASD_CQR_QUEUED; cqr->status = DASD_CQR_IN_ERP;
list_add(&erp->list, &device->ccw_queue); /* add erp request before the cqr */
list_add_tail(&erp->blocklist, &cqr->blocklist);
} }
return erp; return erp;
......
/*
* 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 */
/*
* 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;
}
/*
* 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);
};
...@@ -48,22 +48,6 @@ struct dasd_devmap { ...@@ -48,22 +48,6 @@ struct dasd_devmap {
struct dasd_uid uid; struct dasd_uid uid;
}; };
/*
* dasd_server_ssid_map contains a globally unique storage server subsystem ID.
* dasd_server_ssid_list contains the list of all subsystem IDs accessed by
* the DASD device driver.
*/
struct dasd_server_ssid_map {
struct list_head list;
struct system_id {
char vendor[4];
char serial[15];
__u16 ssid;
} sid;
};
static struct list_head dasd_server_ssid_list;
/* /*
* Parameter parsing functions for dasd= parameter. The syntax is: * Parameter parsing functions for dasd= parameter. The syntax is:
* <devno> : (0x)?[0-9a-fA-F]+ * <devno> : (0x)?[0-9a-fA-F]+
...@@ -721,8 +705,9 @@ dasd_ro_store(struct device *dev, struct device_attribute *attr, ...@@ -721,8 +705,9 @@ dasd_ro_store(struct device *dev, struct device_attribute *attr,
devmap->features &= ~DASD_FEATURE_READONLY; devmap->features &= ~DASD_FEATURE_READONLY;
if (devmap->device) if (devmap->device)
devmap->device->features = devmap->features; devmap->device->features = devmap->features;
if (devmap->device && devmap->device->gdp) if (devmap->device && devmap->device->block
set_disk_ro(devmap->device->gdp, val); && devmap->device->block->gdp)
set_disk_ro(devmap->device->block->gdp, val);
spin_unlock(&dasd_devmap_lock); spin_unlock(&dasd_devmap_lock);
return count; return count;
} }
...@@ -893,12 +878,16 @@ dasd_alias_show(struct device *dev, struct device_attribute *attr, char *buf) ...@@ -893,12 +878,16 @@ dasd_alias_show(struct device *dev, struct device_attribute *attr, char *buf)
devmap = dasd_find_busid(dev->bus_id); devmap = dasd_find_busid(dev->bus_id);
spin_lock(&dasd_devmap_lock); spin_lock(&dasd_devmap_lock);
if (!IS_ERR(devmap)) if (IS_ERR(devmap) || strlen(devmap->uid.vendor) == 0) {
alias = devmap->uid.alias; 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 else
alias = 0; alias = 0;
spin_unlock(&dasd_devmap_lock); spin_unlock(&dasd_devmap_lock);
return sprintf(buf, alias ? "1\n" : "0\n"); return sprintf(buf, alias ? "1\n" : "0\n");
} }
...@@ -930,19 +919,36 @@ static ssize_t ...@@ -930,19 +919,36 @@ static ssize_t
dasd_uid_show(struct device *dev, struct device_attribute *attr, char *buf) dasd_uid_show(struct device *dev, struct device_attribute *attr, char *buf)
{ {
struct dasd_devmap *devmap; 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); devmap = dasd_find_busid(dev->bus_id);
spin_lock(&dasd_devmap_lock); spin_lock(&dasd_devmap_lock);
if (!IS_ERR(devmap) && strlen(devmap->uid.vendor) > 0) if (IS_ERR(devmap) || strlen(devmap->uid.vendor) == 0) {
snprintf(uid, sizeof(uid), "%s.%s.%04x.%02x", spin_unlock(&dasd_devmap_lock);
devmap->uid.vendor, devmap->uid.serial, return sprintf(buf, "\n");
devmap->uid.ssid, devmap->uid.unit_addr); }
else uid = &devmap->uid;
uid[0] = 0; 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); spin_unlock(&dasd_devmap_lock);
return snprintf(buf, PAGE_SIZE, "%s\n", uid_string);
return snprintf(buf, PAGE_SIZE, "%s\n", uid);
} }
static DEVICE_ATTR(uid, 0444, dasd_uid_show, NULL); static DEVICE_ATTR(uid, 0444, dasd_uid_show, NULL);
...@@ -1040,39 +1046,16 @@ int ...@@ -1040,39 +1046,16 @@ int
dasd_set_uid(struct ccw_device *cdev, struct dasd_uid *uid) dasd_set_uid(struct ccw_device *cdev, struct dasd_uid *uid)
{ {
struct dasd_devmap *devmap; struct dasd_devmap *devmap;
struct dasd_server_ssid_map *srv, *tmp;
devmap = dasd_find_busid(cdev->dev.bus_id); devmap = dasd_find_busid(cdev->dev.bus_id);
if (IS_ERR(devmap)) if (IS_ERR(devmap))
return PTR_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); spin_lock(&dasd_devmap_lock);
devmap->uid = *uid; 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); spin_unlock(&dasd_devmap_lock);
return (srv ? 1 : 0); return 0;
} }
EXPORT_SYMBOL_GPL(dasd_set_uid); EXPORT_SYMBOL_GPL(dasd_set_uid);
...@@ -1138,9 +1121,6 @@ dasd_devmap_init(void) ...@@ -1138,9 +1121,6 @@ dasd_devmap_init(void)
dasd_max_devindex = 0; dasd_max_devindex = 0;
for (i = 0; i < 256; i++) for (i = 0; i < 256; i++)
INIT_LIST_HEAD(&dasd_hashlists[i]); INIT_LIST_HEAD(&dasd_hashlists[i]);
/* Initialize servermap structure. */
INIT_LIST_HEAD(&dasd_server_ssid_list);
return 0; return 0;
} }
......
...@@ -142,7 +142,7 @@ dasd_diag_erp(struct dasd_device *device) ...@@ -142,7 +142,7 @@ dasd_diag_erp(struct dasd_device *device)
int rc; int rc;
mdsk_term_io(device); 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) if (rc)
DEV_MESSAGE(KERN_WARNING, device, "DIAG ERP unsuccessful, " DEV_MESSAGE(KERN_WARNING, device, "DIAG ERP unsuccessful, "
"rc=%d", rc); "rc=%d", rc);
...@@ -158,11 +158,11 @@ dasd_start_diag(struct dasd_ccw_req * cqr) ...@@ -158,11 +158,11 @@ dasd_start_diag(struct dasd_ccw_req * cqr)
struct dasd_diag_req *dreq; struct dasd_diag_req *dreq;
int rc; int rc;
device = cqr->device; device = cqr->startdev;
if (cqr->retries < 0) { if (cqr->retries < 0) {
DEV_MESSAGE(KERN_WARNING, device, "DIAG start_IO: request %p " DEV_MESSAGE(KERN_WARNING, device, "DIAG start_IO: request %p "
"- no retry left)", cqr); "- no retry left)", cqr);
cqr->status = DASD_CQR_FAILED; cqr->status = DASD_CQR_ERROR;
return -EIO; return -EIO;
} }
private = (struct dasd_diag_private *) device->private; private = (struct dasd_diag_private *) device->private;
...@@ -184,7 +184,7 @@ dasd_start_diag(struct dasd_ccw_req * cqr) ...@@ -184,7 +184,7 @@ dasd_start_diag(struct dasd_ccw_req * cqr)
switch (rc) { switch (rc) {
case 0: /* Synchronous I/O finished successfully */ case 0: /* Synchronous I/O finished successfully */
cqr->stopclk = get_clock(); cqr->stopclk = get_clock();
cqr->status = DASD_CQR_DONE; cqr->status = DASD_CQR_SUCCESS;
/* Indicate to calling function that only a dasd_schedule_bh() /* Indicate to calling function that only a dasd_schedule_bh()
and no timer is needed */ and no timer is needed */
rc = -EACCES; rc = -EACCES;
...@@ -209,12 +209,12 @@ dasd_diag_term_IO(struct dasd_ccw_req * cqr) ...@@ -209,12 +209,12 @@ dasd_diag_term_IO(struct dasd_ccw_req * cqr)
{ {
struct dasd_device *device; struct dasd_device *device;
device = cqr->device; device = cqr->startdev;
mdsk_term_io(device); mdsk_term_io(device);
mdsk_init_io(device, device->bp_block, 0, NULL); mdsk_init_io(device, device->block->bp_block, 0, NULL);
cqr->status = DASD_CQR_CLEAR; cqr->status = DASD_CQR_CLEAR_PENDING;
cqr->stopclk = get_clock(); cqr->stopclk = get_clock();
dasd_schedule_bh(device); dasd_schedule_device_bh(device);
return 0; return 0;
} }
...@@ -247,7 +247,7 @@ dasd_ext_handler(__u16 code) ...@@ -247,7 +247,7 @@ dasd_ext_handler(__u16 code)
return; return;
} }
cqr = (struct dasd_ccw_req *) ip; 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)) { if (strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) {
DEV_MESSAGE(KERN_WARNING, device, DEV_MESSAGE(KERN_WARNING, device,
" magic number of dasd_ccw_req 0x%08X doesn't" " magic number of dasd_ccw_req 0x%08X doesn't"
...@@ -260,10 +260,10 @@ dasd_ext_handler(__u16 code) ...@@ -260,10 +260,10 @@ dasd_ext_handler(__u16 code)
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
/* Check for a pending clear operation */ /* Check for a pending clear operation */
if (cqr->status == DASD_CQR_CLEAR) { if (cqr->status == DASD_CQR_CLEAR_PENDING) {
cqr->status = DASD_CQR_QUEUED; cqr->status = DASD_CQR_CLEARED;
dasd_clear_timer(device); dasd_device_clear_timer(device);
dasd_schedule_bh(device); dasd_schedule_device_bh(device);
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
return; return;
} }
...@@ -272,11 +272,11 @@ dasd_ext_handler(__u16 code) ...@@ -272,11 +272,11 @@ dasd_ext_handler(__u16 code)
expires = 0; expires = 0;
if (status == 0) { if (status == 0) {
cqr->status = DASD_CQR_DONE; cqr->status = DASD_CQR_SUCCESS;
/* Start first request on queue if possible -> fast_io. */ /* Start first request on queue if possible -> fast_io. */
if (!list_empty(&device->ccw_queue)) { if (!list_empty(&device->ccw_queue)) {
next = list_entry(device->ccw_queue.next, next = list_entry(device->ccw_queue.next,
struct dasd_ccw_req, list); struct dasd_ccw_req, devlist);
if (next->status == DASD_CQR_QUEUED) { if (next->status == DASD_CQR_QUEUED) {
rc = dasd_start_diag(next); rc = dasd_start_diag(next);
if (rc == 0) if (rc == 0)
...@@ -296,10 +296,10 @@ dasd_ext_handler(__u16 code) ...@@ -296,10 +296,10 @@ dasd_ext_handler(__u16 code)
} }
if (expires != 0) if (expires != 0)
dasd_set_timer(device, expires); dasd_device_set_timer(device, expires);
else else
dasd_clear_timer(device); dasd_device_clear_timer(device);
dasd_schedule_bh(device); dasd_schedule_device_bh(device);
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
} }
...@@ -309,6 +309,7 @@ dasd_ext_handler(__u16 code) ...@@ -309,6 +309,7 @@ dasd_ext_handler(__u16 code)
static int static int
dasd_diag_check_device(struct dasd_device *device) dasd_diag_check_device(struct dasd_device *device)
{ {
struct dasd_block *block;
struct dasd_diag_private *private; struct dasd_diag_private *private;
struct dasd_diag_characteristics *rdc_data; struct dasd_diag_characteristics *rdc_data;
struct dasd_diag_bio bio; struct dasd_diag_bio bio;
...@@ -328,6 +329,16 @@ dasd_diag_check_device(struct dasd_device *device) ...@@ -328,6 +329,16 @@ dasd_diag_check_device(struct dasd_device *device)
ccw_device_get_id(device->cdev, &private->dev_id); ccw_device_get_id(device->cdev, &private->dev_id);
device->private = (void *) private; 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 */ /* Read Device Characteristics */
rdc_data = (void *) &(private->rdc_data); rdc_data = (void *) &(private->rdc_data);
rdc_data->dev_nr = private->dev_id.devno; rdc_data->dev_nr = private->dev_id.devno;
...@@ -409,14 +420,14 @@ dasd_diag_check_device(struct dasd_device *device) ...@@ -409,14 +420,14 @@ dasd_diag_check_device(struct dasd_device *device)
sizeof(DASD_DIAG_CMS1)) == 0) { sizeof(DASD_DIAG_CMS1)) == 0) {
/* get formatted blocksize from label block */ /* get formatted blocksize from label block */
bsize = (unsigned int) label->block_size; bsize = (unsigned int) label->block_size;
device->blocks = (unsigned long) label->block_count; block->blocks = (unsigned long) label->block_count;
} else } else
device->blocks = end_block; block->blocks = end_block;
device->bp_block = bsize; block->bp_block = bsize;
device->s2b_shift = 0; /* bits to shift 512 to get a block */ block->s2b_shift = 0; /* bits to shift 512 to get a block */
for (sb = 512; sb < bsize; sb = sb << 1) for (sb = 512; sb < bsize; sb = sb << 1)
device->s2b_shift++; block->s2b_shift++;
rc = mdsk_init_io(device, device->bp_block, 0, NULL); rc = mdsk_init_io(device, block->bp_block, 0, NULL);
if (rc) { if (rc) {
DEV_MESSAGE(KERN_WARNING, device, "DIAG initialization " DEV_MESSAGE(KERN_WARNING, device, "DIAG initialization "
"failed (rc=%d)", rc); "failed (rc=%d)", rc);
...@@ -424,9 +435,9 @@ dasd_diag_check_device(struct dasd_device *device) ...@@ -424,9 +435,9 @@ dasd_diag_check_device(struct dasd_device *device)
} else { } else {
DEV_MESSAGE(KERN_INFO, device, DEV_MESSAGE(KERN_INFO, device,
"(%ld B/blk): %ldkB", "(%ld B/blk): %ldkB",
(unsigned long) device->bp_block, (unsigned long) block->bp_block,
(unsigned long) (device->blocks << (unsigned long) (block->blocks <<
device->s2b_shift) >> 1); block->s2b_shift) >> 1);
} }
out: out:
free_page((long) label); free_page((long) label);
...@@ -436,22 +447,16 @@ dasd_diag_check_device(struct dasd_device *device) ...@@ -436,22 +447,16 @@ dasd_diag_check_device(struct dasd_device *device)
/* Fill in virtual disk geometry for device. Return zero on success, non-zero /* Fill in virtual disk geometry for device. Return zero on success, non-zero
* otherwise. */ * otherwise. */
static int 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; return -EINVAL;
geo->cylinders = (device->blocks << device->s2b_shift) >> 10; geo->cylinders = (block->blocks << block->s2b_shift) >> 10;
geo->heads = 16; geo->heads = 16;
geo->sectors = 128 >> device->s2b_shift; geo->sectors = 128 >> block->s2b_shift;
return 0; 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 static dasd_erp_fn_t
dasd_diag_erp_action(struct dasd_ccw_req * cqr) dasd_diag_erp_action(struct dasd_ccw_req * cqr)
{ {
...@@ -466,8 +471,9 @@ dasd_diag_erp_postaction(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 /* Create DASD request from block device request. Return pointer to new
* request on success, ERR_PTR otherwise. */ * request on success, ERR_PTR otherwise. */
static struct dasd_ccw_req * static struct dasd_ccw_req *dasd_diag_build_cp(struct dasd_device *memdev,
dasd_diag_build_cp(struct dasd_device * device, struct request *req) struct dasd_block *block,
struct request *req)
{ {
struct dasd_ccw_req *cqr; struct dasd_ccw_req *cqr;
struct dasd_diag_req *dreq; struct dasd_diag_req *dreq;
...@@ -486,17 +492,17 @@ dasd_diag_build_cp(struct dasd_device * device, struct request *req) ...@@ -486,17 +492,17 @@ dasd_diag_build_cp(struct dasd_device * device, struct request *req)
rw_cmd = MDSK_WRITE_REQ; rw_cmd = MDSK_WRITE_REQ;
else else
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
blksize = device->bp_block; blksize = block->bp_block;
/* Calculate record id of first and last block. */ /* Calculate record id of first and last block. */
first_rec = req->sector >> device->s2b_shift; first_rec = req->sector >> block->s2b_shift;
last_rec = (req->sector + req->nr_sectors - 1) >> device->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. */ /* Check struct bio and count the number of blocks for the request. */
count = 0; count = 0;
rq_for_each_segment(bv, req, iter) { rq_for_each_segment(bv, req, iter) {
if (bv->bv_len & (blksize - 1)) if (bv->bv_len & (blksize - 1))
/* Fba can only do full blocks. */ /* Fba can only do full blocks. */
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
count += bv->bv_len >> (device->s2b_shift + 9); count += bv->bv_len >> (block->s2b_shift + 9);
} }
/* Paranoia. */ /* Paranoia. */
if (count != last_rec - first_rec + 1) if (count != last_rec - first_rec + 1)
...@@ -505,7 +511,7 @@ dasd_diag_build_cp(struct dasd_device * device, struct request *req) ...@@ -505,7 +511,7 @@ dasd_diag_build_cp(struct dasd_device * device, struct request *req)
datasize = sizeof(struct dasd_diag_req) + datasize = sizeof(struct dasd_diag_req) +
count*sizeof(struct dasd_diag_bio); count*sizeof(struct dasd_diag_bio);
cqr = dasd_smalloc_request(dasd_diag_discipline.name, 0, cqr = dasd_smalloc_request(dasd_diag_discipline.name, 0,
datasize, device); datasize, memdev);
if (IS_ERR(cqr)) if (IS_ERR(cqr))
return cqr; return cqr;
...@@ -529,7 +535,9 @@ dasd_diag_build_cp(struct dasd_device * device, struct request *req) ...@@ -529,7 +535,9 @@ dasd_diag_build_cp(struct dasd_device * device, struct request *req)
cqr->buildclk = get_clock(); cqr->buildclk = get_clock();
if (req->cmd_flags & REQ_FAILFAST) if (req->cmd_flags & REQ_FAILFAST)
set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); 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->expires = DIAG_TIMEOUT;
cqr->status = DASD_CQR_FILLED; cqr->status = DASD_CQR_FILLED;
return cqr; return cqr;
...@@ -543,10 +551,15 @@ dasd_diag_free_cp(struct dasd_ccw_req *cqr, struct request *req) ...@@ -543,10 +551,15 @@ dasd_diag_free_cp(struct dasd_ccw_req *cqr, struct request *req)
int status; int status;
status = cqr->status == DASD_CQR_DONE; status = cqr->status == DASD_CQR_DONE;
dasd_sfree_request(cqr, cqr->device); dasd_sfree_request(cqr, cqr->memdev);
return status; 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. */ /* Fill in IOCTL data for device. */
static int static int
dasd_diag_fill_info(struct dasd_device * device, dasd_diag_fill_info(struct dasd_device * device,
...@@ -583,7 +596,7 @@ static struct dasd_discipline dasd_diag_discipline = { ...@@ -583,7 +596,7 @@ static struct dasd_discipline dasd_diag_discipline = {
.fill_geometry = dasd_diag_fill_geometry, .fill_geometry = dasd_diag_fill_geometry,
.start_IO = dasd_start_diag, .start_IO = dasd_start_diag,
.term_IO = dasd_diag_term_IO, .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_action = dasd_diag_erp_action,
.erp_postaction = dasd_diag_erp_postaction, .erp_postaction = dasd_diag_erp_postaction,
.build_cp = dasd_diag_build_cp, .build_cp = dasd_diag_build_cp,
......
...@@ -52,16 +52,6 @@ MODULE_LICENSE("GPL"); ...@@ -52,16 +52,6 @@ MODULE_LICENSE("GPL");
static struct dasd_discipline dasd_eckd_discipline; 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 /* The ccw bus type uses this table to find devices that it sends to
* dasd_eckd_probe */ * dasd_eckd_probe */
static struct ccw_device_id dasd_eckd_ids[] = { static struct ccw_device_id dasd_eckd_ids[] = {
...@@ -188,7 +178,7 @@ check_XRC (struct ccw1 *de_ccw, ...@@ -188,7 +178,7 @@ check_XRC (struct ccw1 *de_ccw,
if (rc == -ENOSYS || rc == -EACCES) if (rc == -ENOSYS || rc == -EACCES)
rc = 0; rc = 0;
de_ccw->count = sizeof (struct DE_eckd_data); de_ccw->count = sizeof(struct DE_eckd_data);
de_ccw->flags |= CCW_FLAG_SLI; de_ccw->flags |= CCW_FLAG_SLI;
return rc; return rc;
} }
...@@ -208,7 +198,7 @@ define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk, ...@@ -208,7 +198,7 @@ define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk,
ccw->count = 16; ccw->count = 16;
ccw->cda = (__u32) __pa(data); ccw->cda = (__u32) __pa(data);
memset(data, 0, sizeof (struct DE_eckd_data)); memset(data, 0, sizeof(struct DE_eckd_data));
switch (cmd) { switch (cmd) {
case DASD_ECKD_CCW_READ_HOME_ADDRESS: case DASD_ECKD_CCW_READ_HOME_ADDRESS:
case DASD_ECKD_CCW_READ_RECORD_ZERO: case DASD_ECKD_CCW_READ_RECORD_ZERO:
...@@ -280,6 +270,132 @@ define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk, ...@@ -280,6 +270,132 @@ define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk,
return rc; 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 static void
locate_record(struct ccw1 *ccw, struct LO_eckd_data *data, int trk, locate_record(struct ccw1 *ccw, struct LO_eckd_data *data, int trk,
int rec_on_trk, int no_rec, int cmd, 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, ...@@ -300,7 +416,7 @@ locate_record(struct ccw1 *ccw, struct LO_eckd_data *data, int trk,
ccw->count = 16; ccw->count = 16;
ccw->cda = (__u32) __pa(data); ccw->cda = (__u32) __pa(data);
memset(data, 0, sizeof (struct LO_eckd_data)); memset(data, 0, sizeof(struct LO_eckd_data));
sector = 0; sector = 0;
if (rec_on_trk) { if (rec_on_trk) {
switch (private->rdc_data.dev_type) { switch (private->rdc_data.dev_type) {
...@@ -441,12 +557,15 @@ dasd_eckd_generate_uid(struct dasd_device *device, struct dasd_uid *uid) ...@@ -441,12 +557,15 @@ dasd_eckd_generate_uid(struct dasd_device *device, struct dasd_uid *uid)
sizeof(uid->serial) - 1); sizeof(uid->serial) - 1);
EBCASC(uid->serial, sizeof(uid->serial) - 1); EBCASC(uid->serial, sizeof(uid->serial) - 1);
uid->ssid = confdata->neq.subsystemID; uid->ssid = confdata->neq.subsystemID;
if (confdata->ned2.sneq.flags == 0x40) { uid->real_unit_addr = confdata->ned1.unit_addr;
uid->alias = 1; if (confdata->ned2.sneq.flags == 0x40 &&
uid->unit_addr = confdata->ned2.sneq.base_unit_addr; confdata->ned2.sneq.format == 0x0001) {
} else uid->type = confdata->ned2.sneq.sua_flags;
uid->unit_addr = confdata->ned1.unit_addr; 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; return 0;
} }
...@@ -470,7 +589,9 @@ static struct dasd_ccw_req *dasd_eckd_build_rcd_lpm(struct dasd_device *device, ...@@ -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->cda = (__u32)(addr_t)rcd_buffer;
ccw->count = ciw->count; ccw->count = ciw->count;
cqr->device = device; cqr->startdev = device;
cqr->memdev = device;
cqr->block = NULL;
cqr->expires = 10*HZ; cqr->expires = 10*HZ;
cqr->lpm = lpm; cqr->lpm = lpm;
clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
...@@ -511,7 +632,7 @@ static int dasd_eckd_read_conf_lpm(struct dasd_device *device, ...@@ -511,7 +632,7 @@ static int dasd_eckd_read_conf_lpm(struct dasd_device *device,
/* /*
* on success we update the user input parms * on success we update the user input parms
*/ */
dasd_sfree_request(cqr, cqr->device); dasd_sfree_request(cqr, cqr->memdev);
if (ret) if (ret)
goto out_error; goto out_error;
...@@ -557,19 +678,19 @@ dasd_eckd_read_conf(struct dasd_device *device) ...@@ -557,19 +678,19 @@ dasd_eckd_read_conf(struct dasd_device *device)
"data retrieved"); "data retrieved");
continue; /* no error */ continue; /* no error */
} }
if (conf_len != sizeof (struct dasd_eckd_confdata)) { if (conf_len != sizeof(struct dasd_eckd_confdata)) {
MESSAGE(KERN_WARNING, MESSAGE(KERN_WARNING,
"sizes of configuration data mismatch" "sizes of configuration data mismatch"
"%d (read) vs %ld (expected)", "%d (read) vs %ld (expected)",
conf_len, conf_len,
sizeof (struct dasd_eckd_confdata)); sizeof(struct dasd_eckd_confdata));
kfree(conf_data); kfree(conf_data);
continue; /* no error */ continue; /* no error */
} }
/* save first valid configuration data */ /* save first valid configuration data */
if (!conf_data_saved){ if (!conf_data_saved){
memcpy(&private->conf_data, conf_data, memcpy(&private->conf_data, conf_data,
sizeof (struct dasd_eckd_confdata)); sizeof(struct dasd_eckd_confdata));
conf_data_saved++; conf_data_saved++;
} }
switch (((char *)conf_data)[242] & 0x07){ switch (((char *)conf_data)[242] & 0x07){
...@@ -586,39 +707,104 @@ dasd_eckd_read_conf(struct dasd_device *device) ...@@ -586,39 +707,104 @@ dasd_eckd_read_conf(struct dasd_device *device)
return 0; 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. * Build CP for Perform Subsystem Function - SSC.
*/ */
static struct dasd_ccw_req * static struct dasd_ccw_req *dasd_eckd_build_psf_ssc(struct dasd_device *device)
dasd_eckd_build_psf_ssc(struct dasd_device *device)
{ {
struct dasd_ccw_req *cqr; struct dasd_ccw_req *cqr;
struct dasd_psf_ssc_data *psf_ssc_data; struct dasd_psf_ssc_data *psf_ssc_data;
struct ccw1 *ccw; struct ccw1 *ccw;
cqr = dasd_smalloc_request("ECKD", 1 /* PSF */ , cqr = dasd_smalloc_request("ECKD", 1 /* PSF */ ,
sizeof(struct dasd_psf_ssc_data), sizeof(struct dasd_psf_ssc_data),
device); device);
if (IS_ERR(cqr)) { if (IS_ERR(cqr)) {
DEV_MESSAGE(KERN_WARNING, device, "%s", DEV_MESSAGE(KERN_WARNING, device, "%s",
"Could not allocate PSF-SSC request"); "Could not allocate PSF-SSC request");
return cqr; return cqr;
} }
psf_ssc_data = (struct dasd_psf_ssc_data *)cqr->data; psf_ssc_data = (struct dasd_psf_ssc_data *)cqr->data;
psf_ssc_data->order = PSF_ORDER_SSC; psf_ssc_data->order = PSF_ORDER_SSC;
psf_ssc_data->suborder = 0x08; psf_ssc_data->suborder = 0x88;
psf_ssc_data->reserved[0] = 0x88;
ccw = cqr->cpaddr;
ccw->cmd_code = DASD_ECKD_CCW_PSF; ccw = cqr->cpaddr;
ccw->cda = (__u32)(addr_t)psf_ssc_data; ccw->cmd_code = DASD_ECKD_CCW_PSF;
ccw->count = 66; ccw->cda = (__u32)(addr_t)psf_ssc_data;
ccw->count = 66;
cqr->device = device;
cqr->expires = 10*HZ; cqr->startdev = device;
cqr->buildclk = get_clock(); cqr->memdev = device;
cqr->status = DASD_CQR_FILLED; cqr->block = NULL;
return cqr; 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) ...@@ -629,28 +815,28 @@ dasd_eckd_build_psf_ssc(struct dasd_device *device)
static int static int
dasd_eckd_psf_ssc(struct dasd_device *device) dasd_eckd_psf_ssc(struct dasd_device *device)
{ {
struct dasd_ccw_req *cqr; struct dasd_ccw_req *cqr;
int rc; int rc;
cqr = dasd_eckd_build_psf_ssc(device); cqr = dasd_eckd_build_psf_ssc(device);
if (IS_ERR(cqr)) if (IS_ERR(cqr))
return PTR_ERR(cqr); return PTR_ERR(cqr);
rc = dasd_sleep_on(cqr); rc = dasd_sleep_on(cqr);
if (!rc) if (!rc)
/* trigger CIO to reprobe devices */ /* trigger CIO to reprobe devices */
css_schedule_reprobe(); css_schedule_reprobe();
dasd_sfree_request(cqr, cqr->device); dasd_sfree_request(cqr, cqr->memdev);
return rc; return rc;
} }
/* /*
* Valide storage server of current device. * Valide storage server of current device.
*/ */
static int static int dasd_eckd_validate_server(struct dasd_device *device)
dasd_eckd_validate_server(struct dasd_device *device, struct dasd_uid *uid)
{ {
int rc; int rc;
struct dasd_eckd_private *private;
/* Currently PAV is the only reason to 'validate' server on LPAR */ /* Currently PAV is the only reason to 'validate' server on LPAR */
if (dasd_nopav || MACHINE_IS_VM) if (dasd_nopav || MACHINE_IS_VM)
...@@ -659,9 +845,11 @@ dasd_eckd_validate_server(struct dasd_device *device, struct dasd_uid *uid) ...@@ -659,9 +845,11 @@ dasd_eckd_validate_server(struct dasd_device *device, struct dasd_uid *uid)
rc = dasd_eckd_psf_ssc(device); rc = dasd_eckd_psf_ssc(device);
/* may be requested feature is not available on server, /* may be requested feature is not available on server,
* therefore just report error and go ahead */ * therefore just report error and go ahead */
private = (struct dasd_eckd_private *) device->private;
DEV_MESSAGE(KERN_INFO, device, DEV_MESSAGE(KERN_INFO, device,
"PSF-SSC on storage subsystem %s.%s.%04x returned rc=%d", "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 */ /* RE-Read Configuration Data */
return dasd_eckd_read_conf(device); return dasd_eckd_read_conf(device);
} }
...@@ -674,9 +862,9 @@ static int ...@@ -674,9 +862,9 @@ static int
dasd_eckd_check_characteristics(struct dasd_device *device) dasd_eckd_check_characteristics(struct dasd_device *device)
{ {
struct dasd_eckd_private *private; struct dasd_eckd_private *private;
struct dasd_uid uid; struct dasd_block *block;
void *rdc_data; void *rdc_data;
int rc; int is_known, rc;
private = (struct dasd_eckd_private *) device->private; private = (struct dasd_eckd_private *) device->private;
if (private == NULL) { if (private == NULL) {
...@@ -699,27 +887,54 @@ dasd_eckd_check_characteristics(struct dasd_device *device) ...@@ -699,27 +887,54 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
/* Read Configuration Data */ /* Read Configuration Data */
rc = dasd_eckd_read_conf(device); rc = dasd_eckd_read_conf(device);
if (rc) if (rc)
return rc; goto out_err1;
/* Generate device unique id and register in devmap */ /* 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) if (rc)
return rc; goto out_err1;
rc = dasd_set_uid(device->cdev, &uid); dasd_set_uid(device->cdev, &private->uid);
if (rc == 1) /* new server found */
rc = dasd_eckd_validate_server(device, &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) if (rc)
return rc; goto out_err3;
/* Read Device Characteristics */ /* Read Device Characteristics */
rdc_data = (void *) &(private->rdc_data); rdc_data = (void *) &(private->rdc_data);
memset(rdc_data, 0, sizeof(rdc_data)); memset(rdc_data, 0, sizeof(rdc_data));
rc = dasd_generic_read_dev_chars(device, "ECKD", &rdc_data, 64); rc = dasd_generic_read_dev_chars(device, "ECKD", &rdc_data, 64);
if (rc) if (rc) {
DEV_MESSAGE(KERN_WARNING, device, DEV_MESSAGE(KERN_WARNING, device,
"Read device characteristics returned " "Read device characteristics returned "
"rc=%d", rc); "rc=%d", rc);
goto out_err3;
}
DEV_MESSAGE(KERN_INFO, device, DEV_MESSAGE(KERN_INFO, device,
"%04X/%02X(CU:%04X/%02X) Cyl:%d Head:%d Sec:%d", "%04X/%02X(CU:%04X/%02X) Cyl:%d Head:%d Sec:%d",
private->rdc_data.dev_type, private->rdc_data.dev_type,
...@@ -729,9 +944,24 @@ dasd_eckd_check_characteristics(struct dasd_device *device) ...@@ -729,9 +944,24 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
private->rdc_data.no_cyl, private->rdc_data.no_cyl,
private->rdc_data.trk_per_cyl, private->rdc_data.trk_per_cyl,
private->rdc_data.sec_per_trk); 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; return rc;
} }
static void dasd_eckd_uncheck_device(struct dasd_device *device)
{
dasd_alias_disconnect_device_from_lcu(device);
}
static struct dasd_ccw_req * static struct dasd_ccw_req *
dasd_eckd_analysis_ccw(struct dasd_device *device) dasd_eckd_analysis_ccw(struct dasd_device *device)
{ {
...@@ -755,7 +985,7 @@ 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 for the first 3 tracks. */
define_extent(ccw++, cqr->data, 0, 2, define_extent(ccw++, cqr->data, 0, 2,
DASD_ECKD_CCW_READ_COUNT, device); 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. */ /* Locate record for the first 4 records on track 0. */
ccw[-1].flags |= CCW_FLAG_CC; ccw[-1].flags |= CCW_FLAG_CC;
locate_record(ccw++, LO_data++, 0, 0, 4, locate_record(ccw++, LO_data++, 0, 0, 4,
...@@ -783,7 +1013,9 @@ dasd_eckd_analysis_ccw(struct dasd_device *device) ...@@ -783,7 +1013,9 @@ dasd_eckd_analysis_ccw(struct dasd_device *device)
ccw->count = 8; ccw->count = 8;
ccw->cda = (__u32)(addr_t) count_data; ccw->cda = (__u32)(addr_t) count_data;
cqr->device = device; cqr->block = NULL;
cqr->startdev = device;
cqr->memdev = device;
cqr->retries = 0; cqr->retries = 0;
cqr->buildclk = get_clock(); cqr->buildclk = get_clock();
cqr->status = DASD_CQR_FILLED; cqr->status = DASD_CQR_FILLED;
...@@ -803,7 +1035,7 @@ dasd_eckd_analysis_callback(struct dasd_ccw_req *init_cqr, void *data) ...@@ -803,7 +1035,7 @@ dasd_eckd_analysis_callback(struct dasd_ccw_req *init_cqr, void *data)
struct dasd_eckd_private *private; struct dasd_eckd_private *private;
struct dasd_device *device; struct dasd_device *device;
device = init_cqr->device; device = init_cqr->startdev;
private = (struct dasd_eckd_private *) device->private; private = (struct dasd_eckd_private *) device->private;
private->init_cqr_status = init_cqr->status; private->init_cqr_status = init_cqr->status;
dasd_sfree_request(init_cqr, device); dasd_sfree_request(init_cqr, device);
...@@ -811,13 +1043,13 @@ dasd_eckd_analysis_callback(struct dasd_ccw_req *init_cqr, void *data) ...@@ -811,13 +1043,13 @@ dasd_eckd_analysis_callback(struct dasd_ccw_req *init_cqr, void *data)
} }
static int 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_eckd_private *private;
struct dasd_ccw_req *init_cqr; struct dasd_ccw_req *init_cqr;
private = (struct dasd_eckd_private *) device->private; private = (struct dasd_eckd_private *) block->base->private;
init_cqr = dasd_eckd_analysis_ccw(device); init_cqr = dasd_eckd_analysis_ccw(block->base);
if (IS_ERR(init_cqr)) if (IS_ERR(init_cqr))
return PTR_ERR(init_cqr); return PTR_ERR(init_cqr);
init_cqr->callback = dasd_eckd_analysis_callback; init_cqr->callback = dasd_eckd_analysis_callback;
...@@ -828,13 +1060,15 @@ dasd_eckd_start_analysis(struct dasd_device *device) ...@@ -828,13 +1060,15 @@ dasd_eckd_start_analysis(struct dasd_device *device)
} }
static int 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 dasd_eckd_private *private;
struct eckd_count *count_area; struct eckd_count *count_area;
unsigned int sb, blk_per_trk; unsigned int sb, blk_per_trk;
int status, i; int status, i;
device = block->base;
private = (struct dasd_eckd_private *) device->private; private = (struct dasd_eckd_private *) device->private;
status = private->init_cqr_status; status = private->init_cqr_status;
private->init_cqr_status = -1; private->init_cqr_status = -1;
...@@ -846,7 +1080,7 @@ dasd_eckd_end_analysis(struct dasd_device *device) ...@@ -846,7 +1080,7 @@ dasd_eckd_end_analysis(struct dasd_device *device)
private->uses_cdl = 1; private->uses_cdl = 1;
/* Calculate number of blocks/records per track. */ /* 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 */ /* Check Track 0 for Compatible Disk Layout */
count_area = NULL; count_area = NULL;
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++) {
...@@ -876,56 +1110,65 @@ dasd_eckd_end_analysis(struct dasd_device *device) ...@@ -876,56 +1110,65 @@ dasd_eckd_end_analysis(struct dasd_device *device)
if (count_area != NULL && count_area->kl == 0) { if (count_area != NULL && count_area->kl == 0) {
/* we found notthing violating our disk layout */ /* we found notthing violating our disk layout */
if (dasd_check_blocksize(count_area->dl) == 0) 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", DEV_MESSAGE(KERN_WARNING, device, "%s",
"Volume has incompatible disk layout"); "Volume has incompatible disk layout");
return -EMEDIUMTYPE; return -EMEDIUMTYPE;
} }
device->s2b_shift = 0; /* bits to shift 512 to get a block */ block->s2b_shift = 0; /* bits to shift 512 to get a block */
for (sb = 512; sb < device->bp_block; sb = sb << 1) for (sb = 512; sb < block->bp_block; sb = sb << 1)
device->s2b_shift++; block->s2b_shift++;
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);
device->blocks = (private->rdc_data.no_cyl * block->blocks = (private->rdc_data.no_cyl *
private->rdc_data.trk_per_cyl * private->rdc_data.trk_per_cyl *
blk_per_trk); blk_per_trk);
DEV_MESSAGE(KERN_INFO, device, DEV_MESSAGE(KERN_INFO, device,
"(%dkB blks): %dkB at %dkB/trk %s", "(%dkB blks): %dkB at %dkB/trk %s",
(device->bp_block >> 10), (block->bp_block >> 10),
((private->rdc_data.no_cyl * ((private->rdc_data.no_cyl *
private->rdc_data.trk_per_cyl * private->rdc_data.trk_per_cyl *
blk_per_trk * (device->bp_block >> 9)) >> 1), blk_per_trk * (block->bp_block >> 9)) >> 1),
((blk_per_trk * device->bp_block) >> 10), ((blk_per_trk * block->bp_block) >> 10),
private->uses_cdl ? private->uses_cdl ?
"compatible disk layout" : "linux disk layout"); "compatible disk layout" : "linux disk layout");
return 0; return 0;
} }
static int static int dasd_eckd_do_analysis(struct dasd_block *block)
dasd_eckd_do_analysis(struct dasd_device *device)
{ {
struct dasd_eckd_private *private; 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) if (private->init_cqr_status < 0)
return dasd_eckd_start_analysis(device); return dasd_eckd_start_analysis(block);
else 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 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; struct dasd_eckd_private *private;
private = (struct dasd_eckd_private *) device->private; private = (struct dasd_eckd_private *) block->base->private;
if (dasd_check_blocksize(device->bp_block) == 0) { if (dasd_check_blocksize(block->bp_block) == 0) {
geo->sectors = recs_per_track(&private->rdc_data, geo->sectors = recs_per_track(&private->rdc_data,
0, device->bp_block); 0, block->bp_block);
} }
geo->cylinders = private->rdc_data.no_cyl; geo->cylinders = private->rdc_data.no_cyl;
geo->heads = private->rdc_data.trk_per_cyl; geo->heads = private->rdc_data.trk_per_cyl;
...@@ -1037,7 +1280,7 @@ dasd_eckd_format_device(struct dasd_device * device, ...@@ -1037,7 +1280,7 @@ dasd_eckd_format_device(struct dasd_device * device,
locate_record(ccw++, (struct LO_eckd_data *) data, locate_record(ccw++, (struct LO_eckd_data *) data,
fdata->start_unit, 0, rpt + 1, fdata->start_unit, 0, rpt + 1,
DASD_ECKD_CCW_WRITE_RECORD_ZERO, device, DASD_ECKD_CCW_WRITE_RECORD_ZERO, device,
device->bp_block); device->block->bp_block);
data += sizeof(struct LO_eckd_data); data += sizeof(struct LO_eckd_data);
break; break;
case 0x04: /* Invalidate track. */ case 0x04: /* Invalidate track. */
...@@ -1110,43 +1353,28 @@ dasd_eckd_format_device(struct dasd_device * device, ...@@ -1110,43 +1353,28 @@ dasd_eckd_format_device(struct dasd_device * device,
ccw++; ccw++;
} }
} }
fcp->device = device; fcp->startdev = device;
fcp->retries = 2; /* set retry counter to enable ERP */ 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->buildclk = get_clock();
fcp->status = DASD_CQR_FILLED; fcp->status = DASD_CQR_FILLED;
return fcp; return fcp;
} }
static dasd_era_t static void dasd_eckd_handle_terminated_request(struct dasd_ccw_req *cqr)
dasd_eckd_examine_error(struct dasd_ccw_req * cqr, struct irb * irb)
{ {
struct dasd_device *device = (struct dasd_device *) cqr->device; cqr->status = DASD_CQR_FILLED;
struct ccw_device *cdev = device->cdev; if (cqr->block && (cqr->startdev != cqr->block->base)) {
dasd_eckd_reset_ccw_to_base_io(cqr);
if (irb->scsw.cstat == 0x00 && cqr->startdev = cqr->block->base;
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;
} }
} };
static dasd_erp_fn_t static dasd_erp_fn_t
dasd_eckd_erp_action(struct dasd_ccw_req * cqr) 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; struct ccw_device *cdev = device->cdev;
switch (cdev->id.cu_type) { switch (cdev->id.cu_type) {
...@@ -1168,8 +1396,37 @@ dasd_eckd_erp_postaction(struct dasd_ccw_req * cqr) ...@@ -1168,8 +1396,37 @@ dasd_eckd_erp_postaction(struct dasd_ccw_req * cqr)
return dasd_default_erp_postaction; 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; struct dasd_eckd_private *private;
unsigned long *idaws; unsigned long *idaws;
...@@ -1185,8 +1442,11 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req) ...@@ -1185,8 +1442,11 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req)
sector_t first_trk, last_trk; sector_t first_trk, last_trk;
unsigned int first_offs, last_offs; unsigned int first_offs, last_offs;
unsigned char cmd, rcmd; 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) if (rq_data_dir(req) == READ)
cmd = DASD_ECKD_CCW_READ_MT; cmd = DASD_ECKD_CCW_READ_MT;
else if (rq_data_dir(req) == WRITE) else if (rq_data_dir(req) == WRITE)
...@@ -1194,13 +1454,13 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req) ...@@ -1194,13 +1454,13 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req)
else else
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
/* Calculate number of blocks/records per track. */ /* 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); blk_per_trk = recs_per_track(&private->rdc_data, 0, blksize);
/* Calculate record id of first and last block. */ /* 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); first_offs = sector_div(first_trk, blk_per_trk);
last_rec = last_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); last_offs = sector_div(last_trk, blk_per_trk);
/* Check struct bio and count the number of blocks for the request. */ /* Check struct bio and count the number of blocks for the request. */
count = 0; count = 0;
...@@ -1209,20 +1469,33 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req) ...@@ -1209,20 +1469,33 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req)
if (bv->bv_len & (blksize - 1)) if (bv->bv_len & (blksize - 1))
/* Eckd can only do full blocks. */ /* Eckd can only do full blocks. */
return ERR_PTR(-EINVAL); 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 defined(CONFIG_64BIT)
if (idal_is_needed (page_address(bv->bv_page), bv->bv_len)) 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 #endif
} }
/* Paranoia. */ /* Paranoia. */
if (count != last_rec - first_rec + 1) if (count != last_rec - first_rec + 1)
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
/* 1x define extent + 1x locate record + number of blocks */
cplength = 2 + count; /* use the prefix command if available */
/* 1x define extent + 1x locate record + cidaws*sizeof(long) */ use_prefix = private->features.feature[8] & 0x01;
datasize = sizeof(struct DE_eckd_data) + sizeof(struct LO_eckd_data) + if (use_prefix) {
cidaw * sizeof(unsigned long); /* 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. */ /* Find out the number of additional locate record ccws for cdl. */
if (private->uses_cdl && first_rec < 2*blk_per_trk) { if (private->uses_cdl && first_rec < 2*blk_per_trk) {
if (last_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) ...@@ -1232,26 +1505,42 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req)
} }
/* Allocate the ccw request. */ /* Allocate the ccw request. */
cqr = dasd_smalloc_request(dasd_eckd_discipline.name, cqr = dasd_smalloc_request(dasd_eckd_discipline.name,
cplength, datasize, device); cplength, datasize, startdev);
if (IS_ERR(cqr)) if (IS_ERR(cqr))
return cqr; return cqr;
ccw = cqr->cpaddr; ccw = cqr->cpaddr;
/* First ccw is define extent. */ /* First ccw is define extent or prefix. */
if (define_extent(ccw++, cqr->data, first_trk, if (use_prefix) {
last_trk, cmd, device) == -EAGAIN) { if (prefix(ccw++, cqr->data, first_trk,
/* Clock not in sync and XRC is enabled. Try again later. */ last_trk, cmd, basedev, startdev) == -EAGAIN) {
dasd_sfree_request(cqr, device); /* Clock not in sync and XRC is enabled.
return ERR_PTR(-EAGAIN); * 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. */ /* Build locate_record+read/write/ccws. */
idaws = (unsigned long *) (cqr->data + sizeof(struct DE_eckd_data));
LO_data = (struct LO_eckd_data *) (idaws + cidaw); LO_data = (struct LO_eckd_data *) (idaws + cidaw);
recid = first_rec; recid = first_rec;
if (private->uses_cdl == 0 || recid > 2*blk_per_trk) { if (private->uses_cdl == 0 || recid > 2*blk_per_trk) {
/* Only standard blocks so there is just one locate record. */ /* Only standard blocks so there is just one locate record. */
ccw[-1].flags |= CCW_FLAG_CC; ccw[-1].flags |= CCW_FLAG_CC;
locate_record(ccw++, LO_data++, first_trk, first_offs + 1, 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) { rq_for_each_segment(bv, req, iter) {
dst = page_address(bv->bv_page) + bv->bv_offset; dst = page_address(bv->bv_page) + bv->bv_offset;
...@@ -1281,7 +1570,7 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req) ...@@ -1281,7 +1570,7 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req)
ccw[-1].flags |= CCW_FLAG_CC; ccw[-1].flags |= CCW_FLAG_CC;
locate_record(ccw++, LO_data++, locate_record(ccw++, LO_data++,
trkid, recoffs + 1, trkid, recoffs + 1,
1, rcmd, device, count); 1, rcmd, basedev, count);
} }
/* Locate record for standard blocks ? */ /* Locate record for standard blocks ? */
if (private->uses_cdl && recid == 2*blk_per_trk) { if (private->uses_cdl && recid == 2*blk_per_trk) {
...@@ -1289,7 +1578,7 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req) ...@@ -1289,7 +1578,7 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req)
locate_record(ccw++, LO_data++, locate_record(ccw++, LO_data++,
trkid, recoffs + 1, trkid, recoffs + 1,
last_rec - recid + 1, last_rec - recid + 1,
cmd, device, count); cmd, basedev, count);
} }
/* Read/write ccw. */ /* Read/write ccw. */
ccw[-1].flags |= CCW_FLAG_CC; ccw[-1].flags |= CCW_FLAG_CC;
...@@ -1310,7 +1599,9 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req) ...@@ -1310,7 +1599,9 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req)
} }
if (req->cmd_flags & REQ_FAILFAST) if (req->cmd_flags & REQ_FAILFAST)
set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); 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->expires = 5 * 60 * HZ; /* 5 minutes */
cqr->lpm = private->path_data.ppm; cqr->lpm = private->path_data.ppm;
cqr->retries = 256; cqr->retries = 256;
...@@ -1333,10 +1624,10 @@ dasd_eckd_free_cp(struct dasd_ccw_req *cqr, struct request *req) ...@@ -1333,10 +1624,10 @@ dasd_eckd_free_cp(struct dasd_ccw_req *cqr, struct request *req)
if (!dasd_page_cache) if (!dasd_page_cache)
goto out; goto out;
private = (struct dasd_eckd_private *) cqr->device->private; private = (struct dasd_eckd_private *) cqr->block->base->private;
blksize = cqr->device->bp_block; blksize = cqr->block->bp_block;
blk_per_trk = recs_per_track(&private->rdc_data, 0, blksize); 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; ccw = cqr->cpaddr;
/* Skip over define extent & locate record. */ /* Skip over define extent & locate record. */
ccw++; ccw++;
...@@ -1367,10 +1658,71 @@ dasd_eckd_free_cp(struct dasd_ccw_req *cqr, struct request *req) ...@@ -1367,10 +1658,71 @@ dasd_eckd_free_cp(struct dasd_ccw_req *cqr, struct request *req)
} }
out: out:
status = cqr->status == DASD_CQR_DONE; status = cqr->status == DASD_CQR_DONE;
dasd_sfree_request(cqr, cqr->device); dasd_sfree_request(cqr, cqr->memdev);
return status; 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 static int
dasd_eckd_fill_info(struct dasd_device * device, dasd_eckd_fill_info(struct dasd_device * device,
struct dasd_information2_t * info) struct dasd_information2_t * info)
...@@ -1384,9 +1736,9 @@ dasd_eckd_fill_info(struct dasd_device * device, ...@@ -1384,9 +1736,9 @@ dasd_eckd_fill_info(struct dasd_device * device,
info->characteristics_size = sizeof(struct dasd_eckd_characteristics); info->characteristics_size = sizeof(struct dasd_eckd_characteristics);
memcpy(info->characteristics, &private->rdc_data, memcpy(info->characteristics, &private->rdc_data,
sizeof(struct dasd_eckd_characteristics)); 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, memcpy(info->configuration_data, &private->conf_data,
sizeof (struct dasd_eckd_confdata)); sizeof(struct dasd_eckd_confdata));
return 0; return 0;
} }
...@@ -1419,7 +1771,8 @@ dasd_eckd_release(struct dasd_device *device) ...@@ -1419,7 +1771,8 @@ dasd_eckd_release(struct dasd_device *device)
cqr->cpaddr->flags |= CCW_FLAG_SLI; cqr->cpaddr->flags |= CCW_FLAG_SLI;
cqr->cpaddr->count = 32; cqr->cpaddr->count = 32;
cqr->cpaddr->cda = (__u32)(addr_t) cqr->data; 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); clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
cqr->retries = 2; /* set retry counter to enable basic ERP */ cqr->retries = 2; /* set retry counter to enable basic ERP */
...@@ -1429,7 +1782,7 @@ dasd_eckd_release(struct dasd_device *device) ...@@ -1429,7 +1782,7 @@ dasd_eckd_release(struct dasd_device *device)
rc = dasd_sleep_on_immediatly(cqr); rc = dasd_sleep_on_immediatly(cqr);
dasd_sfree_request(cqr, cqr->device); dasd_sfree_request(cqr, cqr->memdev);
return rc; return rc;
} }
...@@ -1459,7 +1812,8 @@ dasd_eckd_reserve(struct dasd_device *device) ...@@ -1459,7 +1812,8 @@ dasd_eckd_reserve(struct dasd_device *device)
cqr->cpaddr->flags |= CCW_FLAG_SLI; cqr->cpaddr->flags |= CCW_FLAG_SLI;
cqr->cpaddr->count = 32; cqr->cpaddr->count = 32;
cqr->cpaddr->cda = (__u32)(addr_t) cqr->data; 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); clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
cqr->retries = 2; /* set retry counter to enable basic ERP */ cqr->retries = 2; /* set retry counter to enable basic ERP */
...@@ -1469,7 +1823,7 @@ dasd_eckd_reserve(struct dasd_device *device) ...@@ -1469,7 +1823,7 @@ dasd_eckd_reserve(struct dasd_device *device)
rc = dasd_sleep_on_immediatly(cqr); rc = dasd_sleep_on_immediatly(cqr);
dasd_sfree_request(cqr, cqr->device); dasd_sfree_request(cqr, cqr->memdev);
return rc; return rc;
} }
...@@ -1498,7 +1852,8 @@ dasd_eckd_steal_lock(struct dasd_device *device) ...@@ -1498,7 +1852,8 @@ dasd_eckd_steal_lock(struct dasd_device *device)
cqr->cpaddr->flags |= CCW_FLAG_SLI; cqr->cpaddr->flags |= CCW_FLAG_SLI;
cqr->cpaddr->count = 32; cqr->cpaddr->count = 32;
cqr->cpaddr->cda = (__u32)(addr_t) cqr->data; 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); clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
cqr->retries = 2; /* set retry counter to enable basic ERP */ cqr->retries = 2; /* set retry counter to enable basic ERP */
...@@ -1508,7 +1863,7 @@ dasd_eckd_steal_lock(struct dasd_device *device) ...@@ -1508,7 +1863,7 @@ dasd_eckd_steal_lock(struct dasd_device *device)
rc = dasd_sleep_on_immediatly(cqr); rc = dasd_sleep_on_immediatly(cqr);
dasd_sfree_request(cqr, cqr->device); dasd_sfree_request(cqr, cqr->memdev);
return rc; return rc;
} }
...@@ -1526,52 +1881,52 @@ dasd_eckd_performance(struct dasd_device *device, void __user *argp) ...@@ -1526,52 +1881,52 @@ dasd_eckd_performance(struct dasd_device *device, void __user *argp)
cqr = dasd_smalloc_request(dasd_eckd_discipline.name, cqr = dasd_smalloc_request(dasd_eckd_discipline.name,
1 /* PSF */ + 1 /* RSSD */ , 1 /* PSF */ + 1 /* RSSD */ ,
(sizeof (struct dasd_psf_prssd_data) + (sizeof(struct dasd_psf_prssd_data) +
sizeof (struct dasd_rssd_perf_stats_t)), sizeof(struct dasd_rssd_perf_stats_t)),
device); device);
if (IS_ERR(cqr)) { if (IS_ERR(cqr)) {
DEV_MESSAGE(KERN_WARNING, device, "%s", DEV_MESSAGE(KERN_WARNING, device, "%s",
"Could not allocate initialization request"); "Could not allocate initialization request");
return PTR_ERR(cqr); return PTR_ERR(cqr);
} }
cqr->device = device; cqr->startdev = device;
cqr->memdev = device;
cqr->retries = 0; cqr->retries = 0;
cqr->expires = 10 * HZ; cqr->expires = 10 * HZ;
/* Prepare for Read Subsystem Data */ /* Prepare for Read Subsystem Data */
prssdp = (struct dasd_psf_prssd_data *) cqr->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->order = PSF_ORDER_PRSSD;
prssdp->suborder = 0x01; /* Performance Statistics */ prssdp->suborder = 0x01; /* Performance Statistics */
prssdp->varies[1] = 0x01; /* Perf Statistics for the Subsystem */ prssdp->varies[1] = 0x01; /* Perf Statistics for the Subsystem */
ccw = cqr->cpaddr; ccw = cqr->cpaddr;
ccw->cmd_code = DASD_ECKD_CCW_PSF; 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->flags |= CCW_FLAG_CC;
ccw->cda = (__u32)(addr_t) prssdp; ccw->cda = (__u32)(addr_t) prssdp;
/* Read Subsystem Data - Performance Statistics */ /* Read Subsystem Data - Performance Statistics */
stats = (struct dasd_rssd_perf_stats_t *) (prssdp + 1); 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++;
ccw->cmd_code = DASD_ECKD_CCW_RSSD; 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; ccw->cda = (__u32)(addr_t) stats;
cqr->buildclk = get_clock(); cqr->buildclk = get_clock();
cqr->status = DASD_CQR_FILLED; cqr->status = DASD_CQR_FILLED;
rc = dasd_sleep_on(cqr); rc = dasd_sleep_on(cqr);
if (rc == 0) { if (rc == 0) {
/* Prepare for Read Subsystem Data */
prssdp = (struct dasd_psf_prssd_data *) cqr->data; prssdp = (struct dasd_psf_prssd_data *) cqr->data;
stats = (struct dasd_rssd_perf_stats_t *) (prssdp + 1); stats = (struct dasd_rssd_perf_stats_t *) (prssdp + 1);
if (copy_to_user(argp, stats, if (copy_to_user(argp, stats,
sizeof(struct dasd_rssd_perf_stats_t))) sizeof(struct dasd_rssd_perf_stats_t)))
rc = -EFAULT; rc = -EFAULT;
} }
dasd_sfree_request(cqr, cqr->device); dasd_sfree_request(cqr, cqr->memdev);
return rc; return rc;
} }
...@@ -1594,7 +1949,7 @@ dasd_eckd_get_attrib(struct dasd_device *device, void __user *argp) ...@@ -1594,7 +1949,7 @@ dasd_eckd_get_attrib(struct dasd_device *device, void __user *argp)
rc = 0; rc = 0;
if (copy_to_user(argp, (long *) &attrib, if (copy_to_user(argp, (long *) &attrib,
sizeof (struct attrib_data_t))) sizeof(struct attrib_data_t)))
rc = -EFAULT; rc = -EFAULT;
return rc; return rc;
...@@ -1627,8 +1982,10 @@ dasd_eckd_set_attrib(struct dasd_device *device, void __user *argp) ...@@ -1627,8 +1982,10 @@ dasd_eckd_set_attrib(struct dasd_device *device, void __user *argp)
} }
static int 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) { switch (cmd) {
case BIODASDGATTR: case BIODASDGATTR:
return dasd_eckd_get_attrib(device, argp); return dasd_eckd_get_attrib(device, argp);
...@@ -1685,9 +2042,8 @@ dasd_eckd_dump_ccw_range(struct ccw1 *from, struct ccw1 *to, char *page) ...@@ -1685,9 +2042,8 @@ dasd_eckd_dump_ccw_range(struct ccw1 *from, struct ccw1 *to, char *page)
* Print sense data and related channel program. * Print sense data and related channel program.
* Parts are printed because printk buffer is only 1024 bytes. * Parts are printed because printk buffer is only 1024 bytes.
*/ */
static void static void dasd_eckd_dump_sense(struct dasd_device *device,
dasd_eckd_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req, struct dasd_ccw_req *req, struct irb *irb)
struct irb *irb)
{ {
char *page; char *page;
struct ccw1 *first, *last, *fail, *from, *to; struct ccw1 *first, *last, *fail, *from, *to;
...@@ -1743,37 +2099,40 @@ dasd_eckd_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req, ...@@ -1743,37 +2099,40 @@ dasd_eckd_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req,
} }
printk("%s", page); printk("%s", page);
/* dump the Channel Program (max 140 Bytes per line) */ if (req) {
/* Count CCW and print first CCWs (maximum 1024 % 140 = 7) */ /* req == NULL for unsolicited interrupts */
first = req->cpaddr; /* dump the Channel Program (max 140 Bytes per line) */
for (last = first; last->flags & (CCW_FLAG_CC | CCW_FLAG_DC); last++); /* Count CCW and print first CCWs (maximum 1024 % 140 = 7) */
to = min(first + 6, last); first = req->cpaddr;
len = sprintf(page, KERN_ERR PRINTK_HEADER for (last = first; last->flags & (CCW_FLAG_CC | CCW_FLAG_DC); last++);
" Related CP in req: %p\n", req); to = min(first + 6, last);
dasd_eckd_dump_ccw_range(first, to, page + len); len = sprintf(page, KERN_ERR PRINTK_HEADER
printk("%s", page); " 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) */ /* print failing CCW area (maximum 4) */
/* scsw->cda is either valid or zero */ /* scsw->cda is either valid or zero */
len = 0; len = 0;
from = ++to; from = ++to;
fail = (struct ccw1 *)(addr_t) irb->scsw.cpa; /* failing CCW */ fail = (struct ccw1 *)(addr_t) irb->scsw.cpa; /* failing CCW */
if (from < fail - 2) { if (from < fail - 2) {
from = fail - 2; /* there is a gap - print header */ from = fail - 2; /* there is a gap - print header */
len += sprintf(page, KERN_ERR PRINTK_HEADER "......\n"); len += sprintf(page, KERN_ERR PRINTK_HEADER "......\n");
} }
to = min(fail + 1, last); to = min(fail + 1, last);
len += dasd_eckd_dump_ccw_range(from, to, page + len); len += dasd_eckd_dump_ccw_range(from, to, page + len);
/* print last CCWs (maximum 2) */ /* print last CCWs (maximum 2) */
from = max(from, ++to); from = max(from, ++to);
if (from < last - 1) { if (from < last - 1) {
from = last - 1; /* there is a gap - print header */ from = last - 1; /* there is a gap - print header */
len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n"); 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); free_page((unsigned long) page);
} }
...@@ -1796,16 +2155,20 @@ static struct dasd_discipline dasd_eckd_discipline = { ...@@ -1796,16 +2155,20 @@ static struct dasd_discipline dasd_eckd_discipline = {
.ebcname = "ECKD", .ebcname = "ECKD",
.max_blocks = 240, .max_blocks = 240,
.check_device = dasd_eckd_check_characteristics, .check_device = dasd_eckd_check_characteristics,
.uncheck_device = dasd_eckd_uncheck_device,
.do_analysis = dasd_eckd_do_analysis, .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, .fill_geometry = dasd_eckd_fill_geometry,
.start_IO = dasd_start_IO, .start_IO = dasd_start_IO,
.term_IO = dasd_term_IO, .term_IO = dasd_term_IO,
.handle_terminated_request = dasd_eckd_handle_terminated_request,
.format_device = dasd_eckd_format_device, .format_device = dasd_eckd_format_device,
.examine_error = dasd_eckd_examine_error,
.erp_action = dasd_eckd_erp_action, .erp_action = dasd_eckd_erp_action,
.erp_postaction = dasd_eckd_erp_postaction, .erp_postaction = dasd_eckd_erp_postaction,
.build_cp = dasd_eckd_build_cp, .handle_unsolicited_interrupt = dasd_eckd_handle_unsolicited_interrupt,
.free_cp = dasd_eckd_free_cp, .build_cp = dasd_eckd_build_alias_cp,
.free_cp = dasd_eckd_free_alias_cp,
.dump_sense = dasd_eckd_dump_sense, .dump_sense = dasd_eckd_dump_sense,
.fill_info = dasd_eckd_fill_info, .fill_info = dasd_eckd_fill_info,
.ioctl = dasd_eckd_ioctl, .ioctl = dasd_eckd_ioctl,
......
...@@ -39,6 +39,8 @@ ...@@ -39,6 +39,8 @@
#define DASD_ECKD_CCW_READ_CKD_MT 0x9e #define DASD_ECKD_CCW_READ_CKD_MT 0x9e
#define DASD_ECKD_CCW_WRITE_CKD_MT 0x9d #define DASD_ECKD_CCW_WRITE_CKD_MT 0x9d
#define DASD_ECKD_CCW_RESERVE 0xB4 #define DASD_ECKD_CCW_RESERVE 0xB4
#define DASD_ECKD_CCW_PFX 0xE7
#define DASD_ECKD_CCW_RSCK 0xF9
/* /*
* Perform Subsystem Function / Sub-Orders * Perform Subsystem Function / Sub-Orders
...@@ -137,6 +139,25 @@ struct LO_eckd_data { ...@@ -137,6 +139,25 @@ struct LO_eckd_data {
__u16 length; __u16 length;
} __attribute__ ((packed)); } __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 { struct dasd_eckd_characteristics {
__u16 cu_type; __u16 cu_type;
struct { struct {
...@@ -254,7 +275,9 @@ struct dasd_eckd_confdata { ...@@ -254,7 +275,9 @@ struct dasd_eckd_confdata {
} __attribute__ ((packed)) ned; } __attribute__ ((packed)) ned;
struct { struct {
unsigned char flags; /* byte 0 */ 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 */ unsigned char sua_flags; /* byte 8 */
__u8 base_unit_addr; /* byte 9 */ __u8 base_unit_addr; /* byte 9 */
unsigned char res3[22]; /* byte 10-31 */ unsigned char res3[22]; /* byte 10-31 */
...@@ -343,6 +366,11 @@ struct dasd_eckd_path { ...@@ -343,6 +366,11 @@ struct dasd_eckd_path {
__u8 npm; __u8 npm;
}; };
struct dasd_rssd_features {
char feature[256];
} __attribute__((packed));
/* /*
* Perform Subsystem Function - Prepare for Read Subsystem Data * Perform Subsystem Function - Prepare for Read Subsystem Data
*/ */
...@@ -365,4 +393,99 @@ struct dasd_psf_ssc_data { ...@@ -365,4 +393,99 @@ struct dasd_psf_ssc_data {
unsigned char reserved[59]; unsigned char reserved[59];
} __attribute__((packed)); } __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 */ #endif /* DASD_ECKD_H */
...@@ -336,7 +336,7 @@ static void dasd_eer_write_snss_trigger(struct dasd_device *device, ...@@ -336,7 +336,7 @@ static void dasd_eer_write_snss_trigger(struct dasd_device *device,
unsigned long flags; unsigned long flags;
struct eerbuffer *eerb; struct eerbuffer *eerb;
snss_rc = (cqr->status == DASD_CQR_FAILED) ? -EIO : 0; snss_rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO;
if (snss_rc) if (snss_rc)
data_size = 0; data_size = 0;
else else
...@@ -404,10 +404,11 @@ void dasd_eer_snss(struct dasd_device *device) ...@@ -404,10 +404,11 @@ void dasd_eer_snss(struct dasd_device *device)
set_bit(DASD_FLAG_EER_SNSS, &device->flags); set_bit(DASD_FLAG_EER_SNSS, &device->flags);
return; return;
} }
/* cdev is already locked, can't use dasd_add_request_head */
clear_bit(DASD_FLAG_EER_SNSS, &device->flags); clear_bit(DASD_FLAG_EER_SNSS, &device->flags);
cqr->status = DASD_CQR_QUEUED; cqr->status = DASD_CQR_QUEUED;
list_add(&cqr->list, &device->ccw_queue); list_add(&cqr->devlist, &device->ccw_queue);
dasd_schedule_bh(device); dasd_schedule_device_bh(device);
} }
/* /*
...@@ -415,7 +416,7 @@ void dasd_eer_snss(struct dasd_device *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) 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; unsigned long flags;
dasd_eer_write(device, cqr, DASD_EER_STATECHANGE); dasd_eer_write(device, cqr, DASD_EER_STATECHANGE);
...@@ -458,7 +459,7 @@ int dasd_eer_enable(struct dasd_device *device) ...@@ -458,7 +459,7 @@ int dasd_eer_enable(struct dasd_device *device)
if (!cqr) if (!cqr)
return -ENOMEM; return -ENOMEM;
cqr->device = device; cqr->startdev = device;
cqr->retries = 255; cqr->retries = 255;
cqr->expires = 10 * HZ; cqr->expires = 10 * HZ;
clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
......
...@@ -46,6 +46,8 @@ dasd_alloc_erp_request(char *magic, int cplength, int datasize, ...@@ -46,6 +46,8 @@ dasd_alloc_erp_request(char *magic, int cplength, int datasize,
if (cqr == NULL) if (cqr == NULL)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
memset(cqr, 0, sizeof(struct dasd_ccw_req)); 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); data = (char *) cqr + ((sizeof(struct dasd_ccw_req) + 7L) & -8L);
cqr->cpaddr = NULL; cqr->cpaddr = NULL;
if (cplength > 0) { if (cplength > 0) {
...@@ -66,7 +68,7 @@ dasd_alloc_erp_request(char *magic, int cplength, int datasize, ...@@ -66,7 +68,7 @@ dasd_alloc_erp_request(char *magic, int cplength, int datasize,
} }
void 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; unsigned long flags;
...@@ -81,11 +83,11 @@ dasd_free_erp_request(struct dasd_ccw_req * cqr, struct dasd_device * device) ...@@ -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 * dasd_default_erp_action just retries the current cqr
*/ */
struct dasd_ccw_req * 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; struct dasd_device *device;
device = cqr->device; device = cqr->startdev;
/* just retry - there is nothing to save ... I got no sense data.... */ /* just retry - there is nothing to save ... I got no sense data.... */
if (cqr->retries > 0) { if (cqr->retries > 0) {
...@@ -93,12 +95,12 @@ dasd_default_erp_action(struct dasd_ccw_req * cqr) ...@@ -93,12 +95,12 @@ dasd_default_erp_action(struct dasd_ccw_req * cqr)
"default ERP called (%i retries left)", "default ERP called (%i retries left)",
cqr->retries); cqr->retries);
cqr->lpm = LPM_ANYPATH; cqr->lpm = LPM_ANYPATH;
cqr->status = DASD_CQR_QUEUED; cqr->status = DASD_CQR_FILLED;
} else { } else {
DEV_MESSAGE (KERN_WARNING, device, "%s", DEV_MESSAGE (KERN_WARNING, device, "%s",
"default ERP called (NO retry left)"); "default ERP called (NO retry left)");
cqr->status = DASD_CQR_FAILED; cqr->status = DASD_CQR_FAILED;
cqr->stopclk = get_clock (); cqr->stopclk = get_clock();
} }
return cqr; return cqr;
} /* end dasd_default_erp_action */ } /* end dasd_default_erp_action */
...@@ -117,15 +119,12 @@ dasd_default_erp_action(struct dasd_ccw_req * cqr) ...@@ -117,15 +119,12 @@ dasd_default_erp_action(struct dasd_ccw_req * cqr)
* RETURN VALUES * RETURN VALUES
* cqr pointer to the original CQR * cqr pointer to the original CQR
*/ */
struct dasd_ccw_req * struct dasd_ccw_req *dasd_default_erp_postaction(struct dasd_ccw_req *cqr)
dasd_default_erp_postaction(struct dasd_ccw_req * cqr)
{ {
struct dasd_device *device;
int success; int success;
BUG_ON(cqr->refers == NULL || cqr->function == NULL); BUG_ON(cqr->refers == NULL || cqr->function == NULL);
device = cqr->device;
success = cqr->status == DASD_CQR_DONE; success = cqr->status == DASD_CQR_DONE;
/* free all ERPs - but NOT the original cqr */ /* free all ERPs - but NOT the original cqr */
...@@ -133,10 +132,10 @@ dasd_default_erp_postaction(struct dasd_ccw_req * cqr) ...@@ -133,10 +132,10 @@ dasd_default_erp_postaction(struct dasd_ccw_req * cqr)
struct dasd_ccw_req *refers; struct dasd_ccw_req *refers;
refers = cqr->refers; refers = cqr->refers;
/* remove the request from the device queue */ /* remove the request from the block queue */
list_del(&cqr->list); list_del(&cqr->blocklist);
/* free the finished erp request */ /* free the finished erp request */
dasd_free_erp_request(cqr, device); dasd_free_erp_request(cqr, cqr->memdev);
cqr = refers; cqr = refers;
} }
...@@ -157,7 +156,7 @@ dasd_log_sense(struct dasd_ccw_req *cqr, struct irb *irb) ...@@ -157,7 +156,7 @@ dasd_log_sense(struct dasd_ccw_req *cqr, struct irb *irb)
{ {
struct dasd_device *device; struct dasd_device *device;
device = cqr->device; device = cqr->startdev;
/* dump sense data */ /* dump sense data */
if (device->discipline && device->discipline->dump_sense) if (device->discipline && device->discipline->dump_sense)
device->discipline->dump_sense(device, cqr, irb); device->discipline->dump_sense(device, cqr, irb);
......
...@@ -117,6 +117,7 @@ locate_record(struct ccw1 * ccw, struct LO_fba_data *data, int rw, ...@@ -117,6 +117,7 @@ locate_record(struct ccw1 * ccw, struct LO_fba_data *data, int rw,
static int static int
dasd_fba_check_characteristics(struct dasd_device *device) dasd_fba_check_characteristics(struct dasd_device *device)
{ {
struct dasd_block *block;
struct dasd_fba_private *private; struct dasd_fba_private *private;
struct ccw_device *cdev = device->cdev; struct ccw_device *cdev = device->cdev;
void *rdc_data; void *rdc_data;
...@@ -133,6 +134,16 @@ dasd_fba_check_characteristics(struct dasd_device *device) ...@@ -133,6 +134,16 @@ dasd_fba_check_characteristics(struct dasd_device *device)
} }
device->private = (void *) private; 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 */ /* Read Device Characteristics */
rdc_data = (void *) &(private->rdc_data); rdc_data = (void *) &(private->rdc_data);
rc = dasd_generic_read_dev_chars(device, "FBA ", &rdc_data, 32); rc = dasd_generic_read_dev_chars(device, "FBA ", &rdc_data, 32);
...@@ -155,60 +166,37 @@ dasd_fba_check_characteristics(struct dasd_device *device) ...@@ -155,60 +166,37 @@ dasd_fba_check_characteristics(struct dasd_device *device)
return 0; return 0;
} }
static int static int dasd_fba_do_analysis(struct dasd_block *block)
dasd_fba_do_analysis(struct dasd_device *device)
{ {
struct dasd_fba_private *private; struct dasd_fba_private *private;
int sb, rc; 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); rc = dasd_check_blocksize(private->rdc_data.blk_size);
if (rc) { if (rc) {
DEV_MESSAGE(KERN_INFO, device, "unknown blocksize %d", DEV_MESSAGE(KERN_INFO, block->base, "unknown blocksize %d",
private->rdc_data.blk_size); private->rdc_data.blk_size);
return rc; return rc;
} }
device->blocks = private->rdc_data.blk_bdsa; block->blocks = private->rdc_data.blk_bdsa;
device->bp_block = private->rdc_data.blk_size; block->bp_block = private->rdc_data.blk_size;
device->s2b_shift = 0; /* bits to shift 512 to get a block */ block->s2b_shift = 0; /* bits to shift 512 to get a block */
for (sb = 512; sb < private->rdc_data.blk_size; sb = sb << 1) for (sb = 512; sb < private->rdc_data.blk_size; sb = sb << 1)
device->s2b_shift++; block->s2b_shift++;
return 0; return 0;
} }
static int static int dasd_fba_fill_geometry(struct dasd_block *block,
dasd_fba_fill_geometry(struct dasd_device *device, struct hd_geometry *geo) struct hd_geometry *geo)
{ {
if (dasd_check_blocksize(device->bp_block) != 0) if (dasd_check_blocksize(block->bp_block) != 0)
return -EINVAL; return -EINVAL;
geo->cylinders = (device->blocks << device->s2b_shift) >> 10; geo->cylinders = (block->blocks << block->s2b_shift) >> 10;
geo->heads = 16; geo->heads = 16;
geo->sectors = 128 >> device->s2b_shift; geo->sectors = 128 >> block->s2b_shift;
return 0; 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 static dasd_erp_fn_t
dasd_fba_erp_action(struct dasd_ccw_req * cqr) dasd_fba_erp_action(struct dasd_ccw_req * cqr)
{ {
...@@ -221,13 +209,34 @@ dasd_fba_erp_postaction(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) if (cqr->function == dasd_default_erp_action)
return dasd_default_erp_postaction; 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); cqr->function);
return NULL; return NULL;
} }
static struct dasd_ccw_req * static void dasd_fba_handle_unsolicited_interrupt(struct dasd_device *device,
dasd_fba_build_cp(struct dasd_device * device, struct request *req) 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; struct dasd_fba_private *private;
unsigned long *idaws; unsigned long *idaws;
...@@ -242,17 +251,17 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req) ...@@ -242,17 +251,17 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req)
unsigned int blksize, off; unsigned int blksize, off;
unsigned char cmd; unsigned char cmd;
private = (struct dasd_fba_private *) device->private; private = (struct dasd_fba_private *) block->base->private;
if (rq_data_dir(req) == READ) { if (rq_data_dir(req) == READ) {
cmd = DASD_FBA_CCW_READ; cmd = DASD_FBA_CCW_READ;
} else if (rq_data_dir(req) == WRITE) { } else if (rq_data_dir(req) == WRITE) {
cmd = DASD_FBA_CCW_WRITE; cmd = DASD_FBA_CCW_WRITE;
} else } else
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
blksize = device->bp_block; blksize = block->bp_block;
/* Calculate record id of first and last block. */ /* Calculate record id of first and last block. */
first_rec = req->sector >> device->s2b_shift; first_rec = req->sector >> block->s2b_shift;
last_rec = (req->sector + req->nr_sectors - 1) >> device->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. */ /* Check struct bio and count the number of blocks for the request. */
count = 0; count = 0;
cidaw = 0; cidaw = 0;
...@@ -260,7 +269,7 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req) ...@@ -260,7 +269,7 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req)
if (bv->bv_len & (blksize - 1)) if (bv->bv_len & (blksize - 1))
/* Fba can only do full blocks. */ /* Fba can only do full blocks. */
return ERR_PTR(-EINVAL); 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 defined(CONFIG_64BIT)
if (idal_is_needed (page_address(bv->bv_page), bv->bv_len)) if (idal_is_needed (page_address(bv->bv_page), bv->bv_len))
cidaw += bv->bv_len / blksize; cidaw += bv->bv_len / blksize;
...@@ -284,13 +293,13 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req) ...@@ -284,13 +293,13 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req)
} }
/* Allocate the ccw request. */ /* Allocate the ccw request. */
cqr = dasd_smalloc_request(dasd_fba_discipline.name, cqr = dasd_smalloc_request(dasd_fba_discipline.name,
cplength, datasize, device); cplength, datasize, memdev);
if (IS_ERR(cqr)) if (IS_ERR(cqr))
return cqr; return cqr;
ccw = cqr->cpaddr; ccw = cqr->cpaddr;
/* First ccw is define extent. */ /* First ccw is define extent. */
define_extent(ccw++, cqr->data, rq_data_dir(req), 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. */ /* Build locate_record + read/write ccws. */
idaws = (unsigned long *) (cqr->data + sizeof(struct DE_fba_data)); idaws = (unsigned long *) (cqr->data + sizeof(struct DE_fba_data));
LO_data = (struct LO_fba_data *) (idaws + cidaw); LO_data = (struct LO_fba_data *) (idaws + cidaw);
...@@ -326,7 +335,7 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req) ...@@ -326,7 +335,7 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req)
ccw[-1].flags |= CCW_FLAG_CC; ccw[-1].flags |= CCW_FLAG_CC;
} }
ccw->cmd_code = cmd; ccw->cmd_code = cmd;
ccw->count = device->bp_block; ccw->count = block->bp_block;
if (idal_is_needed(dst, blksize)) { if (idal_is_needed(dst, blksize)) {
ccw->cda = (__u32)(addr_t) idaws; ccw->cda = (__u32)(addr_t) idaws;
ccw->flags = CCW_FLAG_IDA; ccw->flags = CCW_FLAG_IDA;
...@@ -342,7 +351,9 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req) ...@@ -342,7 +351,9 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req)
} }
if (req->cmd_flags & REQ_FAILFAST) if (req->cmd_flags & REQ_FAILFAST)
set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); 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->expires = 5 * 60 * HZ; /* 5 minutes */
cqr->retries = 32; cqr->retries = 32;
cqr->buildclk = get_clock(); cqr->buildclk = get_clock();
...@@ -363,8 +374,8 @@ dasd_fba_free_cp(struct dasd_ccw_req *cqr, struct request *req) ...@@ -363,8 +374,8 @@ dasd_fba_free_cp(struct dasd_ccw_req *cqr, struct request *req)
if (!dasd_page_cache) if (!dasd_page_cache)
goto out; goto out;
private = (struct dasd_fba_private *) cqr->device->private; private = (struct dasd_fba_private *) cqr->block->base->private;
blksize = cqr->device->bp_block; blksize = cqr->block->bp_block;
ccw = cqr->cpaddr; ccw = cqr->cpaddr;
/* Skip over define extent & locate record. */ /* Skip over define extent & locate record. */
ccw++; ccw++;
...@@ -394,10 +405,15 @@ dasd_fba_free_cp(struct dasd_ccw_req *cqr, struct request *req) ...@@ -394,10 +405,15 @@ dasd_fba_free_cp(struct dasd_ccw_req *cqr, struct request *req)
} }
out: out:
status = cqr->status == DASD_CQR_DONE; status = cqr->status == DASD_CQR_DONE;
dasd_sfree_request(cqr, cqr->device); dasd_sfree_request(cqr, cqr->memdev);
return status; return status;
} }
static void dasd_fba_handle_terminated_request(struct dasd_ccw_req *cqr)
{
cqr->status = DASD_CQR_FILLED;
};
static int static int
dasd_fba_fill_info(struct dasd_device * device, dasd_fba_fill_info(struct dasd_device * device,
struct dasd_information2_t * info) struct dasd_information2_t * info)
...@@ -546,9 +562,10 @@ static struct dasd_discipline dasd_fba_discipline = { ...@@ -546,9 +562,10 @@ static struct dasd_discipline dasd_fba_discipline = {
.fill_geometry = dasd_fba_fill_geometry, .fill_geometry = dasd_fba_fill_geometry,
.start_IO = dasd_start_IO, .start_IO = dasd_start_IO,
.term_IO = dasd_term_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_action = dasd_fba_erp_action,
.erp_postaction = dasd_fba_erp_postaction, .erp_postaction = dasd_fba_erp_postaction,
.handle_unsolicited_interrupt = dasd_fba_handle_unsolicited_interrupt,
.build_cp = dasd_fba_build_cp, .build_cp = dasd_fba_build_cp,
.free_cp = dasd_fba_free_cp, .free_cp = dasd_fba_free_cp,
.dump_sense = dasd_fba_dump_sense, .dump_sense = dasd_fba_dump_sense,
......
...@@ -25,14 +25,15 @@ ...@@ -25,14 +25,15 @@
/* /*
* Allocate and register gendisk structure for device. * Allocate and register gendisk structure for device.
*/ */
int int dasd_gendisk_alloc(struct dasd_block *block)
dasd_gendisk_alloc(struct dasd_device *device)
{ {
struct gendisk *gdp; struct gendisk *gdp;
struct dasd_device *base;
int len; int len;
/* Make sure the minor for this device exists. */ /* 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; return -EBUSY;
gdp = alloc_disk(1 << DASD_PARTN_BITS); gdp = alloc_disk(1 << DASD_PARTN_BITS);
...@@ -41,9 +42,9 @@ dasd_gendisk_alloc(struct dasd_device *device) ...@@ -41,9 +42,9 @@ dasd_gendisk_alloc(struct dasd_device *device)
/* Initialize gendisk structure. */ /* Initialize gendisk structure. */
gdp->major = DASD_MAJOR; 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->fops = &dasd_device_operations;
gdp->driverfs_dev = &device->cdev->dev; gdp->driverfs_dev = &base->cdev->dev;
/* /*
* Set device name. * Set device name.
...@@ -53,53 +54,51 @@ dasd_gendisk_alloc(struct dasd_device *device) ...@@ -53,53 +54,51 @@ dasd_gendisk_alloc(struct dasd_device *device)
* dasdaaaa - dasdzzzz : 456976 devices, added up = 475252 * dasdaaaa - dasdzzzz : 456976 devices, added up = 475252
*/ */
len = sprintf(gdp->disk_name, "dasd"); len = sprintf(gdp->disk_name, "dasd");
if (device->devindex > 25) { if (base->devindex > 25) {
if (device->devindex > 701) { if (base->devindex > 701) {
if (device->devindex > 18277) if (base->devindex > 18277)
len += sprintf(gdp->disk_name + len, "%c", len += sprintf(gdp->disk_name + len, "%c",
'a'+(((device->devindex-18278) 'a'+(((base->devindex-18278)
/17576)%26)); /17576)%26));
len += sprintf(gdp->disk_name + len, "%c", 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", 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); set_disk_ro(gdp, 1);
gdp->private_data = device; gdp->private_data = block;
gdp->queue = device->request_queue; gdp->queue = block->request_queue;
device->gdp = gdp; block->gdp = gdp;
set_capacity(device->gdp, 0); set_capacity(block->gdp, 0);
add_disk(device->gdp); add_disk(block->gdp);
return 0; return 0;
} }
/* /*
* Unregister and free gendisk structure for device. * Unregister and free gendisk structure for device.
*/ */
void void dasd_gendisk_free(struct dasd_block *block)
dasd_gendisk_free(struct dasd_device *device)
{ {
if (device->gdp) { if (block->gdp) {
del_gendisk(device->gdp); del_gendisk(block->gdp);
device->gdp->queue = NULL; block->gdp->queue = NULL;
put_disk(device->gdp); put_disk(block->gdp);
device->gdp = NULL; block->gdp = NULL;
} }
} }
/* /*
* Trigger a partition detection. * Trigger a partition detection.
*/ */
int int dasd_scan_partitions(struct dasd_block *block)
dasd_scan_partitions(struct dasd_device * device)
{ {
struct block_device *bdev; 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) if (!bdev || blkdev_get(bdev, FMODE_READ, 1) < 0)
return -ENODEV; return -ENODEV;
/* /*
...@@ -117,7 +116,7 @@ dasd_scan_partitions(struct dasd_device * device) ...@@ -117,7 +116,7 @@ dasd_scan_partitions(struct dasd_device * device)
* is why the assignment to device->bdev is done AFTER * is why the assignment to device->bdev is done AFTER
* the BLKRRPART ioctl. * the BLKRRPART ioctl.
*/ */
device->bdev = bdev; block->bdev = bdev;
return 0; return 0;
} }
...@@ -125,8 +124,7 @@ dasd_scan_partitions(struct dasd_device * device) ...@@ -125,8 +124,7 @@ dasd_scan_partitions(struct dasd_device * device)
* Remove all inodes in the system for a device, delete the * Remove all inodes in the system for a device, delete the
* partitions and make device unusable by setting its size to zero. * partitions and make device unusable by setting its size to zero.
*/ */
void void dasd_destroy_partitions(struct dasd_block *block)
dasd_destroy_partitions(struct dasd_device * device)
{ {
/* The two structs have 168/176 byte on 31/64 bit. */ /* The two structs have 168/176 byte on 31/64 bit. */
struct blkpg_partition bpart; struct blkpg_partition bpart;
...@@ -137,8 +135,8 @@ dasd_destroy_partitions(struct dasd_device * device) ...@@ -137,8 +135,8 @@ dasd_destroy_partitions(struct dasd_device * device)
* Get the bdev pointer from the device structure and clear * Get the bdev pointer from the device structure and clear
* device->bdev to lower the offline open_count limit again. * device->bdev to lower the offline open_count limit again.
*/ */
bdev = device->bdev; bdev = block->bdev;
device->bdev = NULL; block->bdev = NULL;
/* /*
* See fs/partition/check.c:delete_partition * See fs/partition/check.c:delete_partition
...@@ -149,17 +147,16 @@ dasd_destroy_partitions(struct dasd_device * device) ...@@ -149,17 +147,16 @@ dasd_destroy_partitions(struct dasd_device * device)
memset(&barg, 0, sizeof(struct blkpg_ioctl_arg)); memset(&barg, 0, sizeof(struct blkpg_ioctl_arg));
barg.data = (void __force __user *) &bpart; barg.data = (void __force __user *) &bpart;
barg.op = BLKPG_DEL_PARTITION; 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); 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. */ /* Matching blkdev_put to the blkdev_get in dasd_scan_partitions. */
blkdev_put(bdev); blkdev_put(bdev);
set_capacity(device->gdp, 0); set_capacity(block->gdp, 0);
} }
int int dasd_gendisk_init(void)
dasd_gendisk_init(void)
{ {
int rc; int rc;
...@@ -174,8 +171,7 @@ dasd_gendisk_init(void) ...@@ -174,8 +171,7 @@ dasd_gendisk_init(void)
return 0; return 0;
} }
void void dasd_gendisk_exit(void)
dasd_gendisk_exit(void)
{ {
unregister_blkdev(DASD_MAJOR, "dasd"); unregister_blkdev(DASD_MAJOR, "dasd");
} }
...@@ -64,13 +64,7 @@ ...@@ -64,13 +64,7 @@
* SECTION: Type definitions * SECTION: Type definitions
*/ */
struct dasd_device; struct dasd_device;
struct dasd_block;
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;
/* BIT DEFINITIONS FOR SENSE DATA */ /* BIT DEFINITIONS FOR SENSE DATA */
#define DASD_SENSE_BIT_0 0x80 #define DASD_SENSE_BIT_0 0x80
...@@ -151,19 +145,22 @@ do { \ ...@@ -151,19 +145,22 @@ do { \
struct dasd_ccw_req { struct dasd_ccw_req {
unsigned int magic; /* Eye catcher */ 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... */ /* 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 */ struct ccw1 *cpaddr; /* address of channel program */
char status; /* status of this request */ char status; /* status of this request */
short retries; /* A retry counter */ short retries; /* A retry counter */
unsigned long flags; /* flags of this request */ unsigned long flags; /* flags of this request */
/* ... and how */ /* ... and how */
unsigned long starttime; /* jiffies time of request start */ unsigned long starttime; /* jiffies time of request start */
int expires; /* expiration period in jiffies */ int expires; /* expiration period in jiffies */
char lpm; /* logical path mask */ char lpm; /* logical path mask */
void *data; /* pointer to data area */ void *data; /* pointer to data area */
/* these are important for recovering erroneous requests */ /* these are important for recovering erroneous requests */
...@@ -178,20 +175,27 @@ struct dasd_ccw_req { ...@@ -178,20 +175,27 @@ struct dasd_ccw_req {
unsigned long long endclk; /* TOD-clock of request termination */ unsigned long long endclk; /* TOD-clock of request termination */
/* Callback that is called after reaching final status. */ /* Callback that is called after reaching final status. */
void (*callback)(struct dasd_ccw_req *, void *data); void (*callback)(struct dasd_ccw_req *, void *data);
void *callback_data; void *callback_data;
}; };
/* /*
* dasd_ccw_req -> status can be: * dasd_ccw_req -> status can be:
*/ */
#define DASD_CQR_FILLED 0x00 /* request is ready to be processed */ #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_DONE 0x01 /* request is completed successfully */
#define DASD_CQR_IN_IO 0x02 /* request is currently in IO */ #define DASD_CQR_NEED_ERP 0x02 /* request needs recovery action */
#define DASD_CQR_DONE 0x03 /* request is completed successfully */ #define DASD_CQR_IN_ERP 0x03 /* request is in recovery */
#define DASD_CQR_ERROR 0x04 /* request is completed with error */ #define DASD_CQR_FAILED 0x04 /* request is finally failed */
#define DASD_CQR_FAILED 0x05 /* request is finally failed */ #define DASD_CQR_TERMINATED 0x05 /* request was stopped by driver */
#define DASD_CQR_CLEAR 0x06 /* request is clear pending */
#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 */ /* per dasd_ccw_req flags */
#define DASD_CQR_FLAGS_USE_ERP 0 /* use ERP for this request */ #define DASD_CQR_FLAGS_USE_ERP 0 /* use ERP for this request */
...@@ -214,52 +218,71 @@ struct dasd_discipline { ...@@ -214,52 +218,71 @@ struct dasd_discipline {
struct list_head list; /* used for list of disciplines */ struct list_head list; /* used for list of disciplines */
/* /*
* Device recognition functions. check_device is used to verify * Device recognition functions. check_device is used to verify
* the sense data and the information returned by read device * the sense data and the information returned by read device
* characteristics. It returns 0 if the discipline can be used * characteristics. It returns 0 if the discipline can be used
* for the device in question. * for the device in question. uncheck_device is called during
* do_analysis is used in the step from device state "basic" to * device shutdown to deregister a device from its discipline.
* state "accept". It returns 0 if the device can be made ready, */
* it returns -EMEDIUMTYPE if the device can't be made ready or int (*check_device) (struct dasd_device *);
* -EAGAIN if do_analysis started a ccw that needs to complete void (*uncheck_device) (struct dasd_device *);
* before the analysis may be repeated.
*/ /*
int (*check_device)(struct dasd_device *); * do_analysis is used in the step from device state "basic" to
int (*do_analysis) (struct dasd_device *); * 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
* Device operation functions. build_cp creates a ccw chain for * before the analysis may be repeated.
* a block device request, start_io starts the request and */
* term_IO cancels it (e.g. in case of a timeout). format_device int (*do_analysis) (struct dasd_block *);
* returns a ccw chain to be used to format the device.
*/ /*
* 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_ccw_req *(*build_cp) (struct dasd_device *,
struct dasd_block *,
struct request *); struct request *);
int (*start_IO) (struct dasd_ccw_req *); int (*start_IO) (struct dasd_ccw_req *);
int (*term_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 dasd_ccw_req *(*format_device) (struct dasd_device *,
struct format_data_t *); struct format_data_t *);
int (*free_cp) (struct dasd_ccw_req *, struct request *); 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 * returns 'dasd_era_recover' erp_action() is called to create a
* special error recovery ccw. erp_postaction() is called after * special error recovery ccw. erp_postaction() is called after
* an error recovery ccw has finished its execution. dump_sense * an error recovery ccw has finished its execution. dump_sense
* is called for every error condition to print the sense data * is called for every error condition to print the sense data
* to the console. * to the console.
*/ */
dasd_era_t(*examine_error) (struct dasd_ccw_req *, struct irb *);
dasd_erp_fn_t(*erp_action) (struct dasd_ccw_req *); dasd_erp_fn_t(*erp_action) (struct dasd_ccw_req *);
dasd_erp_fn_t(*erp_postaction) (struct dasd_ccw_req *); dasd_erp_fn_t(*erp_postaction) (struct dasd_ccw_req *);
void (*dump_sense) (struct dasd_device *, struct dasd_ccw_req *, void (*dump_sense) (struct dasd_device *, struct dasd_ccw_req *,
struct irb *); struct irb *);
void (*handle_unsolicited_interrupt) (struct dasd_device *,
struct irb *);
/* i/o control functions. */ /* 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 (*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; extern struct dasd_discipline *dasd_diag_discipline_pointer;
...@@ -267,12 +290,18 @@ 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. * 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 { struct dasd_uid {
__u8 alias; __u8 type;
char vendor[4]; char vendor[4];
char serial[15]; char serial[15];
__u16 ssid; __u16 ssid;
__u8 unit_addr; __u8 real_unit_addr;
__u8 base_unit_addr;
}; };
/* /*
...@@ -293,14 +322,9 @@ struct dasd_uid { ...@@ -293,14 +322,9 @@ struct dasd_uid {
struct dasd_device { struct dasd_device {
/* Block device stuff. */ /* Block device stuff. */
struct gendisk *gdp; struct dasd_block *block;
struct request_queue *request_queue;
spinlock_t request_queue_lock;
struct block_device *bdev;
unsigned int devindex; 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 long flags; /* per device flags */
unsigned short features; /* copy of devmap-features (read-only!) */ unsigned short features; /* copy of devmap-features (read-only!) */
...@@ -316,9 +340,8 @@ struct dasd_device { ...@@ -316,9 +340,8 @@ struct dasd_device {
int state, target; int state, target;
int stopped; /* device (ccw_device_start) was stopped */ int stopped; /* device (ccw_device_start) was stopped */
/* Open and reference count. */ /* reference count. */
atomic_t ref_count; atomic_t ref_count;
atomic_t open_count;
/* ccw queue and memory for static ccw/erp buffers. */ /* ccw queue and memory for static ccw/erp buffers. */
struct list_head ccw_queue; struct list_head ccw_queue;
...@@ -337,20 +360,45 @@ struct dasd_device { ...@@ -337,20 +360,45 @@ struct dasd_device {
struct ccw_device *cdev; 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 #ifdef CONFIG_DASD_PROFILE
struct dasd_profile_info_t profile; struct dasd_profile_info_t profile;
#endif #endif
}; };
/* reasons why device (ccw_device_start) was stopped */ /* reasons why device (ccw_device_start) was stopped */
#define DASD_STOPPED_NOT_ACC 1 /* not accessible */ #define DASD_STOPPED_NOT_ACC 1 /* not accessible */
#define DASD_STOPPED_QUIESCE 2 /* Quiesced */ #define DASD_STOPPED_QUIESCE 2 /* Quiesced */
#define DASD_STOPPED_PENDING 4 /* long busy */ #define DASD_STOPPED_PENDING 4 /* long busy */
#define DASD_STOPPED_DC_WAIT 8 /* disconnected, wait */ #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 */ /* 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_OFFLINE 3 /* device is in offline processing */
#define DASD_FLAG_EER_SNSS 4 /* A SNSS is required */ #define DASD_FLAG_EER_SNSS 4 /* A SNSS is required */
#define DASD_FLAG_EER_IN_USE 5 /* A SNSS request is running */ #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) ...@@ -489,6 +537,9 @@ dasd_kmalloc_set_cda(struct ccw1 *ccw, void *cda, struct dasd_device *device)
struct dasd_device *dasd_alloc_device(void); struct dasd_device *dasd_alloc_device(void);
void dasd_free_device(struct dasd_device *); 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_enable_device(struct dasd_device *);
void dasd_set_target_state(struct dasd_device *, int); void dasd_set_target_state(struct dasd_device *, int);
void dasd_kick_device(struct dasd_device *); void dasd_kick_device(struct dasd_device *);
...@@ -497,18 +548,23 @@ void dasd_add_request_head(struct dasd_ccw_req *); ...@@ -497,18 +548,23 @@ void dasd_add_request_head(struct dasd_ccw_req *);
void dasd_add_request_tail(struct dasd_ccw_req *); void dasd_add_request_tail(struct dasd_ccw_req *);
int dasd_start_IO(struct dasd_ccw_req *); int dasd_start_IO(struct dasd_ccw_req *);
int dasd_term_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(struct dasd_ccw_req *);
int dasd_sleep_on_immediatly(struct dasd_ccw_req *); int dasd_sleep_on_immediatly(struct dasd_ccw_req *);
int dasd_sleep_on_interruptible(struct dasd_ccw_req *); int dasd_sleep_on_interruptible(struct dasd_ccw_req *);
void dasd_set_timer(struct dasd_device *, int); void dasd_device_set_timer(struct dasd_device *, int);
void dasd_clear_timer(struct dasd_device *); 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_cancel_req(struct dasd_ccw_req *);
int dasd_flush_device_queue(struct dasd_device *);
int dasd_generic_probe (struct ccw_device *, struct dasd_discipline *); int dasd_generic_probe (struct ccw_device *, struct dasd_discipline *);
void dasd_generic_remove (struct ccw_device *cdev); void dasd_generic_remove (struct ccw_device *cdev);
int dasd_generic_set_online(struct ccw_device *, struct dasd_discipline *); int dasd_generic_set_online(struct ccw_device *, struct dasd_discipline *);
int dasd_generic_set_offline (struct ccw_device *cdev); int dasd_generic_set_offline (struct ccw_device *cdev);
int dasd_generic_notify(struct ccw_device *, int); 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); int dasd_generic_read_dev_chars(struct dasd_device *, char *, void **, int);
...@@ -542,10 +598,10 @@ int dasd_busid_known(char *); ...@@ -542,10 +598,10 @@ int dasd_busid_known(char *);
/* externals in dasd_gendisk.c */ /* externals in dasd_gendisk.c */
int dasd_gendisk_init(void); int dasd_gendisk_init(void);
void dasd_gendisk_exit(void); void dasd_gendisk_exit(void);
int dasd_gendisk_alloc(struct dasd_device *); int dasd_gendisk_alloc(struct dasd_block *);
void dasd_gendisk_free(struct dasd_device *); void dasd_gendisk_free(struct dasd_block *);
int dasd_scan_partitions(struct dasd_device *); int dasd_scan_partitions(struct dasd_block *);
void dasd_destroy_partitions(struct dasd_device *); void dasd_destroy_partitions(struct dasd_block *);
/* externals in dasd_ioctl.c */ /* externals in dasd_ioctl.c */
int dasd_ioctl(struct inode *, struct file *, unsigned int, unsigned long); 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, ...@@ -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_free_erp_request(struct dasd_ccw_req *, struct dasd_device *);
void dasd_log_sense(struct dasd_ccw_req *, struct irb *); 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 */ /* 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 *); 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 */ /* externals in dasd_eer.c */
#ifdef CONFIG_DASD_EER #ifdef CONFIG_DASD_EER
int dasd_eer_init(void); int dasd_eer_init(void);
......
...@@ -38,15 +38,15 @@ dasd_ioctl_api_version(void __user *argp) ...@@ -38,15 +38,15 @@ dasd_ioctl_api_version(void __user *argp)
static int static int
dasd_ioctl_enable(struct block_device *bdev) 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)) if (!capable(CAP_SYS_ADMIN))
return -EACCES; return -EACCES;
dasd_enable_device(device); dasd_enable_device(block->base);
/* Formatting the dasd device can change the capacity. */ /* Formatting the dasd device can change the capacity. */
mutex_lock(&bdev->bd_mutex); 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); mutex_unlock(&bdev->bd_mutex);
return 0; return 0;
} }
...@@ -58,7 +58,7 @@ dasd_ioctl_enable(struct block_device *bdev) ...@@ -58,7 +58,7 @@ dasd_ioctl_enable(struct block_device *bdev)
static int static int
dasd_ioctl_disable(struct block_device *bdev) 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)) if (!capable(CAP_SYS_ADMIN))
return -EACCES; return -EACCES;
...@@ -71,7 +71,7 @@ dasd_ioctl_disable(struct block_device *bdev) ...@@ -71,7 +71,7 @@ dasd_ioctl_disable(struct block_device *bdev)
* using the BIODASDFMT ioctl. Therefore the correct state for the * using the BIODASDFMT ioctl. Therefore the correct state for the
* device is DASD_STATE_BASIC that allows to do basic i/o. * 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 * Set i_size to zero, since read, write, etc. check against this
* value. * value.
...@@ -85,19 +85,19 @@ dasd_ioctl_disable(struct block_device *bdev) ...@@ -85,19 +85,19 @@ dasd_ioctl_disable(struct block_device *bdev)
/* /*
* Quiesce device. * Quiesce device.
*/ */
static int static int dasd_ioctl_quiesce(struct dasd_block *block)
dasd_ioctl_quiesce(struct dasd_device *device)
{ {
unsigned long flags; unsigned long flags;
struct dasd_device *base;
base = block->base;
if (!capable (CAP_SYS_ADMIN)) if (!capable (CAP_SYS_ADMIN))
return -EACCES; return -EACCES;
DEV_MESSAGE (KERN_DEBUG, device, "%s", DEV_MESSAGE(KERN_DEBUG, base, "%s", "Quiesce IO on device");
"Quiesce IO on device"); spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags);
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); base->stopped |= DASD_STOPPED_QUIESCE;
device->stopped |= DASD_STOPPED_QUIESCE; spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags);
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
return 0; return 0;
} }
...@@ -105,22 +105,21 @@ dasd_ioctl_quiesce(struct dasd_device *device) ...@@ -105,22 +105,21 @@ dasd_ioctl_quiesce(struct dasd_device *device)
/* /*
* Quiesce device. * Quiesce device.
*/ */
static int static int dasd_ioctl_resume(struct dasd_block *block)
dasd_ioctl_resume(struct dasd_device *device)
{ {
unsigned long flags; unsigned long flags;
struct dasd_device *base;
base = block->base;
if (!capable (CAP_SYS_ADMIN)) if (!capable (CAP_SYS_ADMIN))
return -EACCES; return -EACCES;
DEV_MESSAGE (KERN_DEBUG, device, "%s", DEV_MESSAGE(KERN_DEBUG, base, "%s", "resume IO on device");
"resume IO on device"); spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags);
base->stopped &= ~DASD_STOPPED_QUIESCE;
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags);
device->stopped &= ~DASD_STOPPED_QUIESCE;
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
dasd_schedule_bh (device); dasd_schedule_block_bh(block);
return 0; return 0;
} }
...@@ -130,22 +129,23 @@ dasd_ioctl_resume(struct dasd_device *device) ...@@ -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 * 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. * devices this means CCWs are generated to format a single track.
*/ */
static int static int dasd_format(struct dasd_block *block, struct format_data_t *fdata)
dasd_format(struct dasd_device * device, struct format_data_t * fdata)
{ {
struct dasd_ccw_req *cqr; struct dasd_ccw_req *cqr;
struct dasd_device *base;
int rc; int rc;
if (device->discipline->format_device == NULL) base = block->base;
if (base->discipline->format_device == NULL)
return -EPERM; return -EPERM;
if (device->state != DASD_STATE_BASIC) { if (base->state != DASD_STATE_BASIC) {
DEV_MESSAGE(KERN_WARNING, device, "%s", DEV_MESSAGE(KERN_WARNING, base, "%s",
"dasd_format: device is not disabled! "); "dasd_format: device is not disabled! ");
return -EBUSY; return -EBUSY;
} }
DBF_DEV_EVENT(DBF_NOTICE, device, DBF_DEV_EVENT(DBF_NOTICE, base,
"formatting units %d to %d (%d B blocks) flags %d", "formatting units %d to %d (%d B blocks) flags %d",
fdata->start_unit, fdata->start_unit,
fdata->stop_unit, fdata->blksize, fdata->intensity); fdata->stop_unit, fdata->blksize, fdata->intensity);
...@@ -156,20 +156,20 @@ dasd_format(struct dasd_device * device, struct format_data_t * fdata) ...@@ -156,20 +156,20 @@ dasd_format(struct dasd_device * device, struct format_data_t * fdata)
* enabling the device later. * enabling the device later.
*/ */
if (fdata->start_unit == 0) { 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); bdev->bd_inode->i_blkbits = blksize_bits(fdata->blksize);
bdput(bdev); bdput(bdev);
} }
while (fdata->start_unit <= fdata->stop_unit) { 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)) if (IS_ERR(cqr))
return PTR_ERR(cqr); return PTR_ERR(cqr);
rc = dasd_sleep_on_interruptible(cqr); rc = dasd_sleep_on_interruptible(cqr);
dasd_sfree_request(cqr, cqr->device); dasd_sfree_request(cqr, cqr->memdev);
if (rc) { if (rc) {
if (rc != -ERESTARTSYS) if (rc != -ERESTARTSYS)
DEV_MESSAGE(KERN_ERR, device, DEV_MESSAGE(KERN_ERR, base,
" Formatting of unit %d failed " " Formatting of unit %d failed "
"with rc = %d", "with rc = %d",
fdata->start_unit, rc); fdata->start_unit, rc);
...@@ -186,7 +186,7 @@ dasd_format(struct dasd_device * device, struct format_data_t * fdata) ...@@ -186,7 +186,7 @@ dasd_format(struct dasd_device * device, struct format_data_t * fdata)
static int static int
dasd_ioctl_format(struct block_device *bdev, void __user *argp) 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; struct format_data_t fdata;
if (!capable(CAP_SYS_ADMIN)) if (!capable(CAP_SYS_ADMIN))
...@@ -194,51 +194,47 @@ dasd_ioctl_format(struct block_device *bdev, void __user *argp) ...@@ -194,51 +194,47 @@ dasd_ioctl_format(struct block_device *bdev, void __user *argp)
if (!argp) if (!argp)
return -EINVAL; return -EINVAL;
if (device->features & DASD_FEATURE_READONLY) if (block->base->features & DASD_FEATURE_READONLY)
return -EROFS; return -EROFS;
if (copy_from_user(&fdata, argp, sizeof(struct format_data_t))) if (copy_from_user(&fdata, argp, sizeof(struct format_data_t)))
return -EFAULT; return -EFAULT;
if (bdev != bdev->bd_contains) { if (bdev != bdev->bd_contains) {
DEV_MESSAGE(KERN_WARNING, device, "%s", DEV_MESSAGE(KERN_WARNING, block->base, "%s",
"Cannot low-level format a partition"); "Cannot low-level format a partition");
return -EINVAL; return -EINVAL;
} }
return dasd_format(device, &fdata); return dasd_format(block, &fdata);
} }
#ifdef CONFIG_DASD_PROFILE #ifdef CONFIG_DASD_PROFILE
/* /*
* Reset device profile information * Reset device profile information
*/ */
static int static int dasd_ioctl_reset_profile(struct dasd_block *block)
dasd_ioctl_reset_profile(struct dasd_device *device)
{ {
memset(&device->profile, 0, sizeof (struct dasd_profile_info_t)); memset(&block->profile, 0, sizeof(struct dasd_profile_info_t));
return 0; return 0;
} }
/* /*
* Return device profile information * Return device profile information
*/ */
static int static int dasd_ioctl_read_profile(struct dasd_block *block, void __user *argp)
dasd_ioctl_read_profile(struct dasd_device *device, void __user *argp)
{ {
if (dasd_profile_level == DASD_PROFILE_OFF) if (dasd_profile_level == DASD_PROFILE_OFF)
return -EIO; return -EIO;
if (copy_to_user(argp, &device->profile, if (copy_to_user(argp, &block->profile,
sizeof (struct dasd_profile_info_t))) sizeof(struct dasd_profile_info_t)))
return -EFAULT; return -EFAULT;
return 0; return 0;
} }
#else #else
static int static int dasd_ioctl_reset_profile(struct dasd_block *block)
dasd_ioctl_reset_profile(struct dasd_device *device)
{ {
return -ENOSYS; return -ENOSYS;
} }
static int static int dasd_ioctl_read_profile(struct dasd_block *block, void __user *argp)
dasd_ioctl_read_profile(struct dasd_device *device, void __user *argp)
{ {
return -ENOSYS; return -ENOSYS;
} }
...@@ -247,87 +243,88 @@ dasd_ioctl_read_profile(struct dasd_device *device, void __user *argp) ...@@ -247,87 +243,88 @@ dasd_ioctl_read_profile(struct dasd_device *device, void __user *argp)
/* /*
* Return dasd information. Used for BIODASDINFO and BIODASDINFO2. * Return dasd information. Used for BIODASDINFO and BIODASDINFO2.
*/ */
static int static int dasd_ioctl_information(struct dasd_block *block,
dasd_ioctl_information(struct dasd_device *device, unsigned int cmd, void __user *argp)
unsigned int cmd, void __user *argp)
{ {
struct dasd_information2_t *dasd_info; struct dasd_information2_t *dasd_info;
unsigned long flags; unsigned long flags;
int rc; int rc;
struct dasd_device *base;
struct ccw_device *cdev; struct ccw_device *cdev;
struct ccw_dev_id dev_id; struct ccw_dev_id dev_id;
if (!device->discipline->fill_info) base = block->base;
if (!base->discipline->fill_info)
return -EINVAL; return -EINVAL;
dasd_info = kzalloc(sizeof(struct dasd_information2_t), GFP_KERNEL); dasd_info = kzalloc(sizeof(struct dasd_information2_t), GFP_KERNEL);
if (dasd_info == NULL) if (dasd_info == NULL)
return -ENOMEM; return -ENOMEM;
rc = device->discipline->fill_info(device, dasd_info); rc = base->discipline->fill_info(base, dasd_info);
if (rc) { if (rc) {
kfree(dasd_info); kfree(dasd_info);
return rc; return rc;
} }
cdev = device->cdev; cdev = base->cdev;
ccw_device_get_id(cdev, &dev_id); ccw_device_get_id(cdev, &dev_id);
dasd_info->devno = dev_id.devno; 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_type = cdev->id.cu_type;
dasd_info->cu_model = cdev->id.cu_model; dasd_info->cu_model = cdev->id.cu_model;
dasd_info->dev_type = cdev->id.dev_type; dasd_info->dev_type = cdev->id.dev_type;
dasd_info->dev_model = cdev->id.dev_model; 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 open_count is increased for every opener, that includes
* the blkdev_get in dasd_scan_partitions. * the blkdev_get in dasd_scan_partitions.
* This must be hidden from user-space. * This must be hidden from user-space.
*/ */
dasd_info->open_count = atomic_read(&device->open_count); dasd_info->open_count = atomic_read(&block->open_count);
if (!device->bdev) if (!block->bdev)
dasd_info->open_count++; dasd_info->open_count++;
/* /*
* check if device is really formatted * check if device is really formatted
* LDL / CDL was returned by 'fill_info' * LDL / CDL was returned by 'fill_info'
*/ */
if ((device->state < DASD_STATE_READY) || if ((base->state < DASD_STATE_READY) ||
(dasd_check_blocksize(device->bp_block))) (dasd_check_blocksize(block->bp_block)))
dasd_info->format = DASD_FORMAT_NONE; dasd_info->format = DASD_FORMAT_NONE;
dasd_info->features |= dasd_info->features |=
((device->features & DASD_FEATURE_READONLY) != 0); ((base->features & DASD_FEATURE_READONLY) != 0);
if (device->discipline) if (base->discipline)
memcpy(dasd_info->type, device->discipline->name, 4); memcpy(dasd_info->type, base->discipline->name, 4);
else else
memcpy(dasd_info->type, "none", 4); memcpy(dasd_info->type, "none", 4);
if (device->request_queue->request_fn) { if (block->request_queue->request_fn) {
struct list_head *l; struct list_head *l;
#ifdef DASD_EXTENDED_PROFILING #ifdef DASD_EXTENDED_PROFILING
{ {
struct list_head *l; struct list_head *l;
spin_lock_irqsave(&device->lock, flags); spin_lock_irqsave(&block->lock, flags);
list_for_each(l, &device->request_queue->queue_head) list_for_each(l, &block->request_queue->queue_head)
dasd_info->req_queue_len++; dasd_info->req_queue_len++;
spin_unlock_irqrestore(&device->lock, flags); spin_unlock_irqrestore(&block->lock, flags);
} }
#endif /* DASD_EXTENDED_PROFILING */ #endif /* DASD_EXTENDED_PROFILING */
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags);
list_for_each(l, &device->ccw_queue) list_for_each(l, &base->ccw_queue)
dasd_info->chanq_len++; dasd_info->chanq_len++;
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), spin_unlock_irqrestore(get_ccwdev_lock(base->cdev),
flags); flags);
} }
rc = 0; rc = 0;
if (copy_to_user(argp, dasd_info, if (copy_to_user(argp, dasd_info,
((cmd == (unsigned int) BIODASDINFO2) ? ((cmd == (unsigned int) BIODASDINFO2) ?
sizeof (struct dasd_information2_t) : sizeof(struct dasd_information2_t) :
sizeof (struct dasd_information_t)))) sizeof(struct dasd_information_t))))
rc = -EFAULT; rc = -EFAULT;
kfree(dasd_info); kfree(dasd_info);
return rc; return rc;
...@@ -339,7 +336,7 @@ dasd_ioctl_information(struct dasd_device *device, ...@@ -339,7 +336,7 @@ dasd_ioctl_information(struct dasd_device *device,
static int static int
dasd_ioctl_set_ro(struct block_device *bdev, void __user *argp) 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; int intval;
if (!capable(CAP_SYS_ADMIN)) if (!capable(CAP_SYS_ADMIN))
...@@ -351,11 +348,10 @@ dasd_ioctl_set_ro(struct block_device *bdev, void __user *argp) ...@@ -351,11 +348,10 @@ dasd_ioctl_set_ro(struct block_device *bdev, void __user *argp)
return -EFAULT; return -EFAULT;
set_disk_ro(bdev->bd_disk, intval); 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 static int dasd_ioctl_readall_cmb(struct dasd_block *block, unsigned int cmd,
dasd_ioctl_readall_cmb(struct dasd_device *device, unsigned int cmd,
unsigned long arg) unsigned long arg)
{ {
struct cmbdata __user *argp = (void __user *) arg; struct cmbdata __user *argp = (void __user *) arg;
...@@ -363,7 +359,7 @@ dasd_ioctl_readall_cmb(struct dasd_device *device, unsigned int cmd, ...@@ -363,7 +359,7 @@ dasd_ioctl_readall_cmb(struct dasd_device *device, unsigned int cmd,
struct cmbdata data; struct cmbdata data;
int ret; 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)))) if (!ret && copy_to_user(argp, &data, min(size, sizeof(*argp))))
return -EFAULT; return -EFAULT;
return ret; return ret;
...@@ -374,10 +370,10 @@ dasd_ioctl(struct inode *inode, struct file *file, ...@@ -374,10 +370,10 @@ dasd_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg) unsigned int cmd, unsigned long arg)
{ {
struct block_device *bdev = inode->i_bdev; 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; void __user *argp = (void __user *)arg;
if (!device) if (!block)
return -ENODEV; return -ENODEV;
if ((_IOC_DIR(cmd) != _IOC_NONE) && !arg) { if ((_IOC_DIR(cmd) != _IOC_NONE) && !arg) {
...@@ -391,33 +387,33 @@ dasd_ioctl(struct inode *inode, struct file *file, ...@@ -391,33 +387,33 @@ dasd_ioctl(struct inode *inode, struct file *file,
case BIODASDENABLE: case BIODASDENABLE:
return dasd_ioctl_enable(bdev); return dasd_ioctl_enable(bdev);
case BIODASDQUIESCE: case BIODASDQUIESCE:
return dasd_ioctl_quiesce(device); return dasd_ioctl_quiesce(block);
case BIODASDRESUME: case BIODASDRESUME:
return dasd_ioctl_resume(device); return dasd_ioctl_resume(block);
case BIODASDFMT: case BIODASDFMT:
return dasd_ioctl_format(bdev, argp); return dasd_ioctl_format(bdev, argp);
case BIODASDINFO: case BIODASDINFO:
return dasd_ioctl_information(device, cmd, argp); return dasd_ioctl_information(block, cmd, argp);
case BIODASDINFO2: case BIODASDINFO2:
return dasd_ioctl_information(device, cmd, argp); return dasd_ioctl_information(block, cmd, argp);
case BIODASDPRRD: case BIODASDPRRD:
return dasd_ioctl_read_profile(device, argp); return dasd_ioctl_read_profile(block, argp);
case BIODASDPRRST: case BIODASDPRRST:
return dasd_ioctl_reset_profile(device); return dasd_ioctl_reset_profile(block);
case BLKROSET: case BLKROSET:
return dasd_ioctl_set_ro(bdev, argp); return dasd_ioctl_set_ro(bdev, argp);
case DASDAPIVER: case DASDAPIVER:
return dasd_ioctl_api_version(argp); return dasd_ioctl_api_version(argp);
case BIODASDCMFENABLE: case BIODASDCMFENABLE:
return enable_cmf(device->cdev); return enable_cmf(block->base->cdev);
case BIODASDCMFDISABLE: case BIODASDCMFDISABLE:
return disable_cmf(device->cdev); return disable_cmf(block->base->cdev);
case BIODASDREADALLCMB: case BIODASDREADALLCMB:
return dasd_ioctl_readall_cmb(device, cmd, arg); return dasd_ioctl_readall_cmb(block, cmd, arg);
default: default:
/* if the discipline has an ioctl method try it. */ /* if the discipline has an ioctl method try it. */
if (device->discipline->ioctl) { if (block->base->discipline->ioctl) {
int rval = device->discipline->ioctl(device, cmd, argp); int rval = block->base->discipline->ioctl(block, cmd, argp);
if (rval != -ENOIOCTLCMD) if (rval != -ENOIOCTLCMD)
return rval; return rval;
} }
......
...@@ -54,11 +54,16 @@ static int ...@@ -54,11 +54,16 @@ static int
dasd_devices_show(struct seq_file *m, void *v) dasd_devices_show(struct seq_file *m, void *v)
{ {
struct dasd_device *device; struct dasd_device *device;
struct dasd_block *block;
char *substr; char *substr;
device = dasd_device_from_devindex((unsigned long) v - 1); device = dasd_device_from_devindex((unsigned long) v - 1);
if (IS_ERR(device)) if (IS_ERR(device))
return 0; return 0;
if (device->block)
block = device->block;
else
return 0;
/* Print device number. */ /* Print device number. */
seq_printf(m, "%s", device->cdev->dev.bus_id); seq_printf(m, "%s", device->cdev->dev.bus_id);
/* Print discipline string. */ /* Print discipline string. */
...@@ -67,14 +72,14 @@ dasd_devices_show(struct seq_file *m, void *v) ...@@ -67,14 +72,14 @@ dasd_devices_show(struct seq_file *m, void *v)
else else
seq_printf(m, "(none)"); seq_printf(m, "(none)");
/* Print kdev. */ /* Print kdev. */
if (device->gdp) if (block->gdp)
seq_printf(m, " at (%3d:%6d)", seq_printf(m, " at (%3d:%6d)",
device->gdp->major, device->gdp->first_minor); block->gdp->major, block->gdp->first_minor);
else else
seq_printf(m, " at (???:??????)"); seq_printf(m, " at (???:??????)");
/* Print device name. */ /* Print device name. */
if (device->gdp) if (block->gdp)
seq_printf(m, " is %-8s", device->gdp->disk_name); seq_printf(m, " is %-8s", block->gdp->disk_name);
else else
seq_printf(m, " is ????????"); seq_printf(m, " is ????????");
/* Print devices features. */ /* Print devices features. */
...@@ -100,14 +105,14 @@ dasd_devices_show(struct seq_file *m, void *v) ...@@ -100,14 +105,14 @@ dasd_devices_show(struct seq_file *m, void *v)
case DASD_STATE_READY: case DASD_STATE_READY:
case DASD_STATE_ONLINE: case DASD_STATE_ONLINE:
seq_printf(m, "active "); seq_printf(m, "active ");
if (dasd_check_blocksize(device->bp_block)) if (dasd_check_blocksize(block->bp_block))
seq_printf(m, "n/f "); seq_printf(m, "n/f ");
else else
seq_printf(m, seq_printf(m,
"at blocksize: %d, %ld blocks, %ld MB", "at blocksize: %d, %ld blocks, %ld MB",
device->bp_block, device->blocks, block->bp_block, block->blocks,
((device->bp_block >> 9) * ((block->bp_block >> 9) *
device->blocks) >> 11); block->blocks) >> 11);
break; break;
default: default:
seq_printf(m, "no stat"); seq_printf(m, "no stat");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment