📄 chp.c
字号:
if (chp->cmg == -1) /* channel measurements not available */ return sprintf(buf, "unknown\n"); return sprintf(buf, "%x\n", chp->cmg);}static DEVICE_ATTR(cmg, 0444, chp_cmg_show, NULL);static ssize_t chp_shared_show(struct device *dev, struct device_attribute *attr, char *buf){ struct channel_path *chp = to_channelpath(dev); if (!chp) return 0; if (chp->shared == -1) /* channel measurements not available */ return sprintf(buf, "unknown\n"); return sprintf(buf, "%x\n", chp->shared);}static DEVICE_ATTR(shared, 0444, chp_shared_show, NULL);static struct attribute *chp_attrs[] = { &dev_attr_status.attr, &dev_attr_configure.attr, &dev_attr_type.attr, &dev_attr_cmg.attr, &dev_attr_shared.attr, NULL,};static struct attribute_group chp_attr_group = { .attrs = chp_attrs,};static void chp_release(struct device *dev){ struct channel_path *cp; cp = to_channelpath(dev); kfree(cp);}/** * chp_new - register a new channel-path * @chpid - channel-path ID * * Create and register data structure representing new channel-path. Return * zero on success, non-zero otherwise. */int chp_new(struct chp_id chpid){ struct channel_path *chp; int ret; if (chp_is_registered(chpid)) return 0; chp = kzalloc(sizeof(struct channel_path), GFP_KERNEL); if (!chp) return -ENOMEM; /* fill in status, etc. */ chp->chpid = chpid; chp->state = 1; chp->dev.parent = &channel_subsystems[chpid.cssid]->device; chp->dev.release = chp_release; snprintf(chp->dev.bus_id, BUS_ID_SIZE, "chp%x.%02x", chpid.cssid, chpid.id); /* Obtain channel path description and fill it in. */ ret = chsc_determine_channel_path_description(chpid, &chp->desc); if (ret) goto out_free; if ((chp->desc.flags & 0x80) == 0) { ret = -ENODEV; goto out_free; } /* Get channel-measurement characteristics. */ if (css_characteristics_avail && css_chsc_characteristics.scmc && css_chsc_characteristics.secm) { ret = chsc_get_channel_measurement_chars(chp); if (ret) goto out_free; } else { chp->cmg = -1; } /* make it known to the system */ ret = device_register(&chp->dev); if (ret) { CIO_MSG_EVENT(0, "Could not register chp%x.%02x: %d\n", chpid.cssid, chpid.id, ret); goto out_free; } ret = sysfs_create_group(&chp->dev.kobj, &chp_attr_group); if (ret) { device_unregister(&chp->dev); goto out_free; } mutex_lock(&channel_subsystems[chpid.cssid]->mutex); if (channel_subsystems[chpid.cssid]->cm_enabled) { ret = chp_add_cmg_attr(chp); if (ret) { sysfs_remove_group(&chp->dev.kobj, &chp_attr_group); device_unregister(&chp->dev); mutex_unlock(&channel_subsystems[chpid.cssid]->mutex); goto out_free; } } channel_subsystems[chpid.cssid]->chps[chpid.id] = chp; mutex_unlock(&channel_subsystems[chpid.cssid]->mutex); return ret;out_free: kfree(chp); return ret;}/** * chp_get_chp_desc - return newly allocated channel-path description * @chpid: channel-path ID * * On success return a newly allocated copy of the channel-path description * data associated with the given channel-path ID. Return %NULL on error. */void *chp_get_chp_desc(struct chp_id chpid){ struct channel_path *chp; struct channel_path_desc *desc; chp = chpid_to_chp(chpid); if (!chp) return NULL; desc = kmalloc(sizeof(struct channel_path_desc), GFP_KERNEL); if (!desc) return NULL; memcpy(desc, &chp->desc, sizeof(struct channel_path_desc)); return desc;}/** * chp_process_crw - process channel-path status change * @id: channel-path ID number * @status: non-zero if channel-path has become available, zero otherwise * * Handle channel-report-words indicating that the status of a channel-path * has changed. */void chp_process_crw(int id, int status){ struct chp_id chpid; chp_id_init(&chpid); chpid.id = id; if (status) { if (!chp_is_registered(chpid)) chp_new(chpid); chsc_chp_online(chpid); } else chsc_chp_offline(chpid);}static inline int info_bit_num(struct chp_id id){ return id.id + id.cssid * (__MAX_CHPID + 1);}/* Force chp_info refresh on next call to info_validate(). */static void info_expire(void){ mutex_lock(&info_lock); chp_info_expires = jiffies - 1; mutex_unlock(&info_lock);}/* Ensure that chp_info is up-to-date. */static int info_update(void){ int rc; mutex_lock(&info_lock); rc = 0; if (time_after(jiffies, chp_info_expires)) { /* Data is too old, update. */ rc = sclp_chp_read_info(&chp_info); chp_info_expires = jiffies + CHP_INFO_UPDATE_INTERVAL ; } mutex_unlock(&info_lock); return rc;}/** * chp_info_get_status - retrieve configure status of a channel-path * @chpid: channel-path ID * * On success, return 0 for standby, 1 for configured, 2 for reserved, * 3 for not recognized. Return negative error code on error. */int chp_info_get_status(struct chp_id chpid){ int rc; int bit; rc = info_update(); if (rc) return rc; bit = info_bit_num(chpid); mutex_lock(&info_lock); if (!chp_test_bit(chp_info.recognized, bit)) rc = CHP_STATUS_NOT_RECOGNIZED; else if (chp_test_bit(chp_info.configured, bit)) rc = CHP_STATUS_CONFIGURED; else if (chp_test_bit(chp_info.standby, bit)) rc = CHP_STATUS_STANDBY; else rc = CHP_STATUS_RESERVED; mutex_unlock(&info_lock); return rc;}/* Return configure task for chpid. */static enum cfg_task_t cfg_get_task(struct chp_id chpid){ return chp_cfg_task[chpid.cssid][chpid.id];}/* Set configure task for chpid. */static void cfg_set_task(struct chp_id chpid, enum cfg_task_t cfg){ chp_cfg_task[chpid.cssid][chpid.id] = cfg;}/* Perform one configure/deconfigure request. Reschedule work function until * last request. */static void cfg_func(struct work_struct *work){ struct chp_id chpid; enum cfg_task_t t; mutex_lock(&cfg_lock); t = cfg_none; chp_id_for_each(&chpid) { t = cfg_get_task(chpid); if (t != cfg_none) { cfg_set_task(chpid, cfg_none); break; } } mutex_unlock(&cfg_lock); switch (t) { case cfg_configure: sclp_chp_configure(chpid); info_expire(); chsc_chp_online(chpid); break; case cfg_deconfigure: sclp_chp_deconfigure(chpid); info_expire(); chsc_chp_offline(chpid); break; case cfg_none: /* Get updated information after last change. */ info_update(); mutex_lock(&cfg_lock); cfg_busy = 0; mutex_unlock(&cfg_lock); wake_up_interruptible(&cfg_wait_queue); return; } queue_work(chp_wq, &cfg_work);}/** * chp_cfg_schedule - schedule chpid configuration request * @chpid - channel-path ID * @configure - Non-zero for configure, zero for deconfigure * * Schedule a channel-path configuration/deconfiguration request. */void chp_cfg_schedule(struct chp_id chpid, int configure){ CIO_MSG_EVENT(2, "chp_cfg_sched%x.%02x=%d\n", chpid.cssid, chpid.id, configure); mutex_lock(&cfg_lock); cfg_set_task(chpid, configure ? cfg_configure : cfg_deconfigure); cfg_busy = 1; mutex_unlock(&cfg_lock); queue_work(chp_wq, &cfg_work);}/** * chp_cfg_cancel_deconfigure - cancel chpid deconfiguration request * @chpid - channel-path ID * * Cancel an active channel-path deconfiguration request if it has not yet * been performed. */void chp_cfg_cancel_deconfigure(struct chp_id chpid){ CIO_MSG_EVENT(2, "chp_cfg_cancel:%x.%02x\n", chpid.cssid, chpid.id); mutex_lock(&cfg_lock); if (cfg_get_task(chpid) == cfg_deconfigure) cfg_set_task(chpid, cfg_none); mutex_unlock(&cfg_lock);}static int cfg_wait_idle(void){ if (wait_event_interruptible(cfg_wait_queue, !cfg_busy)) return -ERESTARTSYS; return 0;}static int __init chp_init(void){ struct chp_id chpid; chp_wq = create_singlethread_workqueue("cio_chp"); if (!chp_wq) return -ENOMEM; INIT_WORK(&cfg_work, cfg_func); init_waitqueue_head(&cfg_wait_queue); if (info_update()) return 0; /* Register available channel-paths. */ chp_id_for_each(&chpid) { if (chp_info_get_status(chpid) != CHP_STATUS_NOT_RECOGNIZED) chp_new(chpid); } return 0;}subsys_initcall(chp_init);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -