// SPDX-License-Identifier: GPL-2.0/* * driver for channel subsystem * * Copyright IBM Corp. 2002, 2010 * * Author(s): Arnd Bergmann (arndb@de.ibm.com) * Cornelia Huck (cornelia.huck@de.ibm.com) */ #define pr_fmt(fmt) "cio: " fmt #include <linux/export.h>#include <linux/init.h>#include <linux/device.h>#include <linux/slab.h>#include <linux/errno.h>#include <linux/list.h>#include <linux/reboot.h>#include <linux/proc_fs.h>#include <linux/genalloc.h>#include <linux/dma-mapping.h>#include <asm/isc.h>#include <asm/crw.h> #include "css.h"#include "cio.h"#include "blacklist.h"#include "cio_debug.h"#include "ioasm.h"#include "chsc.h"#include "device.h"#include "idset.h"#include "chp.h" int css_init_done = 0;int max_ssid; #define MAX_CSS_IDX 0struct channel_subsystem *channel_subsystems[MAX_CSS_IDX + 1];static const struct bus_type css_bus_type; intfor_each_subchannel(int(*fn)(struct subchannel_id, void *), void *data){ struct subchannel_id schid; int ret; init_subchannel_id(&schid); do { do { ret = fn(schid, data); if (ret) break; } while (schid.sch_no++ < __MAX_SUBCHANNEL); schid.sch_no = 0; } while (schid.ssid++ < max_ssid); return ret;} struct cb_data { void *data; struct idset *set; int (*fn_known_sch)(struct subchannel *, void *); int (*fn_unknown_sch)(struct subchannel_id, void *);}; static int call_fn_known_sch(struct device *dev, void *data){ struct subchannel *sch = to_subchannel(dev); struct cb_data *cb = data; int rc = 0; if (cb->set) idset_sch_del(cb->set, sch->schid); if (cb->fn_known_sch) rc = cb->fn_known_sch(sch, cb->data); return rc;} static int call_fn_unknown_sch(struct subchannel_id schid, void *data){ struct cb_data *cb = data; int rc = 0; if (idset_sch_contains(cb->set, schid)) rc = cb->fn_unknown_sch(schid, cb->data); return rc;} static int call_fn_all_sch(struct subchannel_id schid, void *data){ struct cb_data *cb = data; struct subchannel *sch; int rc = 0; sch = get_subchannel_by_schid(schid); if (sch) { if (cb->fn_known_sch) rc = cb->fn_known_sch(sch, cb->data); put_device(&sch->dev); } else { if (cb->fn_unknown_sch) rc = cb->fn_unknown_sch(schid, cb->data); } return rc;} int for_each_subchannel_staged(int (*fn_known)(struct subchannel *, void *), int (*fn_unknown)(struct subchannel_id, void *), void *data){ struct cb_data cb; int rc; cb.data = data; cb.fn_known_sch = fn_known; cb.fn_unknown_sch = fn_unknown; if (fn_known && !fn_unknown) { /* Skip idset allocation in case of known-only loop. */ cb.set = NULL; return bus_for_each_dev(&css_bus_type, NULL, &cb, call_fn_known_sch); } cb.set = idset_sch_new(); if (!cb.set) /* fall back to brute force scanning in case of oom */ return for_each_subchannel(call_fn_all_sch, &cb); idset_fill(cb.set); /* Process registered subchannels. */ rc = bus_for_each_dev(&css_bus_type, NULL, &cb, call_fn_known_sch); if (rc) goto out; /* Process unregistered subchannels. */ if (fn_unknown) rc = for_each_subchannel(call_fn_unknown_sch, &cb);out: idset_free(cb.set); return rc;} static void css_sch_todo(struct work_struct *work); static void css_sch_create_locks(struct subchannel *sch){ spin_lock_init(&sch->lock); mutex_init(&sch->reg_mutex);} static void css_subchannel_release(struct device *dev){ struct subchannel *sch = to_subchannel(dev); sch->config.intparm = 0; cio_commit_config(sch); kfree(sch);} static int css_validate_subchannel(struct subchannel_id schid, struct schib *schib){ int err; switch (schib->pmcw.st) { case SUBCHANNEL_TYPE_IO: case SUBCHANNEL_TYPE_MSG: if (!css_sch_is_valid(schib)) err = -ENODEV; else if (is_blacklisted(schid.ssid, schib->pmcw.dev)) { CIO_MSG_EVENT(6, "Blacklisted device detected " "at devno %04X, subchannel set %x\n", schib->pmcw.dev, schid.ssid); err = -ENODEV; } else err = 0; break; default: err = 0; } if (err) goto out; CIO_MSG_EVENT(4, "Subchannel 0.%x.%04x reports subchannel type %04X\n", schid.ssid, schid.sch_no, schib->pmcw.st);out: return err;} struct subchannel *css_alloc_subchannel(struct subchannel_id schid, struct schib *schib){ struct subchannel *sch; int ret; ret = css_validate_subchannel(schid, schib); if (ret < 0) return ERR_PTR(ret); sch = kzalloc_obj(*sch, GFP_KERNEL | GFP_DMA); if (!sch) return ERR_PTR(-ENOMEM); sch->schid = schid; sch->schib = *schib; sch->st = schib->pmcw.st; css_sch_create_locks(sch); INIT_WORK(&sch->todo_work, css_sch_todo); sch->dev.release = &css_subchannel_release; sch->dev.dma_mask = &sch->dma_mask; device_initialize(&sch->dev); /* * The physical addresses for some of the dma structures that can * belong to a subchannel need to fit 31 bit width (e.g. ccw). */ ret = dma_set_coherent_mask(&sch->dev, DMA_BIT_MASK(31)); if (ret) goto err; /* * But we don't have such restrictions imposed on the stuff that * is handled by the streaming API. */ ret = dma_set_mask(&sch->dev, DMA_BIT_MASK(64)); if (ret) goto err; return sch; err: put_device(&sch->dev); return ERR_PTR(ret);} static int css_sch_device_register(struct subchannel *sch){ int ret; mutex_lock(&sch->reg_mutex); dev_set_name(&sch->dev, "0.%x.%04x", sch->schid.ssid, sch->schid.sch_no); ret = device_add(&sch->dev); mutex_unlock(&sch->reg_mutex); return ret;} /** * css_sch_device_unregister - unregister a subchannel * @sch: subchannel to be unregistered */void css_sch_device_unregister(struct subchannel *sch){ mutex_lock(&sch->reg_mutex); if (device_is_registered(&sch->dev)) device_unregister(&sch->dev); mutex_unlock(&sch->reg_mutex);}EXPORT_SYMBOL_GPL(css_sch_device_unregister); static void ssd_from_pmcw(struct chsc_ssd_info *ssd, struct pmcw *pmcw){ int i; int mask; memset(ssd, 0, sizeof(struct chsc_ssd_info)); ssd->path_mask = pmcw->pim; for (i = 0; i < 8; i++) { mask = 0x80 >> i; if (pmcw->pim & mask) { chp_id_init(&ssd->chpid[i]); ssd->chpid[i].id = pmcw->chpid[i]; } }} static void ssd_register_chpids(struct chsc_ssd_info *ssd){ int i; int mask; for (i = 0; i < 8; i++) { mask = 0x80 >> i; if (ssd->path_mask & mask) chp_new(ssd->chpid[i]); }} void css_update_ssd_info(struct subchannel *sch){ int ret; ret = chsc_get_ssd_info(sch->schid, &sch->ssd_info); if (ret) ssd_from_pmcw(&sch->ssd_info, &sch->schib.pmcw); ssd_register_chpids(&sch->ssd_info);} static ssize_t type_show(struct device *dev, struct device_attribute *attr, char *buf){ struct subchannel *sch = to_subchannel(dev); return sysfs_emit(buf, "%01x\n", sch->st);} static DEVICE_ATTR_RO(type); static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf){ struct subchannel *sch = to_subchannel(dev); return sysfs_emit(buf, "css:t%01X\n", sch->st);} static DEVICE_ATTR_RO(modalias); static struct attribute *subch_attrs[] = { &dev_attr_type.attr, &dev_attr_modalias.attr, NULL,}; static struct attribute_group subch_attr_group = { .attrs = subch_attrs,}; static const struct attribute_group *default_subch_attr_groups[] = { &subch_attr_group, NULL,}; static ssize_t chpids_show(struct device *dev, struct device_attribute *attr, char *buf){ struct subchannel *sch = to_subchannel(dev); struct chsc_ssd_info *ssd = &sch->ssd_info; ssize_t ret = 0; int mask; int chp; for (chp = 0; chp < 8; chp++) { mask = 0x80 >> chp; if (ssd->path_mask & mask) ret += sysfs_emit_at(buf, ret, "%02x ", ssd->chpid[chp].id); else ret += sysfs_emit_at(buf, ret, "00 "); } ret += sysfs_emit_at(buf, ret, "\n"); return ret;}static DEVICE_ATTR_RO(chpids); static ssize_t pimpampom_show(struct device *dev, struct device_attribute *attr, char *buf){ struct subchannel *sch = to_subchannel(dev); struct pmcw *pmcw = &sch->schib.pmcw; return sysfs_emit(buf, "%02x %02x %02x\n", pmcw->pim, pmcw->pam, pmcw->pom);}static DEVICE_ATTR_RO(pimpampom); static ssize_t dev_busid_show(struct device *dev, struct device_attribute *attr, char *buf){ struct subchannel *sch = to_subchannel(dev); struct pmcw *pmcw = &sch->schib.pmcw; if ((pmcw->st == SUBCHANNEL_TYPE_IO && pmcw->dnv) || (pmcw->st == SUBCHANNEL_TYPE_MSG && pmcw->w)) return sysfs_emit(buf, "0.%x.%04x\n", sch->schid.ssid, pmcw->dev); else return sysfs_emit(buf, "none\n");}static DEVICE_ATTR_RO(dev_busid); static struct attribute *io_subchannel_type_attrs[] = { &dev_attr_chpids.attr, &dev_attr_pimpampom.attr, &dev_attr_dev_busid.attr, NULL,};ATTRIBUTE_GROUPS(io_subchannel_type); static const struct device_type io_subchannel_type = { .groups = io_subchannel_type_groups,}; int css_register_subchannel(struct subchannel *sch){ int ret; /* Initialize the subchannel structure */ sch->dev.parent = &channel_subsystems[0]->device; sch->dev.bus = &css_bus_type; sch->dev.groups = default_subch_attr_groups; if (sch->st == SUBCHANNEL_TYPE_IO) sch->dev.type = &io_subchannel_type; css_update_ssd_info(sch); /* make it known to the system */ ret = css_sch_device_register(sch); if (ret) { CIO_MSG_EVENT(0, "Could not register sch 0.%x.%04x: %d\n", sch->schid.ssid, sch->schid.sch_no, ret); return ret; } return ret;} static int css_probe_device(struct subchannel_id schid, struct schib *schib){ struct subchannel *sch; int ret; sch = css_alloc_subchannel(schid, schib); if (IS_ERR(sch)) return PTR_ERR(sch); ret = css_register_subchannel(sch); if (ret) put_device(&sch->dev); return ret;} static intcheck_subchannel(struct device *dev, const void *data){ struct subchannel *sch; struct subchannel_id *schid = (void *)data; sch = to_subchannel(dev); return schid_equal(&sch->schid, schid);} struct subchannel *get_subchannel_by_schid(struct subchannel_id schid){ struct device *dev; dev = bus_find_device(&css_bus_type, NULL, &schid, check_subchannel); return dev ? to_subchannel(dev) : NULL;} /** * css_sch_is_valid() - check if a subchannel is valid * @schib: subchannel information block for the subchannel */int css_sch_is_valid(struct schib *schib){ if ((schib->pmcw.st == SUBCHANNEL_TYPE_IO) && !schib->pmcw.dnv) return 0; if ((schib->pmcw.st == SUBCHANNEL_TYPE_MSG) && !schib->pmcw.w) return 0; return 1;}EXPORT_SYMBOL_GPL(css_sch_is_valid); static int css_evaluate_new_subchannel(struct subchannel_id schid, int slow){ struct schib schib; int ccode; if (!slow) { /* Will be done on the slow path. */ return -EAGAIN; } /* * The first subchannel that is not-operational (ccode==3) * indicates that there aren't any more devices available. * If stsch gets an exception, it means the current subchannel set * is not valid. */ ccode = stsch(schid, &schib); if (ccode) return (ccode == 3) ? -ENXIO : ccode; return css_probe_device(schid, &schib);} static int css_evaluate_known_subchannel(struct subchannel *sch, int slow){ int ret = 0;Lines 1–500 of 1415. Load more
// SPDX-License-Identifier: GPL-2.0 /* * driver for channel subsystem * * Copyright IBM Corp. 2002, 2010 * * Author(s): Arnd Bergmann (arndb@de.ibm.com) * Cornelia Huck (cornelia.huck@de.ibm.com) */ #define pr_fmt(fmt) "cio: " fmt #include <linux/export.h> #include <linux/init.h> #include <linux/device.h> #include <linux/slab.h> #include <linux/errno.h> #include <linux/list.h> #include <linux/reboot.h> #include <linux/proc_fs.h> #include <linux/genalloc.h> #include <linux/dma-mapping.h> #include <asm/isc.h> #include <asm/crw.h> #include "css.h" #include "cio.h" #include "blacklist.h" #include "cio_debug.h" #include "ioasm.h" #include "chsc.h" #include "device.h" #include "idset.h" #include "chp.h" int css_init_done = 0; int max_ssid; #define MAX_CSS_IDX 0 struct channel_subsystem *channel_subsystems[MAX_CSS_IDX + 1]; static const struct bus_type css_bus_type; int for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *data) { struct subchannel_id schid; int ret; init_subchannel_id(&schid); do { do { ret = fn(schid, data); if (ret) break; } while (schid.sch_no++ < __MAX_SUBCHANNEL); schid.sch_no = 0; } while (schid.ssid++ < max_ssid); return ret; } struct cb_data { void *data; struct idset *set; int (*fn_known_sch)(struct subchannel *, void *); int (*fn_unknown_sch)(struct subchannel_id, void *); }; static int call_fn_known_sch(struct device *dev, void *data) { struct subchannel *sch = to_subchannel(dev); struct cb_data *cb = data; int rc = 0; if (cb->set) idset_sch_del(cb->set, sch->schid); if (cb->fn_known_sch) rc = cb->fn_known_sch(sch, cb->data); return rc; } static int call_fn_unknown_sch(struct subchannel_id schid, void *data) { struct cb_data *cb = data; int rc = 0; if (idset_sch_contains(cb->set, schid)) rc = cb->fn_unknown_sch(schid, cb->data); return rc; } static int call_fn_all_sch(struct subchannel_id schid, void *data) { struct cb_data *cb = data; struct subchannel *sch; int rc = 0; sch = get_subchannel_by_schid(schid); if (sch) { if (cb->fn_known_sch) rc = cb->fn_known_sch(sch, cb->data); put_device(&sch->dev); } else { if (cb->fn_unknown_sch) rc = cb->fn_unknown_sch(schid, cb->data); } return rc; } int for_each_subchannel_staged(int (*fn_known)(struct subchannel *, void *), int (*fn_unknown)(struct subchannel_id, void *), void *data) { struct cb_data cb; int rc; cb.data = data; cb.fn_known_sch = fn_known; cb.fn_unknown_sch = fn_unknown; if (fn_known && !fn_unknown) { /* Skip idset allocation in case of known-only loop. */ cb.set = NULL; return bus_for_each_dev(&css_bus_type, NULL, &cb, call_fn_known_sch); } cb.set = idset_sch_new(); if (!cb.set) /* fall back to brute force scanning in case of oom */ return for_each_subchannel(call_fn_all_sch, &cb); idset_fill(cb.set); /* Process registered subchannels. */ rc = bus_for_each_dev(&css_bus_type, NULL, &cb, call_fn_known_sch); if (rc) goto out; /* Process unregistered subchannels. */ if (fn_unknown) rc = for_each_subchannel(call_fn_unknown_sch, &cb); out: idset_free(cb.set); return rc; } static void css_sch_todo(struct work_struct *work); static void css_sch_create_locks(struct subchannel *sch) { spin_lock_init(&sch->lock); mutex_init(&sch->reg_mutex); } static void css_subchannel_release(struct device *dev) { struct subchannel *sch = to_subchannel(dev); sch->config.intparm = 0; cio_commit_config(sch); kfree(sch); } static int css_validate_subchannel(struct subchannel_id schid, struct schib *schib) { int err; switch (schib->pmcw.st) { case SUBCHANNEL_TYPE_IO: case SUBCHANNEL_TYPE_MSG: if (!css_sch_is_valid(schib)) err = -ENODEV; else if (is_blacklisted(schid.ssid, schib->pmcw.dev)) { CIO_MSG_EVENT(6, "Blacklisted device detected " "at devno %04X, subchannel set %x\n", schib->pmcw.dev, schid.ssid); err = -ENODEV; } else err = 0; break; default: err = 0; } if (err) goto out; CIO_MSG_EVENT(4, "Subchannel 0.%x.%04x reports subchannel type %04X\n", schid.ssid, schid.sch_no, schib->pmcw.st); out: return err; } struct subchannel *css_alloc_subchannel(struct subchannel_id schid, struct schib *schib) { struct subchannel *sch; int ret; ret = css_validate_subchannel(schid, schib); if (ret < 0) return ERR_PTR(ret); sch = kzalloc_obj(*sch, GFP_KERNEL | GFP_DMA); if (!sch) return ERR_PTR(-ENOMEM); sch->schid = schid; sch->schib = *schib; sch->st = schib->pmcw.st; css_sch_create_locks(sch); INIT_WORK(&sch->todo_work, css_sch_todo); sch->dev.release = &css_subchannel_release; sch->dev.dma_mask = &sch->dma_mask; device_initialize(&sch->dev); /* * The physical addresses for some of the dma structures that can * belong to a subchannel need to fit 31 bit width (e.g. ccw). */ ret = dma_set_coherent_mask(&sch->dev, DMA_BIT_MASK(31)); if (ret) goto err; /* * But we don't have such restrictions imposed on the stuff that * is handled by the streaming API. */ ret = dma_set_mask(&sch->dev, DMA_BIT_MASK(64)); if (ret) goto err; return sch; err: put_device(&sch->dev); return ERR_PTR(ret); } static int css_sch_device_register(struct subchannel *sch) { int ret; mutex_lock(&sch->reg_mutex); dev_set_name(&sch->dev, "0.%x.%04x", sch->schid.ssid, sch->schid.sch_no); ret = device_add(&sch->dev); mutex_unlock(&sch->reg_mutex); return ret; } /** * css_sch_device_unregister - unregister a subchannel * @sch: subchannel to be unregistered */ void css_sch_device_unregister(struct subchannel *sch) { mutex_lock(&sch->reg_mutex); if (device_is_registered(&sch->dev)) device_unregister(&sch->dev); mutex_unlock(&sch->reg_mutex); } EXPORT_SYMBOL_GPL(css_sch_device_unregister); static void ssd_from_pmcw(struct chsc_ssd_info *ssd, struct pmcw *pmcw) { int i; int mask; memset(ssd, 0, sizeof(struct chsc_ssd_info)); ssd->path_mask = pmcw->pim; for (i = 0; i < 8; i++) { mask = 0x80 >> i; if (pmcw->pim & mask) { chp_id_init(&ssd->chpid[i]); ssd->chpid[i].id = pmcw->chpid[i]; } } } static void ssd_register_chpids(struct chsc_ssd_info *ssd) { int i; int mask; for (i = 0; i < 8; i++) { mask = 0x80 >> i; if (ssd->path_mask & mask) chp_new(ssd->chpid[i]); } } void css_update_ssd_info(struct subchannel *sch) { int ret; ret = chsc_get_ssd_info(sch->schid, &sch->ssd_info); if (ret) ssd_from_pmcw(&sch->ssd_info, &sch->schib.pmcw); ssd_register_chpids(&sch->ssd_info); } static ssize_t type_show(struct device *dev, struct device_attribute *attr, char *buf) { struct subchannel *sch = to_subchannel(dev); return sysfs_emit(buf, "%01x\n", sch->st); } static DEVICE_ATTR_RO(type); static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf) { struct subchannel *sch = to_subchannel(dev); return sysfs_emit(buf, "css:t%01X\n", sch->st); } static DEVICE_ATTR_RO(modalias); static struct attribute *subch_attrs[] = { &dev_attr_type.attr, &dev_attr_modalias.attr, NULL, }; static struct attribute_group subch_attr_group = { .attrs = subch_attrs, }; static const struct attribute_group *default_subch_attr_groups[] = { &subch_attr_group, NULL, }; static ssize_t chpids_show(struct device *dev, struct device_attribute *attr, char *buf) { struct subchannel *sch = to_subchannel(dev); struct chsc_ssd_info *ssd = &sch->ssd_info; ssize_t ret = 0; int mask; int chp; for (chp = 0; chp < 8; chp++) { mask = 0x80 >> chp; if (ssd->path_mask & mask) ret += sysfs_emit_at(buf, ret, "%02x ", ssd->chpid[chp].id); else ret += sysfs_emit_at(buf, ret, "00 "); } ret += sysfs_emit_at(buf, ret, "\n"); return ret; } static DEVICE_ATTR_RO(chpids); static ssize_t pimpampom_show(struct device *dev, struct device_attribute *attr, char *buf) { struct subchannel *sch = to_subchannel(dev); struct pmcw *pmcw = &sch->schib.pmcw; return sysfs_emit(buf, "%02x %02x %02x\n", pmcw->pim, pmcw->pam, pmcw->pom); } static DEVICE_ATTR_RO(pimpampom); static ssize_t dev_busid_show(struct device *dev, struct device_attribute *attr, char *buf) { struct subchannel *sch = to_subchannel(dev); struct pmcw *pmcw = &sch->schib.pmcw; if ((pmcw->st == SUBCHANNEL_TYPE_IO && pmcw->dnv) || (pmcw->st == SUBCHANNEL_TYPE_MSG && pmcw->w)) return sysfs_emit(buf, "0.%x.%04x\n", sch->schid.ssid, pmcw->dev); else return sysfs_emit(buf, "none\n"); } static DEVICE_ATTR_RO(dev_busid); static struct attribute *io_subchannel_type_attrs[] = { &dev_attr_chpids.attr, &dev_attr_pimpampom.attr, &dev_attr_dev_busid.attr, NULL, }; ATTRIBUTE_GROUPS(io_subchannel_type); static const struct device_type io_subchannel_type = { .groups = io_subchannel_type_groups, }; int css_register_subchannel(struct subchannel *sch) { int ret; /* Initialize the subchannel structure */ sch->dev.parent = &channel_subsystems[0]->device; sch->dev.bus = &css_bus_type; sch->dev.groups = default_subch_attr_groups; if (sch->st == SUBCHANNEL_TYPE_IO) sch->dev.type = &io_subchannel_type; css_update_ssd_info(sch); /* make it known to the system */ ret = css_sch_device_register(sch); if (ret) { CIO_MSG_EVENT(0, "Could not register sch 0.%x.%04x: %d\n", sch->schid.ssid, sch->schid.sch_no, ret); return ret; } return ret; } static int css_probe_device(struct subchannel_id schid, struct schib *schib) { struct subchannel *sch; int ret; sch = css_alloc_subchannel(schid, schib); if (IS_ERR(sch)) return PTR_ERR(sch); ret = css_register_subchannel(sch); if (ret) put_device(&sch->dev); return ret; } static int check_subchannel(struct device *dev, const void *data) { struct subchannel *sch; struct subchannel_id *schid = (void *)data; sch = to_subchannel(dev); return schid_equal(&sch->schid, schid); } struct subchannel * get_subchannel_by_schid(struct subchannel_id schid) { struct device *dev; dev = bus_find_device(&css_bus_type, NULL, &schid, check_subchannel); return dev ? to_subchannel(dev) : NULL; } /** * css_sch_is_valid() - check if a subchannel is valid * @schib: subchannel information block for the subchannel */ int css_sch_is_valid(struct schib *schib) { if ((schib->pmcw.st == SUBCHANNEL_TYPE_IO) && !schib->pmcw.dnv) return 0; if ((schib->pmcw.st == SUBCHANNEL_TYPE_MSG) && !schib->pmcw.w) return 0; return 1; } EXPORT_SYMBOL_GPL(css_sch_is_valid); static int css_evaluate_new_subchannel(struct subchannel_id schid, int slow) { struct schib schib; int ccode; if (!slow) { /* Will be done on the slow path. */ return -EAGAIN; } /* * The first subchannel that is not-operational (ccode==3) * indicates that there aren't any more devices available. * If stsch gets an exception, it means the current subchannel set * is not valid. */ ccode = stsch(schid, &schib); if (ccode) return (ccode == 3) ? -ENXIO : ccode; return css_probe_device(schid, &schib); } static int css_evaluate_known_subchannel(struct subchannel *sch, int slow) { int ret = 0;