diff options
Diffstat (limited to 'drivers/s390/cio/device_ops.c')
-rw-r--r-- | drivers/s390/cio/device_ops.c | 142 |
1 files changed, 80 insertions, 62 deletions
diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c index 2d0efee8a29..6da84543dfe 100644 --- a/drivers/s390/cio/device_ops.c +++ b/drivers/s390/cio/device_ops.c | |||
@@ -11,6 +11,7 @@ | |||
11 | #include <linux/list.h> | 11 | #include <linux/list.h> |
12 | #include <linux/device.h> | 12 | #include <linux/device.h> |
13 | #include <linux/delay.h> | 13 | #include <linux/delay.h> |
14 | #include <linux/completion.h> | ||
14 | 15 | ||
15 | #include <asm/ccwdev.h> | 16 | #include <asm/ccwdev.h> |
16 | #include <asm/idals.h> | 17 | #include <asm/idals.h> |
@@ -46,6 +47,7 @@ int ccw_device_set_options_mask(struct ccw_device *cdev, unsigned long flags) | |||
46 | cdev->private->options.repall = (flags & CCWDEV_REPORT_ALL) != 0; | 47 | cdev->private->options.repall = (flags & CCWDEV_REPORT_ALL) != 0; |
47 | cdev->private->options.pgroup = (flags & CCWDEV_DO_PATHGROUP) != 0; | 48 | cdev->private->options.pgroup = (flags & CCWDEV_DO_PATHGROUP) != 0; |
48 | cdev->private->options.force = (flags & CCWDEV_ALLOW_FORCE) != 0; | 49 | cdev->private->options.force = (flags & CCWDEV_ALLOW_FORCE) != 0; |
50 | cdev->private->options.mpath = (flags & CCWDEV_DO_MULTIPATH) != 0; | ||
49 | return 0; | 51 | return 0; |
50 | } | 52 | } |
51 | 53 | ||
@@ -74,6 +76,7 @@ int ccw_device_set_options(struct ccw_device *cdev, unsigned long flags) | |||
74 | cdev->private->options.repall |= (flags & CCWDEV_REPORT_ALL) != 0; | 76 | cdev->private->options.repall |= (flags & CCWDEV_REPORT_ALL) != 0; |
75 | cdev->private->options.pgroup |= (flags & CCWDEV_DO_PATHGROUP) != 0; | 77 | cdev->private->options.pgroup |= (flags & CCWDEV_DO_PATHGROUP) != 0; |
76 | cdev->private->options.force |= (flags & CCWDEV_ALLOW_FORCE) != 0; | 78 | cdev->private->options.force |= (flags & CCWDEV_ALLOW_FORCE) != 0; |
79 | cdev->private->options.mpath |= (flags & CCWDEV_DO_MULTIPATH) != 0; | ||
77 | return 0; | 80 | return 0; |
78 | } | 81 | } |
79 | 82 | ||
@@ -90,9 +93,34 @@ void ccw_device_clear_options(struct ccw_device *cdev, unsigned long flags) | |||
90 | cdev->private->options.repall &= (flags & CCWDEV_REPORT_ALL) == 0; | 93 | cdev->private->options.repall &= (flags & CCWDEV_REPORT_ALL) == 0; |
91 | cdev->private->options.pgroup &= (flags & CCWDEV_DO_PATHGROUP) == 0; | 94 | cdev->private->options.pgroup &= (flags & CCWDEV_DO_PATHGROUP) == 0; |
92 | cdev->private->options.force &= (flags & CCWDEV_ALLOW_FORCE) == 0; | 95 | cdev->private->options.force &= (flags & CCWDEV_ALLOW_FORCE) == 0; |
96 | cdev->private->options.mpath &= (flags & CCWDEV_DO_MULTIPATH) == 0; | ||
93 | } | 97 | } |
94 | 98 | ||
95 | /** | 99 | /** |
100 | * ccw_device_is_pathgroup - determine if paths to this device are grouped | ||
101 | * @cdev: ccw device | ||
102 | * | ||
103 | * Return non-zero if there is a path group, zero otherwise. | ||
104 | */ | ||
105 | int ccw_device_is_pathgroup(struct ccw_device *cdev) | ||
106 | { | ||
107 | return cdev->private->flags.pgroup; | ||
108 | } | ||
109 | EXPORT_SYMBOL(ccw_device_is_pathgroup); | ||
110 | |||
111 | /** | ||
112 | * ccw_device_is_multipath - determine if device is operating in multipath mode | ||
113 | * @cdev: ccw device | ||
114 | * | ||
115 | * Return non-zero if device is operating in multipath mode, zero otherwise. | ||
116 | */ | ||
117 | int ccw_device_is_multipath(struct ccw_device *cdev) | ||
118 | { | ||
119 | return cdev->private->flags.mpath; | ||
120 | } | ||
121 | EXPORT_SYMBOL(ccw_device_is_multipath); | ||
122 | |||
123 | /** | ||
96 | * ccw_device_clear() - terminate I/O request processing | 124 | * ccw_device_clear() - terminate I/O request processing |
97 | * @cdev: target ccw device | 125 | * @cdev: target ccw device |
98 | * @intparm: interruption parameter; value is only used if no I/O is | 126 | * @intparm: interruption parameter; value is only used if no I/O is |
@@ -167,8 +195,7 @@ int ccw_device_start_key(struct ccw_device *cdev, struct ccw1 *cpa, | |||
167 | return -EINVAL; | 195 | return -EINVAL; |
168 | if (cdev->private->state == DEV_STATE_NOT_OPER) | 196 | if (cdev->private->state == DEV_STATE_NOT_OPER) |
169 | return -ENODEV; | 197 | return -ENODEV; |
170 | if (cdev->private->state == DEV_STATE_VERIFY || | 198 | if (cdev->private->state == DEV_STATE_VERIFY) { |
171 | cdev->private->state == DEV_STATE_CLEAR_VERIFY) { | ||
172 | /* Remember to fake irb when finished. */ | 199 | /* Remember to fake irb when finished. */ |
173 | if (!cdev->private->flags.fake_irb) { | 200 | if (!cdev->private->flags.fake_irb) { |
174 | cdev->private->flags.fake_irb = 1; | 201 | cdev->private->flags.fake_irb = 1; |
@@ -478,74 +505,65 @@ __u8 ccw_device_get_path_mask(struct ccw_device *cdev) | |||
478 | return sch->lpm; | 505 | return sch->lpm; |
479 | } | 506 | } |
480 | 507 | ||
481 | /* | 508 | struct stlck_data { |
482 | * Try to break the lock on a boxed device. | 509 | struct completion done; |
483 | */ | 510 | int rc; |
484 | int | 511 | }; |
485 | ccw_device_stlck(struct ccw_device *cdev) | ||
486 | { | ||
487 | void *buf, *buf2; | ||
488 | unsigned long flags; | ||
489 | struct subchannel *sch; | ||
490 | int ret; | ||
491 | 512 | ||
492 | if (!cdev) | 513 | void ccw_device_stlck_done(struct ccw_device *cdev, void *data, int rc) |
493 | return -ENODEV; | 514 | { |
515 | struct stlck_data *sdata = data; | ||
494 | 516 | ||
495 | if (cdev->drv && !cdev->private->options.force) | 517 | sdata->rc = rc; |
496 | return -EINVAL; | 518 | complete(&sdata->done); |
519 | } | ||
497 | 520 | ||
498 | sch = to_subchannel(cdev->dev.parent); | 521 | /* |
499 | 522 | * Perform unconditional reserve + release. | |
500 | CIO_TRACE_EVENT(2, "stl lock"); | 523 | */ |
501 | CIO_TRACE_EVENT(2, dev_name(&cdev->dev)); | 524 | int ccw_device_stlck(struct ccw_device *cdev) |
525 | { | ||
526 | struct subchannel *sch = to_subchannel(cdev->dev.parent); | ||
527 | struct stlck_data data; | ||
528 | u8 *buffer; | ||
529 | int rc; | ||
502 | 530 | ||
503 | buf = kmalloc(32*sizeof(char), GFP_DMA|GFP_KERNEL); | 531 | /* Check if steal lock operation is valid for this device. */ |
504 | if (!buf) | 532 | if (cdev->drv) { |
505 | return -ENOMEM; | 533 | if (!cdev->private->options.force) |
506 | buf2 = kmalloc(32*sizeof(char), GFP_DMA|GFP_KERNEL); | 534 | return -EINVAL; |
507 | if (!buf2) { | ||
508 | kfree(buf); | ||
509 | return -ENOMEM; | ||
510 | } | 535 | } |
511 | spin_lock_irqsave(sch->lock, flags); | 536 | buffer = kzalloc(64, GFP_DMA | GFP_KERNEL); |
512 | ret = cio_enable_subchannel(sch, (u32)(addr_t)sch); | 537 | if (!buffer) |
513 | if (ret) | 538 | return -ENOMEM; |
514 | goto out_unlock; | 539 | init_completion(&data.done); |
515 | /* | 540 | data.rc = -EIO; |
516 | * Setup ccw. We chain an unconditional reserve and a release so we | 541 | spin_lock_irq(sch->lock); |
517 | * only break the lock. | 542 | rc = cio_enable_subchannel(sch, (u32) (addr_t) sch); |
518 | */ | 543 | if (rc) |
519 | cdev->private->iccws[0].cmd_code = CCW_CMD_STLCK; | ||
520 | cdev->private->iccws[0].cda = (__u32) __pa(buf); | ||
521 | cdev->private->iccws[0].count = 32; | ||
522 | cdev->private->iccws[0].flags = CCW_FLAG_CC; | ||
523 | cdev->private->iccws[1].cmd_code = CCW_CMD_RELEASE; | ||
524 | cdev->private->iccws[1].cda = (__u32) __pa(buf2); | ||
525 | cdev->private->iccws[1].count = 32; | ||
526 | cdev->private->iccws[1].flags = 0; | ||
527 | ret = cio_start(sch, cdev->private->iccws, 0); | ||
528 | if (ret) { | ||
529 | cio_disable_subchannel(sch); //FIXME: return code? | ||
530 | goto out_unlock; | 544 | goto out_unlock; |
545 | /* Perform operation. */ | ||
546 | cdev->private->state = DEV_STATE_STEAL_LOCK, | ||
547 | ccw_device_stlck_start(cdev, &data, &buffer[0], &buffer[32]); | ||
548 | spin_unlock_irq(sch->lock); | ||
549 | /* Wait for operation to finish. */ | ||
550 | if (wait_for_completion_interruptible(&data.done)) { | ||
551 | /* Got a signal. */ | ||
552 | spin_lock_irq(sch->lock); | ||
553 | ccw_request_cancel(cdev); | ||
554 | spin_unlock_irq(sch->lock); | ||
555 | wait_for_completion(&data.done); | ||
531 | } | 556 | } |
532 | cdev->private->irb.scsw.cmd.actl |= SCSW_ACTL_START_PEND; | 557 | rc = data.rc; |
533 | spin_unlock_irqrestore(sch->lock, flags); | 558 | /* Check results. */ |
534 | wait_event(cdev->private->wait_q, | 559 | spin_lock_irq(sch->lock); |
535 | cdev->private->irb.scsw.cmd.actl == 0); | 560 | cio_disable_subchannel(sch); |
536 | spin_lock_irqsave(sch->lock, flags); | 561 | cdev->private->state = DEV_STATE_BOXED; |
537 | cio_disable_subchannel(sch); //FIXME: return code? | ||
538 | if ((cdev->private->irb.scsw.cmd.dstat != | ||
539 | (DEV_STAT_CHN_END|DEV_STAT_DEV_END)) || | ||
540 | (cdev->private->irb.scsw.cmd.cstat != 0)) | ||
541 | ret = -EIO; | ||
542 | /* Clear irb. */ | ||
543 | memset(&cdev->private->irb, 0, sizeof(struct irb)); | ||
544 | out_unlock: | 562 | out_unlock: |
545 | kfree(buf); | 563 | spin_unlock_irq(sch->lock); |
546 | kfree(buf2); | 564 | kfree(buffer); |
547 | spin_unlock_irqrestore(sch->lock, flags); | 565 | |
548 | return ret; | 566 | return rc; |
549 | } | 567 | } |
550 | 568 | ||
551 | void *ccw_device_get_chp_desc(struct ccw_device *cdev, int chp_no) | 569 | void *ccw_device_get_chp_desc(struct ccw_device *cdev, int chp_no) |