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