162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Linux on zSeries Channel Measurement Facility support 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright IBM Corp. 2000, 2006 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Authors: Arnd Bergmann <arndb@de.ibm.com> 862306a36Sopenharmony_ci * Cornelia Huck <cornelia.huck@de.ibm.com> 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * original idea from Natarajan Krishnaswami <nkrishna@us.ibm.com> 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#define KMSG_COMPONENT "cio" 1462306a36Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/memblock.h> 1762306a36Sopenharmony_ci#include <linux/device.h> 1862306a36Sopenharmony_ci#include <linux/init.h> 1962306a36Sopenharmony_ci#include <linux/list.h> 2062306a36Sopenharmony_ci#include <linux/export.h> 2162306a36Sopenharmony_ci#include <linux/moduleparam.h> 2262306a36Sopenharmony_ci#include <linux/slab.h> 2362306a36Sopenharmony_ci#include <linux/timex.h> /* get_tod_clock() */ 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <asm/ccwdev.h> 2662306a36Sopenharmony_ci#include <asm/cio.h> 2762306a36Sopenharmony_ci#include <asm/cmb.h> 2862306a36Sopenharmony_ci#include <asm/div64.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include "cio.h" 3162306a36Sopenharmony_ci#include "css.h" 3262306a36Sopenharmony_ci#include "device.h" 3362306a36Sopenharmony_ci#include "ioasm.h" 3462306a36Sopenharmony_ci#include "chsc.h" 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* 3762306a36Sopenharmony_ci * parameter to enable cmf during boot, possible uses are: 3862306a36Sopenharmony_ci * "s390cmf" -- enable cmf and allocate 2 MB of ram so measuring can be 3962306a36Sopenharmony_ci * used on any subchannel 4062306a36Sopenharmony_ci * "s390cmf=<num>" -- enable cmf and allocate enough memory to measure 4162306a36Sopenharmony_ci * <num> subchannel, where <num> is an integer 4262306a36Sopenharmony_ci * between 1 and 65535, default is 1024 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_ci#define ARGSTRING "s390cmf" 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* indices for READCMB */ 4762306a36Sopenharmony_cienum cmb_index { 4862306a36Sopenharmony_ci avg_utilization = -1, 4962306a36Sopenharmony_ci /* basic and exended format: */ 5062306a36Sopenharmony_ci cmb_ssch_rsch_count = 0, 5162306a36Sopenharmony_ci cmb_sample_count, 5262306a36Sopenharmony_ci cmb_device_connect_time, 5362306a36Sopenharmony_ci cmb_function_pending_time, 5462306a36Sopenharmony_ci cmb_device_disconnect_time, 5562306a36Sopenharmony_ci cmb_control_unit_queuing_time, 5662306a36Sopenharmony_ci cmb_device_active_only_time, 5762306a36Sopenharmony_ci /* extended format only: */ 5862306a36Sopenharmony_ci cmb_device_busy_time, 5962306a36Sopenharmony_ci cmb_initial_command_response_time, 6062306a36Sopenharmony_ci}; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/** 6362306a36Sopenharmony_ci * enum cmb_format - types of supported measurement block formats 6462306a36Sopenharmony_ci * 6562306a36Sopenharmony_ci * @CMF_BASIC: traditional channel measurement blocks supported 6662306a36Sopenharmony_ci * by all machines that we run on 6762306a36Sopenharmony_ci * @CMF_EXTENDED: improved format that was introduced with the z990 6862306a36Sopenharmony_ci * machine 6962306a36Sopenharmony_ci * @CMF_AUTODETECT: default: use extended format when running on a machine 7062306a36Sopenharmony_ci * supporting extended format, otherwise fall back to 7162306a36Sopenharmony_ci * basic format 7262306a36Sopenharmony_ci */ 7362306a36Sopenharmony_cienum cmb_format { 7462306a36Sopenharmony_ci CMF_BASIC, 7562306a36Sopenharmony_ci CMF_EXTENDED, 7662306a36Sopenharmony_ci CMF_AUTODETECT = -1, 7762306a36Sopenharmony_ci}; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* 8062306a36Sopenharmony_ci * format - actual format for all measurement blocks 8162306a36Sopenharmony_ci * 8262306a36Sopenharmony_ci * The format module parameter can be set to a value of 0 (zero) 8362306a36Sopenharmony_ci * or 1, indicating basic or extended format as described for 8462306a36Sopenharmony_ci * enum cmb_format. 8562306a36Sopenharmony_ci */ 8662306a36Sopenharmony_cistatic int format = CMF_AUTODETECT; 8762306a36Sopenharmony_cimodule_param(format, bint, 0444); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci/** 9062306a36Sopenharmony_ci * struct cmb_operations - functions to use depending on cmb_format 9162306a36Sopenharmony_ci * 9262306a36Sopenharmony_ci * Most of these functions operate on a struct ccw_device. There is only 9362306a36Sopenharmony_ci * one instance of struct cmb_operations because the format of the measurement 9462306a36Sopenharmony_ci * data is guaranteed to be the same for every ccw_device. 9562306a36Sopenharmony_ci * 9662306a36Sopenharmony_ci * @alloc: allocate memory for a channel measurement block, 9762306a36Sopenharmony_ci * either with the help of a special pool or with kmalloc 9862306a36Sopenharmony_ci * @free: free memory allocated with @alloc 9962306a36Sopenharmony_ci * @set: enable or disable measurement 10062306a36Sopenharmony_ci * @read: read a measurement entry at an index 10162306a36Sopenharmony_ci * @readall: read a measurement block in a common format 10262306a36Sopenharmony_ci * @reset: clear the data in the associated measurement block and 10362306a36Sopenharmony_ci * reset its time stamp 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_cistruct cmb_operations { 10662306a36Sopenharmony_ci int (*alloc) (struct ccw_device *); 10762306a36Sopenharmony_ci void (*free) (struct ccw_device *); 10862306a36Sopenharmony_ci int (*set) (struct ccw_device *, u32); 10962306a36Sopenharmony_ci u64 (*read) (struct ccw_device *, int); 11062306a36Sopenharmony_ci int (*readall)(struct ccw_device *, struct cmbdata *); 11162306a36Sopenharmony_ci void (*reset) (struct ccw_device *); 11262306a36Sopenharmony_ci/* private: */ 11362306a36Sopenharmony_ci struct attribute_group *attr_group; 11462306a36Sopenharmony_ci}; 11562306a36Sopenharmony_cistatic struct cmb_operations *cmbops; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistruct cmb_data { 11862306a36Sopenharmony_ci void *hw_block; /* Pointer to block updated by hardware */ 11962306a36Sopenharmony_ci void *last_block; /* Last changed block copied from hardware block */ 12062306a36Sopenharmony_ci int size; /* Size of hw_block and last_block */ 12162306a36Sopenharmony_ci unsigned long long last_update; /* when last_block was updated */ 12262306a36Sopenharmony_ci}; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci/* 12562306a36Sopenharmony_ci * Our user interface is designed in terms of nanoseconds, 12662306a36Sopenharmony_ci * while the hardware measures total times in its own 12762306a36Sopenharmony_ci * unit. 12862306a36Sopenharmony_ci */ 12962306a36Sopenharmony_cistatic inline u64 time_to_nsec(u32 value) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci return ((u64)value) * 128000ull; 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci/* 13562306a36Sopenharmony_ci * Users are usually interested in average times, 13662306a36Sopenharmony_ci * not accumulated time. 13762306a36Sopenharmony_ci * This also helps us with atomicity problems 13862306a36Sopenharmony_ci * when reading sinlge values. 13962306a36Sopenharmony_ci */ 14062306a36Sopenharmony_cistatic inline u64 time_to_avg_nsec(u32 value, u32 count) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci u64 ret; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* no samples yet, avoid division by 0 */ 14562306a36Sopenharmony_ci if (count == 0) 14662306a36Sopenharmony_ci return 0; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* value comes in units of 128 µsec */ 14962306a36Sopenharmony_ci ret = time_to_nsec(value); 15062306a36Sopenharmony_ci do_div(ret, count); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci return ret; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci#define CMF_OFF 0 15662306a36Sopenharmony_ci#define CMF_ON 2 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci/* 15962306a36Sopenharmony_ci * Activate or deactivate the channel monitor. When area is NULL, 16062306a36Sopenharmony_ci * the monitor is deactivated. The channel monitor needs to 16162306a36Sopenharmony_ci * be active in order to measure subchannels, which also need 16262306a36Sopenharmony_ci * to be enabled. 16362306a36Sopenharmony_ci */ 16462306a36Sopenharmony_cistatic inline void cmf_activate(void *area, unsigned int onoff) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci /* activate channel measurement */ 16762306a36Sopenharmony_ci asm volatile( 16862306a36Sopenharmony_ci " lgr 1,%[r1]\n" 16962306a36Sopenharmony_ci " lgr 2,%[mbo]\n" 17062306a36Sopenharmony_ci " schm\n" 17162306a36Sopenharmony_ci : 17262306a36Sopenharmony_ci : [r1] "d" ((unsigned long)onoff), [mbo] "d" (area) 17362306a36Sopenharmony_ci : "1", "2"); 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic int set_schib(struct ccw_device *cdev, u32 mme, int mbfc, 17762306a36Sopenharmony_ci unsigned long address) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci struct subchannel *sch = to_subchannel(cdev->dev.parent); 18062306a36Sopenharmony_ci int ret; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci sch->config.mme = mme; 18362306a36Sopenharmony_ci sch->config.mbfc = mbfc; 18462306a36Sopenharmony_ci /* address can be either a block address or a block index */ 18562306a36Sopenharmony_ci if (mbfc) 18662306a36Sopenharmony_ci sch->config.mba = address; 18762306a36Sopenharmony_ci else 18862306a36Sopenharmony_ci sch->config.mbi = address; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci ret = cio_commit_config(sch); 19162306a36Sopenharmony_ci if (!mme && ret == -ENODEV) { 19262306a36Sopenharmony_ci /* 19362306a36Sopenharmony_ci * The task was to disable measurement block updates but 19462306a36Sopenharmony_ci * the subchannel is already gone. Report success. 19562306a36Sopenharmony_ci */ 19662306a36Sopenharmony_ci ret = 0; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci return ret; 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistruct set_schib_struct { 20262306a36Sopenharmony_ci u32 mme; 20362306a36Sopenharmony_ci int mbfc; 20462306a36Sopenharmony_ci unsigned long address; 20562306a36Sopenharmony_ci wait_queue_head_t wait; 20662306a36Sopenharmony_ci int ret; 20762306a36Sopenharmony_ci}; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci#define CMF_PENDING 1 21062306a36Sopenharmony_ci#define SET_SCHIB_TIMEOUT (10 * HZ) 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic int set_schib_wait(struct ccw_device *cdev, u32 mme, 21362306a36Sopenharmony_ci int mbfc, unsigned long address) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci struct set_schib_struct set_data; 21662306a36Sopenharmony_ci int ret = -ENODEV; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci spin_lock_irq(cdev->ccwlock); 21962306a36Sopenharmony_ci if (!cdev->private->cmb) 22062306a36Sopenharmony_ci goto out; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci ret = set_schib(cdev, mme, mbfc, address); 22362306a36Sopenharmony_ci if (ret != -EBUSY) 22462306a36Sopenharmony_ci goto out; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci /* if the device is not online, don't even try again */ 22762306a36Sopenharmony_ci if (cdev->private->state != DEV_STATE_ONLINE) 22862306a36Sopenharmony_ci goto out; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci init_waitqueue_head(&set_data.wait); 23162306a36Sopenharmony_ci set_data.mme = mme; 23262306a36Sopenharmony_ci set_data.mbfc = mbfc; 23362306a36Sopenharmony_ci set_data.address = address; 23462306a36Sopenharmony_ci set_data.ret = CMF_PENDING; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci cdev->private->state = DEV_STATE_CMFCHANGE; 23762306a36Sopenharmony_ci cdev->private->cmb_wait = &set_data; 23862306a36Sopenharmony_ci spin_unlock_irq(cdev->ccwlock); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci ret = wait_event_interruptible_timeout(set_data.wait, 24162306a36Sopenharmony_ci set_data.ret != CMF_PENDING, 24262306a36Sopenharmony_ci SET_SCHIB_TIMEOUT); 24362306a36Sopenharmony_ci spin_lock_irq(cdev->ccwlock); 24462306a36Sopenharmony_ci if (ret <= 0) { 24562306a36Sopenharmony_ci if (set_data.ret == CMF_PENDING) { 24662306a36Sopenharmony_ci set_data.ret = (ret == 0) ? -ETIME : ret; 24762306a36Sopenharmony_ci if (cdev->private->state == DEV_STATE_CMFCHANGE) 24862306a36Sopenharmony_ci cdev->private->state = DEV_STATE_ONLINE; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci cdev->private->cmb_wait = NULL; 25262306a36Sopenharmony_ci ret = set_data.ret; 25362306a36Sopenharmony_ciout: 25462306a36Sopenharmony_ci spin_unlock_irq(cdev->ccwlock); 25562306a36Sopenharmony_ci return ret; 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_civoid retry_set_schib(struct ccw_device *cdev) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci struct set_schib_struct *set_data = cdev->private->cmb_wait; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (!set_data) 26362306a36Sopenharmony_ci return; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci set_data->ret = set_schib(cdev, set_data->mme, set_data->mbfc, 26662306a36Sopenharmony_ci set_data->address); 26762306a36Sopenharmony_ci wake_up(&set_data->wait); 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistatic int cmf_copy_block(struct ccw_device *cdev) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci struct subchannel *sch = to_subchannel(cdev->dev.parent); 27362306a36Sopenharmony_ci struct cmb_data *cmb_data; 27462306a36Sopenharmony_ci void *hw_block; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci if (cio_update_schib(sch)) 27762306a36Sopenharmony_ci return -ENODEV; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (scsw_fctl(&sch->schib.scsw) & SCSW_FCTL_START_FUNC) { 28062306a36Sopenharmony_ci /* Don't copy if a start function is in progress. */ 28162306a36Sopenharmony_ci if ((!(scsw_actl(&sch->schib.scsw) & SCSW_ACTL_SUSPENDED)) && 28262306a36Sopenharmony_ci (scsw_actl(&sch->schib.scsw) & 28362306a36Sopenharmony_ci (SCSW_ACTL_DEVACT | SCSW_ACTL_SCHACT)) && 28462306a36Sopenharmony_ci (!(scsw_stctl(&sch->schib.scsw) & SCSW_STCTL_SEC_STATUS))) 28562306a36Sopenharmony_ci return -EBUSY; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci cmb_data = cdev->private->cmb; 28862306a36Sopenharmony_ci hw_block = cmb_data->hw_block; 28962306a36Sopenharmony_ci memcpy(cmb_data->last_block, hw_block, cmb_data->size); 29062306a36Sopenharmony_ci cmb_data->last_update = get_tod_clock(); 29162306a36Sopenharmony_ci return 0; 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistruct copy_block_struct { 29562306a36Sopenharmony_ci wait_queue_head_t wait; 29662306a36Sopenharmony_ci int ret; 29762306a36Sopenharmony_ci}; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic int cmf_cmb_copy_wait(struct ccw_device *cdev) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci struct copy_block_struct copy_block; 30262306a36Sopenharmony_ci int ret = -ENODEV; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci spin_lock_irq(cdev->ccwlock); 30562306a36Sopenharmony_ci if (!cdev->private->cmb) 30662306a36Sopenharmony_ci goto out; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci ret = cmf_copy_block(cdev); 30962306a36Sopenharmony_ci if (ret != -EBUSY) 31062306a36Sopenharmony_ci goto out; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if (cdev->private->state != DEV_STATE_ONLINE) 31362306a36Sopenharmony_ci goto out; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci init_waitqueue_head(©_block.wait); 31662306a36Sopenharmony_ci copy_block.ret = CMF_PENDING; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci cdev->private->state = DEV_STATE_CMFUPDATE; 31962306a36Sopenharmony_ci cdev->private->cmb_wait = ©_block; 32062306a36Sopenharmony_ci spin_unlock_irq(cdev->ccwlock); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci ret = wait_event_interruptible(copy_block.wait, 32362306a36Sopenharmony_ci copy_block.ret != CMF_PENDING); 32462306a36Sopenharmony_ci spin_lock_irq(cdev->ccwlock); 32562306a36Sopenharmony_ci if (ret) { 32662306a36Sopenharmony_ci if (copy_block.ret == CMF_PENDING) { 32762306a36Sopenharmony_ci copy_block.ret = -ERESTARTSYS; 32862306a36Sopenharmony_ci if (cdev->private->state == DEV_STATE_CMFUPDATE) 32962306a36Sopenharmony_ci cdev->private->state = DEV_STATE_ONLINE; 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci cdev->private->cmb_wait = NULL; 33362306a36Sopenharmony_ci ret = copy_block.ret; 33462306a36Sopenharmony_ciout: 33562306a36Sopenharmony_ci spin_unlock_irq(cdev->ccwlock); 33662306a36Sopenharmony_ci return ret; 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_civoid cmf_retry_copy_block(struct ccw_device *cdev) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci struct copy_block_struct *copy_block = cdev->private->cmb_wait; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (!copy_block) 34462306a36Sopenharmony_ci return; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci copy_block->ret = cmf_copy_block(cdev); 34762306a36Sopenharmony_ci wake_up(©_block->wait); 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic void cmf_generic_reset(struct ccw_device *cdev) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci struct cmb_data *cmb_data; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci spin_lock_irq(cdev->ccwlock); 35562306a36Sopenharmony_ci cmb_data = cdev->private->cmb; 35662306a36Sopenharmony_ci if (cmb_data) { 35762306a36Sopenharmony_ci memset(cmb_data->last_block, 0, cmb_data->size); 35862306a36Sopenharmony_ci /* 35962306a36Sopenharmony_ci * Need to reset hw block as well to make the hardware start 36062306a36Sopenharmony_ci * from 0 again. 36162306a36Sopenharmony_ci */ 36262306a36Sopenharmony_ci memset(cmb_data->hw_block, 0, cmb_data->size); 36362306a36Sopenharmony_ci cmb_data->last_update = 0; 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci cdev->private->cmb_start_time = get_tod_clock(); 36662306a36Sopenharmony_ci spin_unlock_irq(cdev->ccwlock); 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci/** 37062306a36Sopenharmony_ci * struct cmb_area - container for global cmb data 37162306a36Sopenharmony_ci * 37262306a36Sopenharmony_ci * @mem: pointer to CMBs (only in basic measurement mode) 37362306a36Sopenharmony_ci * @list: contains a linked list of all subchannels 37462306a36Sopenharmony_ci * @num_channels: number of channels to be measured 37562306a36Sopenharmony_ci * @lock: protect concurrent access to @mem and @list 37662306a36Sopenharmony_ci */ 37762306a36Sopenharmony_cistruct cmb_area { 37862306a36Sopenharmony_ci struct cmb *mem; 37962306a36Sopenharmony_ci struct list_head list; 38062306a36Sopenharmony_ci int num_channels; 38162306a36Sopenharmony_ci spinlock_t lock; 38262306a36Sopenharmony_ci}; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic struct cmb_area cmb_area = { 38562306a36Sopenharmony_ci .lock = __SPIN_LOCK_UNLOCKED(cmb_area.lock), 38662306a36Sopenharmony_ci .list = LIST_HEAD_INIT(cmb_area.list), 38762306a36Sopenharmony_ci .num_channels = 1024, 38862306a36Sopenharmony_ci}; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci/* ****** old style CMB handling ********/ 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci/* 39362306a36Sopenharmony_ci * Basic channel measurement blocks are allocated in one contiguous 39462306a36Sopenharmony_ci * block of memory, which can not be moved as long as any channel 39562306a36Sopenharmony_ci * is active. Therefore, a maximum number of subchannels needs to 39662306a36Sopenharmony_ci * be defined somewhere. This is a module parameter, defaulting to 39762306a36Sopenharmony_ci * a reasonable value of 1024, or 32 kb of memory. 39862306a36Sopenharmony_ci * Current kernels don't allow kmalloc with more than 128kb, so the 39962306a36Sopenharmony_ci * maximum is 4096. 40062306a36Sopenharmony_ci */ 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cimodule_param_named(maxchannels, cmb_area.num_channels, uint, 0444); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci/** 40562306a36Sopenharmony_ci * struct cmb - basic channel measurement block 40662306a36Sopenharmony_ci * @ssch_rsch_count: number of ssch and rsch 40762306a36Sopenharmony_ci * @sample_count: number of samples 40862306a36Sopenharmony_ci * @device_connect_time: time of device connect 40962306a36Sopenharmony_ci * @function_pending_time: time of function pending 41062306a36Sopenharmony_ci * @device_disconnect_time: time of device disconnect 41162306a36Sopenharmony_ci * @control_unit_queuing_time: time of control unit queuing 41262306a36Sopenharmony_ci * @device_active_only_time: time of device active only 41362306a36Sopenharmony_ci * @reserved: unused in basic measurement mode 41462306a36Sopenharmony_ci * 41562306a36Sopenharmony_ci * The measurement block as used by the hardware. The fields are described 41662306a36Sopenharmony_ci * further in z/Architecture Principles of Operation, chapter 17. 41762306a36Sopenharmony_ci * 41862306a36Sopenharmony_ci * The cmb area made up from these blocks must be a contiguous array and may 41962306a36Sopenharmony_ci * not be reallocated or freed. 42062306a36Sopenharmony_ci * Only one cmb area can be present in the system. 42162306a36Sopenharmony_ci */ 42262306a36Sopenharmony_cistruct cmb { 42362306a36Sopenharmony_ci u16 ssch_rsch_count; 42462306a36Sopenharmony_ci u16 sample_count; 42562306a36Sopenharmony_ci u32 device_connect_time; 42662306a36Sopenharmony_ci u32 function_pending_time; 42762306a36Sopenharmony_ci u32 device_disconnect_time; 42862306a36Sopenharmony_ci u32 control_unit_queuing_time; 42962306a36Sopenharmony_ci u32 device_active_only_time; 43062306a36Sopenharmony_ci u32 reserved[2]; 43162306a36Sopenharmony_ci}; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci/* 43462306a36Sopenharmony_ci * Insert a single device into the cmb_area list. 43562306a36Sopenharmony_ci * Called with cmb_area.lock held from alloc_cmb. 43662306a36Sopenharmony_ci */ 43762306a36Sopenharmony_cistatic int alloc_cmb_single(struct ccw_device *cdev, 43862306a36Sopenharmony_ci struct cmb_data *cmb_data) 43962306a36Sopenharmony_ci{ 44062306a36Sopenharmony_ci struct cmb *cmb; 44162306a36Sopenharmony_ci struct ccw_device_private *node; 44262306a36Sopenharmony_ci int ret; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci spin_lock_irq(cdev->ccwlock); 44562306a36Sopenharmony_ci if (!list_empty(&cdev->private->cmb_list)) { 44662306a36Sopenharmony_ci ret = -EBUSY; 44762306a36Sopenharmony_ci goto out; 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci /* 45162306a36Sopenharmony_ci * Find first unused cmb in cmb_area.mem. 45262306a36Sopenharmony_ci * This is a little tricky: cmb_area.list 45362306a36Sopenharmony_ci * remains sorted by ->cmb->hw_data pointers. 45462306a36Sopenharmony_ci */ 45562306a36Sopenharmony_ci cmb = cmb_area.mem; 45662306a36Sopenharmony_ci list_for_each_entry(node, &cmb_area.list, cmb_list) { 45762306a36Sopenharmony_ci struct cmb_data *data; 45862306a36Sopenharmony_ci data = node->cmb; 45962306a36Sopenharmony_ci if ((struct cmb*)data->hw_block > cmb) 46062306a36Sopenharmony_ci break; 46162306a36Sopenharmony_ci cmb++; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci if (cmb - cmb_area.mem >= cmb_area.num_channels) { 46462306a36Sopenharmony_ci ret = -ENOMEM; 46562306a36Sopenharmony_ci goto out; 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci /* insert new cmb */ 46962306a36Sopenharmony_ci list_add_tail(&cdev->private->cmb_list, &node->cmb_list); 47062306a36Sopenharmony_ci cmb_data->hw_block = cmb; 47162306a36Sopenharmony_ci cdev->private->cmb = cmb_data; 47262306a36Sopenharmony_ci ret = 0; 47362306a36Sopenharmony_ciout: 47462306a36Sopenharmony_ci spin_unlock_irq(cdev->ccwlock); 47562306a36Sopenharmony_ci return ret; 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cistatic int alloc_cmb(struct ccw_device *cdev) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci int ret; 48162306a36Sopenharmony_ci struct cmb *mem; 48262306a36Sopenharmony_ci ssize_t size; 48362306a36Sopenharmony_ci struct cmb_data *cmb_data; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci /* Allocate private cmb_data. */ 48662306a36Sopenharmony_ci cmb_data = kzalloc(sizeof(struct cmb_data), GFP_KERNEL); 48762306a36Sopenharmony_ci if (!cmb_data) 48862306a36Sopenharmony_ci return -ENOMEM; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci cmb_data->last_block = kzalloc(sizeof(struct cmb), GFP_KERNEL); 49162306a36Sopenharmony_ci if (!cmb_data->last_block) { 49262306a36Sopenharmony_ci kfree(cmb_data); 49362306a36Sopenharmony_ci return -ENOMEM; 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci cmb_data->size = sizeof(struct cmb); 49662306a36Sopenharmony_ci spin_lock(&cmb_area.lock); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci if (!cmb_area.mem) { 49962306a36Sopenharmony_ci /* there is no user yet, so we need a new area */ 50062306a36Sopenharmony_ci size = sizeof(struct cmb) * cmb_area.num_channels; 50162306a36Sopenharmony_ci WARN_ON(!list_empty(&cmb_area.list)); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci spin_unlock(&cmb_area.lock); 50462306a36Sopenharmony_ci mem = (void*)__get_free_pages(GFP_KERNEL | GFP_DMA, 50562306a36Sopenharmony_ci get_order(size)); 50662306a36Sopenharmony_ci spin_lock(&cmb_area.lock); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci if (cmb_area.mem) { 50962306a36Sopenharmony_ci /* ok, another thread was faster */ 51062306a36Sopenharmony_ci free_pages((unsigned long)mem, get_order(size)); 51162306a36Sopenharmony_ci } else if (!mem) { 51262306a36Sopenharmony_ci /* no luck */ 51362306a36Sopenharmony_ci ret = -ENOMEM; 51462306a36Sopenharmony_ci goto out; 51562306a36Sopenharmony_ci } else { 51662306a36Sopenharmony_ci /* everything ok */ 51762306a36Sopenharmony_ci memset(mem, 0, size); 51862306a36Sopenharmony_ci cmb_area.mem = mem; 51962306a36Sopenharmony_ci cmf_activate(cmb_area.mem, CMF_ON); 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* do the actual allocation */ 52462306a36Sopenharmony_ci ret = alloc_cmb_single(cdev, cmb_data); 52562306a36Sopenharmony_ciout: 52662306a36Sopenharmony_ci spin_unlock(&cmb_area.lock); 52762306a36Sopenharmony_ci if (ret) { 52862306a36Sopenharmony_ci kfree(cmb_data->last_block); 52962306a36Sopenharmony_ci kfree(cmb_data); 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci return ret; 53262306a36Sopenharmony_ci} 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_cistatic void free_cmb(struct ccw_device *cdev) 53562306a36Sopenharmony_ci{ 53662306a36Sopenharmony_ci struct ccw_device_private *priv; 53762306a36Sopenharmony_ci struct cmb_data *cmb_data; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci spin_lock(&cmb_area.lock); 54062306a36Sopenharmony_ci spin_lock_irq(cdev->ccwlock); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci priv = cdev->private; 54362306a36Sopenharmony_ci cmb_data = priv->cmb; 54462306a36Sopenharmony_ci priv->cmb = NULL; 54562306a36Sopenharmony_ci if (cmb_data) 54662306a36Sopenharmony_ci kfree(cmb_data->last_block); 54762306a36Sopenharmony_ci kfree(cmb_data); 54862306a36Sopenharmony_ci list_del_init(&priv->cmb_list); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci if (list_empty(&cmb_area.list)) { 55162306a36Sopenharmony_ci ssize_t size; 55262306a36Sopenharmony_ci size = sizeof(struct cmb) * cmb_area.num_channels; 55362306a36Sopenharmony_ci cmf_activate(NULL, CMF_OFF); 55462306a36Sopenharmony_ci free_pages((unsigned long)cmb_area.mem, get_order(size)); 55562306a36Sopenharmony_ci cmb_area.mem = NULL; 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci spin_unlock_irq(cdev->ccwlock); 55862306a36Sopenharmony_ci spin_unlock(&cmb_area.lock); 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_cistatic int set_cmb(struct ccw_device *cdev, u32 mme) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci u16 offset; 56462306a36Sopenharmony_ci struct cmb_data *cmb_data; 56562306a36Sopenharmony_ci unsigned long flags; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci spin_lock_irqsave(cdev->ccwlock, flags); 56862306a36Sopenharmony_ci if (!cdev->private->cmb) { 56962306a36Sopenharmony_ci spin_unlock_irqrestore(cdev->ccwlock, flags); 57062306a36Sopenharmony_ci return -EINVAL; 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci cmb_data = cdev->private->cmb; 57362306a36Sopenharmony_ci offset = mme ? (struct cmb *)cmb_data->hw_block - cmb_area.mem : 0; 57462306a36Sopenharmony_ci spin_unlock_irqrestore(cdev->ccwlock, flags); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci return set_schib_wait(cdev, mme, 0, offset); 57762306a36Sopenharmony_ci} 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci/* calculate utilization in 0.1 percent units */ 58062306a36Sopenharmony_cistatic u64 __cmb_utilization(u64 device_connect_time, u64 function_pending_time, 58162306a36Sopenharmony_ci u64 device_disconnect_time, u64 start_time) 58262306a36Sopenharmony_ci{ 58362306a36Sopenharmony_ci u64 utilization, elapsed_time; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci utilization = time_to_nsec(device_connect_time + 58662306a36Sopenharmony_ci function_pending_time + 58762306a36Sopenharmony_ci device_disconnect_time); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci elapsed_time = get_tod_clock() - start_time; 59062306a36Sopenharmony_ci elapsed_time = tod_to_ns(elapsed_time); 59162306a36Sopenharmony_ci elapsed_time /= 1000; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci return elapsed_time ? (utilization / elapsed_time) : 0; 59462306a36Sopenharmony_ci} 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_cistatic u64 read_cmb(struct ccw_device *cdev, int index) 59762306a36Sopenharmony_ci{ 59862306a36Sopenharmony_ci struct cmb_data *cmb_data; 59962306a36Sopenharmony_ci unsigned long flags; 60062306a36Sopenharmony_ci struct cmb *cmb; 60162306a36Sopenharmony_ci u64 ret = 0; 60262306a36Sopenharmony_ci u32 val; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci spin_lock_irqsave(cdev->ccwlock, flags); 60562306a36Sopenharmony_ci cmb_data = cdev->private->cmb; 60662306a36Sopenharmony_ci if (!cmb_data) 60762306a36Sopenharmony_ci goto out; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci cmb = cmb_data->hw_block; 61062306a36Sopenharmony_ci switch (index) { 61162306a36Sopenharmony_ci case avg_utilization: 61262306a36Sopenharmony_ci ret = __cmb_utilization(cmb->device_connect_time, 61362306a36Sopenharmony_ci cmb->function_pending_time, 61462306a36Sopenharmony_ci cmb->device_disconnect_time, 61562306a36Sopenharmony_ci cdev->private->cmb_start_time); 61662306a36Sopenharmony_ci goto out; 61762306a36Sopenharmony_ci case cmb_ssch_rsch_count: 61862306a36Sopenharmony_ci ret = cmb->ssch_rsch_count; 61962306a36Sopenharmony_ci goto out; 62062306a36Sopenharmony_ci case cmb_sample_count: 62162306a36Sopenharmony_ci ret = cmb->sample_count; 62262306a36Sopenharmony_ci goto out; 62362306a36Sopenharmony_ci case cmb_device_connect_time: 62462306a36Sopenharmony_ci val = cmb->device_connect_time; 62562306a36Sopenharmony_ci break; 62662306a36Sopenharmony_ci case cmb_function_pending_time: 62762306a36Sopenharmony_ci val = cmb->function_pending_time; 62862306a36Sopenharmony_ci break; 62962306a36Sopenharmony_ci case cmb_device_disconnect_time: 63062306a36Sopenharmony_ci val = cmb->device_disconnect_time; 63162306a36Sopenharmony_ci break; 63262306a36Sopenharmony_ci case cmb_control_unit_queuing_time: 63362306a36Sopenharmony_ci val = cmb->control_unit_queuing_time; 63462306a36Sopenharmony_ci break; 63562306a36Sopenharmony_ci case cmb_device_active_only_time: 63662306a36Sopenharmony_ci val = cmb->device_active_only_time; 63762306a36Sopenharmony_ci break; 63862306a36Sopenharmony_ci default: 63962306a36Sopenharmony_ci goto out; 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci ret = time_to_avg_nsec(val, cmb->sample_count); 64262306a36Sopenharmony_ciout: 64362306a36Sopenharmony_ci spin_unlock_irqrestore(cdev->ccwlock, flags); 64462306a36Sopenharmony_ci return ret; 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_cistatic int readall_cmb(struct ccw_device *cdev, struct cmbdata *data) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci struct cmb *cmb; 65062306a36Sopenharmony_ci struct cmb_data *cmb_data; 65162306a36Sopenharmony_ci u64 time; 65262306a36Sopenharmony_ci unsigned long flags; 65362306a36Sopenharmony_ci int ret; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci ret = cmf_cmb_copy_wait(cdev); 65662306a36Sopenharmony_ci if (ret < 0) 65762306a36Sopenharmony_ci return ret; 65862306a36Sopenharmony_ci spin_lock_irqsave(cdev->ccwlock, flags); 65962306a36Sopenharmony_ci cmb_data = cdev->private->cmb; 66062306a36Sopenharmony_ci if (!cmb_data) { 66162306a36Sopenharmony_ci ret = -ENODEV; 66262306a36Sopenharmony_ci goto out; 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci if (cmb_data->last_update == 0) { 66562306a36Sopenharmony_ci ret = -EAGAIN; 66662306a36Sopenharmony_ci goto out; 66762306a36Sopenharmony_ci } 66862306a36Sopenharmony_ci cmb = cmb_data->last_block; 66962306a36Sopenharmony_ci time = cmb_data->last_update - cdev->private->cmb_start_time; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci memset(data, 0, sizeof(struct cmbdata)); 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci /* we only know values before device_busy_time */ 67462306a36Sopenharmony_ci data->size = offsetof(struct cmbdata, device_busy_time); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci data->elapsed_time = tod_to_ns(time); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci /* copy data to new structure */ 67962306a36Sopenharmony_ci data->ssch_rsch_count = cmb->ssch_rsch_count; 68062306a36Sopenharmony_ci data->sample_count = cmb->sample_count; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci /* time fields are converted to nanoseconds while copying */ 68362306a36Sopenharmony_ci data->device_connect_time = time_to_nsec(cmb->device_connect_time); 68462306a36Sopenharmony_ci data->function_pending_time = time_to_nsec(cmb->function_pending_time); 68562306a36Sopenharmony_ci data->device_disconnect_time = 68662306a36Sopenharmony_ci time_to_nsec(cmb->device_disconnect_time); 68762306a36Sopenharmony_ci data->control_unit_queuing_time 68862306a36Sopenharmony_ci = time_to_nsec(cmb->control_unit_queuing_time); 68962306a36Sopenharmony_ci data->device_active_only_time 69062306a36Sopenharmony_ci = time_to_nsec(cmb->device_active_only_time); 69162306a36Sopenharmony_ci ret = 0; 69262306a36Sopenharmony_ciout: 69362306a36Sopenharmony_ci spin_unlock_irqrestore(cdev->ccwlock, flags); 69462306a36Sopenharmony_ci return ret; 69562306a36Sopenharmony_ci} 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_cistatic void reset_cmb(struct ccw_device *cdev) 69862306a36Sopenharmony_ci{ 69962306a36Sopenharmony_ci cmf_generic_reset(cdev); 70062306a36Sopenharmony_ci} 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_cistatic int cmf_enabled(struct ccw_device *cdev) 70362306a36Sopenharmony_ci{ 70462306a36Sopenharmony_ci int enabled; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci spin_lock_irq(cdev->ccwlock); 70762306a36Sopenharmony_ci enabled = !!cdev->private->cmb; 70862306a36Sopenharmony_ci spin_unlock_irq(cdev->ccwlock); 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci return enabled; 71162306a36Sopenharmony_ci} 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_cistatic struct attribute_group cmf_attr_group; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_cistatic struct cmb_operations cmbops_basic = { 71662306a36Sopenharmony_ci .alloc = alloc_cmb, 71762306a36Sopenharmony_ci .free = free_cmb, 71862306a36Sopenharmony_ci .set = set_cmb, 71962306a36Sopenharmony_ci .read = read_cmb, 72062306a36Sopenharmony_ci .readall = readall_cmb, 72162306a36Sopenharmony_ci .reset = reset_cmb, 72262306a36Sopenharmony_ci .attr_group = &cmf_attr_group, 72362306a36Sopenharmony_ci}; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci/* ******** extended cmb handling ********/ 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci/** 72862306a36Sopenharmony_ci * struct cmbe - extended channel measurement block 72962306a36Sopenharmony_ci * @ssch_rsch_count: number of ssch and rsch 73062306a36Sopenharmony_ci * @sample_count: number of samples 73162306a36Sopenharmony_ci * @device_connect_time: time of device connect 73262306a36Sopenharmony_ci * @function_pending_time: time of function pending 73362306a36Sopenharmony_ci * @device_disconnect_time: time of device disconnect 73462306a36Sopenharmony_ci * @control_unit_queuing_time: time of control unit queuing 73562306a36Sopenharmony_ci * @device_active_only_time: time of device active only 73662306a36Sopenharmony_ci * @device_busy_time: time of device busy 73762306a36Sopenharmony_ci * @initial_command_response_time: initial command response time 73862306a36Sopenharmony_ci * @reserved: unused 73962306a36Sopenharmony_ci * 74062306a36Sopenharmony_ci * The measurement block as used by the hardware. May be in any 64 bit physical 74162306a36Sopenharmony_ci * location. 74262306a36Sopenharmony_ci * The fields are described further in z/Architecture Principles of Operation, 74362306a36Sopenharmony_ci * third edition, chapter 17. 74462306a36Sopenharmony_ci */ 74562306a36Sopenharmony_cistruct cmbe { 74662306a36Sopenharmony_ci u32 ssch_rsch_count; 74762306a36Sopenharmony_ci u32 sample_count; 74862306a36Sopenharmony_ci u32 device_connect_time; 74962306a36Sopenharmony_ci u32 function_pending_time; 75062306a36Sopenharmony_ci u32 device_disconnect_time; 75162306a36Sopenharmony_ci u32 control_unit_queuing_time; 75262306a36Sopenharmony_ci u32 device_active_only_time; 75362306a36Sopenharmony_ci u32 device_busy_time; 75462306a36Sopenharmony_ci u32 initial_command_response_time; 75562306a36Sopenharmony_ci u32 reserved[7]; 75662306a36Sopenharmony_ci} __packed __aligned(64); 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_cistatic struct kmem_cache *cmbe_cache; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_cistatic int alloc_cmbe(struct ccw_device *cdev) 76162306a36Sopenharmony_ci{ 76262306a36Sopenharmony_ci struct cmb_data *cmb_data; 76362306a36Sopenharmony_ci struct cmbe *cmbe; 76462306a36Sopenharmony_ci int ret = -ENOMEM; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci cmbe = kmem_cache_zalloc(cmbe_cache, GFP_KERNEL); 76762306a36Sopenharmony_ci if (!cmbe) 76862306a36Sopenharmony_ci return ret; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci cmb_data = kzalloc(sizeof(*cmb_data), GFP_KERNEL); 77162306a36Sopenharmony_ci if (!cmb_data) 77262306a36Sopenharmony_ci goto out_free; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci cmb_data->last_block = kzalloc(sizeof(struct cmbe), GFP_KERNEL); 77562306a36Sopenharmony_ci if (!cmb_data->last_block) 77662306a36Sopenharmony_ci goto out_free; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci cmb_data->size = sizeof(*cmbe); 77962306a36Sopenharmony_ci cmb_data->hw_block = cmbe; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci spin_lock(&cmb_area.lock); 78262306a36Sopenharmony_ci spin_lock_irq(cdev->ccwlock); 78362306a36Sopenharmony_ci if (cdev->private->cmb) 78462306a36Sopenharmony_ci goto out_unlock; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci cdev->private->cmb = cmb_data; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci /* activate global measurement if this is the first channel */ 78962306a36Sopenharmony_ci if (list_empty(&cmb_area.list)) 79062306a36Sopenharmony_ci cmf_activate(NULL, CMF_ON); 79162306a36Sopenharmony_ci list_add_tail(&cdev->private->cmb_list, &cmb_area.list); 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci spin_unlock_irq(cdev->ccwlock); 79462306a36Sopenharmony_ci spin_unlock(&cmb_area.lock); 79562306a36Sopenharmony_ci return 0; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ciout_unlock: 79862306a36Sopenharmony_ci spin_unlock_irq(cdev->ccwlock); 79962306a36Sopenharmony_ci spin_unlock(&cmb_area.lock); 80062306a36Sopenharmony_ci ret = -EBUSY; 80162306a36Sopenharmony_ciout_free: 80262306a36Sopenharmony_ci if (cmb_data) 80362306a36Sopenharmony_ci kfree(cmb_data->last_block); 80462306a36Sopenharmony_ci kfree(cmb_data); 80562306a36Sopenharmony_ci kmem_cache_free(cmbe_cache, cmbe); 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci return ret; 80862306a36Sopenharmony_ci} 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_cistatic void free_cmbe(struct ccw_device *cdev) 81162306a36Sopenharmony_ci{ 81262306a36Sopenharmony_ci struct cmb_data *cmb_data; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci spin_lock(&cmb_area.lock); 81562306a36Sopenharmony_ci spin_lock_irq(cdev->ccwlock); 81662306a36Sopenharmony_ci cmb_data = cdev->private->cmb; 81762306a36Sopenharmony_ci cdev->private->cmb = NULL; 81862306a36Sopenharmony_ci if (cmb_data) { 81962306a36Sopenharmony_ci kfree(cmb_data->last_block); 82062306a36Sopenharmony_ci kmem_cache_free(cmbe_cache, cmb_data->hw_block); 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci kfree(cmb_data); 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci /* deactivate global measurement if this is the last channel */ 82562306a36Sopenharmony_ci list_del_init(&cdev->private->cmb_list); 82662306a36Sopenharmony_ci if (list_empty(&cmb_area.list)) 82762306a36Sopenharmony_ci cmf_activate(NULL, CMF_OFF); 82862306a36Sopenharmony_ci spin_unlock_irq(cdev->ccwlock); 82962306a36Sopenharmony_ci spin_unlock(&cmb_area.lock); 83062306a36Sopenharmony_ci} 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_cistatic int set_cmbe(struct ccw_device *cdev, u32 mme) 83362306a36Sopenharmony_ci{ 83462306a36Sopenharmony_ci unsigned long mba; 83562306a36Sopenharmony_ci struct cmb_data *cmb_data; 83662306a36Sopenharmony_ci unsigned long flags; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci spin_lock_irqsave(cdev->ccwlock, flags); 83962306a36Sopenharmony_ci if (!cdev->private->cmb) { 84062306a36Sopenharmony_ci spin_unlock_irqrestore(cdev->ccwlock, flags); 84162306a36Sopenharmony_ci return -EINVAL; 84262306a36Sopenharmony_ci } 84362306a36Sopenharmony_ci cmb_data = cdev->private->cmb; 84462306a36Sopenharmony_ci mba = mme ? (unsigned long) cmb_data->hw_block : 0; 84562306a36Sopenharmony_ci spin_unlock_irqrestore(cdev->ccwlock, flags); 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci return set_schib_wait(cdev, mme, 1, mba); 84862306a36Sopenharmony_ci} 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_cistatic u64 read_cmbe(struct ccw_device *cdev, int index) 85162306a36Sopenharmony_ci{ 85262306a36Sopenharmony_ci struct cmb_data *cmb_data; 85362306a36Sopenharmony_ci unsigned long flags; 85462306a36Sopenharmony_ci struct cmbe *cmb; 85562306a36Sopenharmony_ci u64 ret = 0; 85662306a36Sopenharmony_ci u32 val; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci spin_lock_irqsave(cdev->ccwlock, flags); 85962306a36Sopenharmony_ci cmb_data = cdev->private->cmb; 86062306a36Sopenharmony_ci if (!cmb_data) 86162306a36Sopenharmony_ci goto out; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci cmb = cmb_data->hw_block; 86462306a36Sopenharmony_ci switch (index) { 86562306a36Sopenharmony_ci case avg_utilization: 86662306a36Sopenharmony_ci ret = __cmb_utilization(cmb->device_connect_time, 86762306a36Sopenharmony_ci cmb->function_pending_time, 86862306a36Sopenharmony_ci cmb->device_disconnect_time, 86962306a36Sopenharmony_ci cdev->private->cmb_start_time); 87062306a36Sopenharmony_ci goto out; 87162306a36Sopenharmony_ci case cmb_ssch_rsch_count: 87262306a36Sopenharmony_ci ret = cmb->ssch_rsch_count; 87362306a36Sopenharmony_ci goto out; 87462306a36Sopenharmony_ci case cmb_sample_count: 87562306a36Sopenharmony_ci ret = cmb->sample_count; 87662306a36Sopenharmony_ci goto out; 87762306a36Sopenharmony_ci case cmb_device_connect_time: 87862306a36Sopenharmony_ci val = cmb->device_connect_time; 87962306a36Sopenharmony_ci break; 88062306a36Sopenharmony_ci case cmb_function_pending_time: 88162306a36Sopenharmony_ci val = cmb->function_pending_time; 88262306a36Sopenharmony_ci break; 88362306a36Sopenharmony_ci case cmb_device_disconnect_time: 88462306a36Sopenharmony_ci val = cmb->device_disconnect_time; 88562306a36Sopenharmony_ci break; 88662306a36Sopenharmony_ci case cmb_control_unit_queuing_time: 88762306a36Sopenharmony_ci val = cmb->control_unit_queuing_time; 88862306a36Sopenharmony_ci break; 88962306a36Sopenharmony_ci case cmb_device_active_only_time: 89062306a36Sopenharmony_ci val = cmb->device_active_only_time; 89162306a36Sopenharmony_ci break; 89262306a36Sopenharmony_ci case cmb_device_busy_time: 89362306a36Sopenharmony_ci val = cmb->device_busy_time; 89462306a36Sopenharmony_ci break; 89562306a36Sopenharmony_ci case cmb_initial_command_response_time: 89662306a36Sopenharmony_ci val = cmb->initial_command_response_time; 89762306a36Sopenharmony_ci break; 89862306a36Sopenharmony_ci default: 89962306a36Sopenharmony_ci goto out; 90062306a36Sopenharmony_ci } 90162306a36Sopenharmony_ci ret = time_to_avg_nsec(val, cmb->sample_count); 90262306a36Sopenharmony_ciout: 90362306a36Sopenharmony_ci spin_unlock_irqrestore(cdev->ccwlock, flags); 90462306a36Sopenharmony_ci return ret; 90562306a36Sopenharmony_ci} 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_cistatic int readall_cmbe(struct ccw_device *cdev, struct cmbdata *data) 90862306a36Sopenharmony_ci{ 90962306a36Sopenharmony_ci struct cmbe *cmb; 91062306a36Sopenharmony_ci struct cmb_data *cmb_data; 91162306a36Sopenharmony_ci u64 time; 91262306a36Sopenharmony_ci unsigned long flags; 91362306a36Sopenharmony_ci int ret; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci ret = cmf_cmb_copy_wait(cdev); 91662306a36Sopenharmony_ci if (ret < 0) 91762306a36Sopenharmony_ci return ret; 91862306a36Sopenharmony_ci spin_lock_irqsave(cdev->ccwlock, flags); 91962306a36Sopenharmony_ci cmb_data = cdev->private->cmb; 92062306a36Sopenharmony_ci if (!cmb_data) { 92162306a36Sopenharmony_ci ret = -ENODEV; 92262306a36Sopenharmony_ci goto out; 92362306a36Sopenharmony_ci } 92462306a36Sopenharmony_ci if (cmb_data->last_update == 0) { 92562306a36Sopenharmony_ci ret = -EAGAIN; 92662306a36Sopenharmony_ci goto out; 92762306a36Sopenharmony_ci } 92862306a36Sopenharmony_ci time = cmb_data->last_update - cdev->private->cmb_start_time; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci memset (data, 0, sizeof(struct cmbdata)); 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci /* we only know values before device_busy_time */ 93362306a36Sopenharmony_ci data->size = offsetof(struct cmbdata, device_busy_time); 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci data->elapsed_time = tod_to_ns(time); 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci cmb = cmb_data->last_block; 93862306a36Sopenharmony_ci /* copy data to new structure */ 93962306a36Sopenharmony_ci data->ssch_rsch_count = cmb->ssch_rsch_count; 94062306a36Sopenharmony_ci data->sample_count = cmb->sample_count; 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci /* time fields are converted to nanoseconds while copying */ 94362306a36Sopenharmony_ci data->device_connect_time = time_to_nsec(cmb->device_connect_time); 94462306a36Sopenharmony_ci data->function_pending_time = time_to_nsec(cmb->function_pending_time); 94562306a36Sopenharmony_ci data->device_disconnect_time = 94662306a36Sopenharmony_ci time_to_nsec(cmb->device_disconnect_time); 94762306a36Sopenharmony_ci data->control_unit_queuing_time 94862306a36Sopenharmony_ci = time_to_nsec(cmb->control_unit_queuing_time); 94962306a36Sopenharmony_ci data->device_active_only_time 95062306a36Sopenharmony_ci = time_to_nsec(cmb->device_active_only_time); 95162306a36Sopenharmony_ci data->device_busy_time = time_to_nsec(cmb->device_busy_time); 95262306a36Sopenharmony_ci data->initial_command_response_time 95362306a36Sopenharmony_ci = time_to_nsec(cmb->initial_command_response_time); 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci ret = 0; 95662306a36Sopenharmony_ciout: 95762306a36Sopenharmony_ci spin_unlock_irqrestore(cdev->ccwlock, flags); 95862306a36Sopenharmony_ci return ret; 95962306a36Sopenharmony_ci} 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_cistatic void reset_cmbe(struct ccw_device *cdev) 96262306a36Sopenharmony_ci{ 96362306a36Sopenharmony_ci cmf_generic_reset(cdev); 96462306a36Sopenharmony_ci} 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_cistatic struct attribute_group cmf_attr_group_ext; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_cistatic struct cmb_operations cmbops_extended = { 96962306a36Sopenharmony_ci .alloc = alloc_cmbe, 97062306a36Sopenharmony_ci .free = free_cmbe, 97162306a36Sopenharmony_ci .set = set_cmbe, 97262306a36Sopenharmony_ci .read = read_cmbe, 97362306a36Sopenharmony_ci .readall = readall_cmbe, 97462306a36Sopenharmony_ci .reset = reset_cmbe, 97562306a36Sopenharmony_ci .attr_group = &cmf_attr_group_ext, 97662306a36Sopenharmony_ci}; 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_cistatic ssize_t cmb_show_attr(struct device *dev, char *buf, enum cmb_index idx) 97962306a36Sopenharmony_ci{ 98062306a36Sopenharmony_ci return sprintf(buf, "%lld\n", 98162306a36Sopenharmony_ci (unsigned long long) cmf_read(to_ccwdev(dev), idx)); 98262306a36Sopenharmony_ci} 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_cistatic ssize_t cmb_show_avg_sample_interval(struct device *dev, 98562306a36Sopenharmony_ci struct device_attribute *attr, 98662306a36Sopenharmony_ci char *buf) 98762306a36Sopenharmony_ci{ 98862306a36Sopenharmony_ci struct ccw_device *cdev = to_ccwdev(dev); 98962306a36Sopenharmony_ci unsigned long count; 99062306a36Sopenharmony_ci long interval; 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci count = cmf_read(cdev, cmb_sample_count); 99362306a36Sopenharmony_ci spin_lock_irq(cdev->ccwlock); 99462306a36Sopenharmony_ci if (count) { 99562306a36Sopenharmony_ci interval = get_tod_clock() - cdev->private->cmb_start_time; 99662306a36Sopenharmony_ci interval = tod_to_ns(interval); 99762306a36Sopenharmony_ci interval /= count; 99862306a36Sopenharmony_ci } else 99962306a36Sopenharmony_ci interval = -1; 100062306a36Sopenharmony_ci spin_unlock_irq(cdev->ccwlock); 100162306a36Sopenharmony_ci return sprintf(buf, "%ld\n", interval); 100262306a36Sopenharmony_ci} 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_cistatic ssize_t cmb_show_avg_utilization(struct device *dev, 100562306a36Sopenharmony_ci struct device_attribute *attr, 100662306a36Sopenharmony_ci char *buf) 100762306a36Sopenharmony_ci{ 100862306a36Sopenharmony_ci unsigned long u = cmf_read(to_ccwdev(dev), avg_utilization); 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci return sprintf(buf, "%02lu.%01lu%%\n", u / 10, u % 10); 101162306a36Sopenharmony_ci} 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci#define cmf_attr(name) \ 101462306a36Sopenharmony_cistatic ssize_t show_##name(struct device *dev, \ 101562306a36Sopenharmony_ci struct device_attribute *attr, char *buf) \ 101662306a36Sopenharmony_ci{ return cmb_show_attr((dev), buf, cmb_##name); } \ 101762306a36Sopenharmony_cistatic DEVICE_ATTR(name, 0444, show_##name, NULL); 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci#define cmf_attr_avg(name) \ 102062306a36Sopenharmony_cistatic ssize_t show_avg_##name(struct device *dev, \ 102162306a36Sopenharmony_ci struct device_attribute *attr, char *buf) \ 102262306a36Sopenharmony_ci{ return cmb_show_attr((dev), buf, cmb_##name); } \ 102362306a36Sopenharmony_cistatic DEVICE_ATTR(avg_##name, 0444, show_avg_##name, NULL); 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_cicmf_attr(ssch_rsch_count); 102662306a36Sopenharmony_cicmf_attr(sample_count); 102762306a36Sopenharmony_cicmf_attr_avg(device_connect_time); 102862306a36Sopenharmony_cicmf_attr_avg(function_pending_time); 102962306a36Sopenharmony_cicmf_attr_avg(device_disconnect_time); 103062306a36Sopenharmony_cicmf_attr_avg(control_unit_queuing_time); 103162306a36Sopenharmony_cicmf_attr_avg(device_active_only_time); 103262306a36Sopenharmony_cicmf_attr_avg(device_busy_time); 103362306a36Sopenharmony_cicmf_attr_avg(initial_command_response_time); 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_cistatic DEVICE_ATTR(avg_sample_interval, 0444, cmb_show_avg_sample_interval, 103662306a36Sopenharmony_ci NULL); 103762306a36Sopenharmony_cistatic DEVICE_ATTR(avg_utilization, 0444, cmb_show_avg_utilization, NULL); 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_cistatic struct attribute *cmf_attributes[] = { 104062306a36Sopenharmony_ci &dev_attr_avg_sample_interval.attr, 104162306a36Sopenharmony_ci &dev_attr_avg_utilization.attr, 104262306a36Sopenharmony_ci &dev_attr_ssch_rsch_count.attr, 104362306a36Sopenharmony_ci &dev_attr_sample_count.attr, 104462306a36Sopenharmony_ci &dev_attr_avg_device_connect_time.attr, 104562306a36Sopenharmony_ci &dev_attr_avg_function_pending_time.attr, 104662306a36Sopenharmony_ci &dev_attr_avg_device_disconnect_time.attr, 104762306a36Sopenharmony_ci &dev_attr_avg_control_unit_queuing_time.attr, 104862306a36Sopenharmony_ci &dev_attr_avg_device_active_only_time.attr, 104962306a36Sopenharmony_ci NULL, 105062306a36Sopenharmony_ci}; 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_cistatic struct attribute_group cmf_attr_group = { 105362306a36Sopenharmony_ci .name = "cmf", 105462306a36Sopenharmony_ci .attrs = cmf_attributes, 105562306a36Sopenharmony_ci}; 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_cistatic struct attribute *cmf_attributes_ext[] = { 105862306a36Sopenharmony_ci &dev_attr_avg_sample_interval.attr, 105962306a36Sopenharmony_ci &dev_attr_avg_utilization.attr, 106062306a36Sopenharmony_ci &dev_attr_ssch_rsch_count.attr, 106162306a36Sopenharmony_ci &dev_attr_sample_count.attr, 106262306a36Sopenharmony_ci &dev_attr_avg_device_connect_time.attr, 106362306a36Sopenharmony_ci &dev_attr_avg_function_pending_time.attr, 106462306a36Sopenharmony_ci &dev_attr_avg_device_disconnect_time.attr, 106562306a36Sopenharmony_ci &dev_attr_avg_control_unit_queuing_time.attr, 106662306a36Sopenharmony_ci &dev_attr_avg_device_active_only_time.attr, 106762306a36Sopenharmony_ci &dev_attr_avg_device_busy_time.attr, 106862306a36Sopenharmony_ci &dev_attr_avg_initial_command_response_time.attr, 106962306a36Sopenharmony_ci NULL, 107062306a36Sopenharmony_ci}; 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_cistatic struct attribute_group cmf_attr_group_ext = { 107362306a36Sopenharmony_ci .name = "cmf", 107462306a36Sopenharmony_ci .attrs = cmf_attributes_ext, 107562306a36Sopenharmony_ci}; 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_cistatic ssize_t cmb_enable_show(struct device *dev, 107862306a36Sopenharmony_ci struct device_attribute *attr, 107962306a36Sopenharmony_ci char *buf) 108062306a36Sopenharmony_ci{ 108162306a36Sopenharmony_ci struct ccw_device *cdev = to_ccwdev(dev); 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci return sprintf(buf, "%d\n", cmf_enabled(cdev)); 108462306a36Sopenharmony_ci} 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_cistatic ssize_t cmb_enable_store(struct device *dev, 108762306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, 108862306a36Sopenharmony_ci size_t c) 108962306a36Sopenharmony_ci{ 109062306a36Sopenharmony_ci struct ccw_device *cdev = to_ccwdev(dev); 109162306a36Sopenharmony_ci unsigned long val; 109262306a36Sopenharmony_ci int ret; 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci ret = kstrtoul(buf, 16, &val); 109562306a36Sopenharmony_ci if (ret) 109662306a36Sopenharmony_ci return ret; 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci switch (val) { 109962306a36Sopenharmony_ci case 0: 110062306a36Sopenharmony_ci ret = disable_cmf(cdev); 110162306a36Sopenharmony_ci break; 110262306a36Sopenharmony_ci case 1: 110362306a36Sopenharmony_ci ret = enable_cmf(cdev); 110462306a36Sopenharmony_ci break; 110562306a36Sopenharmony_ci default: 110662306a36Sopenharmony_ci ret = -EINVAL; 110762306a36Sopenharmony_ci } 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci return ret ? ret : c; 111062306a36Sopenharmony_ci} 111162306a36Sopenharmony_ciDEVICE_ATTR_RW(cmb_enable); 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci/** 111462306a36Sopenharmony_ci * enable_cmf() - switch on the channel measurement for a specific device 111562306a36Sopenharmony_ci * @cdev: The ccw device to be enabled 111662306a36Sopenharmony_ci * 111762306a36Sopenharmony_ci * Enable channel measurements for @cdev. If this is called on a device 111862306a36Sopenharmony_ci * for which channel measurement is already enabled a reset of the 111962306a36Sopenharmony_ci * measurement data is triggered. 112062306a36Sopenharmony_ci * Returns: %0 for success or a negative error value. 112162306a36Sopenharmony_ci * Context: 112262306a36Sopenharmony_ci * non-atomic 112362306a36Sopenharmony_ci */ 112462306a36Sopenharmony_ciint enable_cmf(struct ccw_device *cdev) 112562306a36Sopenharmony_ci{ 112662306a36Sopenharmony_ci int ret = 0; 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci device_lock(&cdev->dev); 112962306a36Sopenharmony_ci if (cmf_enabled(cdev)) { 113062306a36Sopenharmony_ci cmbops->reset(cdev); 113162306a36Sopenharmony_ci goto out_unlock; 113262306a36Sopenharmony_ci } 113362306a36Sopenharmony_ci get_device(&cdev->dev); 113462306a36Sopenharmony_ci ret = cmbops->alloc(cdev); 113562306a36Sopenharmony_ci if (ret) 113662306a36Sopenharmony_ci goto out; 113762306a36Sopenharmony_ci cmbops->reset(cdev); 113862306a36Sopenharmony_ci ret = sysfs_create_group(&cdev->dev.kobj, cmbops->attr_group); 113962306a36Sopenharmony_ci if (ret) { 114062306a36Sopenharmony_ci cmbops->free(cdev); 114162306a36Sopenharmony_ci goto out; 114262306a36Sopenharmony_ci } 114362306a36Sopenharmony_ci ret = cmbops->set(cdev, 2); 114462306a36Sopenharmony_ci if (ret) { 114562306a36Sopenharmony_ci sysfs_remove_group(&cdev->dev.kobj, cmbops->attr_group); 114662306a36Sopenharmony_ci cmbops->free(cdev); 114762306a36Sopenharmony_ci } 114862306a36Sopenharmony_ciout: 114962306a36Sopenharmony_ci if (ret) 115062306a36Sopenharmony_ci put_device(&cdev->dev); 115162306a36Sopenharmony_ciout_unlock: 115262306a36Sopenharmony_ci device_unlock(&cdev->dev); 115362306a36Sopenharmony_ci return ret; 115462306a36Sopenharmony_ci} 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci/** 115762306a36Sopenharmony_ci * __disable_cmf() - switch off the channel measurement for a specific device 115862306a36Sopenharmony_ci * @cdev: The ccw device to be disabled 115962306a36Sopenharmony_ci * 116062306a36Sopenharmony_ci * Returns: %0 for success or a negative error value. 116162306a36Sopenharmony_ci * 116262306a36Sopenharmony_ci * Context: 116362306a36Sopenharmony_ci * non-atomic, device_lock() held. 116462306a36Sopenharmony_ci */ 116562306a36Sopenharmony_ciint __disable_cmf(struct ccw_device *cdev) 116662306a36Sopenharmony_ci{ 116762306a36Sopenharmony_ci int ret; 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci ret = cmbops->set(cdev, 0); 117062306a36Sopenharmony_ci if (ret) 117162306a36Sopenharmony_ci return ret; 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci sysfs_remove_group(&cdev->dev.kobj, cmbops->attr_group); 117462306a36Sopenharmony_ci cmbops->free(cdev); 117562306a36Sopenharmony_ci put_device(&cdev->dev); 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci return ret; 117862306a36Sopenharmony_ci} 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci/** 118162306a36Sopenharmony_ci * disable_cmf() - switch off the channel measurement for a specific device 118262306a36Sopenharmony_ci * @cdev: The ccw device to be disabled 118362306a36Sopenharmony_ci * 118462306a36Sopenharmony_ci * Returns: %0 for success or a negative error value. 118562306a36Sopenharmony_ci * 118662306a36Sopenharmony_ci * Context: 118762306a36Sopenharmony_ci * non-atomic 118862306a36Sopenharmony_ci */ 118962306a36Sopenharmony_ciint disable_cmf(struct ccw_device *cdev) 119062306a36Sopenharmony_ci{ 119162306a36Sopenharmony_ci int ret; 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci device_lock(&cdev->dev); 119462306a36Sopenharmony_ci ret = __disable_cmf(cdev); 119562306a36Sopenharmony_ci device_unlock(&cdev->dev); 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci return ret; 119862306a36Sopenharmony_ci} 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci/** 120162306a36Sopenharmony_ci * cmf_read() - read one value from the current channel measurement block 120262306a36Sopenharmony_ci * @cdev: the channel to be read 120362306a36Sopenharmony_ci * @index: the index of the value to be read 120462306a36Sopenharmony_ci * 120562306a36Sopenharmony_ci * Returns: The value read or %0 if the value cannot be read. 120662306a36Sopenharmony_ci * 120762306a36Sopenharmony_ci * Context: 120862306a36Sopenharmony_ci * any 120962306a36Sopenharmony_ci */ 121062306a36Sopenharmony_ciu64 cmf_read(struct ccw_device *cdev, int index) 121162306a36Sopenharmony_ci{ 121262306a36Sopenharmony_ci return cmbops->read(cdev, index); 121362306a36Sopenharmony_ci} 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci/** 121662306a36Sopenharmony_ci * cmf_readall() - read the current channel measurement block 121762306a36Sopenharmony_ci * @cdev: the channel to be read 121862306a36Sopenharmony_ci * @data: a pointer to a data block that will be filled 121962306a36Sopenharmony_ci * 122062306a36Sopenharmony_ci * Returns: %0 on success, a negative error value otherwise. 122162306a36Sopenharmony_ci * 122262306a36Sopenharmony_ci * Context: 122362306a36Sopenharmony_ci * any 122462306a36Sopenharmony_ci */ 122562306a36Sopenharmony_ciint cmf_readall(struct ccw_device *cdev, struct cmbdata *data) 122662306a36Sopenharmony_ci{ 122762306a36Sopenharmony_ci return cmbops->readall(cdev, data); 122862306a36Sopenharmony_ci} 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci/* Reenable cmf when a disconnected device becomes available again. */ 123162306a36Sopenharmony_ciint cmf_reenable(struct ccw_device *cdev) 123262306a36Sopenharmony_ci{ 123362306a36Sopenharmony_ci cmbops->reset(cdev); 123462306a36Sopenharmony_ci return cmbops->set(cdev, 2); 123562306a36Sopenharmony_ci} 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci/** 123862306a36Sopenharmony_ci * cmf_reactivate() - reactivate measurement block updates 123962306a36Sopenharmony_ci * 124062306a36Sopenharmony_ci * Use this during resume from hibernate. 124162306a36Sopenharmony_ci */ 124262306a36Sopenharmony_civoid cmf_reactivate(void) 124362306a36Sopenharmony_ci{ 124462306a36Sopenharmony_ci spin_lock(&cmb_area.lock); 124562306a36Sopenharmony_ci if (!list_empty(&cmb_area.list)) 124662306a36Sopenharmony_ci cmf_activate(cmb_area.mem, CMF_ON); 124762306a36Sopenharmony_ci spin_unlock(&cmb_area.lock); 124862306a36Sopenharmony_ci} 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_cistatic int __init init_cmbe(void) 125162306a36Sopenharmony_ci{ 125262306a36Sopenharmony_ci cmbe_cache = kmem_cache_create("cmbe_cache", sizeof(struct cmbe), 125362306a36Sopenharmony_ci __alignof__(struct cmbe), 0, NULL); 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci return cmbe_cache ? 0 : -ENOMEM; 125662306a36Sopenharmony_ci} 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_cistatic int __init init_cmf(void) 125962306a36Sopenharmony_ci{ 126062306a36Sopenharmony_ci char *format_string; 126162306a36Sopenharmony_ci char *detect_string; 126262306a36Sopenharmony_ci int ret; 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci /* 126562306a36Sopenharmony_ci * If the user did not give a parameter, see if we are running on a 126662306a36Sopenharmony_ci * machine supporting extended measurement blocks, otherwise fall back 126762306a36Sopenharmony_ci * to basic mode. 126862306a36Sopenharmony_ci */ 126962306a36Sopenharmony_ci if (format == CMF_AUTODETECT) { 127062306a36Sopenharmony_ci if (!css_general_characteristics.ext_mb) { 127162306a36Sopenharmony_ci format = CMF_BASIC; 127262306a36Sopenharmony_ci } else { 127362306a36Sopenharmony_ci format = CMF_EXTENDED; 127462306a36Sopenharmony_ci } 127562306a36Sopenharmony_ci detect_string = "autodetected"; 127662306a36Sopenharmony_ci } else { 127762306a36Sopenharmony_ci detect_string = "parameter"; 127862306a36Sopenharmony_ci } 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci switch (format) { 128162306a36Sopenharmony_ci case CMF_BASIC: 128262306a36Sopenharmony_ci format_string = "basic"; 128362306a36Sopenharmony_ci cmbops = &cmbops_basic; 128462306a36Sopenharmony_ci break; 128562306a36Sopenharmony_ci case CMF_EXTENDED: 128662306a36Sopenharmony_ci format_string = "extended"; 128762306a36Sopenharmony_ci cmbops = &cmbops_extended; 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci ret = init_cmbe(); 129062306a36Sopenharmony_ci if (ret) 129162306a36Sopenharmony_ci return ret; 129262306a36Sopenharmony_ci break; 129362306a36Sopenharmony_ci default: 129462306a36Sopenharmony_ci return -EINVAL; 129562306a36Sopenharmony_ci } 129662306a36Sopenharmony_ci pr_info("Channel measurement facility initialized using format " 129762306a36Sopenharmony_ci "%s (mode %s)\n", format_string, detect_string); 129862306a36Sopenharmony_ci return 0; 129962306a36Sopenharmony_ci} 130062306a36Sopenharmony_cidevice_initcall(init_cmf); 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(enable_cmf); 130362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(disable_cmf); 130462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cmf_read); 130562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cmf_readall); 1306