18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Linux on zSeries Channel Measurement Facility support 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2000, 2006 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Authors: Arnd Bergmann <arndb@de.ibm.com> 88c2ecf20Sopenharmony_ci * Cornelia Huck <cornelia.huck@de.ibm.com> 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * original idea from Natarajan Krishnaswami <nkrishna@us.ibm.com> 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "cio" 148c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/memblock.h> 178c2ecf20Sopenharmony_ci#include <linux/device.h> 188c2ecf20Sopenharmony_ci#include <linux/init.h> 198c2ecf20Sopenharmony_ci#include <linux/list.h> 208c2ecf20Sopenharmony_ci#include <linux/export.h> 218c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 228c2ecf20Sopenharmony_ci#include <linux/slab.h> 238c2ecf20Sopenharmony_ci#include <linux/timex.h> /* get_tod_clock() */ 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <asm/ccwdev.h> 268c2ecf20Sopenharmony_ci#include <asm/cio.h> 278c2ecf20Sopenharmony_ci#include <asm/cmb.h> 288c2ecf20Sopenharmony_ci#include <asm/div64.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#include "cio.h" 318c2ecf20Sopenharmony_ci#include "css.h" 328c2ecf20Sopenharmony_ci#include "device.h" 338c2ecf20Sopenharmony_ci#include "ioasm.h" 348c2ecf20Sopenharmony_ci#include "chsc.h" 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* 378c2ecf20Sopenharmony_ci * parameter to enable cmf during boot, possible uses are: 388c2ecf20Sopenharmony_ci * "s390cmf" -- enable cmf and allocate 2 MB of ram so measuring can be 398c2ecf20Sopenharmony_ci * used on any subchannel 408c2ecf20Sopenharmony_ci * "s390cmf=<num>" -- enable cmf and allocate enough memory to measure 418c2ecf20Sopenharmony_ci * <num> subchannel, where <num> is an integer 428c2ecf20Sopenharmony_ci * between 1 and 65535, default is 1024 438c2ecf20Sopenharmony_ci */ 448c2ecf20Sopenharmony_ci#define ARGSTRING "s390cmf" 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/* indices for READCMB */ 478c2ecf20Sopenharmony_cienum cmb_index { 488c2ecf20Sopenharmony_ci avg_utilization = -1, 498c2ecf20Sopenharmony_ci /* basic and exended format: */ 508c2ecf20Sopenharmony_ci cmb_ssch_rsch_count = 0, 518c2ecf20Sopenharmony_ci cmb_sample_count, 528c2ecf20Sopenharmony_ci cmb_device_connect_time, 538c2ecf20Sopenharmony_ci cmb_function_pending_time, 548c2ecf20Sopenharmony_ci cmb_device_disconnect_time, 558c2ecf20Sopenharmony_ci cmb_control_unit_queuing_time, 568c2ecf20Sopenharmony_ci cmb_device_active_only_time, 578c2ecf20Sopenharmony_ci /* extended format only: */ 588c2ecf20Sopenharmony_ci cmb_device_busy_time, 598c2ecf20Sopenharmony_ci cmb_initial_command_response_time, 608c2ecf20Sopenharmony_ci}; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/** 638c2ecf20Sopenharmony_ci * enum cmb_format - types of supported measurement block formats 648c2ecf20Sopenharmony_ci * 658c2ecf20Sopenharmony_ci * @CMF_BASIC: traditional channel measurement blocks supported 668c2ecf20Sopenharmony_ci * by all machines that we run on 678c2ecf20Sopenharmony_ci * @CMF_EXTENDED: improved format that was introduced with the z990 688c2ecf20Sopenharmony_ci * machine 698c2ecf20Sopenharmony_ci * @CMF_AUTODETECT: default: use extended format when running on a machine 708c2ecf20Sopenharmony_ci * supporting extended format, otherwise fall back to 718c2ecf20Sopenharmony_ci * basic format 728c2ecf20Sopenharmony_ci */ 738c2ecf20Sopenharmony_cienum cmb_format { 748c2ecf20Sopenharmony_ci CMF_BASIC, 758c2ecf20Sopenharmony_ci CMF_EXTENDED, 768c2ecf20Sopenharmony_ci CMF_AUTODETECT = -1, 778c2ecf20Sopenharmony_ci}; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci/* 808c2ecf20Sopenharmony_ci * format - actual format for all measurement blocks 818c2ecf20Sopenharmony_ci * 828c2ecf20Sopenharmony_ci * The format module parameter can be set to a value of 0 (zero) 838c2ecf20Sopenharmony_ci * or 1, indicating basic or extended format as described for 848c2ecf20Sopenharmony_ci * enum cmb_format. 858c2ecf20Sopenharmony_ci */ 868c2ecf20Sopenharmony_cistatic int format = CMF_AUTODETECT; 878c2ecf20Sopenharmony_cimodule_param(format, bint, 0444); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci/** 908c2ecf20Sopenharmony_ci * struct cmb_operations - functions to use depending on cmb_format 918c2ecf20Sopenharmony_ci * 928c2ecf20Sopenharmony_ci * Most of these functions operate on a struct ccw_device. There is only 938c2ecf20Sopenharmony_ci * one instance of struct cmb_operations because the format of the measurement 948c2ecf20Sopenharmony_ci * data is guaranteed to be the same for every ccw_device. 958c2ecf20Sopenharmony_ci * 968c2ecf20Sopenharmony_ci * @alloc: allocate memory for a channel measurement block, 978c2ecf20Sopenharmony_ci * either with the help of a special pool or with kmalloc 988c2ecf20Sopenharmony_ci * @free: free memory allocated with @alloc 998c2ecf20Sopenharmony_ci * @set: enable or disable measurement 1008c2ecf20Sopenharmony_ci * @read: read a measurement entry at an index 1018c2ecf20Sopenharmony_ci * @readall: read a measurement block in a common format 1028c2ecf20Sopenharmony_ci * @reset: clear the data in the associated measurement block and 1038c2ecf20Sopenharmony_ci * reset its time stamp 1048c2ecf20Sopenharmony_ci */ 1058c2ecf20Sopenharmony_cistruct cmb_operations { 1068c2ecf20Sopenharmony_ci int (*alloc) (struct ccw_device *); 1078c2ecf20Sopenharmony_ci void (*free) (struct ccw_device *); 1088c2ecf20Sopenharmony_ci int (*set) (struct ccw_device *, u32); 1098c2ecf20Sopenharmony_ci u64 (*read) (struct ccw_device *, int); 1108c2ecf20Sopenharmony_ci int (*readall)(struct ccw_device *, struct cmbdata *); 1118c2ecf20Sopenharmony_ci void (*reset) (struct ccw_device *); 1128c2ecf20Sopenharmony_ci/* private: */ 1138c2ecf20Sopenharmony_ci struct attribute_group *attr_group; 1148c2ecf20Sopenharmony_ci}; 1158c2ecf20Sopenharmony_cistatic struct cmb_operations *cmbops; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistruct cmb_data { 1188c2ecf20Sopenharmony_ci void *hw_block; /* Pointer to block updated by hardware */ 1198c2ecf20Sopenharmony_ci void *last_block; /* Last changed block copied from hardware block */ 1208c2ecf20Sopenharmony_ci int size; /* Size of hw_block and last_block */ 1218c2ecf20Sopenharmony_ci unsigned long long last_update; /* when last_block was updated */ 1228c2ecf20Sopenharmony_ci}; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci/* 1258c2ecf20Sopenharmony_ci * Our user interface is designed in terms of nanoseconds, 1268c2ecf20Sopenharmony_ci * while the hardware measures total times in its own 1278c2ecf20Sopenharmony_ci * unit. 1288c2ecf20Sopenharmony_ci */ 1298c2ecf20Sopenharmony_cistatic inline u64 time_to_nsec(u32 value) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci return ((u64)value) * 128000ull; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci/* 1358c2ecf20Sopenharmony_ci * Users are usually interested in average times, 1368c2ecf20Sopenharmony_ci * not accumulated time. 1378c2ecf20Sopenharmony_ci * This also helps us with atomicity problems 1388c2ecf20Sopenharmony_ci * when reading sinlge values. 1398c2ecf20Sopenharmony_ci */ 1408c2ecf20Sopenharmony_cistatic inline u64 time_to_avg_nsec(u32 value, u32 count) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci u64 ret; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci /* no samples yet, avoid division by 0 */ 1458c2ecf20Sopenharmony_ci if (count == 0) 1468c2ecf20Sopenharmony_ci return 0; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci /* value comes in units of 128 µsec */ 1498c2ecf20Sopenharmony_ci ret = time_to_nsec(value); 1508c2ecf20Sopenharmony_ci do_div(ret, count); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci return ret; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci#define CMF_OFF 0 1568c2ecf20Sopenharmony_ci#define CMF_ON 2 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci/* 1598c2ecf20Sopenharmony_ci * Activate or deactivate the channel monitor. When area is NULL, 1608c2ecf20Sopenharmony_ci * the monitor is deactivated. The channel monitor needs to 1618c2ecf20Sopenharmony_ci * be active in order to measure subchannels, which also need 1628c2ecf20Sopenharmony_ci * to be enabled. 1638c2ecf20Sopenharmony_ci */ 1648c2ecf20Sopenharmony_cistatic inline void cmf_activate(void *area, unsigned int onoff) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci register void * __gpr2 asm("2"); 1678c2ecf20Sopenharmony_ci register long __gpr1 asm("1"); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci __gpr2 = area; 1708c2ecf20Sopenharmony_ci __gpr1 = onoff; 1718c2ecf20Sopenharmony_ci /* activate channel measurement */ 1728c2ecf20Sopenharmony_ci asm("schm" : : "d" (__gpr2), "d" (__gpr1) ); 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic int set_schib(struct ccw_device *cdev, u32 mme, int mbfc, 1768c2ecf20Sopenharmony_ci unsigned long address) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci struct subchannel *sch = to_subchannel(cdev->dev.parent); 1798c2ecf20Sopenharmony_ci int ret; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci sch->config.mme = mme; 1828c2ecf20Sopenharmony_ci sch->config.mbfc = mbfc; 1838c2ecf20Sopenharmony_ci /* address can be either a block address or a block index */ 1848c2ecf20Sopenharmony_ci if (mbfc) 1858c2ecf20Sopenharmony_ci sch->config.mba = address; 1868c2ecf20Sopenharmony_ci else 1878c2ecf20Sopenharmony_ci sch->config.mbi = address; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci ret = cio_commit_config(sch); 1908c2ecf20Sopenharmony_ci if (!mme && ret == -ENODEV) { 1918c2ecf20Sopenharmony_ci /* 1928c2ecf20Sopenharmony_ci * The task was to disable measurement block updates but 1938c2ecf20Sopenharmony_ci * the subchannel is already gone. Report success. 1948c2ecf20Sopenharmony_ci */ 1958c2ecf20Sopenharmony_ci ret = 0; 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci return ret; 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistruct set_schib_struct { 2018c2ecf20Sopenharmony_ci u32 mme; 2028c2ecf20Sopenharmony_ci int mbfc; 2038c2ecf20Sopenharmony_ci unsigned long address; 2048c2ecf20Sopenharmony_ci wait_queue_head_t wait; 2058c2ecf20Sopenharmony_ci int ret; 2068c2ecf20Sopenharmony_ci}; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci#define CMF_PENDING 1 2098c2ecf20Sopenharmony_ci#define SET_SCHIB_TIMEOUT (10 * HZ) 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic int set_schib_wait(struct ccw_device *cdev, u32 mme, 2128c2ecf20Sopenharmony_ci int mbfc, unsigned long address) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct set_schib_struct set_data; 2158c2ecf20Sopenharmony_ci int ret = -ENODEV; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci spin_lock_irq(cdev->ccwlock); 2188c2ecf20Sopenharmony_ci if (!cdev->private->cmb) 2198c2ecf20Sopenharmony_ci goto out; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci ret = set_schib(cdev, mme, mbfc, address); 2228c2ecf20Sopenharmony_ci if (ret != -EBUSY) 2238c2ecf20Sopenharmony_ci goto out; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci /* if the device is not online, don't even try again */ 2268c2ecf20Sopenharmony_ci if (cdev->private->state != DEV_STATE_ONLINE) 2278c2ecf20Sopenharmony_ci goto out; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci init_waitqueue_head(&set_data.wait); 2308c2ecf20Sopenharmony_ci set_data.mme = mme; 2318c2ecf20Sopenharmony_ci set_data.mbfc = mbfc; 2328c2ecf20Sopenharmony_ci set_data.address = address; 2338c2ecf20Sopenharmony_ci set_data.ret = CMF_PENDING; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci cdev->private->state = DEV_STATE_CMFCHANGE; 2368c2ecf20Sopenharmony_ci cdev->private->cmb_wait = &set_data; 2378c2ecf20Sopenharmony_ci spin_unlock_irq(cdev->ccwlock); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci ret = wait_event_interruptible_timeout(set_data.wait, 2408c2ecf20Sopenharmony_ci set_data.ret != CMF_PENDING, 2418c2ecf20Sopenharmony_ci SET_SCHIB_TIMEOUT); 2428c2ecf20Sopenharmony_ci spin_lock_irq(cdev->ccwlock); 2438c2ecf20Sopenharmony_ci if (ret <= 0) { 2448c2ecf20Sopenharmony_ci if (set_data.ret == CMF_PENDING) { 2458c2ecf20Sopenharmony_ci set_data.ret = (ret == 0) ? -ETIME : ret; 2468c2ecf20Sopenharmony_ci if (cdev->private->state == DEV_STATE_CMFCHANGE) 2478c2ecf20Sopenharmony_ci cdev->private->state = DEV_STATE_ONLINE; 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci cdev->private->cmb_wait = NULL; 2518c2ecf20Sopenharmony_ci ret = set_data.ret; 2528c2ecf20Sopenharmony_ciout: 2538c2ecf20Sopenharmony_ci spin_unlock_irq(cdev->ccwlock); 2548c2ecf20Sopenharmony_ci return ret; 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_civoid retry_set_schib(struct ccw_device *cdev) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci struct set_schib_struct *set_data = cdev->private->cmb_wait; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if (!set_data) 2628c2ecf20Sopenharmony_ci return; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci set_data->ret = set_schib(cdev, set_data->mme, set_data->mbfc, 2658c2ecf20Sopenharmony_ci set_data->address); 2668c2ecf20Sopenharmony_ci wake_up(&set_data->wait); 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic int cmf_copy_block(struct ccw_device *cdev) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci struct subchannel *sch = to_subchannel(cdev->dev.parent); 2728c2ecf20Sopenharmony_ci struct cmb_data *cmb_data; 2738c2ecf20Sopenharmony_ci void *hw_block; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (cio_update_schib(sch)) 2768c2ecf20Sopenharmony_ci return -ENODEV; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci if (scsw_fctl(&sch->schib.scsw) & SCSW_FCTL_START_FUNC) { 2798c2ecf20Sopenharmony_ci /* Don't copy if a start function is in progress. */ 2808c2ecf20Sopenharmony_ci if ((!(scsw_actl(&sch->schib.scsw) & SCSW_ACTL_SUSPENDED)) && 2818c2ecf20Sopenharmony_ci (scsw_actl(&sch->schib.scsw) & 2828c2ecf20Sopenharmony_ci (SCSW_ACTL_DEVACT | SCSW_ACTL_SCHACT)) && 2838c2ecf20Sopenharmony_ci (!(scsw_stctl(&sch->schib.scsw) & SCSW_STCTL_SEC_STATUS))) 2848c2ecf20Sopenharmony_ci return -EBUSY; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci cmb_data = cdev->private->cmb; 2878c2ecf20Sopenharmony_ci hw_block = cmb_data->hw_block; 2888c2ecf20Sopenharmony_ci memcpy(cmb_data->last_block, hw_block, cmb_data->size); 2898c2ecf20Sopenharmony_ci cmb_data->last_update = get_tod_clock(); 2908c2ecf20Sopenharmony_ci return 0; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistruct copy_block_struct { 2948c2ecf20Sopenharmony_ci wait_queue_head_t wait; 2958c2ecf20Sopenharmony_ci int ret; 2968c2ecf20Sopenharmony_ci}; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic int cmf_cmb_copy_wait(struct ccw_device *cdev) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci struct copy_block_struct copy_block; 3018c2ecf20Sopenharmony_ci int ret = -ENODEV; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci spin_lock_irq(cdev->ccwlock); 3048c2ecf20Sopenharmony_ci if (!cdev->private->cmb) 3058c2ecf20Sopenharmony_ci goto out; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci ret = cmf_copy_block(cdev); 3088c2ecf20Sopenharmony_ci if (ret != -EBUSY) 3098c2ecf20Sopenharmony_ci goto out; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci if (cdev->private->state != DEV_STATE_ONLINE) 3128c2ecf20Sopenharmony_ci goto out; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci init_waitqueue_head(©_block.wait); 3158c2ecf20Sopenharmony_ci copy_block.ret = CMF_PENDING; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci cdev->private->state = DEV_STATE_CMFUPDATE; 3188c2ecf20Sopenharmony_ci cdev->private->cmb_wait = ©_block; 3198c2ecf20Sopenharmony_ci spin_unlock_irq(cdev->ccwlock); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci ret = wait_event_interruptible(copy_block.wait, 3228c2ecf20Sopenharmony_ci copy_block.ret != CMF_PENDING); 3238c2ecf20Sopenharmony_ci spin_lock_irq(cdev->ccwlock); 3248c2ecf20Sopenharmony_ci if (ret) { 3258c2ecf20Sopenharmony_ci if (copy_block.ret == CMF_PENDING) { 3268c2ecf20Sopenharmony_ci copy_block.ret = -ERESTARTSYS; 3278c2ecf20Sopenharmony_ci if (cdev->private->state == DEV_STATE_CMFUPDATE) 3288c2ecf20Sopenharmony_ci cdev->private->state = DEV_STATE_ONLINE; 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci cdev->private->cmb_wait = NULL; 3328c2ecf20Sopenharmony_ci ret = copy_block.ret; 3338c2ecf20Sopenharmony_ciout: 3348c2ecf20Sopenharmony_ci spin_unlock_irq(cdev->ccwlock); 3358c2ecf20Sopenharmony_ci return ret; 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_civoid cmf_retry_copy_block(struct ccw_device *cdev) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci struct copy_block_struct *copy_block = cdev->private->cmb_wait; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci if (!copy_block) 3438c2ecf20Sopenharmony_ci return; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci copy_block->ret = cmf_copy_block(cdev); 3468c2ecf20Sopenharmony_ci wake_up(©_block->wait); 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic void cmf_generic_reset(struct ccw_device *cdev) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci struct cmb_data *cmb_data; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci spin_lock_irq(cdev->ccwlock); 3548c2ecf20Sopenharmony_ci cmb_data = cdev->private->cmb; 3558c2ecf20Sopenharmony_ci if (cmb_data) { 3568c2ecf20Sopenharmony_ci memset(cmb_data->last_block, 0, cmb_data->size); 3578c2ecf20Sopenharmony_ci /* 3588c2ecf20Sopenharmony_ci * Need to reset hw block as well to make the hardware start 3598c2ecf20Sopenharmony_ci * from 0 again. 3608c2ecf20Sopenharmony_ci */ 3618c2ecf20Sopenharmony_ci memset(cmb_data->hw_block, 0, cmb_data->size); 3628c2ecf20Sopenharmony_ci cmb_data->last_update = 0; 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci cdev->private->cmb_start_time = get_tod_clock(); 3658c2ecf20Sopenharmony_ci spin_unlock_irq(cdev->ccwlock); 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci/** 3698c2ecf20Sopenharmony_ci * struct cmb_area - container for global cmb data 3708c2ecf20Sopenharmony_ci * 3718c2ecf20Sopenharmony_ci * @mem: pointer to CMBs (only in basic measurement mode) 3728c2ecf20Sopenharmony_ci * @list: contains a linked list of all subchannels 3738c2ecf20Sopenharmony_ci * @num_channels: number of channels to be measured 3748c2ecf20Sopenharmony_ci * @lock: protect concurrent access to @mem and @list 3758c2ecf20Sopenharmony_ci */ 3768c2ecf20Sopenharmony_cistruct cmb_area { 3778c2ecf20Sopenharmony_ci struct cmb *mem; 3788c2ecf20Sopenharmony_ci struct list_head list; 3798c2ecf20Sopenharmony_ci int num_channels; 3808c2ecf20Sopenharmony_ci spinlock_t lock; 3818c2ecf20Sopenharmony_ci}; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic struct cmb_area cmb_area = { 3848c2ecf20Sopenharmony_ci .lock = __SPIN_LOCK_UNLOCKED(cmb_area.lock), 3858c2ecf20Sopenharmony_ci .list = LIST_HEAD_INIT(cmb_area.list), 3868c2ecf20Sopenharmony_ci .num_channels = 1024, 3878c2ecf20Sopenharmony_ci}; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci/* ****** old style CMB handling ********/ 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci/* 3928c2ecf20Sopenharmony_ci * Basic channel measurement blocks are allocated in one contiguous 3938c2ecf20Sopenharmony_ci * block of memory, which can not be moved as long as any channel 3948c2ecf20Sopenharmony_ci * is active. Therefore, a maximum number of subchannels needs to 3958c2ecf20Sopenharmony_ci * be defined somewhere. This is a module parameter, defaulting to 3968c2ecf20Sopenharmony_ci * a reasonable value of 1024, or 32 kb of memory. 3978c2ecf20Sopenharmony_ci * Current kernels don't allow kmalloc with more than 128kb, so the 3988c2ecf20Sopenharmony_ci * maximum is 4096. 3998c2ecf20Sopenharmony_ci */ 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cimodule_param_named(maxchannels, cmb_area.num_channels, uint, 0444); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci/** 4048c2ecf20Sopenharmony_ci * struct cmb - basic channel measurement block 4058c2ecf20Sopenharmony_ci * @ssch_rsch_count: number of ssch and rsch 4068c2ecf20Sopenharmony_ci * @sample_count: number of samples 4078c2ecf20Sopenharmony_ci * @device_connect_time: time of device connect 4088c2ecf20Sopenharmony_ci * @function_pending_time: time of function pending 4098c2ecf20Sopenharmony_ci * @device_disconnect_time: time of device disconnect 4108c2ecf20Sopenharmony_ci * @control_unit_queuing_time: time of control unit queuing 4118c2ecf20Sopenharmony_ci * @device_active_only_time: time of device active only 4128c2ecf20Sopenharmony_ci * @reserved: unused in basic measurement mode 4138c2ecf20Sopenharmony_ci * 4148c2ecf20Sopenharmony_ci * The measurement block as used by the hardware. The fields are described 4158c2ecf20Sopenharmony_ci * further in z/Architecture Principles of Operation, chapter 17. 4168c2ecf20Sopenharmony_ci * 4178c2ecf20Sopenharmony_ci * The cmb area made up from these blocks must be a contiguous array and may 4188c2ecf20Sopenharmony_ci * not be reallocated or freed. 4198c2ecf20Sopenharmony_ci * Only one cmb area can be present in the system. 4208c2ecf20Sopenharmony_ci */ 4218c2ecf20Sopenharmony_cistruct cmb { 4228c2ecf20Sopenharmony_ci u16 ssch_rsch_count; 4238c2ecf20Sopenharmony_ci u16 sample_count; 4248c2ecf20Sopenharmony_ci u32 device_connect_time; 4258c2ecf20Sopenharmony_ci u32 function_pending_time; 4268c2ecf20Sopenharmony_ci u32 device_disconnect_time; 4278c2ecf20Sopenharmony_ci u32 control_unit_queuing_time; 4288c2ecf20Sopenharmony_ci u32 device_active_only_time; 4298c2ecf20Sopenharmony_ci u32 reserved[2]; 4308c2ecf20Sopenharmony_ci}; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci/* 4338c2ecf20Sopenharmony_ci * Insert a single device into the cmb_area list. 4348c2ecf20Sopenharmony_ci * Called with cmb_area.lock held from alloc_cmb. 4358c2ecf20Sopenharmony_ci */ 4368c2ecf20Sopenharmony_cistatic int alloc_cmb_single(struct ccw_device *cdev, 4378c2ecf20Sopenharmony_ci struct cmb_data *cmb_data) 4388c2ecf20Sopenharmony_ci{ 4398c2ecf20Sopenharmony_ci struct cmb *cmb; 4408c2ecf20Sopenharmony_ci struct ccw_device_private *node; 4418c2ecf20Sopenharmony_ci int ret; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci spin_lock_irq(cdev->ccwlock); 4448c2ecf20Sopenharmony_ci if (!list_empty(&cdev->private->cmb_list)) { 4458c2ecf20Sopenharmony_ci ret = -EBUSY; 4468c2ecf20Sopenharmony_ci goto out; 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci /* 4508c2ecf20Sopenharmony_ci * Find first unused cmb in cmb_area.mem. 4518c2ecf20Sopenharmony_ci * This is a little tricky: cmb_area.list 4528c2ecf20Sopenharmony_ci * remains sorted by ->cmb->hw_data pointers. 4538c2ecf20Sopenharmony_ci */ 4548c2ecf20Sopenharmony_ci cmb = cmb_area.mem; 4558c2ecf20Sopenharmony_ci list_for_each_entry(node, &cmb_area.list, cmb_list) { 4568c2ecf20Sopenharmony_ci struct cmb_data *data; 4578c2ecf20Sopenharmony_ci data = node->cmb; 4588c2ecf20Sopenharmony_ci if ((struct cmb*)data->hw_block > cmb) 4598c2ecf20Sopenharmony_ci break; 4608c2ecf20Sopenharmony_ci cmb++; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci if (cmb - cmb_area.mem >= cmb_area.num_channels) { 4638c2ecf20Sopenharmony_ci ret = -ENOMEM; 4648c2ecf20Sopenharmony_ci goto out; 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci /* insert new cmb */ 4688c2ecf20Sopenharmony_ci list_add_tail(&cdev->private->cmb_list, &node->cmb_list); 4698c2ecf20Sopenharmony_ci cmb_data->hw_block = cmb; 4708c2ecf20Sopenharmony_ci cdev->private->cmb = cmb_data; 4718c2ecf20Sopenharmony_ci ret = 0; 4728c2ecf20Sopenharmony_ciout: 4738c2ecf20Sopenharmony_ci spin_unlock_irq(cdev->ccwlock); 4748c2ecf20Sopenharmony_ci return ret; 4758c2ecf20Sopenharmony_ci} 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_cistatic int alloc_cmb(struct ccw_device *cdev) 4788c2ecf20Sopenharmony_ci{ 4798c2ecf20Sopenharmony_ci int ret; 4808c2ecf20Sopenharmony_ci struct cmb *mem; 4818c2ecf20Sopenharmony_ci ssize_t size; 4828c2ecf20Sopenharmony_ci struct cmb_data *cmb_data; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci /* Allocate private cmb_data. */ 4858c2ecf20Sopenharmony_ci cmb_data = kzalloc(sizeof(struct cmb_data), GFP_KERNEL); 4868c2ecf20Sopenharmony_ci if (!cmb_data) 4878c2ecf20Sopenharmony_ci return -ENOMEM; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci cmb_data->last_block = kzalloc(sizeof(struct cmb), GFP_KERNEL); 4908c2ecf20Sopenharmony_ci if (!cmb_data->last_block) { 4918c2ecf20Sopenharmony_ci kfree(cmb_data); 4928c2ecf20Sopenharmony_ci return -ENOMEM; 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci cmb_data->size = sizeof(struct cmb); 4958c2ecf20Sopenharmony_ci spin_lock(&cmb_area.lock); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci if (!cmb_area.mem) { 4988c2ecf20Sopenharmony_ci /* there is no user yet, so we need a new area */ 4998c2ecf20Sopenharmony_ci size = sizeof(struct cmb) * cmb_area.num_channels; 5008c2ecf20Sopenharmony_ci WARN_ON(!list_empty(&cmb_area.list)); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci spin_unlock(&cmb_area.lock); 5038c2ecf20Sopenharmony_ci mem = (void*)__get_free_pages(GFP_KERNEL | GFP_DMA, 5048c2ecf20Sopenharmony_ci get_order(size)); 5058c2ecf20Sopenharmony_ci spin_lock(&cmb_area.lock); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci if (cmb_area.mem) { 5088c2ecf20Sopenharmony_ci /* ok, another thread was faster */ 5098c2ecf20Sopenharmony_ci free_pages((unsigned long)mem, get_order(size)); 5108c2ecf20Sopenharmony_ci } else if (!mem) { 5118c2ecf20Sopenharmony_ci /* no luck */ 5128c2ecf20Sopenharmony_ci ret = -ENOMEM; 5138c2ecf20Sopenharmony_ci goto out; 5148c2ecf20Sopenharmony_ci } else { 5158c2ecf20Sopenharmony_ci /* everything ok */ 5168c2ecf20Sopenharmony_ci memset(mem, 0, size); 5178c2ecf20Sopenharmony_ci cmb_area.mem = mem; 5188c2ecf20Sopenharmony_ci cmf_activate(cmb_area.mem, CMF_ON); 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci /* do the actual allocation */ 5238c2ecf20Sopenharmony_ci ret = alloc_cmb_single(cdev, cmb_data); 5248c2ecf20Sopenharmony_ciout: 5258c2ecf20Sopenharmony_ci spin_unlock(&cmb_area.lock); 5268c2ecf20Sopenharmony_ci if (ret) { 5278c2ecf20Sopenharmony_ci kfree(cmb_data->last_block); 5288c2ecf20Sopenharmony_ci kfree(cmb_data); 5298c2ecf20Sopenharmony_ci } 5308c2ecf20Sopenharmony_ci return ret; 5318c2ecf20Sopenharmony_ci} 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_cistatic void free_cmb(struct ccw_device *cdev) 5348c2ecf20Sopenharmony_ci{ 5358c2ecf20Sopenharmony_ci struct ccw_device_private *priv; 5368c2ecf20Sopenharmony_ci struct cmb_data *cmb_data; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci spin_lock(&cmb_area.lock); 5398c2ecf20Sopenharmony_ci spin_lock_irq(cdev->ccwlock); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci priv = cdev->private; 5428c2ecf20Sopenharmony_ci cmb_data = priv->cmb; 5438c2ecf20Sopenharmony_ci priv->cmb = NULL; 5448c2ecf20Sopenharmony_ci if (cmb_data) 5458c2ecf20Sopenharmony_ci kfree(cmb_data->last_block); 5468c2ecf20Sopenharmony_ci kfree(cmb_data); 5478c2ecf20Sopenharmony_ci list_del_init(&priv->cmb_list); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci if (list_empty(&cmb_area.list)) { 5508c2ecf20Sopenharmony_ci ssize_t size; 5518c2ecf20Sopenharmony_ci size = sizeof(struct cmb) * cmb_area.num_channels; 5528c2ecf20Sopenharmony_ci cmf_activate(NULL, CMF_OFF); 5538c2ecf20Sopenharmony_ci free_pages((unsigned long)cmb_area.mem, get_order(size)); 5548c2ecf20Sopenharmony_ci cmb_area.mem = NULL; 5558c2ecf20Sopenharmony_ci } 5568c2ecf20Sopenharmony_ci spin_unlock_irq(cdev->ccwlock); 5578c2ecf20Sopenharmony_ci spin_unlock(&cmb_area.lock); 5588c2ecf20Sopenharmony_ci} 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_cistatic int set_cmb(struct ccw_device *cdev, u32 mme) 5618c2ecf20Sopenharmony_ci{ 5628c2ecf20Sopenharmony_ci u16 offset; 5638c2ecf20Sopenharmony_ci struct cmb_data *cmb_data; 5648c2ecf20Sopenharmony_ci unsigned long flags; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci spin_lock_irqsave(cdev->ccwlock, flags); 5678c2ecf20Sopenharmony_ci if (!cdev->private->cmb) { 5688c2ecf20Sopenharmony_ci spin_unlock_irqrestore(cdev->ccwlock, flags); 5698c2ecf20Sopenharmony_ci return -EINVAL; 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci cmb_data = cdev->private->cmb; 5728c2ecf20Sopenharmony_ci offset = mme ? (struct cmb *)cmb_data->hw_block - cmb_area.mem : 0; 5738c2ecf20Sopenharmony_ci spin_unlock_irqrestore(cdev->ccwlock, flags); 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci return set_schib_wait(cdev, mme, 0, offset); 5768c2ecf20Sopenharmony_ci} 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci/* calculate utilization in 0.1 percent units */ 5798c2ecf20Sopenharmony_cistatic u64 __cmb_utilization(u64 device_connect_time, u64 function_pending_time, 5808c2ecf20Sopenharmony_ci u64 device_disconnect_time, u64 start_time) 5818c2ecf20Sopenharmony_ci{ 5828c2ecf20Sopenharmony_ci u64 utilization, elapsed_time; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci utilization = time_to_nsec(device_connect_time + 5858c2ecf20Sopenharmony_ci function_pending_time + 5868c2ecf20Sopenharmony_ci device_disconnect_time); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci elapsed_time = get_tod_clock() - start_time; 5898c2ecf20Sopenharmony_ci elapsed_time = tod_to_ns(elapsed_time); 5908c2ecf20Sopenharmony_ci elapsed_time /= 1000; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci return elapsed_time ? (utilization / elapsed_time) : 0; 5938c2ecf20Sopenharmony_ci} 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_cistatic u64 read_cmb(struct ccw_device *cdev, int index) 5968c2ecf20Sopenharmony_ci{ 5978c2ecf20Sopenharmony_ci struct cmb_data *cmb_data; 5988c2ecf20Sopenharmony_ci unsigned long flags; 5998c2ecf20Sopenharmony_ci struct cmb *cmb; 6008c2ecf20Sopenharmony_ci u64 ret = 0; 6018c2ecf20Sopenharmony_ci u32 val; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci spin_lock_irqsave(cdev->ccwlock, flags); 6048c2ecf20Sopenharmony_ci cmb_data = cdev->private->cmb; 6058c2ecf20Sopenharmony_ci if (!cmb_data) 6068c2ecf20Sopenharmony_ci goto out; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci cmb = cmb_data->hw_block; 6098c2ecf20Sopenharmony_ci switch (index) { 6108c2ecf20Sopenharmony_ci case avg_utilization: 6118c2ecf20Sopenharmony_ci ret = __cmb_utilization(cmb->device_connect_time, 6128c2ecf20Sopenharmony_ci cmb->function_pending_time, 6138c2ecf20Sopenharmony_ci cmb->device_disconnect_time, 6148c2ecf20Sopenharmony_ci cdev->private->cmb_start_time); 6158c2ecf20Sopenharmony_ci goto out; 6168c2ecf20Sopenharmony_ci case cmb_ssch_rsch_count: 6178c2ecf20Sopenharmony_ci ret = cmb->ssch_rsch_count; 6188c2ecf20Sopenharmony_ci goto out; 6198c2ecf20Sopenharmony_ci case cmb_sample_count: 6208c2ecf20Sopenharmony_ci ret = cmb->sample_count; 6218c2ecf20Sopenharmony_ci goto out; 6228c2ecf20Sopenharmony_ci case cmb_device_connect_time: 6238c2ecf20Sopenharmony_ci val = cmb->device_connect_time; 6248c2ecf20Sopenharmony_ci break; 6258c2ecf20Sopenharmony_ci case cmb_function_pending_time: 6268c2ecf20Sopenharmony_ci val = cmb->function_pending_time; 6278c2ecf20Sopenharmony_ci break; 6288c2ecf20Sopenharmony_ci case cmb_device_disconnect_time: 6298c2ecf20Sopenharmony_ci val = cmb->device_disconnect_time; 6308c2ecf20Sopenharmony_ci break; 6318c2ecf20Sopenharmony_ci case cmb_control_unit_queuing_time: 6328c2ecf20Sopenharmony_ci val = cmb->control_unit_queuing_time; 6338c2ecf20Sopenharmony_ci break; 6348c2ecf20Sopenharmony_ci case cmb_device_active_only_time: 6358c2ecf20Sopenharmony_ci val = cmb->device_active_only_time; 6368c2ecf20Sopenharmony_ci break; 6378c2ecf20Sopenharmony_ci default: 6388c2ecf20Sopenharmony_ci goto out; 6398c2ecf20Sopenharmony_ci } 6408c2ecf20Sopenharmony_ci ret = time_to_avg_nsec(val, cmb->sample_count); 6418c2ecf20Sopenharmony_ciout: 6428c2ecf20Sopenharmony_ci spin_unlock_irqrestore(cdev->ccwlock, flags); 6438c2ecf20Sopenharmony_ci return ret; 6448c2ecf20Sopenharmony_ci} 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_cistatic int readall_cmb(struct ccw_device *cdev, struct cmbdata *data) 6478c2ecf20Sopenharmony_ci{ 6488c2ecf20Sopenharmony_ci struct cmb *cmb; 6498c2ecf20Sopenharmony_ci struct cmb_data *cmb_data; 6508c2ecf20Sopenharmony_ci u64 time; 6518c2ecf20Sopenharmony_ci unsigned long flags; 6528c2ecf20Sopenharmony_ci int ret; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci ret = cmf_cmb_copy_wait(cdev); 6558c2ecf20Sopenharmony_ci if (ret < 0) 6568c2ecf20Sopenharmony_ci return ret; 6578c2ecf20Sopenharmony_ci spin_lock_irqsave(cdev->ccwlock, flags); 6588c2ecf20Sopenharmony_ci cmb_data = cdev->private->cmb; 6598c2ecf20Sopenharmony_ci if (!cmb_data) { 6608c2ecf20Sopenharmony_ci ret = -ENODEV; 6618c2ecf20Sopenharmony_ci goto out; 6628c2ecf20Sopenharmony_ci } 6638c2ecf20Sopenharmony_ci if (cmb_data->last_update == 0) { 6648c2ecf20Sopenharmony_ci ret = -EAGAIN; 6658c2ecf20Sopenharmony_ci goto out; 6668c2ecf20Sopenharmony_ci } 6678c2ecf20Sopenharmony_ci cmb = cmb_data->last_block; 6688c2ecf20Sopenharmony_ci time = cmb_data->last_update - cdev->private->cmb_start_time; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci memset(data, 0, sizeof(struct cmbdata)); 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci /* we only know values before device_busy_time */ 6738c2ecf20Sopenharmony_ci data->size = offsetof(struct cmbdata, device_busy_time); 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci data->elapsed_time = tod_to_ns(time); 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci /* copy data to new structure */ 6788c2ecf20Sopenharmony_ci data->ssch_rsch_count = cmb->ssch_rsch_count; 6798c2ecf20Sopenharmony_ci data->sample_count = cmb->sample_count; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci /* time fields are converted to nanoseconds while copying */ 6828c2ecf20Sopenharmony_ci data->device_connect_time = time_to_nsec(cmb->device_connect_time); 6838c2ecf20Sopenharmony_ci data->function_pending_time = time_to_nsec(cmb->function_pending_time); 6848c2ecf20Sopenharmony_ci data->device_disconnect_time = 6858c2ecf20Sopenharmony_ci time_to_nsec(cmb->device_disconnect_time); 6868c2ecf20Sopenharmony_ci data->control_unit_queuing_time 6878c2ecf20Sopenharmony_ci = time_to_nsec(cmb->control_unit_queuing_time); 6888c2ecf20Sopenharmony_ci data->device_active_only_time 6898c2ecf20Sopenharmony_ci = time_to_nsec(cmb->device_active_only_time); 6908c2ecf20Sopenharmony_ci ret = 0; 6918c2ecf20Sopenharmony_ciout: 6928c2ecf20Sopenharmony_ci spin_unlock_irqrestore(cdev->ccwlock, flags); 6938c2ecf20Sopenharmony_ci return ret; 6948c2ecf20Sopenharmony_ci} 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_cistatic void reset_cmb(struct ccw_device *cdev) 6978c2ecf20Sopenharmony_ci{ 6988c2ecf20Sopenharmony_ci cmf_generic_reset(cdev); 6998c2ecf20Sopenharmony_ci} 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_cistatic int cmf_enabled(struct ccw_device *cdev) 7028c2ecf20Sopenharmony_ci{ 7038c2ecf20Sopenharmony_ci int enabled; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci spin_lock_irq(cdev->ccwlock); 7068c2ecf20Sopenharmony_ci enabled = !!cdev->private->cmb; 7078c2ecf20Sopenharmony_ci spin_unlock_irq(cdev->ccwlock); 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci return enabled; 7108c2ecf20Sopenharmony_ci} 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_cistatic struct attribute_group cmf_attr_group; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_cistatic struct cmb_operations cmbops_basic = { 7158c2ecf20Sopenharmony_ci .alloc = alloc_cmb, 7168c2ecf20Sopenharmony_ci .free = free_cmb, 7178c2ecf20Sopenharmony_ci .set = set_cmb, 7188c2ecf20Sopenharmony_ci .read = read_cmb, 7198c2ecf20Sopenharmony_ci .readall = readall_cmb, 7208c2ecf20Sopenharmony_ci .reset = reset_cmb, 7218c2ecf20Sopenharmony_ci .attr_group = &cmf_attr_group, 7228c2ecf20Sopenharmony_ci}; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci/* ******** extended cmb handling ********/ 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci/** 7278c2ecf20Sopenharmony_ci * struct cmbe - extended channel measurement block 7288c2ecf20Sopenharmony_ci * @ssch_rsch_count: number of ssch and rsch 7298c2ecf20Sopenharmony_ci * @sample_count: number of samples 7308c2ecf20Sopenharmony_ci * @device_connect_time: time of device connect 7318c2ecf20Sopenharmony_ci * @function_pending_time: time of function pending 7328c2ecf20Sopenharmony_ci * @device_disconnect_time: time of device disconnect 7338c2ecf20Sopenharmony_ci * @control_unit_queuing_time: time of control unit queuing 7348c2ecf20Sopenharmony_ci * @device_active_only_time: time of device active only 7358c2ecf20Sopenharmony_ci * @device_busy_time: time of device busy 7368c2ecf20Sopenharmony_ci * @initial_command_response_time: initial command response time 7378c2ecf20Sopenharmony_ci * @reserved: unused 7388c2ecf20Sopenharmony_ci * 7398c2ecf20Sopenharmony_ci * The measurement block as used by the hardware. May be in any 64 bit physical 7408c2ecf20Sopenharmony_ci * location. 7418c2ecf20Sopenharmony_ci * The fields are described further in z/Architecture Principles of Operation, 7428c2ecf20Sopenharmony_ci * third edition, chapter 17. 7438c2ecf20Sopenharmony_ci */ 7448c2ecf20Sopenharmony_cistruct cmbe { 7458c2ecf20Sopenharmony_ci u32 ssch_rsch_count; 7468c2ecf20Sopenharmony_ci u32 sample_count; 7478c2ecf20Sopenharmony_ci u32 device_connect_time; 7488c2ecf20Sopenharmony_ci u32 function_pending_time; 7498c2ecf20Sopenharmony_ci u32 device_disconnect_time; 7508c2ecf20Sopenharmony_ci u32 control_unit_queuing_time; 7518c2ecf20Sopenharmony_ci u32 device_active_only_time; 7528c2ecf20Sopenharmony_ci u32 device_busy_time; 7538c2ecf20Sopenharmony_ci u32 initial_command_response_time; 7548c2ecf20Sopenharmony_ci u32 reserved[7]; 7558c2ecf20Sopenharmony_ci} __packed __aligned(64); 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_cistatic struct kmem_cache *cmbe_cache; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_cistatic int alloc_cmbe(struct ccw_device *cdev) 7608c2ecf20Sopenharmony_ci{ 7618c2ecf20Sopenharmony_ci struct cmb_data *cmb_data; 7628c2ecf20Sopenharmony_ci struct cmbe *cmbe; 7638c2ecf20Sopenharmony_ci int ret = -ENOMEM; 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci cmbe = kmem_cache_zalloc(cmbe_cache, GFP_KERNEL); 7668c2ecf20Sopenharmony_ci if (!cmbe) 7678c2ecf20Sopenharmony_ci return ret; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci cmb_data = kzalloc(sizeof(*cmb_data), GFP_KERNEL); 7708c2ecf20Sopenharmony_ci if (!cmb_data) 7718c2ecf20Sopenharmony_ci goto out_free; 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci cmb_data->last_block = kzalloc(sizeof(struct cmbe), GFP_KERNEL); 7748c2ecf20Sopenharmony_ci if (!cmb_data->last_block) 7758c2ecf20Sopenharmony_ci goto out_free; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci cmb_data->size = sizeof(*cmbe); 7788c2ecf20Sopenharmony_ci cmb_data->hw_block = cmbe; 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci spin_lock(&cmb_area.lock); 7818c2ecf20Sopenharmony_ci spin_lock_irq(cdev->ccwlock); 7828c2ecf20Sopenharmony_ci if (cdev->private->cmb) 7838c2ecf20Sopenharmony_ci goto out_unlock; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci cdev->private->cmb = cmb_data; 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci /* activate global measurement if this is the first channel */ 7888c2ecf20Sopenharmony_ci if (list_empty(&cmb_area.list)) 7898c2ecf20Sopenharmony_ci cmf_activate(NULL, CMF_ON); 7908c2ecf20Sopenharmony_ci list_add_tail(&cdev->private->cmb_list, &cmb_area.list); 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci spin_unlock_irq(cdev->ccwlock); 7938c2ecf20Sopenharmony_ci spin_unlock(&cmb_area.lock); 7948c2ecf20Sopenharmony_ci return 0; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ciout_unlock: 7978c2ecf20Sopenharmony_ci spin_unlock_irq(cdev->ccwlock); 7988c2ecf20Sopenharmony_ci spin_unlock(&cmb_area.lock); 7998c2ecf20Sopenharmony_ci ret = -EBUSY; 8008c2ecf20Sopenharmony_ciout_free: 8018c2ecf20Sopenharmony_ci if (cmb_data) 8028c2ecf20Sopenharmony_ci kfree(cmb_data->last_block); 8038c2ecf20Sopenharmony_ci kfree(cmb_data); 8048c2ecf20Sopenharmony_ci kmem_cache_free(cmbe_cache, cmbe); 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci return ret; 8078c2ecf20Sopenharmony_ci} 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_cistatic void free_cmbe(struct ccw_device *cdev) 8108c2ecf20Sopenharmony_ci{ 8118c2ecf20Sopenharmony_ci struct cmb_data *cmb_data; 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci spin_lock(&cmb_area.lock); 8148c2ecf20Sopenharmony_ci spin_lock_irq(cdev->ccwlock); 8158c2ecf20Sopenharmony_ci cmb_data = cdev->private->cmb; 8168c2ecf20Sopenharmony_ci cdev->private->cmb = NULL; 8178c2ecf20Sopenharmony_ci if (cmb_data) { 8188c2ecf20Sopenharmony_ci kfree(cmb_data->last_block); 8198c2ecf20Sopenharmony_ci kmem_cache_free(cmbe_cache, cmb_data->hw_block); 8208c2ecf20Sopenharmony_ci } 8218c2ecf20Sopenharmony_ci kfree(cmb_data); 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci /* deactivate global measurement if this is the last channel */ 8248c2ecf20Sopenharmony_ci list_del_init(&cdev->private->cmb_list); 8258c2ecf20Sopenharmony_ci if (list_empty(&cmb_area.list)) 8268c2ecf20Sopenharmony_ci cmf_activate(NULL, CMF_OFF); 8278c2ecf20Sopenharmony_ci spin_unlock_irq(cdev->ccwlock); 8288c2ecf20Sopenharmony_ci spin_unlock(&cmb_area.lock); 8298c2ecf20Sopenharmony_ci} 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_cistatic int set_cmbe(struct ccw_device *cdev, u32 mme) 8328c2ecf20Sopenharmony_ci{ 8338c2ecf20Sopenharmony_ci unsigned long mba; 8348c2ecf20Sopenharmony_ci struct cmb_data *cmb_data; 8358c2ecf20Sopenharmony_ci unsigned long flags; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci spin_lock_irqsave(cdev->ccwlock, flags); 8388c2ecf20Sopenharmony_ci if (!cdev->private->cmb) { 8398c2ecf20Sopenharmony_ci spin_unlock_irqrestore(cdev->ccwlock, flags); 8408c2ecf20Sopenharmony_ci return -EINVAL; 8418c2ecf20Sopenharmony_ci } 8428c2ecf20Sopenharmony_ci cmb_data = cdev->private->cmb; 8438c2ecf20Sopenharmony_ci mba = mme ? (unsigned long) cmb_data->hw_block : 0; 8448c2ecf20Sopenharmony_ci spin_unlock_irqrestore(cdev->ccwlock, flags); 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci return set_schib_wait(cdev, mme, 1, mba); 8478c2ecf20Sopenharmony_ci} 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_cistatic u64 read_cmbe(struct ccw_device *cdev, int index) 8508c2ecf20Sopenharmony_ci{ 8518c2ecf20Sopenharmony_ci struct cmb_data *cmb_data; 8528c2ecf20Sopenharmony_ci unsigned long flags; 8538c2ecf20Sopenharmony_ci struct cmbe *cmb; 8548c2ecf20Sopenharmony_ci u64 ret = 0; 8558c2ecf20Sopenharmony_ci u32 val; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci spin_lock_irqsave(cdev->ccwlock, flags); 8588c2ecf20Sopenharmony_ci cmb_data = cdev->private->cmb; 8598c2ecf20Sopenharmony_ci if (!cmb_data) 8608c2ecf20Sopenharmony_ci goto out; 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci cmb = cmb_data->hw_block; 8638c2ecf20Sopenharmony_ci switch (index) { 8648c2ecf20Sopenharmony_ci case avg_utilization: 8658c2ecf20Sopenharmony_ci ret = __cmb_utilization(cmb->device_connect_time, 8668c2ecf20Sopenharmony_ci cmb->function_pending_time, 8678c2ecf20Sopenharmony_ci cmb->device_disconnect_time, 8688c2ecf20Sopenharmony_ci cdev->private->cmb_start_time); 8698c2ecf20Sopenharmony_ci goto out; 8708c2ecf20Sopenharmony_ci case cmb_ssch_rsch_count: 8718c2ecf20Sopenharmony_ci ret = cmb->ssch_rsch_count; 8728c2ecf20Sopenharmony_ci goto out; 8738c2ecf20Sopenharmony_ci case cmb_sample_count: 8748c2ecf20Sopenharmony_ci ret = cmb->sample_count; 8758c2ecf20Sopenharmony_ci goto out; 8768c2ecf20Sopenharmony_ci case cmb_device_connect_time: 8778c2ecf20Sopenharmony_ci val = cmb->device_connect_time; 8788c2ecf20Sopenharmony_ci break; 8798c2ecf20Sopenharmony_ci case cmb_function_pending_time: 8808c2ecf20Sopenharmony_ci val = cmb->function_pending_time; 8818c2ecf20Sopenharmony_ci break; 8828c2ecf20Sopenharmony_ci case cmb_device_disconnect_time: 8838c2ecf20Sopenharmony_ci val = cmb->device_disconnect_time; 8848c2ecf20Sopenharmony_ci break; 8858c2ecf20Sopenharmony_ci case cmb_control_unit_queuing_time: 8868c2ecf20Sopenharmony_ci val = cmb->control_unit_queuing_time; 8878c2ecf20Sopenharmony_ci break; 8888c2ecf20Sopenharmony_ci case cmb_device_active_only_time: 8898c2ecf20Sopenharmony_ci val = cmb->device_active_only_time; 8908c2ecf20Sopenharmony_ci break; 8918c2ecf20Sopenharmony_ci case cmb_device_busy_time: 8928c2ecf20Sopenharmony_ci val = cmb->device_busy_time; 8938c2ecf20Sopenharmony_ci break; 8948c2ecf20Sopenharmony_ci case cmb_initial_command_response_time: 8958c2ecf20Sopenharmony_ci val = cmb->initial_command_response_time; 8968c2ecf20Sopenharmony_ci break; 8978c2ecf20Sopenharmony_ci default: 8988c2ecf20Sopenharmony_ci goto out; 8998c2ecf20Sopenharmony_ci } 9008c2ecf20Sopenharmony_ci ret = time_to_avg_nsec(val, cmb->sample_count); 9018c2ecf20Sopenharmony_ciout: 9028c2ecf20Sopenharmony_ci spin_unlock_irqrestore(cdev->ccwlock, flags); 9038c2ecf20Sopenharmony_ci return ret; 9048c2ecf20Sopenharmony_ci} 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_cistatic int readall_cmbe(struct ccw_device *cdev, struct cmbdata *data) 9078c2ecf20Sopenharmony_ci{ 9088c2ecf20Sopenharmony_ci struct cmbe *cmb; 9098c2ecf20Sopenharmony_ci struct cmb_data *cmb_data; 9108c2ecf20Sopenharmony_ci u64 time; 9118c2ecf20Sopenharmony_ci unsigned long flags; 9128c2ecf20Sopenharmony_ci int ret; 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci ret = cmf_cmb_copy_wait(cdev); 9158c2ecf20Sopenharmony_ci if (ret < 0) 9168c2ecf20Sopenharmony_ci return ret; 9178c2ecf20Sopenharmony_ci spin_lock_irqsave(cdev->ccwlock, flags); 9188c2ecf20Sopenharmony_ci cmb_data = cdev->private->cmb; 9198c2ecf20Sopenharmony_ci if (!cmb_data) { 9208c2ecf20Sopenharmony_ci ret = -ENODEV; 9218c2ecf20Sopenharmony_ci goto out; 9228c2ecf20Sopenharmony_ci } 9238c2ecf20Sopenharmony_ci if (cmb_data->last_update == 0) { 9248c2ecf20Sopenharmony_ci ret = -EAGAIN; 9258c2ecf20Sopenharmony_ci goto out; 9268c2ecf20Sopenharmony_ci } 9278c2ecf20Sopenharmony_ci time = cmb_data->last_update - cdev->private->cmb_start_time; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci memset (data, 0, sizeof(struct cmbdata)); 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci /* we only know values before device_busy_time */ 9328c2ecf20Sopenharmony_ci data->size = offsetof(struct cmbdata, device_busy_time); 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci data->elapsed_time = tod_to_ns(time); 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci cmb = cmb_data->last_block; 9378c2ecf20Sopenharmony_ci /* copy data to new structure */ 9388c2ecf20Sopenharmony_ci data->ssch_rsch_count = cmb->ssch_rsch_count; 9398c2ecf20Sopenharmony_ci data->sample_count = cmb->sample_count; 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci /* time fields are converted to nanoseconds while copying */ 9428c2ecf20Sopenharmony_ci data->device_connect_time = time_to_nsec(cmb->device_connect_time); 9438c2ecf20Sopenharmony_ci data->function_pending_time = time_to_nsec(cmb->function_pending_time); 9448c2ecf20Sopenharmony_ci data->device_disconnect_time = 9458c2ecf20Sopenharmony_ci time_to_nsec(cmb->device_disconnect_time); 9468c2ecf20Sopenharmony_ci data->control_unit_queuing_time 9478c2ecf20Sopenharmony_ci = time_to_nsec(cmb->control_unit_queuing_time); 9488c2ecf20Sopenharmony_ci data->device_active_only_time 9498c2ecf20Sopenharmony_ci = time_to_nsec(cmb->device_active_only_time); 9508c2ecf20Sopenharmony_ci data->device_busy_time = time_to_nsec(cmb->device_busy_time); 9518c2ecf20Sopenharmony_ci data->initial_command_response_time 9528c2ecf20Sopenharmony_ci = time_to_nsec(cmb->initial_command_response_time); 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci ret = 0; 9558c2ecf20Sopenharmony_ciout: 9568c2ecf20Sopenharmony_ci spin_unlock_irqrestore(cdev->ccwlock, flags); 9578c2ecf20Sopenharmony_ci return ret; 9588c2ecf20Sopenharmony_ci} 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_cistatic void reset_cmbe(struct ccw_device *cdev) 9618c2ecf20Sopenharmony_ci{ 9628c2ecf20Sopenharmony_ci cmf_generic_reset(cdev); 9638c2ecf20Sopenharmony_ci} 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_cistatic struct attribute_group cmf_attr_group_ext; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_cistatic struct cmb_operations cmbops_extended = { 9688c2ecf20Sopenharmony_ci .alloc = alloc_cmbe, 9698c2ecf20Sopenharmony_ci .free = free_cmbe, 9708c2ecf20Sopenharmony_ci .set = set_cmbe, 9718c2ecf20Sopenharmony_ci .read = read_cmbe, 9728c2ecf20Sopenharmony_ci .readall = readall_cmbe, 9738c2ecf20Sopenharmony_ci .reset = reset_cmbe, 9748c2ecf20Sopenharmony_ci .attr_group = &cmf_attr_group_ext, 9758c2ecf20Sopenharmony_ci}; 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_cistatic ssize_t cmb_show_attr(struct device *dev, char *buf, enum cmb_index idx) 9788c2ecf20Sopenharmony_ci{ 9798c2ecf20Sopenharmony_ci return sprintf(buf, "%lld\n", 9808c2ecf20Sopenharmony_ci (unsigned long long) cmf_read(to_ccwdev(dev), idx)); 9818c2ecf20Sopenharmony_ci} 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_cistatic ssize_t cmb_show_avg_sample_interval(struct device *dev, 9848c2ecf20Sopenharmony_ci struct device_attribute *attr, 9858c2ecf20Sopenharmony_ci char *buf) 9868c2ecf20Sopenharmony_ci{ 9878c2ecf20Sopenharmony_ci struct ccw_device *cdev = to_ccwdev(dev); 9888c2ecf20Sopenharmony_ci unsigned long count; 9898c2ecf20Sopenharmony_ci long interval; 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci count = cmf_read(cdev, cmb_sample_count); 9928c2ecf20Sopenharmony_ci spin_lock_irq(cdev->ccwlock); 9938c2ecf20Sopenharmony_ci if (count) { 9948c2ecf20Sopenharmony_ci interval = get_tod_clock() - cdev->private->cmb_start_time; 9958c2ecf20Sopenharmony_ci interval = tod_to_ns(interval); 9968c2ecf20Sopenharmony_ci interval /= count; 9978c2ecf20Sopenharmony_ci } else 9988c2ecf20Sopenharmony_ci interval = -1; 9998c2ecf20Sopenharmony_ci spin_unlock_irq(cdev->ccwlock); 10008c2ecf20Sopenharmony_ci return sprintf(buf, "%ld\n", interval); 10018c2ecf20Sopenharmony_ci} 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_cistatic ssize_t cmb_show_avg_utilization(struct device *dev, 10048c2ecf20Sopenharmony_ci struct device_attribute *attr, 10058c2ecf20Sopenharmony_ci char *buf) 10068c2ecf20Sopenharmony_ci{ 10078c2ecf20Sopenharmony_ci unsigned long u = cmf_read(to_ccwdev(dev), avg_utilization); 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci return sprintf(buf, "%02lu.%01lu%%\n", u / 10, u % 10); 10108c2ecf20Sopenharmony_ci} 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci#define cmf_attr(name) \ 10138c2ecf20Sopenharmony_cistatic ssize_t show_##name(struct device *dev, \ 10148c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) \ 10158c2ecf20Sopenharmony_ci{ return cmb_show_attr((dev), buf, cmb_##name); } \ 10168c2ecf20Sopenharmony_cistatic DEVICE_ATTR(name, 0444, show_##name, NULL); 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci#define cmf_attr_avg(name) \ 10198c2ecf20Sopenharmony_cistatic ssize_t show_avg_##name(struct device *dev, \ 10208c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) \ 10218c2ecf20Sopenharmony_ci{ return cmb_show_attr((dev), buf, cmb_##name); } \ 10228c2ecf20Sopenharmony_cistatic DEVICE_ATTR(avg_##name, 0444, show_avg_##name, NULL); 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_cicmf_attr(ssch_rsch_count); 10258c2ecf20Sopenharmony_cicmf_attr(sample_count); 10268c2ecf20Sopenharmony_cicmf_attr_avg(device_connect_time); 10278c2ecf20Sopenharmony_cicmf_attr_avg(function_pending_time); 10288c2ecf20Sopenharmony_cicmf_attr_avg(device_disconnect_time); 10298c2ecf20Sopenharmony_cicmf_attr_avg(control_unit_queuing_time); 10308c2ecf20Sopenharmony_cicmf_attr_avg(device_active_only_time); 10318c2ecf20Sopenharmony_cicmf_attr_avg(device_busy_time); 10328c2ecf20Sopenharmony_cicmf_attr_avg(initial_command_response_time); 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_cistatic DEVICE_ATTR(avg_sample_interval, 0444, cmb_show_avg_sample_interval, 10358c2ecf20Sopenharmony_ci NULL); 10368c2ecf20Sopenharmony_cistatic DEVICE_ATTR(avg_utilization, 0444, cmb_show_avg_utilization, NULL); 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_cistatic struct attribute *cmf_attributes[] = { 10398c2ecf20Sopenharmony_ci &dev_attr_avg_sample_interval.attr, 10408c2ecf20Sopenharmony_ci &dev_attr_avg_utilization.attr, 10418c2ecf20Sopenharmony_ci &dev_attr_ssch_rsch_count.attr, 10428c2ecf20Sopenharmony_ci &dev_attr_sample_count.attr, 10438c2ecf20Sopenharmony_ci &dev_attr_avg_device_connect_time.attr, 10448c2ecf20Sopenharmony_ci &dev_attr_avg_function_pending_time.attr, 10458c2ecf20Sopenharmony_ci &dev_attr_avg_device_disconnect_time.attr, 10468c2ecf20Sopenharmony_ci &dev_attr_avg_control_unit_queuing_time.attr, 10478c2ecf20Sopenharmony_ci &dev_attr_avg_device_active_only_time.attr, 10488c2ecf20Sopenharmony_ci NULL, 10498c2ecf20Sopenharmony_ci}; 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_cistatic struct attribute_group cmf_attr_group = { 10528c2ecf20Sopenharmony_ci .name = "cmf", 10538c2ecf20Sopenharmony_ci .attrs = cmf_attributes, 10548c2ecf20Sopenharmony_ci}; 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_cistatic struct attribute *cmf_attributes_ext[] = { 10578c2ecf20Sopenharmony_ci &dev_attr_avg_sample_interval.attr, 10588c2ecf20Sopenharmony_ci &dev_attr_avg_utilization.attr, 10598c2ecf20Sopenharmony_ci &dev_attr_ssch_rsch_count.attr, 10608c2ecf20Sopenharmony_ci &dev_attr_sample_count.attr, 10618c2ecf20Sopenharmony_ci &dev_attr_avg_device_connect_time.attr, 10628c2ecf20Sopenharmony_ci &dev_attr_avg_function_pending_time.attr, 10638c2ecf20Sopenharmony_ci &dev_attr_avg_device_disconnect_time.attr, 10648c2ecf20Sopenharmony_ci &dev_attr_avg_control_unit_queuing_time.attr, 10658c2ecf20Sopenharmony_ci &dev_attr_avg_device_active_only_time.attr, 10668c2ecf20Sopenharmony_ci &dev_attr_avg_device_busy_time.attr, 10678c2ecf20Sopenharmony_ci &dev_attr_avg_initial_command_response_time.attr, 10688c2ecf20Sopenharmony_ci NULL, 10698c2ecf20Sopenharmony_ci}; 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_cistatic struct attribute_group cmf_attr_group_ext = { 10728c2ecf20Sopenharmony_ci .name = "cmf", 10738c2ecf20Sopenharmony_ci .attrs = cmf_attributes_ext, 10748c2ecf20Sopenharmony_ci}; 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_cistatic ssize_t cmb_enable_show(struct device *dev, 10778c2ecf20Sopenharmony_ci struct device_attribute *attr, 10788c2ecf20Sopenharmony_ci char *buf) 10798c2ecf20Sopenharmony_ci{ 10808c2ecf20Sopenharmony_ci struct ccw_device *cdev = to_ccwdev(dev); 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", cmf_enabled(cdev)); 10838c2ecf20Sopenharmony_ci} 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_cistatic ssize_t cmb_enable_store(struct device *dev, 10868c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *buf, 10878c2ecf20Sopenharmony_ci size_t c) 10888c2ecf20Sopenharmony_ci{ 10898c2ecf20Sopenharmony_ci struct ccw_device *cdev = to_ccwdev(dev); 10908c2ecf20Sopenharmony_ci unsigned long val; 10918c2ecf20Sopenharmony_ci int ret; 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci ret = kstrtoul(buf, 16, &val); 10948c2ecf20Sopenharmony_ci if (ret) 10958c2ecf20Sopenharmony_ci return ret; 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci switch (val) { 10988c2ecf20Sopenharmony_ci case 0: 10998c2ecf20Sopenharmony_ci ret = disable_cmf(cdev); 11008c2ecf20Sopenharmony_ci break; 11018c2ecf20Sopenharmony_ci case 1: 11028c2ecf20Sopenharmony_ci ret = enable_cmf(cdev); 11038c2ecf20Sopenharmony_ci break; 11048c2ecf20Sopenharmony_ci default: 11058c2ecf20Sopenharmony_ci ret = -EINVAL; 11068c2ecf20Sopenharmony_ci } 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci return ret ? ret : c; 11098c2ecf20Sopenharmony_ci} 11108c2ecf20Sopenharmony_ciDEVICE_ATTR_RW(cmb_enable); 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ciint ccw_set_cmf(struct ccw_device *cdev, int enable) 11138c2ecf20Sopenharmony_ci{ 11148c2ecf20Sopenharmony_ci return cmbops->set(cdev, enable ? 2 : 0); 11158c2ecf20Sopenharmony_ci} 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci/** 11188c2ecf20Sopenharmony_ci * enable_cmf() - switch on the channel measurement for a specific device 11198c2ecf20Sopenharmony_ci * @cdev: The ccw device to be enabled 11208c2ecf20Sopenharmony_ci * 11218c2ecf20Sopenharmony_ci * Enable channel measurements for @cdev. If this is called on a device 11228c2ecf20Sopenharmony_ci * for which channel measurement is already enabled a reset of the 11238c2ecf20Sopenharmony_ci * measurement data is triggered. 11248c2ecf20Sopenharmony_ci * Returns: %0 for success or a negative error value. 11258c2ecf20Sopenharmony_ci * Context: 11268c2ecf20Sopenharmony_ci * non-atomic 11278c2ecf20Sopenharmony_ci */ 11288c2ecf20Sopenharmony_ciint enable_cmf(struct ccw_device *cdev) 11298c2ecf20Sopenharmony_ci{ 11308c2ecf20Sopenharmony_ci int ret = 0; 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci device_lock(&cdev->dev); 11338c2ecf20Sopenharmony_ci if (cmf_enabled(cdev)) { 11348c2ecf20Sopenharmony_ci cmbops->reset(cdev); 11358c2ecf20Sopenharmony_ci goto out_unlock; 11368c2ecf20Sopenharmony_ci } 11378c2ecf20Sopenharmony_ci get_device(&cdev->dev); 11388c2ecf20Sopenharmony_ci ret = cmbops->alloc(cdev); 11398c2ecf20Sopenharmony_ci if (ret) 11408c2ecf20Sopenharmony_ci goto out; 11418c2ecf20Sopenharmony_ci cmbops->reset(cdev); 11428c2ecf20Sopenharmony_ci ret = sysfs_create_group(&cdev->dev.kobj, cmbops->attr_group); 11438c2ecf20Sopenharmony_ci if (ret) { 11448c2ecf20Sopenharmony_ci cmbops->free(cdev); 11458c2ecf20Sopenharmony_ci goto out; 11468c2ecf20Sopenharmony_ci } 11478c2ecf20Sopenharmony_ci ret = cmbops->set(cdev, 2); 11488c2ecf20Sopenharmony_ci if (ret) { 11498c2ecf20Sopenharmony_ci sysfs_remove_group(&cdev->dev.kobj, cmbops->attr_group); 11508c2ecf20Sopenharmony_ci cmbops->free(cdev); 11518c2ecf20Sopenharmony_ci } 11528c2ecf20Sopenharmony_ciout: 11538c2ecf20Sopenharmony_ci if (ret) 11548c2ecf20Sopenharmony_ci put_device(&cdev->dev); 11558c2ecf20Sopenharmony_ciout_unlock: 11568c2ecf20Sopenharmony_ci device_unlock(&cdev->dev); 11578c2ecf20Sopenharmony_ci return ret; 11588c2ecf20Sopenharmony_ci} 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci/** 11618c2ecf20Sopenharmony_ci * __disable_cmf() - switch off the channel measurement for a specific device 11628c2ecf20Sopenharmony_ci * @cdev: The ccw device to be disabled 11638c2ecf20Sopenharmony_ci * 11648c2ecf20Sopenharmony_ci * Returns: %0 for success or a negative error value. 11658c2ecf20Sopenharmony_ci * 11668c2ecf20Sopenharmony_ci * Context: 11678c2ecf20Sopenharmony_ci * non-atomic, device_lock() held. 11688c2ecf20Sopenharmony_ci */ 11698c2ecf20Sopenharmony_ciint __disable_cmf(struct ccw_device *cdev) 11708c2ecf20Sopenharmony_ci{ 11718c2ecf20Sopenharmony_ci int ret; 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci ret = cmbops->set(cdev, 0); 11748c2ecf20Sopenharmony_ci if (ret) 11758c2ecf20Sopenharmony_ci return ret; 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci sysfs_remove_group(&cdev->dev.kobj, cmbops->attr_group); 11788c2ecf20Sopenharmony_ci cmbops->free(cdev); 11798c2ecf20Sopenharmony_ci put_device(&cdev->dev); 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci return ret; 11828c2ecf20Sopenharmony_ci} 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci/** 11858c2ecf20Sopenharmony_ci * disable_cmf() - switch off the channel measurement for a specific device 11868c2ecf20Sopenharmony_ci * @cdev: The ccw device to be disabled 11878c2ecf20Sopenharmony_ci * 11888c2ecf20Sopenharmony_ci * Returns: %0 for success or a negative error value. 11898c2ecf20Sopenharmony_ci * 11908c2ecf20Sopenharmony_ci * Context: 11918c2ecf20Sopenharmony_ci * non-atomic 11928c2ecf20Sopenharmony_ci */ 11938c2ecf20Sopenharmony_ciint disable_cmf(struct ccw_device *cdev) 11948c2ecf20Sopenharmony_ci{ 11958c2ecf20Sopenharmony_ci int ret; 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci device_lock(&cdev->dev); 11988c2ecf20Sopenharmony_ci ret = __disable_cmf(cdev); 11998c2ecf20Sopenharmony_ci device_unlock(&cdev->dev); 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci return ret; 12028c2ecf20Sopenharmony_ci} 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci/** 12058c2ecf20Sopenharmony_ci * cmf_read() - read one value from the current channel measurement block 12068c2ecf20Sopenharmony_ci * @cdev: the channel to be read 12078c2ecf20Sopenharmony_ci * @index: the index of the value to be read 12088c2ecf20Sopenharmony_ci * 12098c2ecf20Sopenharmony_ci * Returns: The value read or %0 if the value cannot be read. 12108c2ecf20Sopenharmony_ci * 12118c2ecf20Sopenharmony_ci * Context: 12128c2ecf20Sopenharmony_ci * any 12138c2ecf20Sopenharmony_ci */ 12148c2ecf20Sopenharmony_ciu64 cmf_read(struct ccw_device *cdev, int index) 12158c2ecf20Sopenharmony_ci{ 12168c2ecf20Sopenharmony_ci return cmbops->read(cdev, index); 12178c2ecf20Sopenharmony_ci} 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci/** 12208c2ecf20Sopenharmony_ci * cmf_readall() - read the current channel measurement block 12218c2ecf20Sopenharmony_ci * @cdev: the channel to be read 12228c2ecf20Sopenharmony_ci * @data: a pointer to a data block that will be filled 12238c2ecf20Sopenharmony_ci * 12248c2ecf20Sopenharmony_ci * Returns: %0 on success, a negative error value otherwise. 12258c2ecf20Sopenharmony_ci * 12268c2ecf20Sopenharmony_ci * Context: 12278c2ecf20Sopenharmony_ci * any 12288c2ecf20Sopenharmony_ci */ 12298c2ecf20Sopenharmony_ciint cmf_readall(struct ccw_device *cdev, struct cmbdata *data) 12308c2ecf20Sopenharmony_ci{ 12318c2ecf20Sopenharmony_ci return cmbops->readall(cdev, data); 12328c2ecf20Sopenharmony_ci} 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci/* Reenable cmf when a disconnected device becomes available again. */ 12358c2ecf20Sopenharmony_ciint cmf_reenable(struct ccw_device *cdev) 12368c2ecf20Sopenharmony_ci{ 12378c2ecf20Sopenharmony_ci cmbops->reset(cdev); 12388c2ecf20Sopenharmony_ci return cmbops->set(cdev, 2); 12398c2ecf20Sopenharmony_ci} 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci/** 12428c2ecf20Sopenharmony_ci * cmf_reactivate() - reactivate measurement block updates 12438c2ecf20Sopenharmony_ci * 12448c2ecf20Sopenharmony_ci * Use this during resume from hibernate. 12458c2ecf20Sopenharmony_ci */ 12468c2ecf20Sopenharmony_civoid cmf_reactivate(void) 12478c2ecf20Sopenharmony_ci{ 12488c2ecf20Sopenharmony_ci spin_lock(&cmb_area.lock); 12498c2ecf20Sopenharmony_ci if (!list_empty(&cmb_area.list)) 12508c2ecf20Sopenharmony_ci cmf_activate(cmb_area.mem, CMF_ON); 12518c2ecf20Sopenharmony_ci spin_unlock(&cmb_area.lock); 12528c2ecf20Sopenharmony_ci} 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_cistatic int __init init_cmbe(void) 12558c2ecf20Sopenharmony_ci{ 12568c2ecf20Sopenharmony_ci cmbe_cache = kmem_cache_create("cmbe_cache", sizeof(struct cmbe), 12578c2ecf20Sopenharmony_ci __alignof__(struct cmbe), 0, NULL); 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci return cmbe_cache ? 0 : -ENOMEM; 12608c2ecf20Sopenharmony_ci} 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_cistatic int __init init_cmf(void) 12638c2ecf20Sopenharmony_ci{ 12648c2ecf20Sopenharmony_ci char *format_string; 12658c2ecf20Sopenharmony_ci char *detect_string; 12668c2ecf20Sopenharmony_ci int ret; 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci /* 12698c2ecf20Sopenharmony_ci * If the user did not give a parameter, see if we are running on a 12708c2ecf20Sopenharmony_ci * machine supporting extended measurement blocks, otherwise fall back 12718c2ecf20Sopenharmony_ci * to basic mode. 12728c2ecf20Sopenharmony_ci */ 12738c2ecf20Sopenharmony_ci if (format == CMF_AUTODETECT) { 12748c2ecf20Sopenharmony_ci if (!css_general_characteristics.ext_mb) { 12758c2ecf20Sopenharmony_ci format = CMF_BASIC; 12768c2ecf20Sopenharmony_ci } else { 12778c2ecf20Sopenharmony_ci format = CMF_EXTENDED; 12788c2ecf20Sopenharmony_ci } 12798c2ecf20Sopenharmony_ci detect_string = "autodetected"; 12808c2ecf20Sopenharmony_ci } else { 12818c2ecf20Sopenharmony_ci detect_string = "parameter"; 12828c2ecf20Sopenharmony_ci } 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci switch (format) { 12858c2ecf20Sopenharmony_ci case CMF_BASIC: 12868c2ecf20Sopenharmony_ci format_string = "basic"; 12878c2ecf20Sopenharmony_ci cmbops = &cmbops_basic; 12888c2ecf20Sopenharmony_ci break; 12898c2ecf20Sopenharmony_ci case CMF_EXTENDED: 12908c2ecf20Sopenharmony_ci format_string = "extended"; 12918c2ecf20Sopenharmony_ci cmbops = &cmbops_extended; 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci ret = init_cmbe(); 12948c2ecf20Sopenharmony_ci if (ret) 12958c2ecf20Sopenharmony_ci return ret; 12968c2ecf20Sopenharmony_ci break; 12978c2ecf20Sopenharmony_ci default: 12988c2ecf20Sopenharmony_ci return -EINVAL; 12998c2ecf20Sopenharmony_ci } 13008c2ecf20Sopenharmony_ci pr_info("Channel measurement facility initialized using format " 13018c2ecf20Sopenharmony_ci "%s (mode %s)\n", format_string, detect_string); 13028c2ecf20Sopenharmony_ci return 0; 13038c2ecf20Sopenharmony_ci} 13048c2ecf20Sopenharmony_cidevice_initcall(init_cmf); 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(enable_cmf); 13078c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(disable_cmf); 13088c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cmf_read); 13098c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cmf_readall); 1310