diff options
author | Cornelia Huck <cornelia.huck@de.ibm.com> | 2006-06-29 09:08:41 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2006-06-29 09:08:41 -0400 |
commit | 94bb063312d872d9269deb2e5c0c7c6d5b0318e1 (patch) | |
tree | bef899fbe742ee31d93ff0e077898d4562086f9e /drivers/s390/cio/cmf.c | |
parent | 5b5dd21a8e6652fc8b58a32757575cc7fce2b54d (diff) |
[S390] rework of channel measurement facility.
Fixes for several channel measurement facility bugs:
* Blocks copied from the hardware might not be consistent. Solve this
by moving the copying into idle state and repeating the copying.
* avg_sample_interval changed with every read, even though no new block
was available. Solve this by storing a timestamp when the last new
block was received.
* Several locking issues.
* Measurements were not reenabled after a disconnected device became
available again.
* Remove #defines for ioctls that were never implemented.
Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390/cio/cmf.c')
-rw-r--r-- | drivers/s390/cio/cmf.c | 623 |
1 files changed, 447 insertions, 176 deletions
diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c index 07ef3f640f4a..1c3e8e9012b0 100644 --- a/drivers/s390/cio/cmf.c +++ b/drivers/s390/cio/cmf.c | |||
@@ -3,9 +3,10 @@ | |||
3 | * | 3 | * |
4 | * Linux on zSeries Channel Measurement Facility support | 4 | * Linux on zSeries Channel Measurement Facility support |
5 | * | 5 | * |
6 | * Copyright 2000,2003 IBM Corporation | 6 | * Copyright 2000,2006 IBM Corporation |
7 | * | 7 | * |
8 | * Author: Arnd Bergmann <arndb@de.ibm.com> | 8 | * Authors: Arnd Bergmann <arndb@de.ibm.com> |
9 | * Cornelia Huck <cornelia.huck@de.ibm.com> | ||
9 | * | 10 | * |
10 | * original idea from Natarajan Krishnaswami <nkrishna@us.ibm.com> | 11 | * original idea from Natarajan Krishnaswami <nkrishna@us.ibm.com> |
11 | * | 12 | * |
@@ -96,9 +97,9 @@ module_param(format, bool, 0444); | |||
96 | /** | 97 | /** |
97 | * struct cmb_operations - functions to use depending on cmb_format | 98 | * struct cmb_operations - functions to use depending on cmb_format |
98 | * | 99 | * |
99 | * all these functions operate on a struct cmf_device. There is only | 100 | * Most of these functions operate on a struct ccw_device. There is only |
100 | * one instance of struct cmb_operations because all cmf_device | 101 | * one instance of struct cmb_operations because the format of the measurement |
101 | * objects are guaranteed to be of the same type. | 102 | * data is guaranteed to be the same for every ccw_device. |
102 | * | 103 | * |
103 | * @alloc: allocate memory for a channel measurement block, | 104 | * @alloc: allocate memory for a channel measurement block, |
104 | * either with the help of a special pool or with kmalloc | 105 | * either with the help of a special pool or with kmalloc |
@@ -107,6 +108,7 @@ module_param(format, bool, 0444); | |||
107 | * @readall: read a measurement block in a common format | 108 | * @readall: read a measurement block in a common format |
108 | * @reset: clear the data in the associated measurement block and | 109 | * @reset: clear the data in the associated measurement block and |
109 | * reset its time stamp | 110 | * reset its time stamp |
111 | * @align: align an allocated block so that the hardware can use it | ||
110 | */ | 112 | */ |
111 | struct cmb_operations { | 113 | struct cmb_operations { |
112 | int (*alloc) (struct ccw_device*); | 114 | int (*alloc) (struct ccw_device*); |
@@ -115,11 +117,19 @@ struct cmb_operations { | |||
115 | u64 (*read) (struct ccw_device*, int); | 117 | u64 (*read) (struct ccw_device*, int); |
116 | int (*readall)(struct ccw_device*, struct cmbdata *); | 118 | int (*readall)(struct ccw_device*, struct cmbdata *); |
117 | void (*reset) (struct ccw_device*); | 119 | void (*reset) (struct ccw_device*); |
120 | void * (*align) (void *); | ||
118 | 121 | ||
119 | struct attribute_group *attr_group; | 122 | struct attribute_group *attr_group; |
120 | }; | 123 | }; |
121 | static struct cmb_operations *cmbops; | 124 | static struct cmb_operations *cmbops; |
122 | 125 | ||
126 | struct cmb_data { | ||
127 | void *hw_block; /* Pointer to block updated by hardware */ | ||
128 | void *last_block; /* Last changed block copied from hardware block */ | ||
129 | int size; /* Size of hw_block and last_block */ | ||
130 | unsigned long long last_update; /* when last_block was updated */ | ||
131 | }; | ||
132 | |||
123 | /* our user interface is designed in terms of nanoseconds, | 133 | /* our user interface is designed in terms of nanoseconds, |
124 | * while the hardware measures total times in its own | 134 | * while the hardware measures total times in its own |
125 | * unit.*/ | 135 | * unit.*/ |
@@ -226,63 +236,229 @@ struct set_schib_struct { | |||
226 | unsigned long address; | 236 | unsigned long address; |
227 | wait_queue_head_t wait; | 237 | wait_queue_head_t wait; |
228 | int ret; | 238 | int ret; |
239 | struct kref kref; | ||
229 | }; | 240 | }; |
230 | 241 | ||
242 | static void cmf_set_schib_release(struct kref *kref) | ||
243 | { | ||
244 | struct set_schib_struct *set_data; | ||
245 | |||
246 | set_data = container_of(kref, struct set_schib_struct, kref); | ||
247 | kfree(set_data); | ||
248 | } | ||
249 | |||
250 | #define CMF_PENDING 1 | ||
251 | |||
231 | static int set_schib_wait(struct ccw_device *cdev, u32 mme, | 252 | static int set_schib_wait(struct ccw_device *cdev, u32 mme, |
232 | int mbfc, unsigned long address) | 253 | int mbfc, unsigned long address) |
233 | { | 254 | { |
234 | struct set_schib_struct s = { | 255 | struct set_schib_struct *set_data; |
235 | .mme = mme, | 256 | int ret; |
236 | .mbfc = mbfc, | ||
237 | .address = address, | ||
238 | .wait = __WAIT_QUEUE_HEAD_INITIALIZER(s.wait), | ||
239 | }; | ||
240 | 257 | ||
241 | spin_lock_irq(cdev->ccwlock); | 258 | spin_lock_irq(cdev->ccwlock); |
242 | s.ret = set_schib(cdev, mme, mbfc, address); | 259 | if (!cdev->private->cmb) { |
243 | if (s.ret != -EBUSY) { | 260 | ret = -ENODEV; |
244 | goto out_nowait; | 261 | goto out; |
245 | } | 262 | } |
263 | set_data = kzalloc(sizeof(struct set_schib_struct), GFP_ATOMIC); | ||
264 | if (!set_data) { | ||
265 | ret = -ENOMEM; | ||
266 | goto out; | ||
267 | } | ||
268 | init_waitqueue_head(&set_data->wait); | ||
269 | kref_init(&set_data->kref); | ||
270 | set_data->mme = mme; | ||
271 | set_data->mbfc = mbfc; | ||
272 | set_data->address = address; | ||
273 | |||
274 | ret = set_schib(cdev, mme, mbfc, address); | ||
275 | if (ret != -EBUSY) | ||
276 | goto out_put; | ||
246 | 277 | ||
247 | if (cdev->private->state != DEV_STATE_ONLINE) { | 278 | if (cdev->private->state != DEV_STATE_ONLINE) { |
248 | s.ret = -EBUSY; | ||
249 | /* if the device is not online, don't even try again */ | 279 | /* if the device is not online, don't even try again */ |
250 | goto out_nowait; | 280 | ret = -EBUSY; |
281 | goto out_put; | ||
251 | } | 282 | } |
283 | |||
252 | cdev->private->state = DEV_STATE_CMFCHANGE; | 284 | cdev->private->state = DEV_STATE_CMFCHANGE; |
253 | cdev->private->cmb_wait = &s; | 285 | set_data->ret = CMF_PENDING; |
254 | s.ret = 1; | 286 | cdev->private->cmb_wait = set_data; |
255 | 287 | ||
256 | spin_unlock_irq(cdev->ccwlock); | 288 | spin_unlock_irq(cdev->ccwlock); |
257 | if (wait_event_interruptible(s.wait, s.ret != 1)) { | 289 | if (wait_event_interruptible(set_data->wait, |
290 | set_data->ret != CMF_PENDING)) { | ||
258 | spin_lock_irq(cdev->ccwlock); | 291 | spin_lock_irq(cdev->ccwlock); |
259 | if (s.ret == 1) { | 292 | if (set_data->ret == CMF_PENDING) { |
260 | s.ret = -ERESTARTSYS; | 293 | set_data->ret = -ERESTARTSYS; |
261 | cdev->private->cmb_wait = 0; | ||
262 | if (cdev->private->state == DEV_STATE_CMFCHANGE) | 294 | if (cdev->private->state == DEV_STATE_CMFCHANGE) |
263 | cdev->private->state = DEV_STATE_ONLINE; | 295 | cdev->private->state = DEV_STATE_ONLINE; |
264 | } | 296 | } |
265 | spin_unlock_irq(cdev->ccwlock); | 297 | spin_unlock_irq(cdev->ccwlock); |
266 | } | 298 | } |
267 | return s.ret; | 299 | spin_lock_irq(cdev->ccwlock); |
268 | 300 | cdev->private->cmb_wait = NULL; | |
269 | out_nowait: | 301 | ret = set_data->ret; |
302 | out_put: | ||
303 | kref_put(&set_data->kref, cmf_set_schib_release); | ||
304 | out: | ||
270 | spin_unlock_irq(cdev->ccwlock); | 305 | spin_unlock_irq(cdev->ccwlock); |
271 | return s.ret; | 306 | return ret; |
272 | } | 307 | } |
273 | 308 | ||
274 | void retry_set_schib(struct ccw_device *cdev) | 309 | void retry_set_schib(struct ccw_device *cdev) |
275 | { | 310 | { |
276 | struct set_schib_struct *s; | 311 | struct set_schib_struct *set_data; |
312 | |||
313 | set_data = cdev->private->cmb_wait; | ||
314 | if (!set_data) { | ||
315 | WARN_ON(1); | ||
316 | return; | ||
317 | } | ||
318 | kref_get(&set_data->kref); | ||
319 | set_data->ret = set_schib(cdev, set_data->mme, set_data->mbfc, | ||
320 | set_data->address); | ||
321 | wake_up(&set_data->wait); | ||
322 | kref_put(&set_data->kref, cmf_set_schib_release); | ||
323 | } | ||
324 | |||
325 | static int cmf_copy_block(struct ccw_device *cdev) | ||
326 | { | ||
327 | struct subchannel *sch; | ||
328 | void *reference_buf; | ||
329 | void *hw_block; | ||
330 | struct cmb_data *cmb_data; | ||
331 | |||
332 | sch = to_subchannel(cdev->dev.parent); | ||
333 | |||
334 | if (stsch(sch->schid, &sch->schib)) | ||
335 | return -ENODEV; | ||
336 | |||
337 | if (sch->schib.scsw.fctl & SCSW_FCTL_START_FUNC) { | ||
338 | /* Don't copy if a start function is in progress. */ | ||
339 | if ((!sch->schib.scsw.actl & SCSW_ACTL_SUSPENDED) && | ||
340 | (sch->schib.scsw.actl & | ||
341 | (SCSW_ACTL_DEVACT | SCSW_ACTL_SCHACT)) && | ||
342 | (!sch->schib.scsw.stctl & SCSW_STCTL_SEC_STATUS)) | ||
343 | return -EBUSY; | ||
344 | } | ||
345 | cmb_data = cdev->private->cmb; | ||
346 | hw_block = cmbops->align(cmb_data->hw_block); | ||
347 | if (!memcmp(cmb_data->last_block, hw_block, cmb_data->size)) | ||
348 | /* No need to copy. */ | ||
349 | return 0; | ||
350 | reference_buf = kzalloc(cmb_data->size, GFP_ATOMIC); | ||
351 | if (!reference_buf) | ||
352 | return -ENOMEM; | ||
353 | /* Ensure consistency of block copied from hardware. */ | ||
354 | do { | ||
355 | memcpy(cmb_data->last_block, hw_block, cmb_data->size); | ||
356 | memcpy(reference_buf, hw_block, cmb_data->size); | ||
357 | } while (memcmp(cmb_data->last_block, reference_buf, cmb_data->size)); | ||
358 | cmb_data->last_update = get_clock(); | ||
359 | kfree(reference_buf); | ||
360 | return 0; | ||
361 | } | ||
362 | |||
363 | struct copy_block_struct { | ||
364 | wait_queue_head_t wait; | ||
365 | int ret; | ||
366 | struct kref kref; | ||
367 | }; | ||
368 | |||
369 | static void cmf_copy_block_release(struct kref *kref) | ||
370 | { | ||
371 | struct copy_block_struct *copy_block; | ||
372 | |||
373 | copy_block = container_of(kref, struct copy_block_struct, kref); | ||
374 | kfree(copy_block); | ||
375 | } | ||
376 | |||
377 | static int cmf_cmb_copy_wait(struct ccw_device *cdev) | ||
378 | { | ||
379 | struct copy_block_struct *copy_block; | ||
380 | int ret; | ||
381 | unsigned long flags; | ||
382 | |||
383 | spin_lock_irqsave(cdev->ccwlock, flags); | ||
384 | if (!cdev->private->cmb) { | ||
385 | ret = -ENODEV; | ||
386 | goto out; | ||
387 | } | ||
388 | copy_block = kzalloc(sizeof(struct copy_block_struct), GFP_ATOMIC); | ||
389 | if (!copy_block) { | ||
390 | ret = -ENOMEM; | ||
391 | goto out; | ||
392 | } | ||
393 | init_waitqueue_head(©_block->wait); | ||
394 | kref_init(©_block->kref); | ||
395 | |||
396 | ret = cmf_copy_block(cdev); | ||
397 | if (ret != -EBUSY) | ||
398 | goto out_put; | ||
399 | |||
400 | if (cdev->private->state != DEV_STATE_ONLINE) { | ||
401 | ret = -EBUSY; | ||
402 | goto out_put; | ||
403 | } | ||
404 | |||
405 | cdev->private->state = DEV_STATE_CMFUPDATE; | ||
406 | copy_block->ret = CMF_PENDING; | ||
407 | cdev->private->cmb_wait = copy_block; | ||
408 | |||
409 | spin_unlock_irqrestore(cdev->ccwlock, flags); | ||
410 | if (wait_event_interruptible(copy_block->wait, | ||
411 | copy_block->ret != CMF_PENDING)) { | ||
412 | spin_lock_irqsave(cdev->ccwlock, flags); | ||
413 | if (copy_block->ret == CMF_PENDING) { | ||
414 | copy_block->ret = -ERESTARTSYS; | ||
415 | if (cdev->private->state == DEV_STATE_CMFUPDATE) | ||
416 | cdev->private->state = DEV_STATE_ONLINE; | ||
417 | } | ||
418 | spin_unlock_irqrestore(cdev->ccwlock, flags); | ||
419 | } | ||
420 | spin_lock_irqsave(cdev->ccwlock, flags); | ||
421 | cdev->private->cmb_wait = NULL; | ||
422 | ret = copy_block->ret; | ||
423 | out_put: | ||
424 | kref_put(©_block->kref, cmf_copy_block_release); | ||
425 | out: | ||
426 | spin_unlock_irqrestore(cdev->ccwlock, flags); | ||
427 | return ret; | ||
428 | } | ||
429 | |||
430 | void cmf_retry_copy_block(struct ccw_device *cdev) | ||
431 | { | ||
432 | struct copy_block_struct *copy_block; | ||
277 | 433 | ||
278 | s = cdev->private->cmb_wait; | 434 | copy_block = cdev->private->cmb_wait; |
279 | cdev->private->cmb_wait = 0; | 435 | if (!copy_block) { |
280 | if (!s) { | ||
281 | WARN_ON(1); | 436 | WARN_ON(1); |
282 | return; | 437 | return; |
283 | } | 438 | } |
284 | s->ret = set_schib(cdev, s->mme, s->mbfc, s->address); | 439 | kref_get(©_block->kref); |
285 | wake_up(&s->wait); | 440 | copy_block->ret = cmf_copy_block(cdev); |
441 | wake_up(©_block->wait); | ||
442 | kref_put(©_block->kref, cmf_copy_block_release); | ||
443 | } | ||
444 | |||
445 | static void cmf_generic_reset(struct ccw_device *cdev) | ||
446 | { | ||
447 | struct cmb_data *cmb_data; | ||
448 | |||
449 | spin_lock_irq(cdev->ccwlock); | ||
450 | cmb_data = cdev->private->cmb; | ||
451 | if (cmb_data) { | ||
452 | memset(cmb_data->last_block, 0, cmb_data->size); | ||
453 | /* | ||
454 | * Need to reset hw block as well to make the hardware start | ||
455 | * from 0 again. | ||
456 | */ | ||
457 | memset(cmbops->align(cmb_data->hw_block), 0, cmb_data->size); | ||
458 | cmb_data->last_update = 0; | ||
459 | } | ||
460 | cdev->private->cmb_start_time = get_clock(); | ||
461 | spin_unlock_irq(cdev->ccwlock); | ||
286 | } | 462 | } |
287 | 463 | ||
288 | /** | 464 | /** |
@@ -343,8 +519,8 @@ struct cmb { | |||
343 | /* insert a single device into the cmb_area list | 519 | /* insert a single device into the cmb_area list |
344 | * called with cmb_area.lock held from alloc_cmb | 520 | * called with cmb_area.lock held from alloc_cmb |
345 | */ | 521 | */ |
346 | static inline int | 522 | static inline int alloc_cmb_single (struct ccw_device *cdev, |
347 | alloc_cmb_single (struct ccw_device *cdev) | 523 | struct cmb_data *cmb_data) |
348 | { | 524 | { |
349 | struct cmb *cmb; | 525 | struct cmb *cmb; |
350 | struct ccw_device_private *node; | 526 | struct ccw_device_private *node; |
@@ -358,10 +534,12 @@ alloc_cmb_single (struct ccw_device *cdev) | |||
358 | 534 | ||
359 | /* find first unused cmb in cmb_area.mem. | 535 | /* find first unused cmb in cmb_area.mem. |
360 | * this is a little tricky: cmb_area.list | 536 | * this is a little tricky: cmb_area.list |
361 | * remains sorted by ->cmb pointers */ | 537 | * remains sorted by ->cmb->hw_data pointers */ |
362 | cmb = cmb_area.mem; | 538 | cmb = cmb_area.mem; |
363 | list_for_each_entry(node, &cmb_area.list, cmb_list) { | 539 | list_for_each_entry(node, &cmb_area.list, cmb_list) { |
364 | if ((struct cmb*)node->cmb > cmb) | 540 | struct cmb_data *data; |
541 | data = node->cmb; | ||
542 | if ((struct cmb*)data->hw_block > cmb) | ||
365 | break; | 543 | break; |
366 | cmb++; | 544 | cmb++; |
367 | } | 545 | } |
@@ -372,7 +550,8 @@ alloc_cmb_single (struct ccw_device *cdev) | |||
372 | 550 | ||
373 | /* insert new cmb */ | 551 | /* insert new cmb */ |
374 | list_add_tail(&cdev->private->cmb_list, &node->cmb_list); | 552 | list_add_tail(&cdev->private->cmb_list, &node->cmb_list); |
375 | cdev->private->cmb = cmb; | 553 | cmb_data->hw_block = cmb; |
554 | cdev->private->cmb = cmb_data; | ||
376 | ret = 0; | 555 | ret = 0; |
377 | out: | 556 | out: |
378 | spin_unlock_irq(cdev->ccwlock); | 557 | spin_unlock_irq(cdev->ccwlock); |
@@ -385,7 +564,19 @@ alloc_cmb (struct ccw_device *cdev) | |||
385 | int ret; | 564 | int ret; |
386 | struct cmb *mem; | 565 | struct cmb *mem; |
387 | ssize_t size; | 566 | ssize_t size; |
567 | struct cmb_data *cmb_data; | ||
568 | |||
569 | /* Allocate private cmb_data. */ | ||
570 | cmb_data = kzalloc(sizeof(struct cmb_data), GFP_KERNEL); | ||
571 | if (!cmb_data) | ||
572 | return -ENOMEM; | ||
388 | 573 | ||
574 | cmb_data->last_block = kzalloc(sizeof(struct cmb), GFP_KERNEL); | ||
575 | if (!cmb_data->last_block) { | ||
576 | kfree(cmb_data); | ||
577 | return -ENOMEM; | ||
578 | } | ||
579 | cmb_data->size = sizeof(struct cmb); | ||
389 | spin_lock(&cmb_area.lock); | 580 | spin_lock(&cmb_area.lock); |
390 | 581 | ||
391 | if (!cmb_area.mem) { | 582 | if (!cmb_area.mem) { |
@@ -414,29 +605,36 @@ alloc_cmb (struct ccw_device *cdev) | |||
414 | } | 605 | } |
415 | 606 | ||
416 | /* do the actual allocation */ | 607 | /* do the actual allocation */ |
417 | ret = alloc_cmb_single(cdev); | 608 | ret = alloc_cmb_single(cdev, cmb_data); |
418 | out: | 609 | out: |
419 | spin_unlock(&cmb_area.lock); | 610 | spin_unlock(&cmb_area.lock); |
420 | 611 | if (ret) { | |
612 | kfree(cmb_data->last_block); | ||
613 | kfree(cmb_data); | ||
614 | } | ||
421 | return ret; | 615 | return ret; |
422 | } | 616 | } |
423 | 617 | ||
424 | static void | 618 | static void free_cmb(struct ccw_device *cdev) |
425 | free_cmb(struct ccw_device *cdev) | ||
426 | { | 619 | { |
427 | struct ccw_device_private *priv; | 620 | struct ccw_device_private *priv; |
428 | 621 | struct cmb_data *cmb_data; | |
429 | priv = cdev->private; | ||
430 | 622 | ||
431 | spin_lock(&cmb_area.lock); | 623 | spin_lock(&cmb_area.lock); |
432 | spin_lock_irq(cdev->ccwlock); | 624 | spin_lock_irq(cdev->ccwlock); |
433 | 625 | ||
626 | priv = cdev->private; | ||
627 | |||
434 | if (list_empty(&priv->cmb_list)) { | 628 | if (list_empty(&priv->cmb_list)) { |
435 | /* already freed */ | 629 | /* already freed */ |
436 | goto out; | 630 | goto out; |
437 | } | 631 | } |
438 | 632 | ||
633 | cmb_data = priv->cmb; | ||
439 | priv->cmb = NULL; | 634 | priv->cmb = NULL; |
635 | if (cmb_data) | ||
636 | kfree(cmb_data->last_block); | ||
637 | kfree(cmb_data); | ||
440 | list_del_init(&priv->cmb_list); | 638 | list_del_init(&priv->cmb_list); |
441 | 639 | ||
442 | if (list_empty(&cmb_area.list)) { | 640 | if (list_empty(&cmb_area.list)) { |
@@ -451,83 +649,97 @@ out: | |||
451 | spin_unlock(&cmb_area.lock); | 649 | spin_unlock(&cmb_area.lock); |
452 | } | 650 | } |
453 | 651 | ||
454 | static int | 652 | static int set_cmb(struct ccw_device *cdev, u32 mme) |
455 | set_cmb(struct ccw_device *cdev, u32 mme) | ||
456 | { | 653 | { |
457 | u16 offset; | 654 | u16 offset; |
655 | struct cmb_data *cmb_data; | ||
656 | unsigned long flags; | ||
458 | 657 | ||
459 | if (!cdev->private->cmb) | 658 | spin_lock_irqsave(cdev->ccwlock, flags); |
659 | if (!cdev->private->cmb) { | ||
660 | spin_unlock_irqrestore(cdev->ccwlock, flags); | ||
460 | return -EINVAL; | 661 | return -EINVAL; |
461 | 662 | } | |
462 | offset = mme ? (struct cmb *)cdev->private->cmb - cmb_area.mem : 0; | 663 | cmb_data = cdev->private->cmb; |
664 | offset = mme ? (struct cmb *)cmb_data->hw_block - cmb_area.mem : 0; | ||
665 | spin_unlock_irqrestore(cdev->ccwlock, flags); | ||
463 | 666 | ||
464 | return set_schib_wait(cdev, mme, 0, offset); | 667 | return set_schib_wait(cdev, mme, 0, offset); |
465 | } | 668 | } |
466 | 669 | ||
467 | static u64 | 670 | static u64 read_cmb (struct ccw_device *cdev, int index) |
468 | read_cmb (struct ccw_device *cdev, int index) | ||
469 | { | 671 | { |
470 | /* yes, we have to put it on the stack | 672 | struct cmb *cmb; |
471 | * because the cmb must only be accessed | ||
472 | * atomically, e.g. with mvc */ | ||
473 | struct cmb cmb; | ||
474 | unsigned long flags; | ||
475 | u32 val; | 673 | u32 val; |
674 | int ret; | ||
675 | unsigned long flags; | ||
676 | |||
677 | ret = cmf_cmb_copy_wait(cdev); | ||
678 | if (ret < 0) | ||
679 | return 0; | ||
476 | 680 | ||
477 | spin_lock_irqsave(cdev->ccwlock, flags); | 681 | spin_lock_irqsave(cdev->ccwlock, flags); |
478 | if (!cdev->private->cmb) { | 682 | if (!cdev->private->cmb) { |
479 | spin_unlock_irqrestore(cdev->ccwlock, flags); | 683 | ret = 0; |
480 | return 0; | 684 | goto out; |
481 | } | 685 | } |
482 | 686 | cmb = ((struct cmb_data *)cdev->private->cmb)->last_block; | |
483 | cmb = *(struct cmb*)cdev->private->cmb; | ||
484 | spin_unlock_irqrestore(cdev->ccwlock, flags); | ||
485 | 687 | ||
486 | switch (index) { | 688 | switch (index) { |
487 | case cmb_ssch_rsch_count: | 689 | case cmb_ssch_rsch_count: |
488 | return cmb.ssch_rsch_count; | 690 | ret = cmb->ssch_rsch_count; |
691 | goto out; | ||
489 | case cmb_sample_count: | 692 | case cmb_sample_count: |
490 | return cmb.sample_count; | 693 | ret = cmb->sample_count; |
694 | goto out; | ||
491 | case cmb_device_connect_time: | 695 | case cmb_device_connect_time: |
492 | val = cmb.device_connect_time; | 696 | val = cmb->device_connect_time; |
493 | break; | 697 | break; |
494 | case cmb_function_pending_time: | 698 | case cmb_function_pending_time: |
495 | val = cmb.function_pending_time; | 699 | val = cmb->function_pending_time; |
496 | break; | 700 | break; |
497 | case cmb_device_disconnect_time: | 701 | case cmb_device_disconnect_time: |
498 | val = cmb.device_disconnect_time; | 702 | val = cmb->device_disconnect_time; |
499 | break; | 703 | break; |
500 | case cmb_control_unit_queuing_time: | 704 | case cmb_control_unit_queuing_time: |
501 | val = cmb.control_unit_queuing_time; | 705 | val = cmb->control_unit_queuing_time; |
502 | break; | 706 | break; |
503 | case cmb_device_active_only_time: | 707 | case cmb_device_active_only_time: |
504 | val = cmb.device_active_only_time; | 708 | val = cmb->device_active_only_time; |
505 | break; | 709 | break; |
506 | default: | 710 | default: |
507 | return 0; | 711 | ret = 0; |
712 | goto out; | ||
508 | } | 713 | } |
509 | return time_to_avg_nsec(val, cmb.sample_count); | 714 | ret = time_to_avg_nsec(val, cmb->sample_count); |
715 | out: | ||
716 | spin_unlock_irqrestore(cdev->ccwlock, flags); | ||
717 | return ret; | ||
510 | } | 718 | } |
511 | 719 | ||
512 | static int | 720 | static int readall_cmb (struct ccw_device *cdev, struct cmbdata *data) |
513 | readall_cmb (struct ccw_device *cdev, struct cmbdata *data) | ||
514 | { | 721 | { |
515 | /* yes, we have to put it on the stack | 722 | struct cmb *cmb; |
516 | * because the cmb must only be accessed | 723 | struct cmb_data *cmb_data; |
517 | * atomically, e.g. with mvc */ | ||
518 | struct cmb cmb; | ||
519 | unsigned long flags; | ||
520 | u64 time; | 724 | u64 time; |
725 | unsigned long flags; | ||
726 | int ret; | ||
521 | 727 | ||
728 | ret = cmf_cmb_copy_wait(cdev); | ||
729 | if (ret < 0) | ||
730 | return ret; | ||
522 | spin_lock_irqsave(cdev->ccwlock, flags); | 731 | spin_lock_irqsave(cdev->ccwlock, flags); |
523 | if (!cdev->private->cmb) { | 732 | cmb_data = cdev->private->cmb; |
524 | spin_unlock_irqrestore(cdev->ccwlock, flags); | 733 | if (!cmb_data) { |
525 | return -ENODEV; | 734 | ret = -ENODEV; |
735 | goto out; | ||
526 | } | 736 | } |
527 | 737 | if (cmb_data->last_update == 0) { | |
528 | cmb = *(struct cmb*)cdev->private->cmb; | 738 | ret = -EAGAIN; |
529 | time = get_clock() - cdev->private->cmb_start_time; | 739 | goto out; |
530 | spin_unlock_irqrestore(cdev->ccwlock, flags); | 740 | } |
741 | cmb = cmb_data->last_block; | ||
742 | time = cmb_data->last_update - cdev->private->cmb_start_time; | ||
531 | 743 | ||
532 | memset(data, 0, sizeof(struct cmbdata)); | 744 | memset(data, 0, sizeof(struct cmbdata)); |
533 | 745 | ||
@@ -538,31 +750,32 @@ readall_cmb (struct ccw_device *cdev, struct cmbdata *data) | |||
538 | data->elapsed_time = (time * 1000) >> 12; | 750 | data->elapsed_time = (time * 1000) >> 12; |
539 | 751 | ||
540 | /* copy data to new structure */ | 752 | /* copy data to new structure */ |
541 | data->ssch_rsch_count = cmb.ssch_rsch_count; | 753 | data->ssch_rsch_count = cmb->ssch_rsch_count; |
542 | data->sample_count = cmb.sample_count; | 754 | data->sample_count = cmb->sample_count; |
543 | 755 | ||
544 | /* time fields are converted to nanoseconds while copying */ | 756 | /* time fields are converted to nanoseconds while copying */ |
545 | data->device_connect_time = time_to_nsec(cmb.device_connect_time); | 757 | data->device_connect_time = time_to_nsec(cmb->device_connect_time); |
546 | data->function_pending_time = time_to_nsec(cmb.function_pending_time); | 758 | data->function_pending_time = time_to_nsec(cmb->function_pending_time); |
547 | data->device_disconnect_time = time_to_nsec(cmb.device_disconnect_time); | 759 | data->device_disconnect_time = |
760 | time_to_nsec(cmb->device_disconnect_time); | ||
548 | data->control_unit_queuing_time | 761 | data->control_unit_queuing_time |
549 | = time_to_nsec(cmb.control_unit_queuing_time); | 762 | = time_to_nsec(cmb->control_unit_queuing_time); |
550 | data->device_active_only_time | 763 | data->device_active_only_time |
551 | = time_to_nsec(cmb.device_active_only_time); | 764 | = time_to_nsec(cmb->device_active_only_time); |
765 | ret = 0; | ||
766 | out: | ||
767 | spin_unlock_irqrestore(cdev->ccwlock, flags); | ||
768 | return ret; | ||
769 | } | ||
552 | 770 | ||
553 | return 0; | 771 | static void reset_cmb(struct ccw_device *cdev) |
772 | { | ||
773 | cmf_generic_reset(cdev); | ||
554 | } | 774 | } |
555 | 775 | ||
556 | static void | 776 | static void * align_cmb(void *area) |
557 | reset_cmb(struct ccw_device *cdev) | ||
558 | { | 777 | { |
559 | struct cmb *cmb; | 778 | return area; |
560 | spin_lock_irq(cdev->ccwlock); | ||
561 | cmb = cdev->private->cmb; | ||
562 | if (cmb) | ||
563 | memset (cmb, 0, sizeof (*cmb)); | ||
564 | cdev->private->cmb_start_time = get_clock(); | ||
565 | spin_unlock_irq(cdev->ccwlock); | ||
566 | } | 779 | } |
567 | 780 | ||
568 | static struct attribute_group cmf_attr_group; | 781 | static struct attribute_group cmf_attr_group; |
@@ -574,6 +787,7 @@ static struct cmb_operations cmbops_basic = { | |||
574 | .read = read_cmb, | 787 | .read = read_cmb, |
575 | .readall = readall_cmb, | 788 | .readall = readall_cmb, |
576 | .reset = reset_cmb, | 789 | .reset = reset_cmb, |
790 | .align = align_cmb, | ||
577 | .attr_group = &cmf_attr_group, | 791 | .attr_group = &cmf_attr_group, |
578 | }; | 792 | }; |
579 | 793 | ||
@@ -610,22 +824,34 @@ static inline struct cmbe* cmbe_align(struct cmbe *c) | |||
610 | return (struct cmbe*)addr; | 824 | return (struct cmbe*)addr; |
611 | } | 825 | } |
612 | 826 | ||
613 | static int | 827 | static int alloc_cmbe (struct ccw_device *cdev) |
614 | alloc_cmbe (struct ccw_device *cdev) | ||
615 | { | 828 | { |
616 | struct cmbe *cmbe; | 829 | struct cmbe *cmbe; |
617 | cmbe = kmalloc (sizeof (*cmbe) * 2, GFP_KERNEL); | 830 | struct cmb_data *cmb_data; |
831 | int ret; | ||
832 | |||
833 | cmbe = kzalloc (sizeof (*cmbe) * 2, GFP_KERNEL); | ||
618 | if (!cmbe) | 834 | if (!cmbe) |
619 | return -ENOMEM; | 835 | return -ENOMEM; |
620 | 836 | cmb_data = kzalloc(sizeof(struct cmb_data), GFP_KERNEL); | |
837 | if (!cmb_data) { | ||
838 | ret = -ENOMEM; | ||
839 | goto out_free; | ||
840 | } | ||
841 | cmb_data->last_block = kzalloc(sizeof(struct cmbe), GFP_KERNEL); | ||
842 | if (!cmb_data->last_block) { | ||
843 | ret = -ENOMEM; | ||
844 | goto out_free; | ||
845 | } | ||
846 | cmb_data->size = sizeof(struct cmbe); | ||
621 | spin_lock_irq(cdev->ccwlock); | 847 | spin_lock_irq(cdev->ccwlock); |
622 | if (cdev->private->cmb) { | 848 | if (cdev->private->cmb) { |
623 | kfree(cmbe); | ||
624 | spin_unlock_irq(cdev->ccwlock); | 849 | spin_unlock_irq(cdev->ccwlock); |
625 | return -EBUSY; | 850 | ret = -EBUSY; |
851 | goto out_free; | ||
626 | } | 852 | } |
627 | 853 | cmb_data->hw_block = cmbe; | |
628 | cdev->private->cmb = cmbe; | 854 | cdev->private->cmb = cmb_data; |
629 | spin_unlock_irq(cdev->ccwlock); | 855 | spin_unlock_irq(cdev->ccwlock); |
630 | 856 | ||
631 | /* activate global measurement if this is the first channel */ | 857 | /* activate global measurement if this is the first channel */ |
@@ -636,14 +862,24 @@ alloc_cmbe (struct ccw_device *cdev) | |||
636 | spin_unlock(&cmb_area.lock); | 862 | spin_unlock(&cmb_area.lock); |
637 | 863 | ||
638 | return 0; | 864 | return 0; |
865 | out_free: | ||
866 | if (cmb_data) | ||
867 | kfree(cmb_data->last_block); | ||
868 | kfree(cmb_data); | ||
869 | kfree(cmbe); | ||
870 | return ret; | ||
639 | } | 871 | } |
640 | 872 | ||
641 | static void | 873 | static void free_cmbe (struct ccw_device *cdev) |
642 | free_cmbe (struct ccw_device *cdev) | ||
643 | { | 874 | { |
875 | struct cmb_data *cmb_data; | ||
876 | |||
644 | spin_lock_irq(cdev->ccwlock); | 877 | spin_lock_irq(cdev->ccwlock); |
645 | kfree(cdev->private->cmb); | 878 | cmb_data = cdev->private->cmb; |
646 | cdev->private->cmb = NULL; | 879 | cdev->private->cmb = NULL; |
880 | if (cmb_data) | ||
881 | kfree(cmb_data->last_block); | ||
882 | kfree(cmb_data); | ||
647 | spin_unlock_irq(cdev->ccwlock); | 883 | spin_unlock_irq(cdev->ccwlock); |
648 | 884 | ||
649 | /* deactivate global measurement if this is the last channel */ | 885 | /* deactivate global measurement if this is the last channel */ |
@@ -654,89 +890,105 @@ free_cmbe (struct ccw_device *cdev) | |||
654 | spin_unlock(&cmb_area.lock); | 890 | spin_unlock(&cmb_area.lock); |
655 | } | 891 | } |
656 | 892 | ||
657 | static int | 893 | static int set_cmbe(struct ccw_device *cdev, u32 mme) |
658 | set_cmbe(struct ccw_device *cdev, u32 mme) | ||
659 | { | 894 | { |
660 | unsigned long mba; | 895 | unsigned long mba; |
896 | struct cmb_data *cmb_data; | ||
897 | unsigned long flags; | ||
661 | 898 | ||
662 | if (!cdev->private->cmb) | 899 | spin_lock_irqsave(cdev->ccwlock, flags); |
900 | if (!cdev->private->cmb) { | ||
901 | spin_unlock_irqrestore(cdev->ccwlock, flags); | ||
663 | return -EINVAL; | 902 | return -EINVAL; |
664 | mba = mme ? (unsigned long) cmbe_align(cdev->private->cmb) : 0; | 903 | } |
904 | cmb_data = cdev->private->cmb; | ||
905 | mba = mme ? (unsigned long) cmbe_align(cmb_data->hw_block) : 0; | ||
906 | spin_unlock_irqrestore(cdev->ccwlock, flags); | ||
665 | 907 | ||
666 | return set_schib_wait(cdev, mme, 1, mba); | 908 | return set_schib_wait(cdev, mme, 1, mba); |
667 | } | 909 | } |
668 | 910 | ||
669 | 911 | ||
670 | u64 | 912 | static u64 read_cmbe (struct ccw_device *cdev, int index) |
671 | read_cmbe (struct ccw_device *cdev, int index) | ||
672 | { | 913 | { |
673 | /* yes, we have to put it on the stack | 914 | struct cmbe *cmb; |
674 | * because the cmb must only be accessed | 915 | struct cmb_data *cmb_data; |
675 | * atomically, e.g. with mvc */ | ||
676 | struct cmbe cmb; | ||
677 | unsigned long flags; | ||
678 | u32 val; | 916 | u32 val; |
917 | int ret; | ||
918 | unsigned long flags; | ||
679 | 919 | ||
680 | spin_lock_irqsave(cdev->ccwlock, flags); | 920 | ret = cmf_cmb_copy_wait(cdev); |
681 | if (!cdev->private->cmb) { | 921 | if (ret < 0) |
682 | spin_unlock_irqrestore(cdev->ccwlock, flags); | ||
683 | return 0; | 922 | return 0; |
684 | } | ||
685 | 923 | ||
686 | cmb = *cmbe_align(cdev->private->cmb); | 924 | spin_lock_irqsave(cdev->ccwlock, flags); |
687 | spin_unlock_irqrestore(cdev->ccwlock, flags); | 925 | cmb_data = cdev->private->cmb; |
926 | if (!cmb_data) { | ||
927 | ret = 0; | ||
928 | goto out; | ||
929 | } | ||
930 | cmb = cmb_data->last_block; | ||
688 | 931 | ||
689 | switch (index) { | 932 | switch (index) { |
690 | case cmb_ssch_rsch_count: | 933 | case cmb_ssch_rsch_count: |
691 | return cmb.ssch_rsch_count; | 934 | ret = cmb->ssch_rsch_count; |
935 | goto out; | ||
692 | case cmb_sample_count: | 936 | case cmb_sample_count: |
693 | return cmb.sample_count; | 937 | ret = cmb->sample_count; |
938 | goto out; | ||
694 | case cmb_device_connect_time: | 939 | case cmb_device_connect_time: |
695 | val = cmb.device_connect_time; | 940 | val = cmb->device_connect_time; |
696 | break; | 941 | break; |
697 | case cmb_function_pending_time: | 942 | case cmb_function_pending_time: |
698 | val = cmb.function_pending_time; | 943 | val = cmb->function_pending_time; |
699 | break; | 944 | break; |
700 | case cmb_device_disconnect_time: | 945 | case cmb_device_disconnect_time: |
701 | val = cmb.device_disconnect_time; | 946 | val = cmb->device_disconnect_time; |
702 | break; | 947 | break; |
703 | case cmb_control_unit_queuing_time: | 948 | case cmb_control_unit_queuing_time: |
704 | val = cmb.control_unit_queuing_time; | 949 | val = cmb->control_unit_queuing_time; |
705 | break; | 950 | break; |
706 | case cmb_device_active_only_time: | 951 | case cmb_device_active_only_time: |
707 | val = cmb.device_active_only_time; | 952 | val = cmb->device_active_only_time; |
708 | break; | 953 | break; |
709 | case cmb_device_busy_time: | 954 | case cmb_device_busy_time: |
710 | val = cmb.device_busy_time; | 955 | val = cmb->device_busy_time; |
711 | break; | 956 | break; |
712 | case cmb_initial_command_response_time: | 957 | case cmb_initial_command_response_time: |
713 | val = cmb.initial_command_response_time; | 958 | val = cmb->initial_command_response_time; |
714 | break; | 959 | break; |
715 | default: | 960 | default: |
716 | return 0; | 961 | ret = 0; |
962 | goto out; | ||
717 | } | 963 | } |
718 | return time_to_avg_nsec(val, cmb.sample_count); | 964 | ret = time_to_avg_nsec(val, cmb->sample_count); |
965 | out: | ||
966 | spin_unlock_irqrestore(cdev->ccwlock, flags); | ||
967 | return ret; | ||
719 | } | 968 | } |
720 | 969 | ||
721 | static int | 970 | static int readall_cmbe (struct ccw_device *cdev, struct cmbdata *data) |
722 | readall_cmbe (struct ccw_device *cdev, struct cmbdata *data) | ||
723 | { | 971 | { |
724 | /* yes, we have to put it on the stack | 972 | struct cmbe *cmb; |
725 | * because the cmb must only be accessed | 973 | struct cmb_data *cmb_data; |
726 | * atomically, e.g. with mvc */ | ||
727 | struct cmbe cmb; | ||
728 | unsigned long flags; | ||
729 | u64 time; | 974 | u64 time; |
975 | unsigned long flags; | ||
976 | int ret; | ||
730 | 977 | ||
978 | ret = cmf_cmb_copy_wait(cdev); | ||
979 | if (ret < 0) | ||
980 | return ret; | ||
731 | spin_lock_irqsave(cdev->ccwlock, flags); | 981 | spin_lock_irqsave(cdev->ccwlock, flags); |
732 | if (!cdev->private->cmb) { | 982 | cmb_data = cdev->private->cmb; |
733 | spin_unlock_irqrestore(cdev->ccwlock, flags); | 983 | if (!cmb_data) { |
734 | return -ENODEV; | 984 | ret = -ENODEV; |
985 | goto out; | ||
735 | } | 986 | } |
736 | 987 | if (cmb_data->last_update == 0) { | |
737 | cmb = *cmbe_align(cdev->private->cmb); | 988 | ret = -EAGAIN; |
738 | time = get_clock() - cdev->private->cmb_start_time; | 989 | goto out; |
739 | spin_unlock_irqrestore(cdev->ccwlock, flags); | 990 | } |
991 | time = cmb_data->last_update - cdev->private->cmb_start_time; | ||
740 | 992 | ||
741 | memset (data, 0, sizeof(struct cmbdata)); | 993 | memset (data, 0, sizeof(struct cmbdata)); |
742 | 994 | ||
@@ -746,35 +998,38 @@ readall_cmbe (struct ccw_device *cdev, struct cmbdata *data) | |||
746 | /* conver to nanoseconds */ | 998 | /* conver to nanoseconds */ |
747 | data->elapsed_time = (time * 1000) >> 12; | 999 | data->elapsed_time = (time * 1000) >> 12; |
748 | 1000 | ||
1001 | cmb = cmb_data->last_block; | ||
749 | /* copy data to new structure */ | 1002 | /* copy data to new structure */ |
750 | data->ssch_rsch_count = cmb.ssch_rsch_count; | 1003 | data->ssch_rsch_count = cmb->ssch_rsch_count; |
751 | data->sample_count = cmb.sample_count; | 1004 | data->sample_count = cmb->sample_count; |
752 | 1005 | ||
753 | /* time fields are converted to nanoseconds while copying */ | 1006 | /* time fields are converted to nanoseconds while copying */ |
754 | data->device_connect_time = time_to_nsec(cmb.device_connect_time); | 1007 | data->device_connect_time = time_to_nsec(cmb->device_connect_time); |
755 | data->function_pending_time = time_to_nsec(cmb.function_pending_time); | 1008 | data->function_pending_time = time_to_nsec(cmb->function_pending_time); |
756 | data->device_disconnect_time = time_to_nsec(cmb.device_disconnect_time); | 1009 | data->device_disconnect_time = |
1010 | time_to_nsec(cmb->device_disconnect_time); | ||
757 | data->control_unit_queuing_time | 1011 | data->control_unit_queuing_time |
758 | = time_to_nsec(cmb.control_unit_queuing_time); | 1012 | = time_to_nsec(cmb->control_unit_queuing_time); |
759 | data->device_active_only_time | 1013 | data->device_active_only_time |
760 | = time_to_nsec(cmb.device_active_only_time); | 1014 | = time_to_nsec(cmb->device_active_only_time); |
761 | data->device_busy_time = time_to_nsec(cmb.device_busy_time); | 1015 | data->device_busy_time = time_to_nsec(cmb->device_busy_time); |
762 | data->initial_command_response_time | 1016 | data->initial_command_response_time |
763 | = time_to_nsec(cmb.initial_command_response_time); | 1017 | = time_to_nsec(cmb->initial_command_response_time); |
764 | 1018 | ||
765 | return 0; | 1019 | ret = 0; |
1020 | out: | ||
1021 | spin_unlock_irqrestore(cdev->ccwlock, flags); | ||
1022 | return ret; | ||
766 | } | 1023 | } |
767 | 1024 | ||
768 | static void | 1025 | static void reset_cmbe(struct ccw_device *cdev) |
769 | reset_cmbe(struct ccw_device *cdev) | ||
770 | { | 1026 | { |
771 | struct cmbe *cmb; | 1027 | cmf_generic_reset(cdev); |
772 | spin_lock_irq(cdev->ccwlock); | 1028 | } |
773 | cmb = cmbe_align(cdev->private->cmb); | 1029 | |
774 | if (cmb) | 1030 | static void * align_cmbe(void *area) |
775 | memset (cmb, 0, sizeof (*cmb)); | 1031 | { |
776 | cdev->private->cmb_start_time = get_clock(); | 1032 | return cmbe_align(area); |
777 | spin_unlock_irq(cdev->ccwlock); | ||
778 | } | 1033 | } |
779 | 1034 | ||
780 | static struct attribute_group cmf_attr_group_ext; | 1035 | static struct attribute_group cmf_attr_group_ext; |
@@ -786,6 +1041,7 @@ static struct cmb_operations cmbops_extended = { | |||
786 | .read = read_cmbe, | 1041 | .read = read_cmbe, |
787 | .readall = readall_cmbe, | 1042 | .readall = readall_cmbe, |
788 | .reset = reset_cmbe, | 1043 | .reset = reset_cmbe, |
1044 | .align = align_cmbe, | ||
789 | .attr_group = &cmf_attr_group_ext, | 1045 | .attr_group = &cmf_attr_group_ext, |
790 | }; | 1046 | }; |
791 | 1047 | ||
@@ -803,14 +1059,19 @@ cmb_show_avg_sample_interval(struct device *dev, struct device_attribute *attr, | |||
803 | struct ccw_device *cdev; | 1059 | struct ccw_device *cdev; |
804 | long interval; | 1060 | long interval; |
805 | unsigned long count; | 1061 | unsigned long count; |
1062 | struct cmb_data *cmb_data; | ||
806 | 1063 | ||
807 | cdev = to_ccwdev(dev); | 1064 | cdev = to_ccwdev(dev); |
808 | interval = get_clock() - cdev->private->cmb_start_time; | ||
809 | count = cmf_read(cdev, cmb_sample_count); | 1065 | count = cmf_read(cdev, cmb_sample_count); |
810 | if (count) | 1066 | spin_lock_irq(cdev->ccwlock); |
1067 | cmb_data = cdev->private->cmb; | ||
1068 | if (count) { | ||
1069 | interval = cmb_data->last_update - | ||
1070 | cdev->private->cmb_start_time; | ||
811 | interval /= count; | 1071 | interval /= count; |
812 | else | 1072 | } else |
813 | interval = -1; | 1073 | interval = -1; |
1074 | spin_unlock_irq(cdev->ccwlock); | ||
814 | return sprintf(buf, "%ld\n", interval); | 1075 | return sprintf(buf, "%ld\n", interval); |
815 | } | 1076 | } |
816 | 1077 | ||
@@ -823,7 +1084,10 @@ cmb_show_avg_utilization(struct device *dev, struct device_attribute *attr, char | |||
823 | int ret; | 1084 | int ret; |
824 | 1085 | ||
825 | ret = cmf_readall(to_ccwdev(dev), &data); | 1086 | ret = cmf_readall(to_ccwdev(dev), &data); |
826 | if (ret) | 1087 | if (ret == -EAGAIN || ret == -ENODEV) |
1088 | /* No data (yet/currently) available to use for calculation. */ | ||
1089 | return sprintf(buf, "n/a\n"); | ||
1090 | else if (ret) | ||
827 | return ret; | 1091 | return ret; |
828 | 1092 | ||
829 | utilization = data.device_connect_time + | 1093 | utilization = data.device_connect_time + |
@@ -982,6 +1246,13 @@ cmf_readall(struct ccw_device *cdev, struct cmbdata *data) | |||
982 | return cmbops->readall(cdev, data); | 1246 | return cmbops->readall(cdev, data); |
983 | } | 1247 | } |
984 | 1248 | ||
1249 | /* Reenable cmf when a disconnected device becomes available again. */ | ||
1250 | int cmf_reenable(struct ccw_device *cdev) | ||
1251 | { | ||
1252 | cmbops->reset(cdev); | ||
1253 | return cmbops->set(cdev, 2); | ||
1254 | } | ||
1255 | |||
985 | static int __init | 1256 | static int __init |
986 | init_cmf(void) | 1257 | init_cmf(void) |
987 | { | 1258 | { |