From 773bab4a468b856a8c3b6626e76589d81d8d4e07 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Tue, 23 Oct 2012 20:28:37 +0200 Subject: s390/dasd: fix multi-line printks with multiple KERN_s Do not use more than one KERN_ per printk. Signed-off-by: Sebastian Ott Acked-by: Stefan Haberland Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd_eckd.c | 76 ++++++++++++++++++++---------------------- drivers/s390/block/dasd_fba.c | 23 ++++++------- 2 files changed, 48 insertions(+), 51 deletions(-) (limited to 'drivers/s390/block') diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 108332b44d98..705b1829b8f8 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -3847,7 +3847,7 @@ dasd_eckd_dump_ccw_range(struct ccw1 *from, struct ccw1 *to, char *page) len = 0; while (from <= to) { - len += sprintf(page + len, KERN_ERR PRINTK_HEADER + len += sprintf(page + len, PRINTK_HEADER " CCW %p: %08X %08X DAT:", from, ((int *) from)[0], ((int *) from)[1]); @@ -3908,23 +3908,23 @@ static void dasd_eckd_dump_sense_ccw(struct dasd_device *device, return; } /* dump the sense data */ - len = sprintf(page, KERN_ERR PRINTK_HEADER + len = sprintf(page, PRINTK_HEADER " I/O status report for device %s:\n", dev_name(&device->cdev->dev)); - len += sprintf(page + len, KERN_ERR PRINTK_HEADER + len += sprintf(page + len, PRINTK_HEADER " in req: %p CC:%02X FC:%02X AC:%02X SC:%02X DS:%02X " "CS:%02X RC:%d\n", req, scsw_cc(&irb->scsw), scsw_fctl(&irb->scsw), scsw_actl(&irb->scsw), scsw_stctl(&irb->scsw), scsw_dstat(&irb->scsw), scsw_cstat(&irb->scsw), req ? req->intrc : 0); - len += sprintf(page + len, KERN_ERR PRINTK_HEADER + len += sprintf(page + len, PRINTK_HEADER " device %s: Failing CCW: %p\n", dev_name(&device->cdev->dev), (void *) (addr_t) irb->scsw.cmd.cpa); if (irb->esw.esw0.erw.cons) { for (sl = 0; sl < 4; sl++) { - len += sprintf(page + len, KERN_ERR PRINTK_HEADER + len += sprintf(page + len, PRINTK_HEADER " Sense(hex) %2d-%2d:", (8 * sl), ((8 * sl) + 7)); @@ -3937,23 +3937,23 @@ static void dasd_eckd_dump_sense_ccw(struct dasd_device *device, if (irb->ecw[27] & DASD_SENSE_BIT_0) { /* 24 Byte Sense Data */ - sprintf(page + len, KERN_ERR PRINTK_HEADER + sprintf(page + len, PRINTK_HEADER " 24 Byte: %x MSG %x, " "%s MSGb to SYSOP\n", irb->ecw[7] >> 4, irb->ecw[7] & 0x0f, irb->ecw[1] & 0x10 ? "" : "no"); } else { /* 32 Byte Sense Data */ - sprintf(page + len, KERN_ERR PRINTK_HEADER + sprintf(page + len, PRINTK_HEADER " 32 Byte: Format: %x " "Exception class %x\n", irb->ecw[6] & 0x0f, irb->ecw[22] >> 4); } } else { - sprintf(page + len, KERN_ERR PRINTK_HEADER + sprintf(page + len, PRINTK_HEADER " SORRY - NO VALID SENSE AVAILABLE\n"); } - printk("%s", page); + printk(KERN_ERR "%s", page); if (req) { /* req == NULL for unsolicited interrupts */ @@ -3962,10 +3962,10 @@ static void dasd_eckd_dump_sense_ccw(struct dasd_device *device, 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 + len = sprintf(page, PRINTK_HEADER " Related CP in req: %p\n", req); dasd_eckd_dump_ccw_range(first, to, page + len); - printk("%s", page); + printk(KERN_ERR "%s", page); /* print failing CCW area (maximum 4) */ /* scsw->cda is either valid or zero */ @@ -3975,7 +3975,7 @@ static void dasd_eckd_dump_sense_ccw(struct dasd_device *device, irb->scsw.cmd.cpa; /* failing CCW */ if (from < fail - 2) { from = fail - 2; /* there is a gap - print header */ - len += sprintf(page, KERN_ERR PRINTK_HEADER "......\n"); + len += sprintf(page, PRINTK_HEADER "......\n"); } to = min(fail + 1, last); len += dasd_eckd_dump_ccw_range(from, to, page + len); @@ -3984,11 +3984,11 @@ static void dasd_eckd_dump_sense_ccw(struct dasd_device *device, 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 += sprintf(page + len, PRINTK_HEADER "......\n"); } len += dasd_eckd_dump_ccw_range(from, last, page + len); if (len > 0) - printk("%s", page); + printk(KERN_ERR "%s", page); } free_page((unsigned long) page); } @@ -4012,10 +4012,10 @@ static void dasd_eckd_dump_sense_tcw(struct dasd_device *device, return; } /* dump the sense data */ - len = sprintf(page, KERN_ERR PRINTK_HEADER + len = sprintf(page, PRINTK_HEADER " I/O status report for device %s:\n", dev_name(&device->cdev->dev)); - len += sprintf(page + len, KERN_ERR PRINTK_HEADER + len += sprintf(page + len, PRINTK_HEADER " in req: %p CC:%02X FC:%02X AC:%02X SC:%02X DS:%02X " "CS:%02X fcxs:%02X schxs:%02X RC:%d\n", req, scsw_cc(&irb->scsw), scsw_fctl(&irb->scsw), @@ -4023,7 +4023,7 @@ static void dasd_eckd_dump_sense_tcw(struct dasd_device *device, scsw_dstat(&irb->scsw), scsw_cstat(&irb->scsw), irb->scsw.tm.fcxs, irb->scsw.tm.schxs, req ? req->intrc : 0); - len += sprintf(page + len, KERN_ERR PRINTK_HEADER + len += sprintf(page + len, PRINTK_HEADER " device %s: Failing TCW: %p\n", dev_name(&device->cdev->dev), (void *) (addr_t) irb->scsw.tm.tcw); @@ -4035,43 +4035,42 @@ static void dasd_eckd_dump_sense_tcw(struct dasd_device *device, (struct tcw *)(unsigned long)irb->scsw.tm.tcw); if (tsb) { - len += sprintf(page + len, KERN_ERR PRINTK_HEADER + len += sprintf(page + len, PRINTK_HEADER " tsb->length %d\n", tsb->length); - len += sprintf(page + len, KERN_ERR PRINTK_HEADER + len += sprintf(page + len, PRINTK_HEADER " tsb->flags %x\n", tsb->flags); - len += sprintf(page + len, KERN_ERR PRINTK_HEADER + len += sprintf(page + len, PRINTK_HEADER " tsb->dcw_offset %d\n", tsb->dcw_offset); - len += sprintf(page + len, KERN_ERR PRINTK_HEADER + len += sprintf(page + len, PRINTK_HEADER " tsb->count %d\n", tsb->count); residual = tsb->count - 28; - len += sprintf(page + len, KERN_ERR PRINTK_HEADER + len += sprintf(page + len, PRINTK_HEADER " residual %d\n", residual); switch (tsb->flags & 0x07) { case 1: /* tsa_iostat */ - len += sprintf(page + len, KERN_ERR PRINTK_HEADER + len += sprintf(page + len, PRINTK_HEADER " tsb->tsa.iostat.dev_time %d\n", tsb->tsa.iostat.dev_time); - len += sprintf(page + len, KERN_ERR PRINTK_HEADER + len += sprintf(page + len, PRINTK_HEADER " tsb->tsa.iostat.def_time %d\n", tsb->tsa.iostat.def_time); - len += sprintf(page + len, KERN_ERR PRINTK_HEADER + len += sprintf(page + len, PRINTK_HEADER " tsb->tsa.iostat.queue_time %d\n", tsb->tsa.iostat.queue_time); - len += sprintf(page + len, KERN_ERR PRINTK_HEADER + len += sprintf(page + len, PRINTK_HEADER " tsb->tsa.iostat.dev_busy_time %d\n", tsb->tsa.iostat.dev_busy_time); - len += sprintf(page + len, KERN_ERR PRINTK_HEADER + len += sprintf(page + len, PRINTK_HEADER " tsb->tsa.iostat.dev_act_time %d\n", tsb->tsa.iostat.dev_act_time); sense = tsb->tsa.iostat.sense; break; case 2: /* ts_ddpc */ - len += sprintf(page + len, KERN_ERR PRINTK_HEADER + len += sprintf(page + len, PRINTK_HEADER " tsb->tsa.ddpc.rc %d\n", tsb->tsa.ddpc.rc); for (sl = 0; sl < 2; sl++) { - len += sprintf(page + len, - KERN_ERR PRINTK_HEADER + len += sprintf(page + len, PRINTK_HEADER " tsb->tsa.ddpc.rcq %2d-%2d: ", (8 * sl), ((8 * sl) + 7)); rcq = tsb->tsa.ddpc.rcq; @@ -4084,15 +4083,14 @@ static void dasd_eckd_dump_sense_tcw(struct dasd_device *device, sense = tsb->tsa.ddpc.sense; break; case 3: /* tsa_intrg */ - len += sprintf(page + len, KERN_ERR PRINTK_HEADER - " tsb->tsa.intrg.: not supportet yet \n"); + len += sprintf(page + len, PRINTK_HEADER + " tsb->tsa.intrg.: not supportet yet\n"); break; } if (sense) { for (sl = 0; sl < 4; sl++) { - len += sprintf(page + len, - KERN_ERR PRINTK_HEADER + len += sprintf(page + len, PRINTK_HEADER " Sense(hex) %2d-%2d:", (8 * sl), ((8 * sl) + 7)); for (sct = 0; sct < 8; sct++) { @@ -4104,27 +4102,27 @@ static void dasd_eckd_dump_sense_tcw(struct dasd_device *device, if (sense[27] & DASD_SENSE_BIT_0) { /* 24 Byte Sense Data */ - sprintf(page + len, KERN_ERR PRINTK_HEADER + sprintf(page + len, PRINTK_HEADER " 24 Byte: %x MSG %x, " "%s MSGb to SYSOP\n", sense[7] >> 4, sense[7] & 0x0f, sense[1] & 0x10 ? "" : "no"); } else { /* 32 Byte Sense Data */ - sprintf(page + len, KERN_ERR PRINTK_HEADER + sprintf(page + len, PRINTK_HEADER " 32 Byte: Format: %x " "Exception class %x\n", sense[6] & 0x0f, sense[22] >> 4); } } else { - sprintf(page + len, KERN_ERR PRINTK_HEADER + sprintf(page + len, PRINTK_HEADER " SORRY - NO VALID SENSE AVAILABLE\n"); } } else { - sprintf(page + len, KERN_ERR PRINTK_HEADER + sprintf(page + len, PRINTK_HEADER " SORRY - NO TSB DATA AVAILABLE\n"); } - printk("%s", page); + printk(KERN_ERR "%s", page); free_page((unsigned long) page); } diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c index fb7f3bdc6604..eb748507c7fa 100644 --- a/drivers/s390/block/dasd_fba.c +++ b/drivers/s390/block/dasd_fba.c @@ -479,19 +479,19 @@ dasd_fba_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req, "No memory to dump sense data"); return; } - len = sprintf(page, KERN_ERR PRINTK_HEADER + len = sprintf(page, PRINTK_HEADER " I/O status report for device %s:\n", dev_name(&device->cdev->dev)); - len += sprintf(page + len, KERN_ERR PRINTK_HEADER + len += sprintf(page + len, PRINTK_HEADER " in req: %p CS: 0x%02X DS: 0x%02X\n", req, irb->scsw.cmd.cstat, irb->scsw.cmd.dstat); - len += sprintf(page + len, KERN_ERR PRINTK_HEADER + len += sprintf(page + len, PRINTK_HEADER " device %s: Failing CCW: %p\n", dev_name(&device->cdev->dev), (void *) (addr_t) irb->scsw.cmd.cpa); if (irb->esw.esw0.erw.cons) { for (sl = 0; sl < 4; sl++) { - len += sprintf(page + len, KERN_ERR PRINTK_HEADER + len += sprintf(page + len, PRINTK_HEADER " Sense(hex) %2d-%2d:", (8 * sl), ((8 * sl) + 7)); @@ -502,7 +502,7 @@ dasd_fba_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req, len += sprintf(page + len, "\n"); } } else { - len += sprintf(page + len, KERN_ERR PRINTK_HEADER + len += sprintf(page + len, PRINTK_HEADER " SORRY - NO VALID SENSE AVAILABLE\n"); } printk(KERN_ERR "%s", page); @@ -512,10 +512,9 @@ dasd_fba_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req, act = req->cpaddr; for (last = act; last->flags & (CCW_FLAG_CC | CCW_FLAG_DC); last++); end = min(act + 8, last); - len = sprintf(page, KERN_ERR PRINTK_HEADER - " Related CP in req: %p\n", req); + len = sprintf(page, PRINTK_HEADER " Related CP in req: %p\n", req); while (act <= end) { - len += sprintf(page + len, KERN_ERR PRINTK_HEADER + len += sprintf(page + len, PRINTK_HEADER " CCW %p: %08X %08X DAT:", act, ((int *) act)[0], ((int *) act)[1]); for (count = 0; count < 32 && count < act->count; @@ -533,11 +532,11 @@ dasd_fba_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req, len = 0; if (act < ((struct ccw1 *)(addr_t) irb->scsw.cmd.cpa) - 2) { act = ((struct ccw1 *)(addr_t) irb->scsw.cmd.cpa) - 2; - len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n"); + len += sprintf(page + len, PRINTK_HEADER "......\n"); } end = min((struct ccw1 *)(addr_t) irb->scsw.cmd.cpa + 2, last); while (act <= end) { - len += sprintf(page + len, KERN_ERR PRINTK_HEADER + len += sprintf(page + len, PRINTK_HEADER " CCW %p: %08X %08X DAT:", act, ((int *) act)[0], ((int *) act)[1]); for (count = 0; count < 32 && count < act->count; @@ -552,10 +551,10 @@ dasd_fba_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req, /* print last CCWs */ if (act < last - 2) { act = last - 2; - len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n"); + len += sprintf(page + len, PRINTK_HEADER "......\n"); } while (act <= last) { - len += sprintf(page + len, KERN_ERR PRINTK_HEADER + len += sprintf(page + len, PRINTK_HEADER " CCW %p: %08X %08X DAT:", act, ((int *) act)[0], ((int *) act)[1]); for (count = 0; count < 32 && count < act->count; -- cgit v1.2.3 From 287a0933237a227d1d6508de0ad6425b55a60476 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 29 Oct 2012 16:50:20 +0100 Subject: s390: use ccw_device_get_schid Let the dasd driver and qdio use ccw_device_get_schid and get rid of other similar functions. Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd_ioctl.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers/s390/block') diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c index 8252f37d04ed..03c0e0444553 100644 --- a/drivers/s390/block/dasd_ioctl.c +++ b/drivers/s390/block/dasd_ioctl.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -308,11 +309,12 @@ 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 subchannel_id sch_id; + struct ccw_dev_id dev_id; struct dasd_device *base; struct ccw_device *cdev; - struct ccw_dev_id dev_id; + unsigned long flags; + int rc; base = block->base; if (!base->discipline || !base->discipline->fill_info) @@ -330,9 +332,10 @@ static int dasd_ioctl_information(struct dasd_block *block, cdev = base->cdev; ccw_device_get_id(cdev, &dev_id); + ccw_device_get_schid(cdev, &sch_id); dasd_info->devno = dev_id.devno; - dasd_info->schid = _ccw_device_get_subchannel_number(base->cdev); + dasd_info->schid = sch_id.sch_no; dasd_info->cu_type = cdev->id.cu_type; dasd_info->cu_model = cdev->id.cu_model; dasd_info->dev_type = cdev->id.dev_type; -- cgit v1.2.3 From 55d3a85cd2fa3274a6dfa0901a3be342b433bfa0 Mon Sep 17 00:00:00 2001 From: Stefan Haberland Date: Tue, 27 Nov 2012 17:04:14 +0100 Subject: s390/dasd: fix unaccessible device after resume If a channel path is cabled incorrectly and the device is suspended and resumed the device may be inaccessible afterwards. Make the path connection check not interrupt the resume callback there could be other valid paths available. Signed-off-by: Stefan Haberland Reference-ID: RQM 1262 Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd_eckd.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) (limited to 'drivers/s390/block') diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 705b1829b8f8..806fe912d6e7 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -1026,7 +1026,7 @@ static int dasd_eckd_read_conf(struct dasd_device *device) { void *conf_data; int conf_len, conf_data_saved; - int rc; + int rc, path_err; __u8 lpm, opm; struct dasd_eckd_private *private, path_private; struct dasd_path *path_data; @@ -1037,6 +1037,7 @@ static int dasd_eckd_read_conf(struct dasd_device *device) path_data = &device->path_data; opm = ccw_device_get_path_mask(device->cdev); conf_data_saved = 0; + path_err = 0; /* get configuration data per operational path */ for (lpm = 0x80; lpm; lpm>>= 1) { if (!(lpm & opm)) @@ -1122,7 +1123,8 @@ static int dasd_eckd_read_conf(struct dasd_device *device) "the same device, path %02X leads to " "device %s instead of %s\n", lpm, print_path_uid, print_device_uid); - return -EINVAL; + path_err = -EINVAL; + continue; } path_private.conf_data = NULL; @@ -1142,7 +1144,7 @@ static int dasd_eckd_read_conf(struct dasd_device *device) kfree(conf_data); } - return 0; + return path_err; } static int verify_fcx_max_data(struct dasd_device *device, __u8 lpm) @@ -4159,9 +4161,7 @@ static int dasd_eckd_restore_device(struct dasd_device *device) private = (struct dasd_eckd_private *) device->private; /* Read Configuration Data */ - rc = dasd_eckd_read_conf(device); - if (rc) - goto out_err; + dasd_eckd_read_conf(device); dasd_eckd_get_uid(device, &temp_uid); /* Generate device unique id */ @@ -4181,9 +4181,7 @@ static int dasd_eckd_restore_device(struct dasd_device *device) dasd_eckd_validate_server(device, DASD_CQR_FLAGS_FAILFAST); /* RE-Read Configuration Data */ - rc = dasd_eckd_read_conf(device); - if (rc) - goto out_err; + dasd_eckd_read_conf(device); /* Read Feature Codes */ dasd_eckd_read_features(device); -- cgit v1.2.3 From d07dc5d8ab6f15353c866e2768c389abdc1faba6 Mon Sep 17 00:00:00 2001 From: Stefan Haberland Date: Wed, 28 Nov 2012 13:43:38 +0100 Subject: s390/dasd: add safe offline interface The regular behavior of the DASD device driver when setting a device offline is to return all outstanding I/O as failed. This behavior is different from that of other System z operating systems and may lead to unexpected data loss. Adding an explicit 'safe' offline function will allow customers to use DASDs in the way they expect them to work. Signed-off-by: Stefan Haberland Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd.c | 97 +++++++++++++++++++++++++++++++++------- drivers/s390/block/dasd_devmap.c | 34 ++++++++++++++ drivers/s390/block/dasd_int.h | 2 + 3 files changed, 116 insertions(+), 17 deletions(-) (limited to 'drivers/s390/block') diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 0595c763dafd..29225e1c159c 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -349,6 +349,16 @@ static int dasd_state_basic_to_ready(struct dasd_device *device) return rc; } +static inline +int _wait_for_empty_queues(struct dasd_device *device) +{ + if (device->block) + return list_empty(&device->ccw_queue) && + list_empty(&device->block->ccw_queue); + else + return list_empty(&device->ccw_queue); +} + /* * Remove device from block device layer. Destroy dirty buffers. * Forget format information. Check if the target level is basic @@ -1841,6 +1851,13 @@ static void __dasd_device_check_expire(struct dasd_device *device) 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 (test_bit(DASD_FLAG_SAFE_OFFLINE_RUNNING, &device->flags)) { + /* + * IO in safe offline processing should not + * run out of retries + */ + cqr->retries++; + } if (device->discipline->term_IO(cqr) != 0) { /* Hmpf, try again in 5 sec */ dev_err(&device->cdev->dev, @@ -3024,11 +3041,11 @@ void dasd_generic_remove(struct ccw_device *cdev) cdev->handler = NULL; - dasd_remove_sysfs_files(cdev); device = dasd_device_from_cdev(cdev); if (IS_ERR(device)) return; - if (test_and_set_bit(DASD_FLAG_OFFLINE, &device->flags)) { + if (test_and_set_bit(DASD_FLAG_OFFLINE, &device->flags) && + !test_bit(DASD_FLAG_SAFE_OFFLINE_RUNNING, &device->flags)) { /* Already doing offline processing */ dasd_put_device(device); return; @@ -3048,6 +3065,8 @@ void dasd_generic_remove(struct ccw_device *cdev) */ if (block) dasd_free_block(block); + + dasd_remove_sysfs_files(cdev); } /* @@ -3126,16 +3145,13 @@ int dasd_generic_set_offline(struct ccw_device *cdev) { struct dasd_device *device; struct dasd_block *block; - int max_count, open_count; + int max_count, open_count, rc; + rc = 0; device = dasd_device_from_cdev(cdev); if (IS_ERR(device)) return PTR_ERR(device); - if (test_and_set_bit(DASD_FLAG_OFFLINE, &device->flags)) { - /* Already doing offline processing */ - dasd_put_device(device); - return 0; - } + /* * We must make sure that this device is currently not in use. * The open_count is increased for every opener, that includes @@ -3159,6 +3175,54 @@ int dasd_generic_set_offline(struct ccw_device *cdev) return -EBUSY; } } + + if (test_bit(DASD_FLAG_SAFE_OFFLINE_RUNNING, &device->flags)) { + /* + * safe offline allready running + * could only be called by normal offline so safe_offline flag + * needs to be removed to run normal offline and kill all I/O + */ + if (test_and_set_bit(DASD_FLAG_OFFLINE, &device->flags)) { + /* Already doing normal offline processing */ + dasd_put_device(device); + return -EBUSY; + } else + clear_bit(DASD_FLAG_SAFE_OFFLINE, &device->flags); + + } else + if (test_bit(DASD_FLAG_OFFLINE, &device->flags)) { + /* Already doing offline processing */ + dasd_put_device(device); + return -EBUSY; + } + + /* + * if safe_offline called set safe_offline_running flag and + * clear safe_offline so that a call to normal offline + * can overrun safe_offline processing + */ + if (test_and_clear_bit(DASD_FLAG_SAFE_OFFLINE, &device->flags) && + !test_and_set_bit(DASD_FLAG_SAFE_OFFLINE_RUNNING, &device->flags)) { + /* + * If we want to set the device safe offline all IO operations + * should be finished before continuing the offline process + * so sync bdev first and then wait for our queues to become + * empty + */ + /* sync blockdev and partitions */ + rc = fsync_bdev(device->block->bdev); + if (rc != 0) + goto interrupted; + + /* schedule device tasklet and wait for completion */ + dasd_schedule_device_bh(device); + rc = wait_event_interruptible(shutdown_waitq, + _wait_for_empty_queues(device)); + if (rc != 0) + goto interrupted; + } + + set_bit(DASD_FLAG_OFFLINE, &device->flags); dasd_set_target_state(device, DASD_STATE_NEW); /* dasd_delete_device destroys the device reference. */ block = device->block; @@ -3170,6 +3234,14 @@ int dasd_generic_set_offline(struct ccw_device *cdev) if (block) dasd_free_block(block); return 0; + +interrupted: + /* interrupted by signal */ + clear_bit(DASD_FLAG_SAFE_OFFLINE, &device->flags); + clear_bit(DASD_FLAG_SAFE_OFFLINE_RUNNING, &device->flags); + clear_bit(DASD_FLAG_OFFLINE, &device->flags); + dasd_put_device(device); + return rc; } int dasd_generic_last_path_gone(struct dasd_device *device) @@ -3489,15 +3561,6 @@ char *dasd_get_sense(struct irb *irb) } EXPORT_SYMBOL_GPL(dasd_get_sense); -static inline int _wait_for_empty_queues(struct dasd_device *device) -{ - if (device->block) - return list_empty(&device->ccw_queue) && - list_empty(&device->block->ccw_queue); - else - return list_empty(&device->ccw_queue); -} - void dasd_generic_shutdown(struct ccw_device *cdev) { struct dasd_device *device; diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index b2b8c18eeced..4d12370337aa 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c @@ -951,6 +951,39 @@ dasd_use_raw_store(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(raw_track_access, 0644, dasd_use_raw_show, dasd_use_raw_store); +static ssize_t +dasd_safe_offline_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ccw_device *cdev = to_ccwdev(dev); + struct dasd_device *device; + int rc; + + device = dasd_device_from_cdev(cdev); + if (IS_ERR(device)) { + rc = PTR_ERR(device); + goto out; + } + + if (test_bit(DASD_FLAG_OFFLINE, &device->flags) || + test_bit(DASD_FLAG_SAFE_OFFLINE_RUNNING, &device->flags)) { + /* Already doing offline processing */ + dasd_put_device(device); + rc = -EBUSY; + goto out; + } + + set_bit(DASD_FLAG_SAFE_OFFLINE, &device->flags); + dasd_put_device(device); + + rc = ccw_device_set_offline(cdev); + +out: + return rc ? rc : count; +} + +static DEVICE_ATTR(safe_offline, 0200, NULL, dasd_safe_offline_store); + static ssize_t dasd_discipline_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -1320,6 +1353,7 @@ static struct attribute * dasd_attrs[] = { &dev_attr_expires.attr, &dev_attr_reservation_policy.attr, &dev_attr_last_known_reservation_state.attr, + &dev_attr_safe_offline.attr, NULL, }; diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index 7ff93eea673d..899e3f5a56e5 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -516,6 +516,8 @@ struct dasd_block { #define DASD_FLAG_IS_RESERVED 7 /* The device is reserved */ #define DASD_FLAG_LOCK_STOLEN 8 /* The device lock was stolen */ #define DASD_FLAG_SUSPENDED 9 /* The device was suspended */ +#define DASD_FLAG_SAFE_OFFLINE 10 /* safe offline processing requested*/ +#define DASD_FLAG_SAFE_OFFLINE_RUNNING 11 /* safe offline running */ void dasd_put_device_wake(struct dasd_device *); -- cgit v1.2.3