/* * parameter to enable cmf during boot, possible uses are: * "s390cmf" -- enable cmf and allocate 2 MB of ram so measuring can be * used on any subchannel * "s390cmf=<num>" -- enable cmf and allocate enough memory to measure * <num> subchannel, where <num> is an integer * between 1 and 65535, default is 1024
*/ #define ARGSTRING "s390cmf"
/* indices for READCMB */ enum cmb_index {
avg_utilization = -1, /* basic and extended format: */
cmb_ssch_rsch_count = 0,
cmb_sample_count,
cmb_device_connect_time,
cmb_function_pending_time,
cmb_device_disconnect_time,
cmb_control_unit_queuing_time,
cmb_device_active_only_time, /* extended format only: */
cmb_device_busy_time,
cmb_initial_command_response_time,
};
/** * enum cmb_format - types of supported measurement block formats * * @CMF_BASIC: traditional channel measurement blocks supported * by all machines that we run on * @CMF_EXTENDED: improved format that was introduced with the z990 * machine * @CMF_AUTODETECT: default: use extended format when running on a machine * supporting extended format, otherwise fall back to * basic format
*/ enum cmb_format {
CMF_BASIC,
CMF_EXTENDED,
CMF_AUTODETECT = -1,
};
/* * format - actual format for all measurement blocks * * The format module parameter can be set to a value of 0 (zero) * or 1, indicating basic or extended format as described for * enum cmb_format.
*/ staticint format = CMF_AUTODETECT;
module_param(format, bint, 0444);
/** * struct cmb_operations - functions to use depending on cmb_format * * Most of these functions operate on a struct ccw_device. There is only * one instance of struct cmb_operations because the format of the measurement * data is guaranteed to be the same for every ccw_device. * * @alloc: allocate memory for a channel measurement block, * either with the help of a special pool or with kmalloc * @free: free memory allocated with @alloc * @set: enable or disable measurement * @read: read a measurement entry at an index * @readall: read a measurement block in a common format * @reset: clear the data in the associated measurement block and * reset its time stamp
*/ struct cmb_operations { int (*alloc) (struct ccw_device *); void (*free) (struct ccw_device *); int (*set) (struct ccw_device *, u32);
u64 (*read) (struct ccw_device *, int); int (*readall)(struct ccw_device *, struct cmbdata *); void (*reset) (struct ccw_device *); /* private: */ struct attribute_group *attr_group;
}; staticstruct cmb_operations *cmbops;
struct cmb_data { void *hw_block; /* Pointer to block updated by hardware */ void *last_block; /* Last changed block copied from hardware block */ int size; /* Size of hw_block and last_block */ unsignedlonglong last_update; /* when last_block was updated */
};
/* * Our user interface is designed in terms of nanoseconds, * while the hardware measures total times in its own * unit.
*/ staticinline u64 time_to_nsec(u32 value)
{ return ((u64)value) * 128000ull;
}
/* * Users are usually interested in average times, * not accumulated time. * This also helps us with atomicity problems * when reading single values.
*/ staticinline u64 time_to_avg_nsec(u32 value, u32 count)
{
u64 ret;
/* no samples yet, avoid division by 0 */ if (count == 0) return 0;
/* value comes in units of 128 µsec */
ret = time_to_nsec(value);
do_div(ret, count);
return ret;
}
#define CMF_OFF 0 #define CMF_ON 2
/* * Activate or deactivate the channel monitor. When area is NULL, * the monitor is deactivated. The channel monitor needs to * be active in order to measure subchannels, which also need * to be enabled.
*/ staticinlinevoid cmf_activate(void *area, unsignedint onoff)
{ /* activate channel measurement */ asmvolatile( " lgr 1,%[r1]\n" " lgr 2,%[mbo]\n" " schm\n"
:
: [r1] "d" ((unsignedlong)onoff),
[mbo] "d" (virt_to_phys(area))
: "1", "2");
}
staticint set_schib(struct ccw_device *cdev, u32 mme, int mbfc, unsignedlong address)
{ struct subchannel *sch = to_subchannel(cdev->dev.parent); int ret;
sch->config.mme = mme;
sch->config.mbfc = mbfc; /* address can be either a block address or a block index */ if (mbfc)
sch->config.mba = address; else
sch->config.mbi = address;
ret = cio_commit_config(sch); if (!mme && ret == -ENODEV) { /* * The task was to disable measurement block updates but * the subchannel is already gone. Report success.
*/
ret = 0;
} return ret;
}
struct set_schib_struct {
u32 mme; int mbfc; unsignedlong address;
wait_queue_head_t wait; int ret;
};
spin_lock_irq(cdev->ccwlock);
cmb_data = cdev->private->cmb; if (cmb_data) {
memset(cmb_data->last_block, 0, cmb_data->size); /* * Need to reset hw block as well to make the hardware start * from 0 again.
*/
memset(cmb_data->hw_block, 0, cmb_data->size);
cmb_data->last_update = 0;
}
cdev->private->cmb_start_time = get_tod_clock();
spin_unlock_irq(cdev->ccwlock);
}
/** * struct cmb_area - container for global cmb data * * @mem: pointer to CMBs (only in basic measurement mode) * @list: contains a linked list of all subchannels * @num_channels: number of channels to be measured * @lock: protect concurrent access to @mem and @list
*/ struct cmb_area { struct cmb *mem; struct list_head list; int num_channels;
spinlock_t lock;
};
/* * Basic channel measurement blocks are allocated in one contiguous * block of memory, which can not be moved as long as any channel * is active. Therefore, a maximum number of subchannels needs to * be defined somewhere. This is a module parameter, defaulting to * a reasonable value of 1024, or 32 kb of memory. * Current kernels don't allow kmalloc with more than 128kb, so the * maximum is 4096.
*/
/** * struct cmb - basic channel measurement block * @ssch_rsch_count: number of ssch and rsch * @sample_count: number of samples * @device_connect_time: time of device connect * @function_pending_time: time of function pending * @device_disconnect_time: time of device disconnect * @control_unit_queuing_time: time of control unit queuing * @device_active_only_time: time of device active only * @reserved: unused in basic measurement mode * * The measurement block as used by the hardware. The fields are described * further in z/Architecture Principles of Operation, chapter 17. * * The cmb area made up from these blocks must be a contiguous array and may * not be reallocated or freed. * Only one cmb area can be present in the system.
*/ struct cmb {
u16 ssch_rsch_count;
u16 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 reserved[2];
};
/* * Insert a single device into the cmb_area list. * Called with cmb_area.lock held from alloc_cmb.
*/ staticint alloc_cmb_single(struct ccw_device *cdev, struct cmb_data *cmb_data)
{ struct cmb *cmb; struct ccw_device_private *node; int ret;
spin_lock_irq(cdev->ccwlock); if (!list_empty(&cdev->private->cmb_list)) {
ret = -EBUSY; goto out;
}
/* * Find first unused cmb in cmb_area.mem. * This is a little tricky: cmb_area.list * remains sorted by ->cmb->hw_data pointers.
*/
cmb = cmb_area.mem;
list_for_each_entry(node, &cmb_area.list, cmb_list) { struct cmb_data *data;
data = node->cmb; if ((struct cmb*)data->hw_block > cmb) break;
cmb++;
} if (cmb - cmb_area.mem >= cmb_area.num_channels) {
ret = -ENOMEM; goto out;
}
if (!cmb_area.mem) { /* there is no user yet, so we need a new area */
size = sizeof(struct cmb) * cmb_area.num_channels;
WARN_ON(!list_empty(&cmb_area.list));
spin_unlock(&cmb_area.lock);
mem = (void *)__get_free_pages(GFP_KERNEL, get_order(size));
spin_lock(&cmb_area.lock);
if (cmb_area.mem) { /* ok, another thread was faster */
free_pages((unsignedlong)mem, get_order(size));
} elseif (!mem) { /* no luck */
ret = -ENOMEM; goto out;
} else { /* everything ok */
memset(mem, 0, size);
cmb_area.mem = mem;
cmf_activate(cmb_area.mem, CMF_ON);
}
}
/* do the actual allocation */
ret = alloc_cmb_single(cdev, cmb_data);
out:
spin_unlock(&cmb_area.lock); if (ret) {
kfree(cmb_data->last_block);
kfree(cmb_data);
} return ret;
}
spin_lock_irqsave(cdev->ccwlock, flags);
cmb_data = cdev->private->cmb; if (!cmb_data) goto out;
cmb = cmb_data->hw_block; switch (index) { case avg_utilization:
ret = __cmb_utilization(cmb->device_connect_time,
cmb->function_pending_time,
cmb->device_disconnect_time,
cdev->private->cmb_start_time); goto out; 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; default: goto out;
}
ret = time_to_avg_nsec(val, cmb->sample_count);
out:
spin_unlock_irqrestore(cdev->ccwlock, flags); return ret;
}
/** * struct cmbe - extended channel measurement block * @ssch_rsch_count: number of ssch and rsch * @sample_count: number of samples * @device_connect_time: time of device connect * @function_pending_time: time of function pending * @device_disconnect_time: time of device disconnect * @control_unit_queuing_time: time of control unit queuing * @device_active_only_time: time of device active only * @device_busy_time: time of device busy * @initial_command_response_time: initial command response time * @reserved: unused * * The measurement block as used by the hardware. May be in any 64 bit physical * location. * The fields are described further 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];
} __packed __aligned(64);
staticstruct kmem_cache *cmbe_cache;
staticint alloc_cmbe(struct ccw_device *cdev)
{ struct cmb_data *cmb_data; struct cmbe *cmbe; int ret = -ENOMEM;
cmbe = kmem_cache_zalloc(cmbe_cache, GFP_KERNEL); if (!cmbe) return ret;
cmb_data = kzalloc(sizeof(*cmb_data), GFP_KERNEL); if (!cmb_data) goto out_free;
cmb_data->last_block = kzalloc(sizeof(struct cmbe), GFP_KERNEL); if (!cmb_data->last_block) goto out_free;
spin_lock(&cmb_area.lock);
spin_lock_irq(cdev->ccwlock); if (cdev->private->cmb) goto out_unlock;
cdev->private->cmb = cmb_data;
/* activate global measurement if this is the first channel */ if (list_empty(&cmb_area.list))
cmf_activate(NULL, CMF_ON);
list_add_tail(&cdev->private->cmb_list, &cmb_area.list);
/* deactivate global measurement if this is the last channel */
list_del_init(&cdev->private->cmb_list); if (list_empty(&cmb_area.list))
cmf_activate(NULL, CMF_OFF);
spin_unlock_irq(cdev->ccwlock);
spin_unlock(&cmb_area.lock);
}
spin_lock_irqsave(cdev->ccwlock, flags);
cmb_data = cdev->private->cmb; if (!cmb_data) goto out;
cmb = cmb_data->hw_block; switch (index) { case avg_utilization:
ret = __cmb_utilization(cmb->device_connect_time,
cmb->function_pending_time,
cmb->device_disconnect_time,
cdev->private->cmb_start_time); goto out; 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: goto out;
}
ret = time_to_avg_nsec(val, cmb->sample_count);
out:
spin_unlock_irqrestore(cdev->ccwlock, flags); return ret;
}
ret = kstrtoul(buf, 16, &val); if (ret) return ret;
switch (val) { case 0:
ret = disable_cmf(cdev); break; case 1:
ret = enable_cmf(cdev); break; default:
ret = -EINVAL;
}
return ret ? ret : c;
}
DEVICE_ATTR_RW(cmb_enable);
/** * enable_cmf() - switch on the channel measurement for a specific device * @cdev: The ccw device to be enabled * * Enable channel measurements for @cdev. If this is called on a device * for which channel measurement is already enabled a reset of the * measurement data is triggered. * Returns: %0 for success or a negative error value. * Context: * non-atomic
*/ int enable_cmf(struct ccw_device *cdev)
{ int ret = 0;
device_lock(&cdev->dev); if (cmf_enabled(cdev)) {
cmbops->reset(cdev); goto out_unlock;
}
get_device(&cdev->dev);
ret = cmbops->alloc(cdev); if (ret) goto out;
cmbops->reset(cdev);
ret = sysfs_create_group(&cdev->dev.kobj, cmbops->attr_group); if (ret) {
cmbops->free(cdev); goto out;
}
ret = cmbops->set(cdev, 2); if (ret) {
sysfs_remove_group(&cdev->dev.kobj, cmbops->attr_group);
cmbops->free(cdev);
}
out: if (ret)
put_device(&cdev->dev);
out_unlock:
device_unlock(&cdev->dev); 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, device_lock() held.
*/ int __disable_cmf(struct ccw_device *cdev)
{ int 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;
device_lock(&cdev->dev);
ret = __disable_cmf(cdev);
device_unlock(&cdev->dev);
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);
}
/* Re-enable cmf when a disconnected device becomes available again. */ int cmf_reenable(struct ccw_device *cdev)
{
cmbops->reset(cdev); return cmbops->set(cdev, 2);
}
/** * cmf_reactivate() - reactivate measurement block updates * * Use this during resume from hibernate.
*/ void cmf_reactivate(void)
{
spin_lock(&cmb_area.lock); if (!list_empty(&cmb_area.list))
cmf_activate(cmb_area.mem, CMF_ON);
spin_unlock(&cmb_area.lock);
}
staticint __init init_cmf(void)
{ char *format_string; char *detect_string; int ret;
/* * 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_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;
ret = init_cmbe(); if (ret) return ret; break; default: return -EINVAL;
}
pr_info("Channel measurement facility initialized using format " "%s (mode %s)\n", format_string, detect_string); return 0;
}
device_initcall(init_cmf);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.