// SPDX-License-Identifier: GPL-2.0+
/*
* Linux on zSeries Channel Measurement Facility support
*
* Copyright IBM Corp. 2000, 2006
*
* Authors: Arnd Bergmann <arndb@de.ibm.com>
* Cornelia Huck <cornelia.huck@de.ibm.com>
*
* original idea from Natarajan Krishnaswami <nkrishna@us.ibm.com>
*/
#define KMSG_COMPONENT "cio"
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
#include <linux/memblock.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/export.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/timex.h> /* get_tod_clock() */
#include <asm/ccwdev.h>
#include <asm/cio.h>
#include <asm/cmb.h>
#include <asm/div64.h>
#include "cio.h"
#include "css.h"
#include "device.h"
#include "ioasm.h"
#include "chsc.h"
/*
* 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 exended 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.
*/
static int 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;
};
static struct 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 */
unsigned long long 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.
*/
static inline 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 sinlge values.
*/<