📄 cmf.c
字号:
}static int set_cmbe(struct ccw_device *cdev, u32 mme){ unsigned long mba; struct cmb_data *cmb_data; unsigned long flags; spin_lock_irqsave(cdev->ccwlock, flags); if (!cdev->private->cmb) { spin_unlock_irqrestore(cdev->ccwlock, flags); return -EINVAL; } cmb_data = cdev->private->cmb; mba = mme ? (unsigned long) cmbe_align(cmb_data->hw_block) : 0; spin_unlock_irqrestore(cdev->ccwlock, flags); return set_schib_wait(cdev, mme, 1, mba);}static u64 read_cmbe(struct ccw_device *cdev, int index){ struct cmbe *cmb; struct cmb_data *cmb_data; u32 val; int ret; unsigned long flags; ret = cmf_cmb_copy_wait(cdev); if (ret < 0) return 0; spin_lock_irqsave(cdev->ccwlock, flags); cmb_data = cdev->private->cmb; if (!cmb_data) { ret = 0; goto out; } cmb = cmb_data->last_block; switch (index) { case cmb_ssch_rsch_count: ret = cmb->ssch_rsch_count; goto out; case cmb_sample_count: ret = cmb->sample_count; goto out; 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: ret = 0; goto out; } ret = time_to_avg_nsec(val, cmb->sample_count);out: spin_unlock_irqrestore(cdev->ccwlock, flags); return ret;}static int readall_cmbe(struct ccw_device *cdev, struct cmbdata *data){ struct cmbe *cmb; struct cmb_data *cmb_data; u64 time; unsigned long flags; int ret; ret = cmf_cmb_copy_wait(cdev); if (ret < 0) return ret; spin_lock_irqsave(cdev->ccwlock, flags); cmb_data = cdev->private->cmb; if (!cmb_data) { ret = -ENODEV; goto out; } if (cmb_data->last_update == 0) { ret = -EAGAIN; goto out; } time = cmb_data->last_update - cdev->private->cmb_start_time; 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; cmb = cmb_data->last_block; /* 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); ret = 0;out: spin_unlock_irqrestore(cdev->ccwlock, flags); return ret;}static void reset_cmbe(struct ccw_device *cdev){ cmf_generic_reset(cdev);}static void * align_cmbe(void *area){ return cmbe_align(area);}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, .align = align_cmbe, .attr_group = &cmf_attr_group_ext,};static ssize_t cmb_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_t cmb_show_avg_sample_interval(struct device *dev, struct device_attribute *attr, char *buf){ struct ccw_device *cdev; long interval; unsigned long count; struct cmb_data *cmb_data; cdev = to_ccwdev(dev); count = cmf_read(cdev, cmb_sample_count); spin_lock_irq(cdev->ccwlock); cmb_data = cdev->private->cmb; if (count) { interval = cmb_data->last_update - cdev->private->cmb_start_time; interval = (interval * 1000) >> 12; interval /= count; } else interval = -1; spin_unlock_irq(cdev->ccwlock); return sprintf(buf, "%ld\n", interval);}static ssize_t cmb_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 == -EAGAIN || ret == -ENODEV) /* No data (yet/currently) available to use for calculation. */ return sprintf(buf, "n/a\n"); else 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, NULL,};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, NULL,};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) dev_info(&cdev->dev, "disable_cmf failed (%d)\n", ret); break; case '1': ret = enable_cmf(cdev); if (ret && ret != -EBUSY) dev_info(&cdev->dev, "enable_cmf failed (%d)\n", ret); break; } return c;}DEVICE_ATTR(cmb_enable, 0644, cmb_enable_show, cmb_enable_store);/** * enable_cmf() - switch on the channel measurement for a specific device * @cdev: The ccw device to be enabled * * Returns %0 for success or a negative error value. * * Context: * non-atomic */int enable_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;}/** * disable_cmf() - switch off the channel measurement for a specific device * @cdev: The ccw device to be disabled * * Returns %0 for success or a negative error value. * * Context: * non-atomic */int disable_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;}/** * cmf_read() - read one value from the current channel measurement block * @cdev: the channel to be read * @index: the index of the value to be read * * Returns the value read or %0 if the value cannot be read. * * Context: * any */u64 cmf_read(struct ccw_device *cdev, int index){ return cmbops->read(cdev, index);}/** * cmf_readall() - read the current channel measurement block * @cdev: the channel to be read * @data: a pointer to a data block that will be filled * * Returns %0 on success, a negative error value otherwise. * * Context: * any */int cmf_readall(struct ccw_device *cdev, struct cmbdata *data){ return cmbops->readall(cdev, data);}/* Reenable cmf when a disconnected device becomes available again. */int cmf_reenable(struct ccw_device *cdev){ cmbops->reset(cdev); return cmbops->set(cdev, 2);}static int __init init_cmf(void){ char *format_string; char *detect_string = "parameter"; /* * If the user did not give a parameter, see if we are running on a * machine supporting extended measurement blocks, 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; break; case CMF_EXTENDED: format_string = "extended"; cmbops = &cmbops_extended; break; default: printk(KERN_ERR "cio: Invalid format %d for channel " "measurement facility\n", format); return 1; } printk(KERN_INFO "cio: 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 + -