From: Martin Schwidefsky - Fix interrupt status examination. - Make dasd device attributes dependent on the devmap structure instead of the device structure to make them persistent and to be able to modify them in the offline state. - Allow changing the readonly attribute while dasd is online. - Add (diag) option to dasd= paramter. - Add missing spin_lock_init call. - Increase ref_count in dasd_device_from_cdev and add matching dasd_put_device pairs. - Adapt to notify api change in cio. - Fix bug in 3990 error recovery for cable pulls on ESS. - Replace kmap by page_address (no highmem on s/390). - Set correct default cache mode on ESS for eckd devices. - Change dasd names from "dasdx" to "dasd__". --- 25-akpm/drivers/s390/block/dasd.c | 255 +++++++++++------------------ 25-akpm/drivers/s390/block/dasd_3990_erp.c | 8 25-akpm/drivers/s390/block/dasd_devmap.c | 233 +++++++++++++++++++++++--- 25-akpm/drivers/s390/block/dasd_diag.c | 4 25-akpm/drivers/s390/block/dasd_eckd.c | 43 ++++ 25-akpm/drivers/s390/block/dasd_fba.c | 7 25-akpm/drivers/s390/block/dasd_genhd.c | 21 -- 25-akpm/drivers/s390/block/dasd_int.h | 11 - 25-akpm/drivers/s390/block/dasd_proc.c | 11 - 25-akpm/include/asm-s390/dasd.h | 6 25-akpm/include/linux/genhd.h | 2 11 files changed, 378 insertions(+), 223 deletions(-) diff -puN drivers/s390/block/dasd_3990_erp.c~s390-04-dasd-driver drivers/s390/block/dasd_3990_erp.c --- 25/drivers/s390/block/dasd_3990_erp.c~s390-04-dasd-driver Thu Jan 8 14:11:30 2004 +++ 25-akpm/drivers/s390/block/dasd_3990_erp.c Thu Jan 8 14:11:30 2004 @@ -5,7 +5,7 @@ * Bugreports.to..: * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000, 2001 * - * $Revision: 1.25 $ + * $Revision: 1.26 $ */ #include @@ -312,7 +312,8 @@ dasd_3990_erp_alternate_path(struct dasd erp->lpm, erp->dstat->esw.esw0.sublog.lpum, opm); /* reset status to queued to handle the request again... */ - erp->status = DASD_CQR_QUEUED; + if (erp->status > DASD_CQR_QUEUED) + erp->status = DASD_CQR_QUEUED; erp->retries = 1; } else { DEV_MESSAGE(KERN_ERR, device, @@ -321,7 +322,8 @@ dasd_3990_erp_alternate_path(struct dasd erp->dstat->esw.esw0.sublog.lpum, opm); /* post request with permanent error */ - erp->status = DASD_CQR_FAILED; + if (erp->status > DASD_CQR_QUEUED) + erp->status = DASD_CQR_FAILED; } } /* end dasd_3990_erp_alternate_path */ diff -puN drivers/s390/block/dasd.c~s390-04-dasd-driver drivers/s390/block/dasd.c --- 25/drivers/s390/block/dasd.c~s390-04-dasd-driver Thu Jan 8 14:11:30 2004 +++ 25-akpm/drivers/s390/block/dasd.c Thu Jan 8 14:11:30 2004 @@ -7,7 +7,7 @@ * Bugreports.to..: * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001 * - * $Revision: 1.114 $ + * $Revision: 1.123 $ */ #include @@ -43,7 +43,6 @@ MODULE_DESCRIPTION("Linux on S/390 DASD " Copyright 2000 IBM Corporation"); MODULE_SUPPORTED_DEVICE("dasd"); MODULE_PARM(dasd, "1-" __MODULE_STRING(256) "s"); -MODULE_PARM(dasd_disciplines, "1-" __MODULE_STRING(8) "s"); MODULE_LICENSE("GPL"); /* @@ -57,7 +56,6 @@ static void dasd_int_handler(struct ccw_ static void dasd_flush_ccw_queue(struct dasd_device *, int); static void dasd_tasklet(struct dasd_device *); static void do_kick_device(void *data); -static int dasd_add_sysfs_files(struct ccw_device *cdev); /* * SECTION: Operations on the device structure. @@ -93,6 +91,7 @@ dasd_alloc_device(void) dasd_init_chunklist(&device->ccw_chunks, device->ccw_mem, PAGE_SIZE*2); dasd_init_chunklist(&device->erp_chunks, device->erp_mem, PAGE_SIZE); + spin_lock_init(&device->mem_lock); spin_lock_init(&device->request_queue_lock); atomic_set (&device->tasklet_scheduled, 0); tasklet_init(&device->tasklet, @@ -860,7 +859,7 @@ dasd_handle_killed_request(struct ccw_de device = (struct dasd_device *) cqr->device; if (device == NULL || - device != cdev->dev.driver_data || + device != dasd_device_from_cdev(cdev) || strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) { MESSAGE(KERN_DEBUG, "invalid device in request: bus_id %s", cdev->dev.bus_id); @@ -872,6 +871,7 @@ dasd_handle_killed_request(struct ccw_de dasd_clear_timer(device); dasd_schedule_bh(device); + dasd_put_device(device); } static void @@ -931,7 +931,11 @@ dasd_int_handler(struct ccw_device *cdev /* 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_handle_state_change_pending(cdev->dev.driver_data); + device = dasd_device_from_cdev(cdev); + if (!IS_ERR(device)) { + dasd_handle_state_change_pending(device); + dasd_put_device(device); + } return; } @@ -949,7 +953,6 @@ dasd_int_handler(struct ccw_device *cdev device = (struct dasd_device *) cqr->device; if (device == NULL || - device != cdev->dev.driver_data || strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) { MESSAGE(KERN_DEBUG, "invalid device in request: bus_id %s", cdev->dev.bus_id); @@ -959,17 +962,19 @@ dasd_int_handler(struct ccw_device *cdev DBF_DEV_EVENT(DBF_DEBUG, device, "Int: CS/DS 0x%04x", ((irb->scsw.cstat << 8) | irb->scsw.dstat)); - /* Find out the appropriate era_action. */ - era = dasd_era_none; - if (irb->scsw.dstat & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END) || - irb->esw.esw0.erw.cons) { - /* The request did end abnormally. */ - if (irb->scsw.fctl & SCSW_FCTL_HALT_FUNC) - era = dasd_era_fatal; - else - era = device->discipline->examine_error(cqr, irb); - DBF_EVENT(DBF_NOTICE, "era_code %d", era); - } + /* Find out the appropriate era_action. */ + if (irb->scsw.fctl & SCSW_FCTL_HALT_FUNC) + era = dasd_era_fatal; + else if (irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END) && + irb->scsw.cstat == 0 && + !irb->esw.esw0.erw.cons) + era = dasd_era_none; + else if (irb->esw.esw0.erw.cons) + era = device->discipline->examine_error(cqr, irb); + else + era = dasd_era_recover; + + DBF_DEV_EVENT(DBF_DEBUG, device, "era_code %d", era); expires = 0; if (era == dasd_era_none) { cqr->status = DASD_CQR_DONE; @@ -1149,6 +1154,11 @@ __dasd_process_blk_queue(struct dasd_dev 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) @@ -1728,17 +1738,10 @@ int dasd_generic_probe (struct ccw_device *cdev, struct dasd_discipline *discipline) { - int ret = 0; - - if (dasd_autodetect && - (ret = dasd_add_busid(cdev->dev.bus_id, DASD_FEATURE_DEFAULT))) { - printk (KERN_WARNING - "dasd_generic_probe: cannot autodetect %s\n", - cdev->dev.bus_id); - return ret; - } + int ret; - if (!ret && (ret = dasd_add_sysfs_files(cdev))) { + ret = dasd_add_sysfs_files(cdev); + if (ret) { printk(KERN_WARNING "dasd_generic_probe: could not add driverfs entries" "for %s\n", cdev->dev.bus_id); @@ -1751,16 +1754,17 @@ dasd_generic_probe (struct ccw_device *c /* this will one day be called from a global not_oper handler. * It is also used by driver_unregister during module unload */ -int +void dasd_generic_remove (struct ccw_device *cdev) { struct dasd_device *device; - device = cdev->dev.driver_data; - cdev->dev.driver_data = NULL; - if (device) - kfree(device); - return 0; + device = dasd_device_from_cdev(cdev); + if (!IS_ERR(device)) { + dasd_set_target_state(device, DASD_STATE_NEW); + /* dasd_delete_device destroys the device reference. */ + dasd_delete_device(device); + } } /* activate a device. This is called from dasd_{eckd,fba}_probe() when either @@ -1779,24 +1783,14 @@ dasd_generic_set_online (struct ccw_devi return PTR_ERR(device); if (device->use_diag_flag) - device->discipline = dasd_diag_discipline_pointer; - - rc = 0; - if (!device->discipline || - (rc = device->discipline->check_device(device))) { - pr_debug("device %s is not diag (%d)\n", - cdev->dev.bus_id, rc); - if (device->private != NULL) { - kfree(device->private); - device->private = NULL; - } - device->discipline = discipline; - rc = discipline->check_device(device); - } - + discipline = dasd_diag_discipline_pointer; + device->discipline = discipline; + rc = discipline->check_device(device); if (rc) { - printk (KERN_WARNING "dasd_generic found a bad device %s\n", - cdev->dev.bus_id); + printk (KERN_WARNING + "dasd_generic couldn't online device %s " + "with discipline %s\n", + cdev->dev.bus_id, discipline->name); dasd_delete_device(device); return rc; } @@ -1809,16 +1803,16 @@ dasd_generic_set_online (struct ccw_devi rc = -ENODEV; dasd_set_target_state(device, DASD_STATE_NEW); dasd_delete_device(device); - } else { + } else pr_debug("dasd_generic device %s found\n", cdev->dev.bus_id); - cdev->dev.driver_data = device; - } /* FIXME: we have to wait for the root device but we don't want * to wait for each single device but for all at once. */ wait_event(dasd_init_waitq, _wait_for_device(device)); + dasd_put_device(device); + return rc; } @@ -1827,18 +1821,68 @@ dasd_generic_set_offline (struct ccw_dev { struct dasd_device *device; - device = cdev->dev.driver_data; - dasd_set_target_state(device, DASD_STATE_NEW); - dasd_delete_device(device); - + device = dasd_device_from_cdev(cdev); + if (atomic_read(&device->open_count) > 0) { + printk (KERN_WARNING "Can't offline dasd device with open" + " count = %i.\n", + atomic_read(&device->open_count)); + dasd_put_device(device); + return -EBUSY; + } + dasd_put_device(device); + dasd_generic_remove (cdev); return 0; } +int +dasd_generic_notify(struct ccw_device *cdev, int event) +{ + struct dasd_device *device; + struct dasd_ccw_req *cqr; + unsigned long flags; + int ret; + + device = dasd_device_from_cdev(cdev); + if (IS_ERR(device)) + return 0; + spin_lock_irqsave(get_ccwdev_lock(cdev), flags); + ret = 0; + switch (event) { + case CIO_GONE: + case CIO_NO_PATH: + if (device->state < DASD_STATE_BASIC) + break; + /* Device is active. We want to keep it. */ + if (device->disconnect_error_flag) { + 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; + dasd_schedule_bh(device); + } else { + list_for_each_entry(cqr, &device->ccw_queue, list) + if (cqr->status == DASD_CQR_IN_IO) + cqr->status = DASD_CQR_QUEUED; + device->stopped |= DASD_STOPPED_DC_WAIT; + dasd_set_timer(device, 0); + } + 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); + ret = 1; + break; + } + spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); + dasd_put_device(device); + return ret; +} + /* * Automatically online either all dasd devices (dasd_autodetect) or - * all devices specified with dasd= parameters. For dasd_autodetect - * dasd_generic_probe has added devmaps for all dasd devices. We - * scan all present dasd devmaps and call ccw_device_set_online. + * all devices specified with dasd= parameters. */ void dasd_generic_auto_online (struct ccw_driver *dasd_discipline_driver) @@ -1863,97 +1907,6 @@ dasd_generic_auto_online (struct ccw_dri put_driver(drv); } -/* - * SECTION: files in sysfs - */ - -/* - * readonly controls the readonly status of a dasd - */ -static ssize_t -dasd_ro_show(struct device *dev, char *buf) -{ - struct dasd_device *device; - - device = dev->driver_data; - if (!device) - return snprintf(buf, PAGE_SIZE, "n/a\n"); - - return snprintf(buf, PAGE_SIZE, device->ro_flag ? "1\n" : "0\n"); -} - -static ssize_t -dasd_ro_store(struct device *dev, const char *buf, size_t count) -{ - struct dasd_device *device = dev->driver_data; - - if (device) - device->ro_flag = (buf[0] == '1') ? 1 : 0; - return count; -} - -static DEVICE_ATTR(readonly, 0644, dasd_ro_show, dasd_ro_store); - -/* - * use_diag controls whether the driver should use diag rather than ssch - * to talk to the device - */ -/* TODO: Implement */ -static ssize_t -dasd_use_diag_show(struct device *dev, char *buf) -{ - struct dasd_device *device; - - device = dev->driver_data; - if (!device) - return sprintf(buf, "n/a\n"); - - return sprintf(buf, device->use_diag_flag ? "1\n" : "0\n"); -} - -static ssize_t -dasd_use_diag_store(struct device *dev, const char *buf, size_t count) -{ - struct dasd_device *device = dev->driver_data; - - if (device) - device->use_diag_flag = (buf[0] == '1') ? 1 : 0; - return count; -} - -static -DEVICE_ATTR(use_diag, 0644, dasd_use_diag_show, dasd_use_diag_store); - -static ssize_t -dasd_discipline_show(struct device *dev, char *buf) -{ - struct dasd_device *device; - - device = dev->driver_data; - if (!device || !device->discipline) - return sprintf(buf, "none\n"); - return snprintf(buf, PAGE_SIZE, "%s\n", device->discipline->name); -} - -static DEVICE_ATTR(discipline, 0444, dasd_discipline_show, NULL); - -static struct attribute * dasd_attrs[] = { - &dev_attr_readonly.attr, - &dev_attr_discipline.attr, - &dev_attr_use_diag.attr, - NULL, -}; - -static struct attribute_group dasd_attr_group = { - .attrs = dasd_attrs, -}; - -static int -dasd_add_sysfs_files(struct ccw_device *cdev) -{ - return sysfs_create_group(&cdev->dev.kobj, &dasd_attr_group); -} - static int __init dasd_init(void) { diff -puN drivers/s390/block/dasd_devmap.c~s390-04-dasd-driver drivers/s390/block/dasd_devmap.c --- 25/drivers/s390/block/dasd_devmap.c~s390-04-dasd-driver Thu Jan 8 14:11:30 2004 +++ 25-akpm/drivers/s390/block/dasd_devmap.c Thu Jan 8 14:11:30 2004 @@ -11,7 +11,7 @@ * functions may not be called from interrupt context. In particular * dasd_get_device is a no-no from interrupt context. * - * $Revision: 1.19 $ + * $Revision: 1.25 $ */ #include @@ -79,6 +79,8 @@ static spinlock_t dasd_devmap_lock = SPI static struct list_head dasd_hashlists[256]; int dasd_max_devindex; +static struct dasd_devmap *dasd_add_busid(char *, int); + static inline int dasd_hash_busid(char *bus_id) { @@ -168,12 +170,16 @@ dasd_feature_list(char *str, char **endp *endp = str; return DASD_FEATURE_DEFAULT; } + str++; features = 0; + while (1) { for (len = 0; str[len] && str[len] != ':' && str[len] != ')'; len++); if (len == 2 && !strncmp(str, "ro", 2)) features |= DASD_FEATURE_READONLY; + else if (len == 4 && !strncmp(str, "diag", 4)) + features |= DASD_FEATURE_USEDIAG; else { MESSAGE(KERN_WARNING, "unsupported feature: %*s, " @@ -203,6 +209,7 @@ dasd_feature_list(char *str, char **endp static inline int dasd_ranges_list(char *str) { + struct dasd_devmap *devmap; int from, from_id0, from_id1; int to, to_id0, to_id1; int features, rc; @@ -233,9 +240,9 @@ dasd_ranges_list(char *str) while (from <= to) { sprintf(bus_id, "%01x.%01x.%04x", from_id0, from_id1, from++); - rc = dasd_add_busid(bus_id, features); - if (rc) - return rc; + devmap = dasd_add_busid(bus_id, features); + if (IS_ERR(devmap)) + return PTR_ERR(devmap); } if (*str != ',') break; @@ -299,7 +306,7 @@ dasd_parse(void) * added through this function will define the kdevs for the individual * devices. */ -int +static struct dasd_devmap * dasd_add_busid(char *bus_id, int features) { struct dasd_devmap *devmap, *new, *tmp; @@ -308,7 +315,7 @@ dasd_add_busid(char *bus_id, int feature new = (struct dasd_devmap *) kmalloc(sizeof(struct dasd_devmap), GFP_KERNEL); if (!new) - return -ENOMEM; + return ERR_PTR(-ENOMEM); spin_lock(&dasd_devmap_lock); devmap = 0; hash = dasd_hash_busid(bus_id); @@ -330,7 +337,7 @@ dasd_add_busid(char *bus_id, int feature spin_unlock(&dasd_devmap_lock); if (new) kfree(new); - return 0; + return devmap; } /* @@ -343,7 +350,7 @@ dasd_find_busid(char *bus_id) int hash; spin_lock(&dasd_devmap_lock); - devmap = 0; + devmap = ERR_PTR(-ENODEV); hash = dasd_hash_busid(bus_id); list_for_each_entry(tmp, &dasd_hashlists[hash], list) { if (strncmp(tmp->bus_id, bus_id, BUS_ID_SIZE) == 0) { @@ -361,7 +368,7 @@ dasd_find_busid(char *bus_id) int dasd_busid_known(char *bus_id) { - return dasd_find_busid(bus_id) ? 0 : -ENOENT; + return IS_ERR(dasd_find_busid(bus_id)) ? -ENOENT : 0; } /* @@ -415,6 +422,28 @@ dasd_device_from_devindex(int devindex) } /* + * Return devmap for cdev. If no devmap exists yet, create one and + * connect it to the cdev. + */ +static struct dasd_devmap * +dasd_devmap_from_cdev(struct ccw_device *cdev) +{ + struct dasd_devmap *devmap; + + if (cdev->dev.driver_data) + return (struct dasd_devmap *) cdev->dev.driver_data; + devmap = dasd_find_busid(cdev->dev.bus_id); + if (!IS_ERR(devmap)) { + cdev->dev.driver_data = devmap; + return devmap; + } + devmap = dasd_add_busid(cdev->dev.bus_id, DASD_FEATURE_DEFAULT); + if (!IS_ERR(devmap)) + cdev->dev.driver_data = devmap; + return devmap; +} + +/* * Create a dasd device structure for cdev. */ struct dasd_device * @@ -424,41 +453,35 @@ dasd_create_device(struct ccw_device *cd struct dasd_device *device; int rc; - rc = dasd_add_busid(cdev->dev.bus_id, DASD_FEATURE_DEFAULT); - if (rc) - return ERR_PTR(rc); - - devmap = dasd_find_busid(cdev->dev.bus_id); + devmap = dasd_devmap_from_cdev(cdev); if (IS_ERR(devmap)) return (void *) devmap; device = dasd_alloc_device(); if (IS_ERR(device)) return device; - atomic_set(&device->ref_count, 1); - device->devindex = devmap->devindex; - device->ro_flag = (devmap->features & DASD_FEATURE_READONLY) ? 1 : 0; - device->use_diag_flag = 1; + atomic_set(&device->ref_count, 2); - spin_lock_irq(get_ccwdev_lock(cdev)); - if (cdev->dev.driver_data == NULL) { + spin_lock(&dasd_devmap_lock); + if (!devmap->device) { + devmap->device = device; + device->devindex = devmap->devindex; + device->ro_flag = + (devmap->features & DASD_FEATURE_READONLY) != 0; + device->use_diag_flag = + (devmap->features & DASD_FEATURE_USEDIAG) != 0; get_device(&cdev->dev); - cdev->dev.driver_data = device; device->cdev = cdev; rc = 0; } else /* Someone else was faster. */ rc = -EBUSY; - spin_unlock_irq(get_ccwdev_lock(cdev)); + spin_unlock(&dasd_devmap_lock); + if (rc) { dasd_free_device(device); return ERR_PTR(rc); } - /* Device created successfully. Make it known via devmap. */ - spin_lock(&dasd_devmap_lock); - devmap->device = device; - spin_unlock(&dasd_devmap_lock); - return device; } @@ -468,7 +491,8 @@ dasd_create_device(struct ccw_device *cd static DECLARE_WAIT_QUEUE_HEAD(dasd_delete_wq); /* - * Remove a dasd device structure. + * Remove a dasd device structure. The passed referenced + * is destroyed. */ void dasd_delete_device(struct dasd_device *device) @@ -479,17 +503,24 @@ dasd_delete_device(struct dasd_device *d /* First remove device pointer from devmap. */ devmap = dasd_find_busid(device->cdev->dev.bus_id); spin_lock(&dasd_devmap_lock); + if (devmap->device != device) { + spin_unlock(&dasd_devmap_lock); + dasd_put_device(device); + return; + } devmap->device = NULL; spin_unlock(&dasd_devmap_lock); + /* Drop ref_count by 2, one for the devmap reference and + * one for the passed reference. */ + atomic_sub(2, &device->ref_count); + /* Wait for reference counter to drop to zero. */ - atomic_dec(&device->ref_count); wait_event(dasd_delete_wq, atomic_read(&device->ref_count) == 0); /* Disconnect dasd_device structure from ccw_device structure. */ cdev = device->cdev; device->cdev = NULL; - cdev->dev.driver_data = NULL; /* Put ccw_device structure. */ put_device(&cdev->dev); @@ -508,6 +539,148 @@ dasd_put_device_wake(struct dasd_device wake_up(&dasd_delete_wq); } +/* + * Return dasd_device structure associated with cdev. + */ +struct dasd_device * +dasd_device_from_cdev(struct ccw_device *cdev) +{ + struct dasd_devmap *devmap; + struct dasd_device *device; + + device = ERR_PTR(-ENODEV); + spin_lock(&dasd_devmap_lock); + devmap = cdev->dev.driver_data; + if (devmap && devmap->device) { + device = devmap->device; + dasd_get_device(device); + } + spin_unlock(&dasd_devmap_lock); + return device; +} + +/* + * SECTION: files in sysfs + */ + +/* + * readonly controls the readonly status of a dasd + */ +static ssize_t +dasd_ro_show(struct device *dev, char *buf) +{ + struct dasd_devmap *devmap; + int ro_flag; + + devmap = dev->driver_data; + if (devmap) + ro_flag = (devmap->features & DASD_FEATURE_READONLY) != 0; + else + ro_flag = (DASD_FEATURE_DEFAULT & DASD_FEATURE_READONLY) != 0; + return snprintf(buf, PAGE_SIZE, ro_flag ? "1\n" : "0\n"); +} + +static ssize_t +dasd_ro_store(struct device *dev, const char *buf, size_t count) +{ + struct dasd_devmap *devmap; + int ro_flag; + + devmap = dasd_devmap_from_cdev(to_ccwdev(dev)); + ro_flag = buf[0] == '1'; + spin_lock(&dasd_devmap_lock); + if (ro_flag) + devmap->features |= DASD_FEATURE_READONLY; + else + devmap->features &= ~DASD_FEATURE_READONLY; + if (devmap->device) { + if (devmap->device->gdp) + set_disk_ro(devmap->device->gdp, ro_flag); + devmap->device->ro_flag = ro_flag; + } + spin_unlock(&dasd_devmap_lock); + return count; +} + +static DEVICE_ATTR(readonly, 0644, dasd_ro_show, dasd_ro_store); + +/* + * use_diag controls whether the driver should use diag rather than ssch + * to talk to the device + */ +/* TODO: Implement */ +static ssize_t +dasd_use_diag_show(struct device *dev, char *buf) +{ + struct dasd_devmap *devmap; + int use_diag; + + devmap = dev->driver_data; + if (devmap) + use_diag = (devmap->features & DASD_FEATURE_USEDIAG) != 0; + else + use_diag = (DASD_FEATURE_DEFAULT & DASD_FEATURE_USEDIAG) != 0; + return sprintf(buf, use_diag ? "1\n" : "0\n"); +} + +static ssize_t +dasd_use_diag_store(struct device *dev, const char *buf, size_t count) +{ + struct dasd_devmap *devmap; + int use_diag; + + devmap = dasd_devmap_from_cdev(to_ccwdev(dev)); + use_diag = buf[0] == '1'; + spin_lock(&dasd_devmap_lock); + /* Changing diag discipline flag is only allowed in offline state. */ + if (!devmap->device) { + if (use_diag) + devmap->features |= DASD_FEATURE_USEDIAG; + else + devmap->features &= ~DASD_FEATURE_USEDIAG; + } else + count = -EPERM; + spin_unlock(&dasd_devmap_lock); + return count; +} + +static +DEVICE_ATTR(use_diag, 0644, dasd_use_diag_show, dasd_use_diag_store); + +static ssize_t +dasd_discipline_show(struct device *dev, char *buf) +{ + struct dasd_devmap *devmap; + char *dname; + + spin_lock(&dasd_devmap_lock); + dname = "none"; + devmap = dev->driver_data; + if (devmap && devmap->device && devmap->device->discipline) + dname = devmap->device->discipline->name; + spin_unlock(&dasd_devmap_lock); + return snprintf(buf, PAGE_SIZE, "%s\n", dname); +} + +static DEVICE_ATTR(discipline, 0444, dasd_discipline_show, NULL); + +static struct attribute * dasd_attrs[] = { + &dev_attr_readonly.attr, + &dev_attr_discipline.attr, + &dev_attr_use_diag.attr, + NULL, +}; + +static struct attribute_group dasd_attr_group = { + .attrs = dasd_attrs, +}; + +int +dasd_add_sysfs_files(struct ccw_device *cdev) +{ + return sysfs_create_group(&cdev->dev.kobj, &dasd_attr_group); +} + int dasd_devmap_init(void) { diff -puN drivers/s390/block/dasd_diag.c~s390-04-dasd-driver drivers/s390/block/dasd_diag.c --- 25/drivers/s390/block/dasd_diag.c~s390-04-dasd-driver Thu Jan 8 14:11:30 2004 +++ 25-akpm/drivers/s390/block/dasd_diag.c Thu Jan 8 14:11:30 2004 @@ -6,7 +6,7 @@ * Bugreports.to..: * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 * - * $Revision: 1.32 $ + * $Revision: 1.33 $ */ #include @@ -393,7 +393,7 @@ dasd_diag_build_cp(struct dasd_device * recid = first_rec; rq_for_each_bio(bio, req) { bio_for_each_segment(bv, bio, i) { - dst = kmap(bv->bv_page) + bv->bv_offset; + dst = page_address(bv->bv_page) + bv->bv_offset; for (off = 0; off < bv->bv_len; off += blksize) { memset(dbio, 0, sizeof (struct dasd_diag_bio)); dbio->type = rw_cmd; diff -puN drivers/s390/block/dasd_eckd.c~s390-04-dasd-driver drivers/s390/block/dasd_eckd.c --- 25/drivers/s390/block/dasd_eckd.c~s390-04-dasd-driver Thu Jan 8 14:11:30 2004 +++ 25-akpm/drivers/s390/block/dasd_eckd.c Thu Jan 8 14:11:30 2004 @@ -7,7 +7,7 @@ * Bugreports.to..: * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 * - * $Revision: 1.47 $ + * $Revision: 1.49 $ */ #include @@ -103,6 +103,7 @@ static struct ccw_driver dasd_eckd_drive .remove = dasd_generic_remove, .set_offline = dasd_generic_set_offline, .set_online = dasd_eckd_set_online, + .notify = dasd_generic_notify, }; static const int sizes_trk0[] = { 28, 148, 84 }; @@ -460,8 +461,8 @@ dasd_eckd_check_characteristics(struct d /* Invalidate status of initial analysis. */ private->init_cqr_status = -1; /* Set default cache operations. */ - private->attrib.operation = DASD_SEQ_ACCESS; - private->attrib.nr_cyl = 0x02; + private->attrib.operation = DASD_NORMAL_CACHE; + private->attrib.nr_cyl = 0; /* Read Device Characteristics */ rdc_data = (void *) &(private->rdc_data); @@ -1292,6 +1293,36 @@ dasd_eckd_performance(struct block_devic } /* + * Get attributes (cache operations) + * Returnes the cache attributes used in Define Extend (DE). + */ +static int +dasd_eckd_get_attrib (struct block_device *bdev, int no, long args) +{ + struct dasd_device *device; + struct dasd_eckd_private *private; + struct attrib_data_t attrib; + int rc; + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + if (!args) + return -EINVAL; + + device = bdev->bd_disk->private_data; + if (device == NULL) + return -ENODEV; + + private = (struct dasd_eckd_private *) device->private; + attrib = private->attrib; + + rc = copy_to_user((long *) args, (long *) &attrib, + sizeof (struct attrib_data_t)); + + return rc; +} + +/* * Set attributes (cache operations) * Stores the attributes for cache operation to be used in Define Extend (DE). */ @@ -1433,6 +1464,8 @@ dasd_eckd_init(void) { int ret; + dasd_ioctl_no_register(THIS_MODULE, BIODASDGATTR, + dasd_eckd_get_attrib); dasd_ioctl_no_register(THIS_MODULE, BIODASDSATTR, dasd_eckd_set_attrib); dasd_ioctl_no_register(THIS_MODULE, BIODASDPSRD, @@ -1448,6 +1481,8 @@ dasd_eckd_init(void) ret = ccw_driver_register(&dasd_eckd_driver); if (ret) { + dasd_ioctl_no_unregister(THIS_MODULE, BIODASDGATTR, + dasd_eckd_get_attrib); dasd_ioctl_no_unregister(THIS_MODULE, BIODASDSATTR, dasd_eckd_set_attrib); dasd_ioctl_no_unregister(THIS_MODULE, BIODASDPSRD, @@ -1470,6 +1505,8 @@ dasd_eckd_cleanup(void) { ccw_driver_unregister(&dasd_eckd_driver); + dasd_ioctl_no_unregister(THIS_MODULE, BIODASDGATTR, + dasd_eckd_get_attrib); dasd_ioctl_no_unregister(THIS_MODULE, BIODASDSATTR, dasd_eckd_set_attrib); dasd_ioctl_no_unregister(THIS_MODULE, BIODASDPSRD, diff -puN drivers/s390/block/dasd_fba.c~s390-04-dasd-driver drivers/s390/block/dasd_fba.c --- 25/drivers/s390/block/dasd_fba.c~s390-04-dasd-driver Thu Jan 8 14:11:30 2004 +++ 25-akpm/drivers/s390/block/dasd_fba.c Thu Jan 8 14:11:30 2004 @@ -4,7 +4,7 @@ * Bugreports.to..: * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 * - * $Revision: 1.30 $ + * $Revision: 1.32 $ */ #include @@ -80,6 +80,7 @@ static struct ccw_driver dasd_fba_driver .remove = dasd_generic_remove, .set_offline = dasd_generic_set_offline, .set_online = dasd_fba_set_online, + .notify = dasd_generic_notify, }; static inline void @@ -269,7 +270,7 @@ dasd_fba_build_cp(struct dasd_device * d return ERR_PTR(-EINVAL); count += bv->bv_len >> (device->s2b_shift + 9); #if defined(CONFIG_ARCH_S390X) - cidaw += idal_nr_words(kmap(bv->bv_page) + + cidaw += idal_nr_words(page_address(bv->bv_page) + bv->bv_offset, bv->bv_len); #endif } @@ -309,7 +310,7 @@ dasd_fba_build_cp(struct dasd_device * d } recid = first_rec; rq_for_each_bio(bio, req) bio_for_each_segment(bv, bio, i) { - dst = kmap(bv->bv_page) + bv->bv_offset; + dst = page_address(bv->bv_page) + bv->bv_offset; for (off = 0; off < bv->bv_len; off += blksize) { /* Locate record for stupid devices. */ if (private->rdc_data.mode.bits.data_chain == 0) { diff -puN drivers/s390/block/dasd_genhd.c~s390-04-dasd-driver drivers/s390/block/dasd_genhd.c --- 25/drivers/s390/block/dasd_genhd.c~s390-04-dasd-driver Thu Jan 8 14:11:30 2004 +++ 25-akpm/drivers/s390/block/dasd_genhd.c Thu Jan 8 14:11:30 2004 @@ -9,7 +9,7 @@ * * gendisk related functions for the dasd driver. * - * $Revision: 1.41 $ + * $Revision: 1.42 $ */ #include @@ -31,7 +31,6 @@ int dasd_gendisk_alloc(struct dasd_device *device) { struct gendisk *gdp; - int len; /* Make sure the minor for this device exists. */ if (device->devindex >= DASD_PER_MAJOR) @@ -47,22 +46,8 @@ dasd_gendisk_alloc(struct dasd_device *d gdp->fops = &dasd_device_operations; gdp->driverfs_dev = &device->cdev->dev; - /* - * Set device name. - * dasda - dasdz : 26 devices - * dasdaa - dasdzz : 676 devices, added up = 702 - * dasdaaa - dasdzzz : 17576 devices, added up = 18278 - */ - len = sprintf(gdp->disk_name, "dasd"); - if (device->devindex > 25) { - if (device->devindex > 701) - len += sprintf(gdp->disk_name + len, "%c", - 'a'+(((device->devindex-702)/676)%26)); - len += sprintf(gdp->disk_name + len, "%c", - 'a'+(((device->devindex-26)/26)%26)); - } - len += sprintf(gdp->disk_name + len, "%c", 'a'+(device->devindex%26)); - + /* Set device name */ + sprintf(gdp->disk_name, "dasd_%s_", device->cdev->dev.bus_id); sprintf(gdp->devfs_name, "dasd/%s", device->cdev->dev.bus_id); if (device->ro_flag) diff -puN drivers/s390/block/dasd_int.h~s390-04-dasd-driver drivers/s390/block/dasd_int.h --- 25/drivers/s390/block/dasd_int.h~s390-04-dasd-driver Thu Jan 8 14:11:30 2004 +++ 25-akpm/drivers/s390/block/dasd_int.h Thu Jan 8 14:11:30 2004 @@ -6,7 +6,7 @@ * Bugreports.to..: * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 * - * $Revision: 1.48 $ + * $Revision: 1.52 $ */ #ifndef DASD_INT_H @@ -268,6 +268,7 @@ struct dasd_device { unsigned int s2b_shift; /* log2 (bp_block/512) */ int ro_flag; /* read-only flag */ int use_diag_flag; /* diag allowed flag */ + int disconnect_error_flag; /* return -EIO when disconnected */ /* Device discipline stuff. */ @@ -308,6 +309,8 @@ struct dasd_device { #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 */ void dasd_put_device_wake(struct dasd_device *); @@ -458,9 +461,10 @@ void dasd_set_timer(struct dasd_device * void dasd_clear_timer(struct dasd_device *); int dasd_cancel_req(struct dasd_ccw_req *); int dasd_generic_probe (struct ccw_device *, struct dasd_discipline *); -int dasd_generic_remove (struct ccw_device *cdev); +void dasd_generic_remove (struct ccw_device *cdev); int dasd_generic_set_online(struct ccw_device *, struct dasd_discipline *); int dasd_generic_set_offline (struct ccw_device *cdev); +int dasd_generic_notify(struct ccw_device *, int); void dasd_generic_auto_online (struct ccw_driver *); /* externals in dasd_devmap.c */ @@ -474,10 +478,11 @@ void dasd_devmap_exit(void); struct dasd_device *dasd_create_device(struct ccw_device *); void dasd_delete_device(struct dasd_device *); +int dasd_add_sysfs_files(struct ccw_device *); +struct dasd_device *dasd_device_from_cdev(struct ccw_device *); struct dasd_device *dasd_device_from_devindex(int); int dasd_parse(void); -int dasd_add_busid(char *, int); int dasd_busid_known(char *); /* externals in dasd_gendisk.c */ diff -puN drivers/s390/block/dasd_proc.c~s390-04-dasd-driver drivers/s390/block/dasd_proc.c --- 25/drivers/s390/block/dasd_proc.c~s390-04-dasd-driver Thu Jan 8 14:11:30 2004 +++ 25-akpm/drivers/s390/block/dasd_proc.c Thu Jan 8 14:11:30 2004 @@ -9,7 +9,7 @@ * * /proc interface for the dasd driver. * - * $Revision: 1.23 $ + * $Revision: 1.24 $ */ #include @@ -67,15 +67,10 @@ dasd_devices_show(struct seq_file *m, vo seq_printf(m, "(none)"); /* Print kdev. */ if (device->gdp) - seq_printf(m, " at (%3d:%3d)", + seq_printf(m, " at (%3d:%7d)", device->gdp->major, device->gdp->first_minor); else - seq_printf(m, " at (???:???)"); - /* Print device name. */ - if (device->gdp) - seq_printf(m, " is %-7s", device->gdp->disk_name); - else - seq_printf(m, " is ???????"); + seq_printf(m, " at (???:???????)"); /* Print devices features. */ substr = device->ro_flag ? "(ro)" : " "; seq_printf(m, "%4s: ", substr); diff -puN include/asm-s390/dasd.h~s390-04-dasd-driver include/asm-s390/dasd.h --- 25/include/asm-s390/dasd.h~s390-04-dasd-driver Thu Jan 8 14:11:30 2004 +++ 25-akpm/include/asm-s390/dasd.h Thu Jan 8 14:11:30 2004 @@ -8,7 +8,7 @@ * any future changes wrt the API will result in a change of the APIVERSION reported * to userspace by the DASDAPIVER-ioctl * - * $Revision: 1.4 $ + * $Revision: 1.6 $ * */ @@ -69,9 +69,11 @@ typedef struct dasd_information2_t { * values to be used for dasd_information_t.features * 0x00: default features * 0x01: readonly (ro) + * 0x02: use diag discipline (diag) */ #define DASD_FEATURE_DEFAULT 0 #define DASD_FEATURE_READONLY 1 +#define DASD_FEATURE_USEDIAG 2 #define DASD_PARTN_BITS 2 @@ -234,6 +236,8 @@ typedef struct attrib_data_t { #define BIODASDINFO2 _IOR(DASD_IOCTL_LETTER,3,dasd_information2_t) /* Performance Statistics Read */ #define BIODASDPSRD _IOR(DASD_IOCTL_LETTER,4,dasd_rssd_perf_stats_t) +/* Get Attributes (cache operations) */ +#define BIODASDGATTR _IOR(DASD_IOCTL_LETTER,5,attrib_data_t) /* #define BIODASDFORMAT _IOW(IOCTL_LETTER,0,format_data_t) , deprecated */ diff -puN include/linux/genhd.h~s390-04-dasd-driver include/linux/genhd.h --- 25/include/linux/genhd.h~s390-04-dasd-driver Thu Jan 8 14:11:30 2004 +++ 25-akpm/include/linux/genhd.h Thu Jan 8 14:11:30 2004 @@ -82,7 +82,7 @@ struct gendisk { int major; /* major number of driver */ int first_minor; int minors; - char disk_name[16]; /* name of major driver */ + char disk_name[32]; /* name of major driver */ struct hd_struct **part; /* [indexed by minor] */ struct block_device_operations *fops; struct request_queue *queue; _