]>
Commit | Line | Data |
---|---|---|
724117b7 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
1da177e4 | 2 | /* |
1da177e4 LT |
3 | * Linux on zSeries Channel Measurement Facility support |
4 | * | |
a53c8fab | 5 | * Copyright IBM Corp. 2000, 2006 |
1da177e4 | 6 | * |
94bb0633 CH |
7 | * Authors: Arnd Bergmann <arndb@de.ibm.com> |
8 | * Cornelia Huck <cornelia.huck@de.ibm.com> | |
1da177e4 LT |
9 | * |
10 | * original idea from Natarajan Krishnaswami <nkrishna@us.ibm.com> | |
1da177e4 LT |
11 | */ |
12 | ||
e6d5a428 ME |
13 | #define KMSG_COMPONENT "cio" |
14 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt | |
15 | ||
1da177e4 LT |
16 | #include <linux/bootmem.h> |
17 | #include <linux/device.h> | |
18 | #include <linux/init.h> | |
19 | #include <linux/list.h> | |
a00f761f | 20 | #include <linux/export.h> |
1da177e4 | 21 | #include <linux/moduleparam.h> |
4e57b681 | 22 | #include <linux/slab.h> |
1aae0560 | 23 | #include <linux/timex.h> /* get_tod_clock() */ |
1da177e4 LT |
24 | |
25 | #include <asm/ccwdev.h> | |
26 | #include <asm/cio.h> | |
27 | #include <asm/cmb.h> | |
4e57b681 | 28 | #include <asm/div64.h> |
1da177e4 LT |
29 | |
30 | #include "cio.h" | |
31 | #include "css.h" | |
32 | #include "device.h" | |
33 | #include "ioasm.h" | |
34 | #include "chsc.h" | |
35 | ||
fc5019c5 CH |
36 | /* |
37 | * parameter to enable cmf during boot, possible uses are: | |
1da177e4 LT |
38 | * "s390cmf" -- enable cmf and allocate 2 MB of ram so measuring can be |
39 | * used on any subchannel | |
40 | * "s390cmf=<num>" -- enable cmf and allocate enough memory to measure | |
41 | * <num> subchannel, where <num> is an integer | |
42 | * between 1 and 65535, default is 1024 | |
43 | */ | |
44 | #define ARGSTRING "s390cmf" | |
45 | ||
46 | /* indices for READCMB */ | |
47 | enum cmb_index { | |
cb09b356 | 48 | avg_utilization = -1, |
1da177e4 | 49 | /* basic and exended format: */ |
cb09b356 | 50 | cmb_ssch_rsch_count = 0, |
1da177e4 LT |
51 | cmb_sample_count, |
52 | cmb_device_connect_time, | |
53 | cmb_function_pending_time, | |
54 | cmb_device_disconnect_time, | |
55 | cmb_control_unit_queuing_time, | |
56 | cmb_device_active_only_time, | |
57 | /* extended format only: */ | |
58 | cmb_device_busy_time, | |
59 | cmb_initial_command_response_time, | |
60 | }; | |
61 | ||
62 | /** | |
63 | * enum cmb_format - types of supported measurement block formats | |
64 | * | |
65 | * @CMF_BASIC: traditional channel measurement blocks supported | |
c0208716 | 66 | * by all machines that we run on |
1da177e4 | 67 | * @CMF_EXTENDED: improved format that was introduced with the z990 |
c0208716 CH |
68 | * machine |
69 | * @CMF_AUTODETECT: default: use extended format when running on a machine | |
70 | * supporting extended format, otherwise fall back to | |
71 | * basic format | |
72 | */ | |
1da177e4 LT |
73 | enum cmb_format { |
74 | CMF_BASIC, | |
75 | CMF_EXTENDED, | |
76 | CMF_AUTODETECT = -1, | |
77 | }; | |
fc5019c5 | 78 | |
c0208716 | 79 | /* |
1da177e4 LT |
80 | * format - actual format for all measurement blocks |
81 | * | |
82 | * The format module parameter can be set to a value of 0 (zero) | |
83 | * or 1, indicating basic or extended format as described for | |
84 | * enum cmb_format. | |
85 | */ | |
86 | static int format = CMF_AUTODETECT; | |
69116f27 | 87 | module_param(format, bint, 0444); |
1da177e4 LT |
88 | |
89 | /** | |
90 | * struct cmb_operations - functions to use depending on cmb_format | |
91 | * | |
94bb0633 CH |
92 | * Most of these functions operate on a struct ccw_device. There is only |
93 | * one instance of struct cmb_operations because the format of the measurement | |
94 | * data is guaranteed to be the same for every ccw_device. | |
1da177e4 LT |
95 | * |
96 | * @alloc: allocate memory for a channel measurement block, | |
97 | * either with the help of a special pool or with kmalloc | |
98 | * @free: free memory allocated with @alloc | |
99 | * @set: enable or disable measurement | |
c0208716 | 100 | * @read: read a measurement entry at an index |
1da177e4 LT |
101 | * @readall: read a measurement block in a common format |
102 | * @reset: clear the data in the associated measurement block and | |
103 | * reset its time stamp | |
104 | */ | |
105 | struct cmb_operations { | |
fc5019c5 CH |
106 | int (*alloc) (struct ccw_device *); |
107 | void (*free) (struct ccw_device *); | |
108 | int (*set) (struct ccw_device *, u32); | |
109 | u64 (*read) (struct ccw_device *, int); | |
110 | int (*readall)(struct ccw_device *, struct cmbdata *); | |
111 | void (*reset) (struct ccw_device *); | |
c0208716 | 112 | /* private: */ |
1da177e4 LT |
113 | struct attribute_group *attr_group; |
114 | }; | |
115 | static struct cmb_operations *cmbops; | |
116 | ||
94bb0633 CH |
117 | struct cmb_data { |
118 | void *hw_block; /* Pointer to block updated by hardware */ | |
119 | void *last_block; /* Last changed block copied from hardware block */ | |
120 | int size; /* Size of hw_block and last_block */ | |
121 | unsigned long long last_update; /* when last_block was updated */ | |
122 | }; | |
123 | ||
fc5019c5 CH |
124 | /* |
125 | * Our user interface is designed in terms of nanoseconds, | |
1da177e4 | 126 | * while the hardware measures total times in its own |
fc5019c5 CH |
127 | * unit. |
128 | */ | |
1da177e4 LT |
129 | static inline u64 time_to_nsec(u32 value) |
130 | { | |
131 | return ((u64)value) * 128000ull; | |
132 | } | |
133 | ||
134 | /* | |
135 | * Users are usually interested in average times, | |
136 | * not accumulated time. | |
137 | * This also helps us with atomicity problems | |
138 | * when reading sinlge values. | |
139 | */ | |
140 | static inline u64 time_to_avg_nsec(u32 value, u32 count) | |
141 | { | |
142 | u64 ret; | |
143 | ||
144 | /* no samples yet, avoid division by 0 */ | |
145 | if (count == 0) | |
146 | return 0; | |
147 | ||
96de0e25 | 148 | /* value comes in units of 128 µsec */ |
1da177e4 LT |
149 | ret = time_to_nsec(value); |
150 | do_div(ret, count); | |
151 | ||
152 | return ret; | |
153 | } | |
154 | ||
7b4ff87c HC |
155 | #define CMF_OFF 0 |
156 | #define CMF_ON 2 | |
157 | ||
fc5019c5 CH |
158 | /* |
159 | * Activate or deactivate the channel monitor. When area is NULL, | |
1da177e4 LT |
160 | * the monitor is deactivated. The channel monitor needs to |
161 | * be active in order to measure subchannels, which also need | |
fc5019c5 CH |
162 | * to be enabled. |
163 | */ | |
164 | static inline void cmf_activate(void *area, unsigned int onoff) | |
1da177e4 LT |
165 | { |
166 | register void * __gpr2 asm("2"); | |
167 | register long __gpr1 asm("1"); | |
168 | ||
169 | __gpr2 = area; | |
7b4ff87c | 170 | __gpr1 = onoff; |
1da177e4 LT |
171 | /* activate channel measurement */ |
172 | asm("schm" : : "d" (__gpr2), "d" (__gpr1) ); | |
173 | } | |
174 | ||
fc5019c5 CH |
175 | static int set_schib(struct ccw_device *cdev, u32 mme, int mbfc, |
176 | unsigned long address) | |
1da177e4 | 177 | { |
a6ef1565 SO |
178 | struct subchannel *sch = to_subchannel(cdev->dev.parent); |
179 | int ret; | |
1da177e4 | 180 | |
13952ec1 SO |
181 | sch->config.mme = mme; |
182 | sch->config.mbfc = mbfc; | |
183 | /* address can be either a block address or a block index */ | |
184 | if (mbfc) | |
185 | sch->config.mba = address; | |
186 | else | |
187 | sch->config.mbi = address; | |
1da177e4 | 188 | |
a6ef1565 SO |
189 | ret = cio_commit_config(sch); |
190 | if (!mme && ret == -ENODEV) { | |
191 | /* | |
192 | * The task was to disable measurement block updates but | |
193 | * the subchannel is already gone. Report success. | |
194 | */ | |
195 | ret = 0; | |
196 | } | |
197 | return ret; | |
1da177e4 LT |
198 | } |
199 | ||
200 | struct set_schib_struct { | |
201 | u32 mme; | |
202 | int mbfc; | |
203 | unsigned long address; | |
204 | wait_queue_head_t wait; | |
205 | int ret; | |
206 | }; | |
207 | ||
94bb0633 | 208 | #define CMF_PENDING 1 |
adc69b4d | 209 | #define SET_SCHIB_TIMEOUT (10 * HZ) |
94bb0633 | 210 | |
1da177e4 | 211 | static int set_schib_wait(struct ccw_device *cdev, u32 mme, |
eeec1e43 | 212 | int mbfc, unsigned long address) |
1da177e4 | 213 | { |
eeec1e43 SO |
214 | struct set_schib_struct set_data; |
215 | int ret = -ENODEV; | |
1da177e4 LT |
216 | |
217 | spin_lock_irq(cdev->ccwlock); | |
eeec1e43 | 218 | if (!cdev->private->cmb) |
94bb0633 | 219 | goto out; |
94bb0633 CH |
220 | |
221 | ret = set_schib(cdev, mme, mbfc, address); | |
222 | if (ret != -EBUSY) | |
eeec1e43 | 223 | goto out; |
1da177e4 | 224 | |
eeec1e43 SO |
225 | /* if the device is not online, don't even try again */ |
226 | if (cdev->private->state != DEV_STATE_ONLINE) | |
227 | goto out; | |
228 | ||
229 | init_waitqueue_head(&set_data.wait); | |
230 | set_data.mme = mme; | |
231 | set_data.mbfc = mbfc; | |
232 | set_data.address = address; | |
233 | set_data.ret = CMF_PENDING; | |
94bb0633 | 234 | |
1da177e4 | 235 | cdev->private->state = DEV_STATE_CMFCHANGE; |
eeec1e43 | 236 | cdev->private->cmb_wait = &set_data; |
1da177e4 | 237 | spin_unlock_irq(cdev->ccwlock); |
adc69b4d | 238 | |
eeec1e43 SO |
239 | ret = wait_event_interruptible_timeout(set_data.wait, |
240 | set_data.ret != CMF_PENDING, | |
adc69b4d SO |
241 | SET_SCHIB_TIMEOUT); |
242 | spin_lock_irq(cdev->ccwlock); | |
243 | if (ret <= 0) { | |
eeec1e43 SO |
244 | if (set_data.ret == CMF_PENDING) { |
245 | set_data.ret = (ret == 0) ? -ETIME : ret; | |
1da177e4 LT |
246 | if (cdev->private->state == DEV_STATE_CMFCHANGE) |
247 | cdev->private->state = DEV_STATE_ONLINE; | |
248 | } | |
1da177e4 | 249 | } |
94bb0633 | 250 | cdev->private->cmb_wait = NULL; |
eeec1e43 | 251 | ret = set_data.ret; |
94bb0633 | 252 | out: |
1da177e4 | 253 | spin_unlock_irq(cdev->ccwlock); |
94bb0633 | 254 | return ret; |
1da177e4 LT |
255 | } |
256 | ||
257 | void retry_set_schib(struct ccw_device *cdev) | |
258 | { | |
eeec1e43 | 259 | struct set_schib_struct *set_data = cdev->private->cmb_wait; |
94bb0633 | 260 | |
eeec1e43 | 261 | if (!set_data) |
94bb0633 | 262 | return; |
eeec1e43 | 263 | |
94bb0633 CH |
264 | set_data->ret = set_schib(cdev, set_data->mme, set_data->mbfc, |
265 | set_data->address); | |
266 | wake_up(&set_data->wait); | |
94bb0633 CH |
267 | } |
268 | ||
269 | static int cmf_copy_block(struct ccw_device *cdev) | |
270 | { | |
81b050b5 | 271 | struct subchannel *sch = to_subchannel(cdev->dev.parent); |
94bb0633 | 272 | struct cmb_data *cmb_data; |
81b050b5 | 273 | void *hw_block; |
94bb0633 | 274 | |
cdb912a4 | 275 | if (cio_update_schib(sch)) |
94bb0633 CH |
276 | return -ENODEV; |
277 | ||
23d805b6 | 278 | if (scsw_fctl(&sch->schib.scsw) & SCSW_FCTL_START_FUNC) { |
94bb0633 | 279 | /* Don't copy if a start function is in progress. */ |
23d805b6 PO |
280 | if ((!(scsw_actl(&sch->schib.scsw) & SCSW_ACTL_SUSPENDED)) && |
281 | (scsw_actl(&sch->schib.scsw) & | |
94bb0633 | 282 | (SCSW_ACTL_DEVACT | SCSW_ACTL_SCHACT)) && |
23d805b6 | 283 | (!(scsw_stctl(&sch->schib.scsw) & SCSW_STCTL_SEC_STATUS))) |
94bb0633 CH |
284 | return -EBUSY; |
285 | } | |
286 | cmb_data = cdev->private->cmb; | |
45bf4b96 | 287 | hw_block = cmb_data->hw_block; |
81b050b5 | 288 | memcpy(cmb_data->last_block, hw_block, cmb_data->size); |
1aae0560 | 289 | cmb_data->last_update = get_tod_clock(); |
94bb0633 CH |
290 | return 0; |
291 | } | |
292 | ||
293 | struct copy_block_struct { | |
294 | wait_queue_head_t wait; | |
295 | int ret; | |
94bb0633 CH |
296 | }; |
297 | ||
94bb0633 CH |
298 | static int cmf_cmb_copy_wait(struct ccw_device *cdev) |
299 | { | |
60f3eac3 SO |
300 | struct copy_block_struct copy_block; |
301 | int ret = -ENODEV; | |
94bb0633 | 302 | |
60f3eac3 SO |
303 | spin_lock_irq(cdev->ccwlock); |
304 | if (!cdev->private->cmb) | |
94bb0633 | 305 | goto out; |
94bb0633 CH |
306 | |
307 | ret = cmf_copy_block(cdev); | |
308 | if (ret != -EBUSY) | |
60f3eac3 | 309 | goto out; |
94bb0633 | 310 | |
60f3eac3 SO |
311 | if (cdev->private->state != DEV_STATE_ONLINE) |
312 | goto out; | |
313 | ||
314 | init_waitqueue_head(©_block.wait); | |
315 | copy_block.ret = CMF_PENDING; | |
94bb0633 CH |
316 | |
317 | cdev->private->state = DEV_STATE_CMFUPDATE; | |
60f3eac3 SO |
318 | cdev->private->cmb_wait = ©_block; |
319 | spin_unlock_irq(cdev->ccwlock); | |
320 | ||
321 | ret = wait_event_interruptible(copy_block.wait, | |
322 | copy_block.ret != CMF_PENDING); | |
323 | spin_lock_irq(cdev->ccwlock); | |
324 | if (ret) { | |
325 | if (copy_block.ret == CMF_PENDING) { | |
326 | copy_block.ret = -ERESTARTSYS; | |
94bb0633 CH |
327 | if (cdev->private->state == DEV_STATE_CMFUPDATE) |
328 | cdev->private->state = DEV_STATE_ONLINE; | |
329 | } | |
94bb0633 | 330 | } |
94bb0633 | 331 | cdev->private->cmb_wait = NULL; |
60f3eac3 | 332 | ret = copy_block.ret; |
94bb0633 | 333 | out: |
60f3eac3 | 334 | spin_unlock_irq(cdev->ccwlock); |
94bb0633 CH |
335 | return ret; |
336 | } | |
337 | ||
338 | void cmf_retry_copy_block(struct ccw_device *cdev) | |
339 | { | |
60f3eac3 | 340 | struct copy_block_struct *copy_block = cdev->private->cmb_wait; |
1da177e4 | 341 | |
60f3eac3 | 342 | if (!copy_block) |
1da177e4 | 343 | return; |
60f3eac3 | 344 | |
94bb0633 CH |
345 | copy_block->ret = cmf_copy_block(cdev); |
346 | wake_up(©_block->wait); | |
94bb0633 CH |
347 | } |
348 | ||
349 | static void cmf_generic_reset(struct ccw_device *cdev) | |
350 | { | |
351 | struct cmb_data *cmb_data; | |
352 | ||
353 | spin_lock_irq(cdev->ccwlock); | |
354 | cmb_data = cdev->private->cmb; | |
355 | if (cmb_data) { | |
356 | memset(cmb_data->last_block, 0, cmb_data->size); | |
357 | /* | |
358 | * Need to reset hw block as well to make the hardware start | |
359 | * from 0 again. | |
360 | */ | |
45bf4b96 | 361 | memset(cmb_data->hw_block, 0, cmb_data->size); |
94bb0633 CH |
362 | cmb_data->last_update = 0; |
363 | } | |
1aae0560 | 364 | cdev->private->cmb_start_time = get_tod_clock(); |
94bb0633 | 365 | spin_unlock_irq(cdev->ccwlock); |
1da177e4 LT |
366 | } |
367 | ||
368 | /** | |
369 | * struct cmb_area - container for global cmb data | |
370 | * | |
371 | * @mem: pointer to CMBs (only in basic measurement mode) | |
372 | * @list: contains a linked list of all subchannels | |
c0208716 | 373 | * @num_channels: number of channels to be measured |
1da177e4 LT |
374 | * @lock: protect concurrent access to @mem and @list |
375 | */ | |
376 | struct cmb_area { | |
377 | struct cmb *mem; | |
378 | struct list_head list; | |
379 | int num_channels; | |
380 | spinlock_t lock; | |
381 | }; | |
382 | ||
383 | static struct cmb_area cmb_area = { | |
cb629a01 | 384 | .lock = __SPIN_LOCK_UNLOCKED(cmb_area.lock), |
1da177e4 LT |
385 | .list = LIST_HEAD_INIT(cmb_area.list), |
386 | .num_channels = 1024, | |
387 | }; | |
388 | ||
1da177e4 LT |
389 | /* ****** old style CMB handling ********/ |
390 | ||
fc5019c5 | 391 | /* |
1da177e4 LT |
392 | * Basic channel measurement blocks are allocated in one contiguous |
393 | * block of memory, which can not be moved as long as any channel | |
394 | * is active. Therefore, a maximum number of subchannels needs to | |
395 | * be defined somewhere. This is a module parameter, defaulting to | |
af901ca1 | 396 | * a reasonable value of 1024, or 32 kb of memory. |
1da177e4 | 397 | * Current kernels don't allow kmalloc with more than 128kb, so the |
fc5019c5 | 398 | * maximum is 4096. |
1da177e4 LT |
399 | */ |
400 | ||
401 | module_param_named(maxchannels, cmb_area.num_channels, uint, 0444); | |
402 | ||
403 | /** | |
404 | * struct cmb - basic channel measurement block | |
c0208716 CH |
405 | * @ssch_rsch_count: number of ssch and rsch |
406 | * @sample_count: number of samples | |
407 | * @device_connect_time: time of device connect | |
408 | * @function_pending_time: time of function pending | |
409 | * @device_disconnect_time: time of device disconnect | |
410 | * @control_unit_queuing_time: time of control unit queuing | |
411 | * @device_active_only_time: time of device active only | |
412 | * @reserved: unused in basic measurement mode | |
413 | * | |
414 | * The measurement block as used by the hardware. The fields are described | |
415 | * further in z/Architecture Principles of Operation, chapter 17. | |
1da177e4 | 416 | * |
c0208716 CH |
417 | * The cmb area made up from these blocks must be a contiguous array and may |
418 | * not be reallocated or freed. | |
1da177e4 LT |
419 | * Only one cmb area can be present in the system. |
420 | */ | |
421 | struct cmb { | |
422 | u16 ssch_rsch_count; | |
423 | u16 sample_count; | |
424 | u32 device_connect_time; | |
425 | u32 function_pending_time; | |
426 | u32 device_disconnect_time; | |
427 | u32 control_unit_queuing_time; | |
428 | u32 device_active_only_time; | |
429 | u32 reserved[2]; | |
430 | }; | |
431 | ||
fc5019c5 CH |
432 | /* |
433 | * Insert a single device into the cmb_area list. | |
434 | * Called with cmb_area.lock held from alloc_cmb. | |
1da177e4 | 435 | */ |
4d284cac HC |
436 | static int alloc_cmb_single(struct ccw_device *cdev, |
437 | struct cmb_data *cmb_data) | |
1da177e4 LT |
438 | { |
439 | struct cmb *cmb; | |
440 | struct ccw_device_private *node; | |
441 | int ret; | |
442 | ||
443 | spin_lock_irq(cdev->ccwlock); | |
444 | if (!list_empty(&cdev->private->cmb_list)) { | |
445 | ret = -EBUSY; | |
446 | goto out; | |
447 | } | |
448 | ||
fc5019c5 CH |
449 | /* |
450 | * Find first unused cmb in cmb_area.mem. | |
451 | * This is a little tricky: cmb_area.list | |
452 | * remains sorted by ->cmb->hw_data pointers. | |
453 | */ | |
1da177e4 LT |
454 | cmb = cmb_area.mem; |
455 | list_for_each_entry(node, &cmb_area.list, cmb_list) { | |
94bb0633 CH |
456 | struct cmb_data *data; |
457 | data = node->cmb; | |
458 | if ((struct cmb*)data->hw_block > cmb) | |
1da177e4 LT |
459 | break; |
460 | cmb++; | |
461 | } | |
462 | if (cmb - cmb_area.mem >= cmb_area.num_channels) { | |
463 | ret = -ENOMEM; | |
464 | goto out; | |
465 | } | |
466 | ||
467 | /* insert new cmb */ | |
468 | list_add_tail(&cdev->private->cmb_list, &node->cmb_list); | |
94bb0633 CH |
469 | cmb_data->hw_block = cmb; |
470 | cdev->private->cmb = cmb_data; | |
1da177e4 LT |
471 | ret = 0; |
472 | out: | |
473 | spin_unlock_irq(cdev->ccwlock); | |
474 | return ret; | |
475 | } | |
476 | ||
fc5019c5 | 477 | static int alloc_cmb(struct ccw_device *cdev) |
1da177e4 LT |
478 | { |
479 | int ret; | |
480 | struct cmb *mem; | |
481 | ssize_t size; | |
94bb0633 CH |
482 | struct cmb_data *cmb_data; |
483 | ||
484 | /* Allocate private cmb_data. */ | |
485 | cmb_data = kzalloc(sizeof(struct cmb_data), GFP_KERNEL); | |
486 | if (!cmb_data) | |
487 | return -ENOMEM; | |
1da177e4 | 488 | |
94bb0633 CH |
489 | cmb_data->last_block = kzalloc(sizeof(struct cmb), GFP_KERNEL); |
490 | if (!cmb_data->last_block) { | |
491 | kfree(cmb_data); | |
492 | return -ENOMEM; | |
493 | } | |
494 | cmb_data->size = sizeof(struct cmb); | |
1da177e4 LT |
495 | spin_lock(&cmb_area.lock); |
496 | ||
497 | if (!cmb_area.mem) { | |
498 | /* there is no user yet, so we need a new area */ | |
499 | size = sizeof(struct cmb) * cmb_area.num_channels; | |
500 | WARN_ON(!list_empty(&cmb_area.list)); | |
501 | ||
502 | spin_unlock(&cmb_area.lock); | |
503 | mem = (void*)__get_free_pages(GFP_KERNEL | GFP_DMA, | |
504 | get_order(size)); | |
505 | spin_lock(&cmb_area.lock); | |
506 | ||
507 | if (cmb_area.mem) { | |
508 | /* ok, another thread was faster */ | |
509 | free_pages((unsigned long)mem, get_order(size)); | |
510 | } else if (!mem) { | |
511 | /* no luck */ | |
512 | ret = -ENOMEM; | |
513 | goto out; | |
514 | } else { | |
515 | /* everything ok */ | |
516 | memset(mem, 0, size); | |
517 | cmb_area.mem = mem; | |
7b4ff87c | 518 | cmf_activate(cmb_area.mem, CMF_ON); |
1da177e4 LT |
519 | } |
520 | } | |
521 | ||
522 | /* do the actual allocation */ | |
94bb0633 | 523 | ret = alloc_cmb_single(cdev, cmb_data); |
1da177e4 LT |
524 | out: |
525 | spin_unlock(&cmb_area.lock); | |
94bb0633 CH |
526 | if (ret) { |
527 | kfree(cmb_data->last_block); | |
528 | kfree(cmb_data); | |
529 | } | |
1da177e4 LT |
530 | return ret; |
531 | } | |
532 | ||
94bb0633 | 533 | static void free_cmb(struct ccw_device *cdev) |
1da177e4 LT |
534 | { |
535 | struct ccw_device_private *priv; | |
94bb0633 | 536 | struct cmb_data *cmb_data; |
1da177e4 LT |
537 | |
538 | spin_lock(&cmb_area.lock); | |
539 | spin_lock_irq(cdev->ccwlock); | |
540 | ||
94bb0633 | 541 | priv = cdev->private; |
94bb0633 | 542 | cmb_data = priv->cmb; |
1da177e4 | 543 | priv->cmb = NULL; |
94bb0633 CH |
544 | if (cmb_data) |
545 | kfree(cmb_data->last_block); | |
546 | kfree(cmb_data); | |
1da177e4 LT |
547 | list_del_init(&priv->cmb_list); |
548 | ||
549 | if (list_empty(&cmb_area.list)) { | |
550 | ssize_t size; | |
551 | size = sizeof(struct cmb) * cmb_area.num_channels; | |
7b4ff87c | 552 | cmf_activate(NULL, CMF_OFF); |
1da177e4 LT |
553 | free_pages((unsigned long)cmb_area.mem, get_order(size)); |
554 | cmb_area.mem = NULL; | |
555 | } | |
1da177e4 LT |
556 | spin_unlock_irq(cdev->ccwlock); |
557 | spin_unlock(&cmb_area.lock); | |
558 | } | |
559 | ||
94bb0633 | 560 | static int set_cmb(struct ccw_device *cdev, u32 mme) |
1da177e4 LT |
561 | { |
562 | u16 offset; | |
94bb0633 CH |
563 | struct cmb_data *cmb_data; |
564 | unsigned long flags; | |
1da177e4 | 565 | |
94bb0633 CH |
566 | spin_lock_irqsave(cdev->ccwlock, flags); |
567 | if (!cdev->private->cmb) { | |
568 | spin_unlock_irqrestore(cdev->ccwlock, flags); | |
1da177e4 | 569 | return -EINVAL; |
94bb0633 CH |
570 | } |
571 | cmb_data = cdev->private->cmb; | |
572 | offset = mme ? (struct cmb *)cmb_data->hw_block - cmb_area.mem : 0; | |
573 | spin_unlock_irqrestore(cdev->ccwlock, flags); | |
1da177e4 LT |
574 | |
575 | return set_schib_wait(cdev, mme, 0, offset); | |
576 | } | |
577 | ||
cb09b356 SO |
578 | /* calculate utilization in 0.1 percent units */ |
579 | static u64 __cmb_utilization(u64 device_connect_time, u64 function_pending_time, | |
580 | u64 device_disconnect_time, u64 start_time) | |
581 | { | |
582 | u64 utilization, elapsed_time; | |
583 | ||
584 | utilization = time_to_nsec(device_connect_time + | |
585 | function_pending_time + | |
586 | device_disconnect_time); | |
587 | ||
588 | elapsed_time = get_tod_clock() - start_time; | |
589 | elapsed_time = tod_to_ns(elapsed_time); | |
590 | elapsed_time /= 1000; | |
591 | ||
592 | return elapsed_time ? (utilization / elapsed_time) : 0; | |
593 | } | |
594 | ||
fc5019c5 | 595 | static u64 read_cmb(struct ccw_device *cdev, int index) |
1da177e4 | 596 | { |
d4d287e8 SO |
597 | struct cmb_data *cmb_data; |
598 | unsigned long flags; | |
94bb0633 | 599 | struct cmb *cmb; |
cb09b356 | 600 | u64 ret = 0; |
1da177e4 LT |
601 | u32 val; |
602 | ||
603 | spin_lock_irqsave(cdev->ccwlock, flags); | |
d4d287e8 SO |
604 | cmb_data = cdev->private->cmb; |
605 | if (!cmb_data) | |
94bb0633 | 606 | goto out; |
1da177e4 | 607 | |
d4d287e8 | 608 | cmb = cmb_data->hw_block; |
1da177e4 | 609 | switch (index) { |
cb09b356 SO |
610 | case avg_utilization: |
611 | ret = __cmb_utilization(cmb->device_connect_time, | |
612 | cmb->function_pending_time, | |
613 | cmb->device_disconnect_time, | |
614 | cdev->private->cmb_start_time); | |
615 | goto out; | |
1da177e4 | 616 | case cmb_ssch_rsch_count: |
94bb0633 CH |
617 | ret = cmb->ssch_rsch_count; |
618 | goto out; | |
1da177e4 | 619 | case cmb_sample_count: |
94bb0633 CH |
620 | ret = cmb->sample_count; |
621 | goto out; | |
1da177e4 | 622 | case cmb_device_connect_time: |
94bb0633 | 623 | val = cmb->device_connect_time; |
1da177e4 LT |
624 | break; |
625 | case cmb_function_pending_time: | |
94bb0633 | 626 | val = cmb->function_pending_time; |
1da177e4 LT |
627 | break; |
628 | case cmb_device_disconnect_time: | |
94bb0633 | 629 | val = cmb->device_disconnect_time; |
1da177e4 LT |
630 | break; |
631 | case cmb_control_unit_queuing_time: | |
94bb0633 | 632 | val = cmb->control_unit_queuing_time; |
1da177e4 LT |
633 | break; |
634 | case cmb_device_active_only_time: | |
94bb0633 | 635 | val = cmb->device_active_only_time; |
1da177e4 LT |
636 | break; |
637 | default: | |
94bb0633 | 638 | goto out; |
1da177e4 | 639 | } |
94bb0633 CH |
640 | ret = time_to_avg_nsec(val, cmb->sample_count); |
641 | out: | |
642 | spin_unlock_irqrestore(cdev->ccwlock, flags); | |
643 | return ret; | |
1da177e4 LT |
644 | } |
645 | ||
fc5019c5 | 646 | static int readall_cmb(struct ccw_device *cdev, struct cmbdata *data) |
1da177e4 | 647 | { |
94bb0633 CH |
648 | struct cmb *cmb; |
649 | struct cmb_data *cmb_data; | |
1da177e4 | 650 | u64 time; |
94bb0633 CH |
651 | unsigned long flags; |
652 | int ret; | |
1da177e4 | 653 | |
94bb0633 CH |
654 | ret = cmf_cmb_copy_wait(cdev); |
655 | if (ret < 0) | |
656 | return ret; | |
1da177e4 | 657 | spin_lock_irqsave(cdev->ccwlock, flags); |
94bb0633 CH |
658 | cmb_data = cdev->private->cmb; |
659 | if (!cmb_data) { | |
660 | ret = -ENODEV; | |
661 | goto out; | |
1da177e4 | 662 | } |
94bb0633 CH |
663 | if (cmb_data->last_update == 0) { |
664 | ret = -EAGAIN; | |
665 | goto out; | |
666 | } | |
667 | cmb = cmb_data->last_block; | |
668 | time = cmb_data->last_update - cdev->private->cmb_start_time; | |
1da177e4 LT |
669 | |
670 | memset(data, 0, sizeof(struct cmbdata)); | |
671 | ||
672 | /* we only know values before device_busy_time */ | |
673 | data->size = offsetof(struct cmbdata, device_busy_time); | |
674 | ||
08c6df97 | 675 | data->elapsed_time = tod_to_ns(time); |
1da177e4 LT |
676 | |
677 | /* copy data to new structure */ | |
94bb0633 CH |
678 | data->ssch_rsch_count = cmb->ssch_rsch_count; |
679 | data->sample_count = cmb->sample_count; | |
1da177e4 LT |
680 | |
681 | /* time fields are converted to nanoseconds while copying */ | |
94bb0633 CH |
682 | data->device_connect_time = time_to_nsec(cmb->device_connect_time); |
683 | data->function_pending_time = time_to_nsec(cmb->function_pending_time); | |
684 | data->device_disconnect_time = | |
685 | time_to_nsec(cmb->device_disconnect_time); | |
1da177e4 | 686 | data->control_unit_queuing_time |
94bb0633 | 687 | = time_to_nsec(cmb->control_unit_queuing_time); |
1da177e4 | 688 | data->device_active_only_time |
94bb0633 CH |
689 | = time_to_nsec(cmb->device_active_only_time); |
690 | ret = 0; | |
691 | out: | |
692 | spin_unlock_irqrestore(cdev->ccwlock, flags); | |
693 | return ret; | |
694 | } | |
1da177e4 | 695 | |
94bb0633 CH |
696 | static void reset_cmb(struct ccw_device *cdev) |
697 | { | |
698 | cmf_generic_reset(cdev); | |
1da177e4 LT |
699 | } |
700 | ||
0f5d050c SO |
701 | static int cmf_enabled(struct ccw_device *cdev) |
702 | { | |
703 | int enabled; | |
704 | ||
705 | spin_lock_irq(cdev->ccwlock); | |
706 | enabled = !!cdev->private->cmb; | |
707 | spin_unlock_irq(cdev->ccwlock); | |
708 | ||
709 | return enabled; | |
710 | } | |
711 | ||
1da177e4 LT |
712 | static struct attribute_group cmf_attr_group; |
713 | ||
714 | static struct cmb_operations cmbops_basic = { | |
715 | .alloc = alloc_cmb, | |
716 | .free = free_cmb, | |
717 | .set = set_cmb, | |
718 | .read = read_cmb, | |
719 | .readall = readall_cmb, | |
720 | .reset = reset_cmb, | |
721 | .attr_group = &cmf_attr_group, | |
722 | }; | |
364c8558 | 723 | |
1da177e4 LT |
724 | /* ******** extended cmb handling ********/ |
725 | ||
726 | /** | |
727 | * struct cmbe - extended channel measurement block | |
c0208716 CH |
728 | * @ssch_rsch_count: number of ssch and rsch |
729 | * @sample_count: number of samples | |
730 | * @device_connect_time: time of device connect | |
731 | * @function_pending_time: time of function pending | |
732 | * @device_disconnect_time: time of device disconnect | |
733 | * @control_unit_queuing_time: time of control unit queuing | |
734 | * @device_active_only_time: time of device active only | |
735 | * @device_busy_time: time of device busy | |
736 | * @initial_command_response_time: initial command response time | |
737 | * @reserved: unused | |
1da177e4 | 738 | * |
c0208716 CH |
739 | * The measurement block as used by the hardware. May be in any 64 bit physical |
740 | * location. | |
741 | * The fields are described further in z/Architecture Principles of Operation, | |
1da177e4 LT |
742 | * third edition, chapter 17. |
743 | */ | |
744 | struct cmbe { | |
745 | u32 ssch_rsch_count; | |
746 | u32 sample_count; | |
747 | u32 device_connect_time; | |
748 | u32 function_pending_time; | |
749 | u32 device_disconnect_time; | |
750 | u32 control_unit_queuing_time; | |
751 | u32 device_active_only_time; | |
752 | u32 device_busy_time; | |
753 | u32 initial_command_response_time; | |
754 | u32 reserved[7]; | |
45bf4b96 | 755 | } __packed __aligned(64); |
1da177e4 | 756 | |
45bf4b96 | 757 | static struct kmem_cache *cmbe_cache; |
1da177e4 | 758 | |
fc5019c5 | 759 | static int alloc_cmbe(struct ccw_device *cdev) |
1da177e4 | 760 | { |
94bb0633 | 761 | struct cmb_data *cmb_data; |
616503d1 SO |
762 | struct cmbe *cmbe; |
763 | int ret = -ENOMEM; | |
94bb0633 | 764 | |
45bf4b96 | 765 | cmbe = kmem_cache_zalloc(cmbe_cache, GFP_KERNEL); |
1da177e4 | 766 | if (!cmbe) |
616503d1 SO |
767 | return ret; |
768 | ||
45bf4b96 | 769 | cmb_data = kzalloc(sizeof(*cmb_data), GFP_KERNEL); |
616503d1 | 770 | if (!cmb_data) |
94bb0633 | 771 | goto out_free; |
616503d1 | 772 | |
94bb0633 | 773 | cmb_data->last_block = kzalloc(sizeof(struct cmbe), GFP_KERNEL); |
616503d1 | 774 | if (!cmb_data->last_block) |
94bb0633 | 775 | goto out_free; |
616503d1 | 776 | |
45bf4b96 | 777 | cmb_data->size = sizeof(*cmbe); |
94bb0633 | 778 | cmb_data->hw_block = cmbe; |
616503d1 SO |
779 | |
780 | spin_lock(&cmb_area.lock); | |
781 | spin_lock_irq(cdev->ccwlock); | |
782 | if (cdev->private->cmb) | |
783 | goto out_unlock; | |
784 | ||
94bb0633 | 785 | cdev->private->cmb = cmb_data; |
1da177e4 LT |
786 | |
787 | /* activate global measurement if this is the first channel */ | |
1da177e4 | 788 | if (list_empty(&cmb_area.list)) |
7b4ff87c | 789 | cmf_activate(NULL, CMF_ON); |
1da177e4 | 790 | list_add_tail(&cdev->private->cmb_list, &cmb_area.list); |
1da177e4 | 791 | |
616503d1 SO |
792 | spin_unlock_irq(cdev->ccwlock); |
793 | spin_unlock(&cmb_area.lock); | |
1da177e4 | 794 | return 0; |
616503d1 SO |
795 | |
796 | out_unlock: | |
797 | spin_unlock_irq(cdev->ccwlock); | |
798 | spin_unlock(&cmb_area.lock); | |
799 | ret = -EBUSY; | |
94bb0633 CH |
800 | out_free: |
801 | if (cmb_data) | |
802 | kfree(cmb_data->last_block); | |
803 | kfree(cmb_data); | |
45bf4b96 SO |
804 | kmem_cache_free(cmbe_cache, cmbe); |
805 | ||
94bb0633 | 806 | return ret; |
1da177e4 LT |
807 | } |
808 | ||
fc5019c5 | 809 | static void free_cmbe(struct ccw_device *cdev) |
1da177e4 | 810 | { |
94bb0633 CH |
811 | struct cmb_data *cmb_data; |
812 | ||
616503d1 | 813 | spin_lock(&cmb_area.lock); |
1da177e4 | 814 | spin_lock_irq(cdev->ccwlock); |
94bb0633 | 815 | cmb_data = cdev->private->cmb; |
1da177e4 | 816 | cdev->private->cmb = NULL; |
a5e9ca57 | 817 | if (cmb_data) { |
94bb0633 | 818 | kfree(cmb_data->last_block); |
45bf4b96 | 819 | kmem_cache_free(cmbe_cache, cmb_data->hw_block); |
a5e9ca57 | 820 | } |
94bb0633 | 821 | kfree(cmb_data); |
1da177e4 LT |
822 | |
823 | /* deactivate global measurement if this is the last channel */ | |
1da177e4 LT |
824 | list_del_init(&cdev->private->cmb_list); |
825 | if (list_empty(&cmb_area.list)) | |
7b4ff87c | 826 | cmf_activate(NULL, CMF_OFF); |
616503d1 | 827 | spin_unlock_irq(cdev->ccwlock); |
1da177e4 LT |
828 | spin_unlock(&cmb_area.lock); |
829 | } | |
830 | ||
94bb0633 | 831 | static int set_cmbe(struct ccw_device *cdev, u32 mme) |
1da177e4 LT |
832 | { |
833 | unsigned long mba; | |
94bb0633 CH |
834 | struct cmb_data *cmb_data; |
835 | unsigned long flags; | |
1da177e4 | 836 | |
94bb0633 CH |
837 | spin_lock_irqsave(cdev->ccwlock, flags); |
838 | if (!cdev->private->cmb) { | |
839 | spin_unlock_irqrestore(cdev->ccwlock, flags); | |
1da177e4 | 840 | return -EINVAL; |
94bb0633 CH |
841 | } |
842 | cmb_data = cdev->private->cmb; | |
45bf4b96 | 843 | mba = mme ? (unsigned long) cmb_data->hw_block : 0; |
94bb0633 | 844 | spin_unlock_irqrestore(cdev->ccwlock, flags); |
1da177e4 LT |
845 | |
846 | return set_schib_wait(cdev, mme, 1, mba); | |
847 | } | |
848 | ||
fc5019c5 | 849 | static u64 read_cmbe(struct ccw_device *cdev, int index) |
1da177e4 | 850 | { |
94bb0633 | 851 | struct cmb_data *cmb_data; |
94bb0633 | 852 | unsigned long flags; |
d4d287e8 | 853 | struct cmbe *cmb; |
cb09b356 | 854 | u64 ret = 0; |
d4d287e8 | 855 | u32 val; |
1da177e4 | 856 | |
94bb0633 CH |
857 | spin_lock_irqsave(cdev->ccwlock, flags); |
858 | cmb_data = cdev->private->cmb; | |
d4d287e8 | 859 | if (!cmb_data) |
94bb0633 | 860 | goto out; |
1da177e4 | 861 | |
d4d287e8 | 862 | cmb = cmb_data->hw_block; |
1da177e4 | 863 | switch (index) { |
cb09b356 SO |
864 | case avg_utilization: |
865 | ret = __cmb_utilization(cmb->device_connect_time, | |
866 | cmb->function_pending_time, | |
867 | cmb->device_disconnect_time, | |
868 | cdev->private->cmb_start_time); | |
869 | goto out; | |
1da177e4 | 870 | case cmb_ssch_rsch_count: |
94bb0633 CH |
871 | ret = cmb->ssch_rsch_count; |
872 | goto out; | |
1da177e4 | 873 | case cmb_sample_count: |
94bb0633 CH |
874 | ret = cmb->sample_count; |
875 | goto out; | |
1da177e4 | 876 | case cmb_device_connect_time: |
94bb0633 | 877 | val = cmb->device_connect_time; |
1da177e4 LT |
878 | break; |
879 | case cmb_function_pending_time: | |
94bb0633 | 880 | val = cmb->function_pending_time; |
1da177e4 LT |
881 | break; |
882 | case cmb_device_disconnect_time: | |
94bb0633 | 883 | val = cmb->device_disconnect_time; |
1da177e4 LT |
884 | break; |
885 | case cmb_control_unit_queuing_time: | |
94bb0633 | 886 | val = cmb->control_unit_queuing_time; |
1da177e4 LT |
887 | break; |
888 | case cmb_device_active_only_time: | |
94bb0633 | 889 | val = cmb->device_active_only_time; |
1da177e4 LT |
890 | break; |
891 | case cmb_device_busy_time: | |
94bb0633 | 892 | val = cmb->device_busy_time; |
1da177e4 LT |
893 | break; |
894 | case cmb_initial_command_response_time: | |
94bb0633 | 895 | val = cmb->initial_command_response_time; |
1da177e4 LT |
896 | break; |
897 | default: | |
94bb0633 | 898 | goto out; |
1da177e4 | 899 | } |
94bb0633 CH |
900 | ret = time_to_avg_nsec(val, cmb->sample_count); |
901 | out: | |
902 | spin_unlock_irqrestore(cdev->ccwlock, flags); | |
903 | return ret; | |
1da177e4 LT |
904 | } |
905 | ||
fc5019c5 | 906 | static int readall_cmbe(struct ccw_device *cdev, struct cmbdata *data) |
1da177e4 | 907 | { |
94bb0633 CH |
908 | struct cmbe *cmb; |
909 | struct cmb_data *cmb_data; | |
1da177e4 | 910 | u64 time; |
94bb0633 CH |
911 | unsigned long flags; |
912 | int ret; | |
1da177e4 | 913 | |
94bb0633 CH |
914 | ret = cmf_cmb_copy_wait(cdev); |
915 | if (ret < 0) | |
916 | return ret; | |
1da177e4 | 917 | spin_lock_irqsave(cdev->ccwlock, flags); |
94bb0633 CH |
918 | cmb_data = cdev->private->cmb; |
919 | if (!cmb_data) { | |
920 | ret = -ENODEV; | |
921 | goto out; | |
1da177e4 | 922 | } |
94bb0633 CH |
923 | if (cmb_data->last_update == 0) { |
924 | ret = -EAGAIN; | |
925 | goto out; | |
926 | } | |
927 | time = cmb_data->last_update - cdev->private->cmb_start_time; | |
1da177e4 LT |
928 | |
929 | memset (data, 0, sizeof(struct cmbdata)); | |
930 | ||
931 | /* we only know values before device_busy_time */ | |
932 | data->size = offsetof(struct cmbdata, device_busy_time); | |
933 | ||
08c6df97 | 934 | data->elapsed_time = tod_to_ns(time); |
1da177e4 | 935 | |
94bb0633 | 936 | cmb = cmb_data->last_block; |
1da177e4 | 937 | /* copy data to new structure */ |
94bb0633 CH |
938 | data->ssch_rsch_count = cmb->ssch_rsch_count; |
939 | data->sample_count = cmb->sample_count; | |
1da177e4 LT |
940 | |
941 | /* time fields are converted to nanoseconds while copying */ | |
94bb0633 CH |
942 | data->device_connect_time = time_to_nsec(cmb->device_connect_time); |
943 | data->function_pending_time = time_to_nsec(cmb->function_pending_time); | |
944 | data->device_disconnect_time = | |
945 | time_to_nsec(cmb->device_disconnect_time); | |
1da177e4 | 946 | data->control_unit_queuing_time |
94bb0633 | 947 | = time_to_nsec(cmb->control_unit_queuing_time); |
1da177e4 | 948 | data->device_active_only_time |
94bb0633 CH |
949 | = time_to_nsec(cmb->device_active_only_time); |
950 | data->device_busy_time = time_to_nsec(cmb->device_busy_time); | |
1da177e4 | 951 | data->initial_command_response_time |
94bb0633 | 952 | = time_to_nsec(cmb->initial_command_response_time); |
1da177e4 | 953 | |
94bb0633 CH |
954 | ret = 0; |
955 | out: | |
956 | spin_unlock_irqrestore(cdev->ccwlock, flags); | |
957 | return ret; | |
1da177e4 LT |
958 | } |
959 | ||
94bb0633 | 960 | static void reset_cmbe(struct ccw_device *cdev) |
1da177e4 | 961 | { |
94bb0633 CH |
962 | cmf_generic_reset(cdev); |
963 | } | |
964 | ||
1da177e4 LT |
965 | static struct attribute_group cmf_attr_group_ext; |
966 | ||
967 | static struct cmb_operations cmbops_extended = { | |
968 | .alloc = alloc_cmbe, | |
969 | .free = free_cmbe, | |
970 | .set = set_cmbe, | |
971 | .read = read_cmbe, | |
972 | .readall = readall_cmbe, | |
973 | .reset = reset_cmbe, | |
974 | .attr_group = &cmf_attr_group_ext, | |
975 | }; | |
1da177e4 | 976 | |
fc5019c5 | 977 | static ssize_t cmb_show_attr(struct device *dev, char *buf, enum cmb_index idx) |
1da177e4 LT |
978 | { |
979 | return sprintf(buf, "%lld\n", | |
980 | (unsigned long long) cmf_read(to_ccwdev(dev), idx)); | |
981 | } | |
982 | ||
fc5019c5 CH |
983 | static ssize_t cmb_show_avg_sample_interval(struct device *dev, |
984 | struct device_attribute *attr, | |
985 | char *buf) | |
1da177e4 | 986 | { |
d4d287e8 | 987 | struct ccw_device *cdev = to_ccwdev(dev); |
1da177e4 | 988 | unsigned long count; |
d4d287e8 | 989 | long interval; |
1da177e4 | 990 | |
1da177e4 | 991 | count = cmf_read(cdev, cmb_sample_count); |
94bb0633 | 992 | spin_lock_irq(cdev->ccwlock); |
94bb0633 | 993 | if (count) { |
d4d287e8 | 994 | interval = get_tod_clock() - cdev->private->cmb_start_time; |
08c6df97 | 995 | interval = tod_to_ns(interval); |
1da177e4 | 996 | interval /= count; |
94bb0633 | 997 | } else |
1da177e4 | 998 | interval = -1; |
94bb0633 | 999 | spin_unlock_irq(cdev->ccwlock); |
1da177e4 LT |
1000 | return sprintf(buf, "%ld\n", interval); |
1001 | } | |
1002 | ||
fc5019c5 CH |
1003 | static ssize_t cmb_show_avg_utilization(struct device *dev, |
1004 | struct device_attribute *attr, | |
1005 | char *buf) | |
1da177e4 | 1006 | { |
cb09b356 | 1007 | unsigned long u = cmf_read(to_ccwdev(dev), avg_utilization); |
1da177e4 | 1008 | |
cb09b356 | 1009 | return sprintf(buf, "%02lu.%01lu%%\n", u / 10, u % 10); |
1da177e4 LT |
1010 | } |
1011 | ||
1012 | #define cmf_attr(name) \ | |
fc5019c5 CH |
1013 | static ssize_t show_##name(struct device *dev, \ |
1014 | struct device_attribute *attr, char *buf) \ | |
1015 | { return cmb_show_attr((dev), buf, cmb_##name); } \ | |
1016 | static DEVICE_ATTR(name, 0444, show_##name, NULL); | |
1da177e4 LT |
1017 | |
1018 | #define cmf_attr_avg(name) \ | |
fc5019c5 CH |
1019 | static ssize_t show_avg_##name(struct device *dev, \ |
1020 | struct device_attribute *attr, char *buf) \ | |
1021 | { return cmb_show_attr((dev), buf, cmb_##name); } \ | |
1022 | static DEVICE_ATTR(avg_##name, 0444, show_avg_##name, NULL); | |
1da177e4 LT |
1023 | |
1024 | cmf_attr(ssch_rsch_count); | |
1025 | cmf_attr(sample_count); | |
1026 | cmf_attr_avg(device_connect_time); | |
1027 | cmf_attr_avg(function_pending_time); | |
1028 | cmf_attr_avg(device_disconnect_time); | |
1029 | cmf_attr_avg(control_unit_queuing_time); | |
1030 | cmf_attr_avg(device_active_only_time); | |
1031 | cmf_attr_avg(device_busy_time); | |
1032 | cmf_attr_avg(initial_command_response_time); | |
1033 | ||
fc5019c5 CH |
1034 | static DEVICE_ATTR(avg_sample_interval, 0444, cmb_show_avg_sample_interval, |
1035 | NULL); | |
1da177e4 LT |
1036 | static DEVICE_ATTR(avg_utilization, 0444, cmb_show_avg_utilization, NULL); |
1037 | ||
1038 | static struct attribute *cmf_attributes[] = { | |
1039 | &dev_attr_avg_sample_interval.attr, | |
1040 | &dev_attr_avg_utilization.attr, | |
1041 | &dev_attr_ssch_rsch_count.attr, | |
1042 | &dev_attr_sample_count.attr, | |
1043 | &dev_attr_avg_device_connect_time.attr, | |
1044 | &dev_attr_avg_function_pending_time.attr, | |
1045 | &dev_attr_avg_device_disconnect_time.attr, | |
1046 | &dev_attr_avg_control_unit_queuing_time.attr, | |
1047 | &dev_attr_avg_device_active_only_time.attr, | |
d2c993d8 | 1048 | NULL, |
1da177e4 LT |
1049 | }; |
1050 | ||
1051 | static struct attribute_group cmf_attr_group = { | |
1052 | .name = "cmf", | |
1053 | .attrs = cmf_attributes, | |
1054 | }; | |
1055 | ||
1056 | static struct attribute *cmf_attributes_ext[] = { | |
1057 | &dev_attr_avg_sample_interval.attr, | |
1058 | &dev_attr_avg_utilization.attr, | |
1059 | &dev_attr_ssch_rsch_count.attr, | |
1060 | &dev_attr_sample_count.attr, | |
1061 | &dev_attr_avg_device_connect_time.attr, | |
1062 | &dev_attr_avg_function_pending_time.attr, | |
1063 | &dev_attr_avg_device_disconnect_time.attr, | |
1064 | &dev_attr_avg_control_unit_queuing_time.attr, | |
1065 | &dev_attr_avg_device_active_only_time.attr, | |
1066 | &dev_attr_avg_device_busy_time.attr, | |
1067 | &dev_attr_avg_initial_command_response_time.attr, | |
d2c993d8 | 1068 | NULL, |
1da177e4 LT |
1069 | }; |
1070 | ||
1071 | static struct attribute_group cmf_attr_group_ext = { | |
1072 | .name = "cmf", | |
1073 | .attrs = cmf_attributes_ext, | |
1074 | }; | |
1075 | ||
fc5019c5 CH |
1076 | static ssize_t cmb_enable_show(struct device *dev, |
1077 | struct device_attribute *attr, | |
1078 | char *buf) | |
1da177e4 | 1079 | { |
279b8f9a | 1080 | struct ccw_device *cdev = to_ccwdev(dev); |
279b8f9a | 1081 | |
0f5d050c | 1082 | return sprintf(buf, "%d\n", cmf_enabled(cdev)); |
1da177e4 LT |
1083 | } |
1084 | ||
fc5019c5 CH |
1085 | static ssize_t cmb_enable_store(struct device *dev, |
1086 | struct device_attribute *attr, const char *buf, | |
1087 | size_t c) | |
1da177e4 | 1088 | { |
279b8f9a | 1089 | struct ccw_device *cdev = to_ccwdev(dev); |
2f972202 | 1090 | unsigned long val; |
279b8f9a | 1091 | int ret; |
2f972202 | 1092 | |
0178722b | 1093 | ret = kstrtoul(buf, 16, &val); |
2f972202 CH |
1094 | if (ret) |
1095 | return ret; | |
1da177e4 | 1096 | |
2f972202 CH |
1097 | switch (val) { |
1098 | case 0: | |
1da177e4 | 1099 | ret = disable_cmf(cdev); |
1da177e4 | 1100 | break; |
2f972202 | 1101 | case 1: |
1da177e4 | 1102 | ret = enable_cmf(cdev); |
1da177e4 | 1103 | break; |
279b8f9a SO |
1104 | default: |
1105 | ret = -EINVAL; | |
1da177e4 LT |
1106 | } |
1107 | ||
279b8f9a | 1108 | return ret ? ret : c; |
1da177e4 | 1109 | } |
279b8f9a | 1110 | DEVICE_ATTR_RW(cmb_enable); |
1da177e4 | 1111 | |
823d494a SO |
1112 | int ccw_set_cmf(struct ccw_device *cdev, int enable) |
1113 | { | |
1114 | return cmbops->set(cdev, enable ? 2 : 0); | |
1115 | } | |
1116 | ||
c0208716 CH |
1117 | /** |
1118 | * enable_cmf() - switch on the channel measurement for a specific device | |
1119 | * @cdev: The ccw device to be enabled | |
1120 | * | |
1121 | * Returns %0 for success or a negative error value. | |
0f5d050c SO |
1122 | * Note: If this is called on a device for which channel measurement is already |
1123 | * enabled a reset of the measurement data is triggered. | |
c0208716 CH |
1124 | * Context: |
1125 | * non-atomic | |
1126 | */ | |
fc5019c5 | 1127 | int enable_cmf(struct ccw_device *cdev) |
1da177e4 | 1128 | { |
0f5d050c | 1129 | int ret = 0; |
1da177e4 | 1130 | |
1bc6664b | 1131 | device_lock(&cdev->dev); |
0f5d050c SO |
1132 | if (cmf_enabled(cdev)) { |
1133 | cmbops->reset(cdev); | |
1134 | goto out_unlock; | |
1135 | } | |
a6ef1565 | 1136 | get_device(&cdev->dev); |
1da177e4 | 1137 | ret = cmbops->alloc(cdev); |
1da177e4 | 1138 | if (ret) |
1bc6664b SO |
1139 | goto out; |
1140 | cmbops->reset(cdev); | |
1141 | ret = sysfs_create_group(&cdev->dev.kobj, cmbops->attr_group); | |
1142 | if (ret) { | |
1143 | cmbops->free(cdev); | |
1144 | goto out; | |
1145 | } | |
1da177e4 LT |
1146 | ret = cmbops->set(cdev, 2); |
1147 | if (ret) { | |
1bc6664b | 1148 | sysfs_remove_group(&cdev->dev.kobj, cmbops->attr_group); |
1da177e4 | 1149 | cmbops->free(cdev); |
1da177e4 | 1150 | } |
1bc6664b | 1151 | out: |
a6ef1565 SO |
1152 | if (ret) |
1153 | put_device(&cdev->dev); | |
0f5d050c | 1154 | out_unlock: |
1bc6664b | 1155 | device_unlock(&cdev->dev); |
1da177e4 LT |
1156 | return ret; |
1157 | } | |
1158 | ||
c0208716 | 1159 | /** |
1bc6664b | 1160 | * __disable_cmf() - switch off the channel measurement for a specific device |
c0208716 CH |
1161 | * @cdev: The ccw device to be disabled |
1162 | * | |
1163 | * Returns %0 for success or a negative error value. | |
1164 | * | |
1165 | * Context: | |
1bc6664b | 1166 | * non-atomic, device_lock() held. |
c0208716 | 1167 | */ |
1bc6664b | 1168 | int __disable_cmf(struct ccw_device *cdev) |
1da177e4 LT |
1169 | { |
1170 | int ret; | |
1171 | ||
1172 | ret = cmbops->set(cdev, 0); | |
1173 | if (ret) | |
1174 | return ret; | |
1bc6664b | 1175 | |
1da177e4 | 1176 | sysfs_remove_group(&cdev->dev.kobj, cmbops->attr_group); |
1bc6664b | 1177 | cmbops->free(cdev); |
a6ef1565 | 1178 | put_device(&cdev->dev); |
1bc6664b SO |
1179 | |
1180 | return ret; | |
1181 | } | |
1182 | ||
1183 | /** | |
1184 | * disable_cmf() - switch off the channel measurement for a specific device | |
1185 | * @cdev: The ccw device to be disabled | |
1186 | * | |
1187 | * Returns %0 for success or a negative error value. | |
1188 | * | |
1189 | * Context: | |
1190 | * non-atomic | |
1191 | */ | |
1192 | int disable_cmf(struct ccw_device *cdev) | |
1193 | { | |
1194 | int ret; | |
1195 | ||
1196 | device_lock(&cdev->dev); | |
1197 | ret = __disable_cmf(cdev); | |
1198 | device_unlock(&cdev->dev); | |
1199 | ||
1da177e4 LT |
1200 | return ret; |
1201 | } | |
1202 | ||
c0208716 CH |
1203 | /** |
1204 | * cmf_read() - read one value from the current channel measurement block | |
1205 | * @cdev: the channel to be read | |
1206 | * @index: the index of the value to be read | |
1207 | * | |
1208 | * Returns the value read or %0 if the value cannot be read. | |
1209 | * | |
1210 | * Context: | |
1211 | * any | |
1212 | */ | |
fc5019c5 | 1213 | u64 cmf_read(struct ccw_device *cdev, int index) |
1da177e4 LT |
1214 | { |
1215 | return cmbops->read(cdev, index); | |
1216 | } | |
1217 | ||
c0208716 CH |
1218 | /** |
1219 | * cmf_readall() - read the current channel measurement block | |
1220 | * @cdev: the channel to be read | |
1221 | * @data: a pointer to a data block that will be filled | |
1222 | * | |
1223 | * Returns %0 on success, a negative error value otherwise. | |
1224 | * | |
1225 | * Context: | |
1226 | * any | |
1227 | */ | |
fc5019c5 | 1228 | int cmf_readall(struct ccw_device *cdev, struct cmbdata *data) |
1da177e4 LT |
1229 | { |
1230 | return cmbops->readall(cdev, data); | |
1231 | } | |
1232 | ||
94bb0633 CH |
1233 | /* Reenable cmf when a disconnected device becomes available again. */ |
1234 | int cmf_reenable(struct ccw_device *cdev) | |
1235 | { | |
1236 | cmbops->reset(cdev); | |
1237 | return cmbops->set(cdev, 2); | |
1238 | } | |
1239 | ||
ab97d211 SO |
1240 | /** |
1241 | * cmf_reactivate() - reactivate measurement block updates | |
1242 | * | |
1243 | * Use this during resume from hibernate. | |
1244 | */ | |
1245 | void cmf_reactivate(void) | |
1246 | { | |
1247 | spin_lock(&cmb_area.lock); | |
1248 | if (!list_empty(&cmb_area.list)) | |
7b4ff87c | 1249 | cmf_activate(cmb_area.mem, CMF_ON); |
ab97d211 SO |
1250 | spin_unlock(&cmb_area.lock); |
1251 | } | |
1252 | ||
45bf4b96 SO |
1253 | static int __init init_cmbe(void) |
1254 | { | |
1255 | cmbe_cache = kmem_cache_create("cmbe_cache", sizeof(struct cmbe), | |
1256 | __alignof__(struct cmbe), 0, NULL); | |
1257 | ||
1258 | return cmbe_cache ? 0 : -ENOMEM; | |
1259 | } | |
1260 | ||
fc5019c5 | 1261 | static int __init init_cmf(void) |
1da177e4 LT |
1262 | { |
1263 | char *format_string; | |
45bf4b96 SO |
1264 | char *detect_string; |
1265 | int ret; | |
1da177e4 | 1266 | |
fc5019c5 CH |
1267 | /* |
1268 | * If the user did not give a parameter, see if we are running on a | |
1269 | * machine supporting extended measurement blocks, otherwise fall back | |
1270 | * to basic mode. | |
1271 | */ | |
1da177e4 | 1272 | if (format == CMF_AUTODETECT) { |
75784c00 | 1273 | if (!css_general_characteristics.ext_mb) { |
1da177e4 LT |
1274 | format = CMF_BASIC; |
1275 | } else { | |
1276 | format = CMF_EXTENDED; | |
1277 | } | |
1278 | detect_string = "autodetected"; | |
1279 | } else { | |
1280 | detect_string = "parameter"; | |
1281 | } | |
1282 | ||
1283 | switch (format) { | |
1284 | case CMF_BASIC: | |
1285 | format_string = "basic"; | |
1286 | cmbops = &cmbops_basic; | |
1da177e4 LT |
1287 | break; |
1288 | case CMF_EXTENDED: | |
fc5019c5 | 1289 | format_string = "extended"; |
1da177e4 | 1290 | cmbops = &cmbops_extended; |
45bf4b96 SO |
1291 | |
1292 | ret = init_cmbe(); | |
1293 | if (ret) | |
1294 | return ret; | |
1da177e4 LT |
1295 | break; |
1296 | default: | |
45bf4b96 | 1297 | return -EINVAL; |
1da177e4 | 1298 | } |
e6d5a428 ME |
1299 | pr_info("Channel measurement facility initialized using format " |
1300 | "%s (mode %s)\n", format_string, detect_string); | |
1da177e4 LT |
1301 | return 0; |
1302 | } | |
a00f761f | 1303 | device_initcall(init_cmf); |
1da177e4 LT |
1304 | |
1305 | EXPORT_SYMBOL_GPL(enable_cmf); | |
1306 | EXPORT_SYMBOL_GPL(disable_cmf); | |
1307 | EXPORT_SYMBOL_GPL(cmf_read); | |
1308 | EXPORT_SYMBOL_GPL(cmf_readall); |