📄 chp.c
字号:
/* * drivers/s390/cio/chp.c * * Copyright IBM Corp. 1999,2007 * Author(s): Cornelia Huck (cornelia.huck@de.ibm.com) * Arnd Bergmann (arndb@de.ibm.com) * Peter Oberparleiter <peter.oberparleiter@de.ibm.com> */#include <linux/bug.h>#include <linux/workqueue.h>#include <linux/spinlock.h>#include <linux/init.h>#include <linux/jiffies.h>#include <linux/wait.h>#include <linux/mutex.h>#include <linux/errno.h>#include <asm/chpid.h>#include <asm/sclp.h>#include "cio.h"#include "css.h"#include "ioasm.h"#include "cio_debug.h"#include "chp.h"#define to_channelpath(device) container_of(device, struct channel_path, dev)#define CHP_INFO_UPDATE_INTERVAL 1*HZenum cfg_task_t { cfg_none, cfg_configure, cfg_deconfigure};/* Map for pending configure tasks. */static enum cfg_task_t chp_cfg_task[__MAX_CSSID + 1][__MAX_CHPID + 1];static DEFINE_MUTEX(cfg_lock);static int cfg_busy;/* Map for channel-path status. */static struct sclp_chp_info chp_info;static DEFINE_MUTEX(info_lock);/* Time after which channel-path status may be outdated. */static unsigned long chp_info_expires;/* Workqueue to perform pending configure tasks. */static struct workqueue_struct *chp_wq;static struct work_struct cfg_work;/* Wait queue for configure completion events. */static wait_queue_head_t cfg_wait_queue;/* Return channel_path struct for given chpid. */static inline struct channel_path *chpid_to_chp(struct chp_id chpid){ return channel_subsystems[chpid.cssid]->chps[chpid.id];}/* Set vary state for given chpid. */static void set_chp_logically_online(struct chp_id chpid, int onoff){ chpid_to_chp(chpid)->state = onoff;}/* On succes return 0 if channel-path is varied offline, 1 if it is varied * online. Return -ENODEV if channel-path is not registered. */int chp_get_status(struct chp_id chpid){ return (chpid_to_chp(chpid) ? chpid_to_chp(chpid)->state : -ENODEV);}/** * chp_get_sch_opm - return opm for subchannel * @sch: subchannel * * Calculate and return the operational path mask (opm) based on the chpids * used by the subchannel and the status of the associated channel-paths. */u8 chp_get_sch_opm(struct subchannel *sch){ struct chp_id chpid; int opm; int i; opm = 0; chp_id_init(&chpid); for (i = 0; i < 8; i++) { opm <<= 1; chpid.id = sch->schib.pmcw.chpid[i]; if (chp_get_status(chpid) != 0) opm |= 1; } return opm;}/** * chp_is_registered - check if a channel-path is registered * @chpid: channel-path ID * * Return non-zero if a channel-path with the given chpid is registered, * zero otherwise. */int chp_is_registered(struct chp_id chpid){ return chpid_to_chp(chpid) != NULL;}/* * Function: s390_vary_chpid * Varies the specified chpid online or offline */static int s390_vary_chpid(struct chp_id chpid, int on){ char dbf_text[15]; int status; sprintf(dbf_text, on?"varyon%x.%02x":"varyoff%x.%02x", chpid.cssid, chpid.id); CIO_TRACE_EVENT(2, dbf_text); status = chp_get_status(chpid); if (!on && !status) { printk(KERN_ERR "cio: chpid %x.%02x is already offline\n", chpid.cssid, chpid.id); return -EINVAL; } set_chp_logically_online(chpid, on); chsc_chp_vary(chpid, on); return 0;}/* * Channel measurement related functions */static ssize_t chp_measurement_chars_read(struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count){ struct channel_path *chp; struct device *device; unsigned int size; device = container_of(kobj, struct device, kobj); chp = to_channelpath(device); if (!chp->cmg_chars) return 0; size = sizeof(struct cmg_chars); if (off > size) return 0; if (off + count > size) count = size - off; memcpy(buf, chp->cmg_chars + off, count); return count;}static struct bin_attribute chp_measurement_chars_attr = { .attr = { .name = "measurement_chars", .mode = S_IRUSR, }, .size = sizeof(struct cmg_chars), .read = chp_measurement_chars_read,};static void chp_measurement_copy_block(struct cmg_entry *buf, struct channel_subsystem *css, struct chp_id chpid){ void *area; struct cmg_entry *entry, reference_buf; int idx; if (chpid.id < 128) { area = css->cub_addr1; idx = chpid.id; } else { area = css->cub_addr2; idx = chpid.id - 128; } entry = area + (idx * sizeof(struct cmg_entry)); do { memcpy(buf, entry, sizeof(*entry)); memcpy(&reference_buf, entry, sizeof(*entry)); } while (reference_buf.values[0] != buf->values[0]);}static ssize_t chp_measurement_read(struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count){ struct channel_path *chp; struct channel_subsystem *css; struct device *device; unsigned int size; device = container_of(kobj, struct device, kobj); chp = to_channelpath(device); css = to_css(chp->dev.parent); size = sizeof(struct cmg_entry); /* Only allow single reads. */ if (off || count < size) return 0; chp_measurement_copy_block((struct cmg_entry *)buf, css, chp->chpid); count = size; return count;}static struct bin_attribute chp_measurement_attr = { .attr = { .name = "measurement", .mode = S_IRUSR, }, .size = sizeof(struct cmg_entry), .read = chp_measurement_read,};void chp_remove_cmg_attr(struct channel_path *chp){ device_remove_bin_file(&chp->dev, &chp_measurement_chars_attr); device_remove_bin_file(&chp->dev, &chp_measurement_attr);}int chp_add_cmg_attr(struct channel_path *chp){ int ret; ret = device_create_bin_file(&chp->dev, &chp_measurement_chars_attr); if (ret) return ret; ret = device_create_bin_file(&chp->dev, &chp_measurement_attr); if (ret) device_remove_bin_file(&chp->dev, &chp_measurement_chars_attr); return ret;}/* * Files for the channel path entries. */static ssize_t chp_status_show(struct device *dev, struct device_attribute *attr, char *buf){ struct channel_path *chp = to_channelpath(dev); if (!chp) return 0; return (chp_get_status(chp->chpid) ? sprintf(buf, "online\n") : sprintf(buf, "offline\n"));}static ssize_t chp_status_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count){ struct channel_path *cp = to_channelpath(dev); char cmd[10]; int num_args; int error; num_args = sscanf(buf, "%5s", cmd); if (!num_args) return count; if (!strnicmp(cmd, "on", 2) || !strcmp(cmd, "1")) error = s390_vary_chpid(cp->chpid, 1); else if (!strnicmp(cmd, "off", 3) || !strcmp(cmd, "0")) error = s390_vary_chpid(cp->chpid, 0); else error = -EINVAL; return error < 0 ? error : count;}static DEVICE_ATTR(status, 0644, chp_status_show, chp_status_write);static ssize_t chp_configure_show(struct device *dev, struct device_attribute *attr, char *buf){ struct channel_path *cp; int status; cp = to_channelpath(dev); status = chp_info_get_status(cp->chpid); if (status < 0) return status; return snprintf(buf, PAGE_SIZE, "%d\n", status);}static int cfg_wait_idle(void);static ssize_t chp_configure_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count){ struct channel_path *cp; int val; char delim; if (sscanf(buf, "%d %c", &val, &delim) != 1) return -EINVAL; if (val != 0 && val != 1) return -EINVAL; cp = to_channelpath(dev); chp_cfg_schedule(cp->chpid, val); cfg_wait_idle(); return count;}static DEVICE_ATTR(configure, 0644, chp_configure_show, chp_configure_write);static ssize_t chp_type_show(struct device *dev, struct device_attribute *attr, char *buf){ struct channel_path *chp = to_channelpath(dev); if (!chp) return 0; return sprintf(buf, "%x\n", chp->desc.desc);}static DEVICE_ATTR(type, 0444, chp_type_show, NULL);static ssize_t chp_cmg_show(struct device *dev, struct device_attribute *attr, char *buf){ struct channel_path *chp = to_channelpath(dev); if (!chp) return 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -