📄 cmf.c
字号:
spin_unlock_irqrestore(cdev->ccwlock, flags); return -ENODEV; } cmb = *(struct cmb*)cdev->private->cmb; time = get_clock() - cdev->private->cmb_start_time; spin_unlock_irqrestore(cdev->ccwlock, flags); memset(data, 0, sizeof(struct cmbdata)); /* we only know values before device_busy_time */ data->size = offsetof(struct cmbdata, device_busy_time); /* convert to nanoseconds */ data->elapsed_time = (time * 1000) >> 12; /* copy data to new structure */ data->ssch_rsch_count = cmb.ssch_rsch_count; data->sample_count = cmb.sample_count; /* time fields are converted to nanoseconds while copying */ data->device_connect_time = time_to_nsec(cmb.device_connect_time); data->function_pending_time = time_to_nsec(cmb.function_pending_time); data->device_disconnect_time = time_to_nsec(cmb.device_disconnect_time); data->control_unit_queuing_time = time_to_nsec(cmb.control_unit_queuing_time); data->device_active_only_time = time_to_nsec(cmb.device_active_only_time); return 0;}static voidreset_cmb(struct ccw_device *cdev){ struct cmb *cmb; spin_lock_irq(cdev->ccwlock); cmb = cdev->private->cmb; if (cmb) memset (cmb, 0, sizeof (*cmb)); cdev->private->cmb_start_time = get_clock(); spin_unlock_irq(cdev->ccwlock);}static struct attribute_group cmf_attr_group;static struct cmb_operations cmbops_basic = { .alloc = alloc_cmb, .free = free_cmb, .set = set_cmb, .read = read_cmb, .readall = readall_cmb, .reset = reset_cmb, .attr_group = &cmf_attr_group,};/* ******** extended cmb handling ********//** * struct cmbe - extended channel measurement block * * cmb as used by the hardware, may be in any 64 bit physical location, * the fields are described in z/Architecture Principles of Operation, * third edition, chapter 17. */struct cmbe { u32 ssch_rsch_count; u32 sample_count; u32 device_connect_time; u32 function_pending_time; u32 device_disconnect_time; u32 control_unit_queuing_time; u32 device_active_only_time; u32 device_busy_time; u32 initial_command_response_time; u32 reserved[7];};/* kmalloc only guarantees 8 byte alignment, but we need cmbe * pointers to be naturally aligned. Make sure to allocate * enough space for two cmbes */static inline struct cmbe* cmbe_align(struct cmbe *c){ unsigned long addr; addr = ((unsigned long)c + sizeof (struct cmbe) - sizeof(long)) & ~(sizeof (struct cmbe) - sizeof(long)); return (struct cmbe*)addr;}static intalloc_cmbe (struct ccw_device *cdev){ struct cmbe *cmbe; cmbe = kmalloc (sizeof (*cmbe) * 2, GFP_KERNEL); if (!cmbe) return -ENOMEM; spin_lock_irq(cdev->ccwlock); if (cdev->private->cmb) { kfree(cmbe); spin_unlock_irq(cdev->ccwlock); return -EBUSY; } cdev->private->cmb = cmbe; spin_unlock_irq(cdev->ccwlock); /* activate global measurement if this is the first channel */ spin_lock(&cmb_area.lock); if (list_empty(&cmb_area.list)) cmf_activate(NULL, 1); list_add_tail(&cdev->private->cmb_list, &cmb_area.list); spin_unlock(&cmb_area.lock); return 0;}static voidfree_cmbe (struct ccw_device *cdev){ spin_lock_irq(cdev->ccwlock); kfree(cdev->private->cmb); cdev->private->cmb = NULL; spin_unlock_irq(cdev->ccwlock); /* deactivate global measurement if this is the last channel */ spin_lock(&cmb_area.lock); list_del_init(&cdev->private->cmb_list); if (list_empty(&cmb_area.list)) cmf_activate(NULL, 0); spin_unlock(&cmb_area.lock);}static intset_cmbe(struct ccw_device *cdev, u32 mme){ unsigned long mba; if (!cdev->private->cmb) return -EINVAL; mba = mme ? (unsigned long) cmbe_align(cdev->private->cmb) : 0; return set_schib_wait(cdev, mme, 1, mba);}u64read_cmbe (struct ccw_device *cdev, int index){ /* yes, we have to put it on the stack * because the cmb must only be accessed * atomically, e.g. with mvc */ struct cmbe cmb; unsigned long flags; u32 val; spin_lock_irqsave(cdev->ccwlock, flags); if (!cdev->private->cmb) { spin_unlock_irqrestore(cdev->ccwlock, flags); return 0; } cmb = *cmbe_align(cdev->private->cmb); spin_unlock_irqrestore(cdev->ccwlock, flags); switch (index) { case cmb_ssch_rsch_count: return cmb.ssch_rsch_count; case cmb_sample_count: return cmb.sample_count; case cmb_device_connect_time: val = cmb.device_connect_time; break; case cmb_function_pending_time: val = cmb.function_pending_time; break; case cmb_device_disconnect_time: val = cmb.device_disconnect_time; break; case cmb_control_unit_queuing_time: val = cmb.control_unit_queuing_time; break; case cmb_device_active_only_time: val = cmb.device_active_only_time; break; case cmb_device_busy_time: val = cmb.device_busy_time; break; case cmb_initial_command_response_time: val = cmb.initial_command_response_time; break; default: return 0; } return time_to_avg_nsec(val, cmb.sample_count);}static intreadall_cmbe (struct ccw_device *cdev, struct cmbdata *data){ /* yes, we have to put it on the stack * because the cmb must only be accessed * atomically, e.g. with mvc */ struct cmbe cmb; unsigned long flags; u64 time; spin_lock_irqsave(cdev->ccwlock, flags); if (!cdev->private->cmb) { spin_unlock_irqrestore(cdev->ccwlock, flags); return -ENODEV; } cmb = *cmbe_align(cdev->private->cmb); time = get_clock() - cdev->private->cmb_start_time; spin_unlock_irqrestore(cdev->ccwlock, flags); memset (data, 0, sizeof(struct cmbdata)); /* we only know values before device_busy_time */ data->size = offsetof(struct cmbdata, device_busy_time); /* conver to nanoseconds */ data->elapsed_time = (time * 1000) >> 12; /* copy data to new structure */ data->ssch_rsch_count = cmb.ssch_rsch_count; data->sample_count = cmb.sample_count; /* time fields are converted to nanoseconds while copying */ data->device_connect_time = time_to_nsec(cmb.device_connect_time); data->function_pending_time = time_to_nsec(cmb.function_pending_time); data->device_disconnect_time = time_to_nsec(cmb.device_disconnect_time); data->control_unit_queuing_time = time_to_nsec(cmb.control_unit_queuing_time); data->device_active_only_time = time_to_nsec(cmb.device_active_only_time); data->device_busy_time = time_to_nsec(cmb.device_busy_time); data->initial_command_response_time = time_to_nsec(cmb.initial_command_response_time); return 0;}static voidreset_cmbe(struct ccw_device *cdev){ struct cmbe *cmb; spin_lock_irq(cdev->ccwlock); cmb = cmbe_align(cdev->private->cmb); if (cmb) memset (cmb, 0, sizeof (*cmb)); cdev->private->cmb_start_time = get_clock(); spin_unlock_irq(cdev->ccwlock);}static struct attribute_group cmf_attr_group_ext;static struct cmb_operations cmbops_extended = { .alloc = alloc_cmbe, .free = free_cmbe, .set = set_cmbe, .read = read_cmbe, .readall = readall_cmbe, .reset = reset_cmbe, .attr_group = &cmf_attr_group_ext,};static ssize_tcmb_show_attr(struct device *dev, char *buf, enum cmb_index idx){ return sprintf(buf, "%lld\n", (unsigned long long) cmf_read(to_ccwdev(dev), idx));}static ssize_tcmb_show_avg_sample_interval(struct device *dev, struct device_attribute *attr, char *buf){ struct ccw_device *cdev; long interval; unsigned long count; cdev = to_ccwdev(dev); interval = get_clock() - cdev->private->cmb_start_time; count = cmf_read(cdev, cmb_sample_count); if (count) interval /= count; else interval = -1; return sprintf(buf, "%ld\n", interval);}static ssize_tcmb_show_avg_utilization(struct device *dev, struct device_attribute *attr, char *buf){ struct cmbdata data; u64 utilization; unsigned long t, u; int ret; ret = cmf_readall(to_ccwdev(dev), &data); if (ret) return ret; utilization = data.device_connect_time + data.function_pending_time + data.device_disconnect_time; /* shift to avoid long long division */ while (-1ul < (data.elapsed_time | utilization)) { utilization >>= 8; data.elapsed_time >>= 8; } /* calculate value in 0.1 percent units */ t = (unsigned long) data.elapsed_time / 1000; u = (unsigned long) utilization / t; return sprintf(buf, "%02ld.%01ld%%\n", u/ 10, u - (u/ 10) * 10);}#define cmf_attr(name) \static ssize_t show_ ## name (struct device * dev, struct device_attribute *attr, char * buf) \{ return cmb_show_attr((dev), buf, cmb_ ## name); } \static DEVICE_ATTR(name, 0444, show_ ## name, NULL);#define cmf_attr_avg(name) \static ssize_t show_avg_ ## name (struct device * dev, struct device_attribute *attr, char * buf) \{ return cmb_show_attr((dev), buf, cmb_ ## name); } \static DEVICE_ATTR(avg_ ## name, 0444, show_avg_ ## name, NULL);cmf_attr(ssch_rsch_count);cmf_attr(sample_count);cmf_attr_avg(device_connect_time);cmf_attr_avg(function_pending_time);cmf_attr_avg(device_disconnect_time);cmf_attr_avg(control_unit_queuing_time);cmf_attr_avg(device_active_only_time);cmf_attr_avg(device_busy_time);cmf_attr_avg(initial_command_response_time);static DEVICE_ATTR(avg_sample_interval, 0444, cmb_show_avg_sample_interval, NULL);static DEVICE_ATTR(avg_utilization, 0444, cmb_show_avg_utilization, NULL);static struct attribute *cmf_attributes[] = { &dev_attr_avg_sample_interval.attr, &dev_attr_avg_utilization.attr, &dev_attr_ssch_rsch_count.attr, &dev_attr_sample_count.attr, &dev_attr_avg_device_connect_time.attr, &dev_attr_avg_function_pending_time.attr, &dev_attr_avg_device_disconnect_time.attr, &dev_attr_avg_control_unit_queuing_time.attr, &dev_attr_avg_device_active_only_time.attr, 0,};static struct attribute_group cmf_attr_group = { .name = "cmf", .attrs = cmf_attributes,};static struct attribute *cmf_attributes_ext[] = { &dev_attr_avg_sample_interval.attr, &dev_attr_avg_utilization.attr, &dev_attr_ssch_rsch_count.attr, &dev_attr_sample_count.attr, &dev_attr_avg_device_connect_time.attr, &dev_attr_avg_function_pending_time.attr, &dev_attr_avg_device_disconnect_time.attr, &dev_attr_avg_control_unit_queuing_time.attr, &dev_attr_avg_device_active_only_time.attr, &dev_attr_avg_device_busy_time.attr, &dev_attr_avg_initial_command_response_time.attr, 0,};static struct attribute_group cmf_attr_group_ext = { .name = "cmf", .attrs = cmf_attributes_ext,};static ssize_t cmb_enable_show(struct device *dev, struct device_attribute *attr, char *buf){ return sprintf(buf, "%d\n", to_ccwdev(dev)->private->cmb ? 1 : 0);}static ssize_t cmb_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t c){ struct ccw_device *cdev; int ret; cdev = to_ccwdev(dev); switch (buf[0]) { case '0': ret = disable_cmf(cdev); if (ret) printk(KERN_INFO "disable_cmf failed (%d)\n", ret); break; case '1': ret = enable_cmf(cdev); if (ret && ret != -EBUSY) printk(KERN_INFO "enable_cmf failed (%d)\n", ret); break; } return c;}DEVICE_ATTR(cmb_enable, 0644, cmb_enable_show, cmb_enable_store);/* enable_cmf/disable_cmf: module interface for cmf (de)activation */intenable_cmf(struct ccw_device *cdev){ int ret; ret = cmbops->alloc(cdev); cmbops->reset(cdev); if (ret) return ret; ret = cmbops->set(cdev, 2); if (ret) { cmbops->free(cdev); return ret; } ret = sysfs_create_group(&cdev->dev.kobj, cmbops->attr_group); if (!ret) return 0; cmbops->set(cdev, 0); //FIXME: this can fail cmbops->free(cdev); return ret;}intdisable_cmf(struct ccw_device *cdev){ int ret; ret = cmbops->set(cdev, 0); if (ret) return ret; cmbops->free(cdev); sysfs_remove_group(&cdev->dev.kobj, cmbops->attr_group); return ret;}u64cmf_read(struct ccw_device *cdev, int index){ return cmbops->read(cdev, index);}intcmf_readall(struct ccw_device *cdev, struct cmbdata *data){ return cmbops->readall(cdev, data);}static int __initinit_cmf(void){ char *format_string; char *detect_string = "parameter"; /* We cannot really autoprobe this. If the user did not give a parameter, see if we are running on z990 or up, otherwise fall back to basic mode. */ if (format == CMF_AUTODETECT) { if (!css_characteristics_avail || !css_general_characteristics.ext_mb) { format = CMF_BASIC; } else { format = CMF_EXTENDED; } detect_string = "autodetected"; } else { detect_string = "parameter"; } switch (format) { case CMF_BASIC: format_string = "basic"; cmbops = &cmbops_basic; if (cmb_area.num_channels > 4096 || cmb_area.num_channels < 1) { printk(KERN_ERR "Basic channel measurement facility" " can only use 1 to 4096 devices\n" KERN_ERR "when the cmf driver is built" " as a loadable module\n"); return 1; } break; case CMF_EXTENDED: format_string = "extended"; cmbops = &cmbops_extended; break; default: printk(KERN_ERR "Invalid format %d for channel " "measurement facility\n", format); return 1; } printk(KERN_INFO "Channel measurement facility using %s format (%s)\n", format_string, detect_string); return 0;}module_init(init_cmf);MODULE_AUTHOR("Arnd Bergmann <arndb@de.ibm.com>");MODULE_LICENSE("GPL");MODULE_DESCRIPTION("channel measurement facility base driver\n" "Copyright 2003 IBM Corporation\n");EXPORT_SYMBOL_GPL(enable_cmf);EXPORT_SYMBOL_GPL(disable_cmf);EXPORT_SYMBOL_GPL(cmf_read);EXPORT_SYMBOL_GPL(cmf_readall);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -