static int cio_get_console_sch_no(void) { struct subchannel_id schid; init_subchannel_id(&schid); if (console_irq != -1) { /* VM provided us with the irq number of the console. */ schid.sch_no = console_irq; if (stsch_err(schid, &console_subchannel.schib) != 0 || (console_subchannel.schib.pmcw.st != SUBCHANNEL_TYPE_IO) || !console_subchannel.schib.pmcw.dnv) return -1; console_devno = console_subchannel.schib.pmcw.dev; } else if (console_devno != -1) { /* At least the console device number is known. */ for_each_subchannel(cio_test_for_console, NULL); if (console_irq == -1) return -1; } else { /* unlike in 2.4, we cannot autoprobe here, since * the channel subsystem is not fully initialized. * With some luck, the HWC console can take over */ return -1; } return console_irq; }
static int __shutdown_subchannel_easy(struct subchannel_id schid, void *data) { struct schib schib; if (stsch_reset(schid, &schib)) return -ENXIO; if (!schib.pmcw.ena) return 0; switch(__disable_subchannel_easy(schid, &schib)) { case 0: case -ENODEV: break; default: /* -EBUSY */ switch (schib.pmcw.st) { case SUBCHANNEL_TYPE_IO: if (__clear_io_subchannel_easy(schid)) goto out; /* give up... */ break; case SUBCHANNEL_TYPE_CHSC: __clear_chsc_subchannel_easy(); break; default: /* No default clear strategy */ break; } stsch_err(schid, &schib); __disable_subchannel_easy(schid, &schib); } out: return 0; }
/** * cio_validate_subchannel - basic validation of subchannel * @sch: subchannel structure to be filled out * @schid: subchannel id * * Find out subchannel type and initialize struct subchannel. * Return codes: * 0 on success * -ENXIO for non-defined subchannels * -ENODEV for invalid subchannels or blacklisted devices * -EIO for subchannels in an invalid subchannel set */ int cio_validate_subchannel(struct subchannel *sch, struct subchannel_id schid) { char dbf_txt[15]; int ccode; int err; sprintf(dbf_txt, "valsch%x", schid.sch_no); CIO_TRACE_EVENT(4, dbf_txt); /* Nuke all fields. */ memset(sch, 0, sizeof(struct subchannel)); sch->schid = schid; if (cio_is_console(schid)) { sch->lock = cio_get_console_lock(); } else { err = cio_create_sch_lock(sch); if (err) goto out; } mutex_init(&sch->reg_mutex); /* * 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_err (schid, &sch->schib); if (ccode) { err = (ccode == 3) ? -ENXIO : ccode; goto out; } /* Copy subchannel type from path management control word. */ sch->st = sch->schib.pmcw.st; switch (sch->st) { case SUBCHANNEL_TYPE_IO: err = cio_validate_io_subchannel(sch); break; case SUBCHANNEL_TYPE_MSG: err = cio_validate_msg_subchannel(sch); break; default: err = 0; } if (err) goto out; CIO_MSG_EVENT(4, "Subchannel 0.%x.%04x reports subchannel type %04X\n", sch->schid.ssid, sch->schid.sch_no, sch->st); return 0; out: if (!cio_is_console(schid)) kfree(sch->lock); sch->lock = NULL; return err; }
/** * cio_update_schib - Perform stsch and update schib if subchannel is valid. * @sch: subchannel on which to perform stsch * Return zero on success, -ENODEV otherwise. */ int cio_update_schib(struct subchannel *sch) { struct schib schib; if (stsch_err(sch->schid, &schib) || !css_sch_is_valid(&schib)) return -ENODEV; memcpy(&sch->schib, &schib, sizeof(schib)); return 0; }
/* * cio_commit_config - apply configuration to the subchannel */ int cio_commit_config(struct subchannel *sch) { int ccode, retry, ret = 0; struct schib schib; struct irb irb; if (stsch_err(sch->schid, &schib) || !css_sch_is_valid(&schib)) return -ENODEV; for (retry = 0; retry < 5; retry++) { /* copy desired changes to local schib */ cio_apply_config(sch, &schib); ccode = msch_err(sch->schid, &schib); if (ccode < 0) /* -EIO if msch gets a program check. */ return ccode; switch (ccode) { case 0: /* successful */ if (stsch_err(sch->schid, &schib) || !css_sch_is_valid(&schib)) return -ENODEV; if (cio_check_config(sch, &schib)) { /* commit changes from local schib */ memcpy(&sch->schib, &schib, sizeof(schib)); return 0; } ret = -EAGAIN; break; case 1: /* status pending */ ret = -EBUSY; if (tsch(sch->schid, &irb)) return ret; break; case 2: /* busy */ udelay(100); /* allow for recovery */ ret = -EBUSY; break; case 3: /* not operational */ return -ENODEV; } } return ret; }
static int __s390_vary_chpid_on(struct subchannel_id schid, void *data) { struct schib schib; if (stsch_err(schid, &schib)) /* We're through */ return -ENXIO; /* Put it on the slow path. */ css_schedule_eval(schid); return 0; }
int cio_validate_subchannel(struct subchannel *sch, struct subchannel_id schid) { char dbf_txt[15]; int ccode; int err; sprintf(dbf_txt, "valsch%x", schid.sch_no); CIO_TRACE_EVENT(4, dbf_txt); memset(sch, 0, sizeof(struct subchannel)); sch->schid = schid; if (cio_is_console(schid)) { sch->lock = cio_get_console_lock(); } else { err = cio_create_sch_lock(sch); if (err) goto out; } mutex_init(&sch->reg_mutex); ccode = stsch_err (schid, &sch->schib); if (ccode) { err = (ccode == 3) ? -ENXIO : ccode; goto out; } sch->st = sch->schib.pmcw.st; switch (sch->st) { case SUBCHANNEL_TYPE_IO: err = cio_validate_io_subchannel(sch); break; case SUBCHANNEL_TYPE_MSG: err = cio_validate_msg_subchannel(sch); break; default: err = 0; } if (err) goto out; CIO_MSG_EVENT(4, "Subchannel 0.%x.%04x reports subchannel type %04X\n", sch->schid.ssid, sch->schid.sch_no, sch->st); return 0; out: if (!cio_is_console(schid)) kfree(sch->lock); sch->lock = NULL; return err; }
static int cio_test_for_console(struct subchannel_id schid, void *data) { if (stsch_err(schid, &console_subchannel.schib) != 0) return -ENXIO; if ((console_subchannel.schib.pmcw.st == SUBCHANNEL_TYPE_IO) && console_subchannel.schib.pmcw.dnv && (console_subchannel.schib.pmcw.dev == console_devno)) { console_irq = schid.sch_no; return 1; /* found */ } return 0; }
int cio_commit_config(struct subchannel *sch) { struct schib schib; int ccode, retry, ret = 0; if (stsch_err(sch->schid, &schib) || !css_sch_is_valid(&schib)) return -ENODEV; for (retry = 0; retry < 5; retry++) { cio_apply_config(sch, &schib); ccode = msch_err(sch->schid, &schib); if (ccode < 0) return ccode; switch (ccode) { case 0: if (stsch_err(sch->schid, &schib) || !css_sch_is_valid(&schib)) return -ENODEV; if (cio_check_config(sch, &schib)) { memcpy(&sch->schib, &schib, sizeof(schib)); return 0; } ret = -EAGAIN; break; case 1: return -EBUSY; case 2: udelay(100); ret = -EBUSY; break; case 3: return -ENODEV; } } return ret; }
static int stsch_reset(struct subchannel_id schid, struct schib *addr) { int rc; pgm_check_occured = 0; s390_base_pgm_handler_fn = cio_reset_pgm_check_handler; rc = stsch_err(schid, addr); s390_base_pgm_handler_fn = NULL; /* The program check handler could have changed pgm_check_occured. */ barrier(); if (pgm_check_occured) return -EIO; else return rc; }
static int __disable_subchannel_easy(struct subchannel_id schid, struct schib *schib) { int retry, cc; cc = 0; for (retry=0;retry<3;retry++) { schib->pmcw.ena = 0; cc = msch_err(schid, schib); if (cc) return (cc==3?-ENODEV:-EBUSY); if (stsch_err(schid, schib) || !css_sch_is_valid(schib)) return -ENODEV; if (!schib->pmcw.ena) return 0; } return -EBUSY; /* uhm... */ }
int __init cio_get_iplinfo(struct cio_iplinfo *iplinfo) { struct subchannel_id schid; struct schib schib; schid = *(struct subchannel_id *)&S390_lowcore.subchannel_id; if (!schid.one) return -ENODEV; if (stsch_err(schid, &schib)) return -ENODEV; if (schib.pmcw.st != SUBCHANNEL_TYPE_IO) return -ENODEV; if (!schib.pmcw.dnv) return -ENODEV; iplinfo->devno = schib.pmcw.dev; iplinfo->is_qdio = schib.pmcw.qf; return 0; }
static int cio_get_console_sch_no(void) { struct subchannel_id schid; struct schib schib; init_subchannel_id(&schid); if (console_irq != -1) { /* VM provided us with the irq number of the console. */ schid.sch_no = console_irq; if (stsch_err(schid, &schib) != 0 || (schib.pmcw.st != SUBCHANNEL_TYPE_IO) || !schib.pmcw.dnv) return -1; console_devno = schib.pmcw.dev; } else if (console_devno != -1) { /* At least the console device number is known. */ for_each_subchannel(cio_test_for_console, NULL); } return console_irq; }
static int s390_process_res_acc_new_sch(struct subchannel_id schid, void *data) { struct schib schib; /* * We don't know the device yet, but since a path * may be available now to the device we'll have * to do recognition again. * Since we don't have any idea about which chpid * that beast may be on we'll have to do a stsch * on all devices, grr... */ if (stsch_err(schid, &schib)) /* We're through */ return -ENXIO; /* Put it on the slow path. */ css_schedule_eval(schid); return 0; }
/** * cio_validate_subchannel - basic validation of subchannel * @sch: subchannel structure to be filled out * @schid: subchannel id * * Find out subchannel type and initialize struct subchannel. * Return codes: * 0 on success * -ENXIO for non-defined subchannels * -ENODEV for invalid subchannels or blacklisted devices * -EIO for subchannels in an invalid subchannel set */ int cio_validate_subchannel(struct subchannel *sch, struct subchannel_id schid) { char dbf_txt[15]; int ccode; int err; sprintf(dbf_txt, "valsch%x", schid.sch_no); CIO_TRACE_EVENT(4, dbf_txt); /* * 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_err(schid, &sch->schib); if (ccode) { err = (ccode == 3) ? -ENXIO : ccode; goto out; } sch->st = sch->schib.pmcw.st; sch->schid = schid; switch (sch->st) { case SUBCHANNEL_TYPE_IO: err = cio_validate_io_subchannel(sch); break; case SUBCHANNEL_TYPE_MSG: err = cio_validate_msg_subchannel(sch); break; default: err = 0; } if (err) goto out; CIO_MSG_EVENT(4, "Subchannel 0.%x.%04x reports subchannel type %04X\n", sch->schid.ssid, sch->schid.sch_no, sch->st); out: return err; }
static int css_evaluate_new_subchannel(struct subchannel_id schid, int slow) { struct schib schib; if (!slow) { /* Will be done on the slow path. */ return -EAGAIN; } if (stsch_err(schid, &schib)) { /* Subchannel is not provided. */ return -ENXIO; } if (!css_sch_is_valid(&schib)) { /* Unusable - ignore. */ return 0; } CIO_MSG_EVENT(4, "event: sch 0.%x.%04x, new\n", schid.ssid, schid.sch_no); return css_probe_device(schid); }
static int __shutdown_subchannel_easy(struct subchannel_id schid, void *data) { struct schib schib; if (stsch_err(schid, &schib)) return -ENXIO; if (!schib.pmcw.ena) return 0; switch(__disable_subchannel_easy(schid, &schib)) { case 0: case -ENODEV: break; default: /* -EBUSY */ if (__clear_subchannel_easy(schid)) break; /* give up... */ stsch(schid, &schib); __disable_subchannel_easy(schid, &schib); } return 0; }
} __setup("ccw_timeout_log", ccw_timeout_log_setup); static void ccw_timeout_log(struct ccw_device *cdev) { struct schib schib; struct subchannel *sch; struct io_subchannel_private *private; union orb *orb; int cc; sch = to_subchannel(cdev->dev.parent); private = to_io_private(sch); orb = &private->orb; cc = stsch_err(sch->schid, &schib); printk(KERN_WARNING "cio: ccw device timeout occurred at %llx, " "device information:\n", get_clock()); printk(KERN_WARNING "cio: orb:\n"); print_hex_dump(KERN_WARNING, "cio: ", DUMP_PREFIX_NONE, 16, 1, orb, sizeof(*orb), 0); printk(KERN_WARNING "cio: ccw device bus id: %s\n", dev_name(&cdev->dev)); printk(KERN_WARNING "cio: subchannel bus id: %s\n", dev_name(&sch->dev)); printk(KERN_WARNING "cio: subchannel lpm: %02x, opm: %02x, " "vpm: %02x\n", sch->lpm, sch->opm, sch->vpm); if (orb->tm.b) { printk(KERN_WARNING "cio: orb indicates transport mode\n");
/* * cio_validate_subchannel() * * Find out subchannel type and initialize struct subchannel. * Return codes: * SUBCHANNEL_TYPE_IO for a normal io subchannel * SUBCHANNEL_TYPE_CHSC for a chsc subchannel * SUBCHANNEL_TYPE_MESSAGE for a messaging subchannel * SUBCHANNEL_TYPE_ADM for a adm(?) subchannel * -ENXIO for non-defined subchannels * -ENODEV for subchannels with invalid device number or blacklisted devices */ int cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid) { char dbf_txt[15]; int ccode; sprintf (dbf_txt, "valsch%x", schid.sch_no); CIO_TRACE_EVENT (4, dbf_txt); /* Nuke all fields. */ memset(sch, 0, sizeof(struct subchannel)); spin_lock_init(&sch->lock); mutex_init(&sch->reg_mutex); /* Set a name for the subchannel */ snprintf (sch->dev.bus_id, BUS_ID_SIZE, "0.%x.%04x", schid.ssid, schid.sch_no); /* * 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_err (schid, &sch->schib); if (ccode) return (ccode == 3) ? -ENXIO : ccode; sch->schid = schid; /* Copy subchannel type from path management control word. */ sch->st = sch->schib.pmcw.st; /* * ... just being curious we check for non I/O subchannels */ if (sch->st != 0) { CIO_DEBUG(KERN_INFO, 0, "Subchannel 0.%x.%04x reports " "non-I/O subchannel type %04X\n", sch->schid.ssid, sch->schid.sch_no, sch->st); /* We stop here for non-io subchannels. */ return sch->st; } /* Initialization for io subchannels. */ if (!sch->schib.pmcw.dnv) /* io subchannel but device number is invalid. */ return -ENODEV; /* Devno is valid. */ if (is_blacklisted (sch->schid.ssid, sch->schib.pmcw.dev)) { /* * This device must not be known to Linux. So we simply * say that there is no device and return ENODEV. */ CIO_MSG_EVENT(0, "Blacklisted device detected " "at devno %04X, subchannel set %x\n", sch->schib.pmcw.dev, sch->schid.ssid); return -ENODEV; } sch->opm = 0xff; if (!cio_is_console(sch->schid)) chsc_validate_chpids(sch); sch->lpm = sch->schib.pmcw.pim & sch->schib.pmcw.pam & sch->schib.pmcw.pom & sch->opm; CIO_DEBUG(KERN_INFO, 0, "Detected device %04x on subchannel 0.%x.%04X" " - PIM = %02X, PAM = %02X, POM = %02X\n", sch->schib.pmcw.dev, sch->schid.ssid, sch->schid.sch_no, sch->schib.pmcw.pim, sch->schib.pmcw.pam, sch->schib.pmcw.pom); /* * We now have to initially ... * ... set "interruption subclass" * ... enable "concurrent sense" * ... enable "multipath mode" if more than one * CHPID is available. This is done regardless * whether multiple paths are available for us. */ sch->schib.pmcw.isc = 3; /* could be smth. else */ sch->schib.pmcw.csense = 1; /* concurrent sense */ sch->schib.pmcw.ena = 0; if ((sch->lpm & (sch->lpm - 1)) != 0) sch->schib.pmcw.mp = 1; /* multipath mode */ return 0; }