aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390/cio/device_ops.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/s390/cio/device_ops.c')
-rw-r--r--drivers/s390/cio/device_ops.c142
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 */
105int ccw_device_is_pathgroup(struct ccw_device *cdev)
106{
107 return cdev->private->flags.pgroup;
108}
109EXPORT_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 */
117int ccw_device_is_multipath(struct ccw_device *cdev)
118{
119 return cdev->private->flags.mpath;
120}
121EXPORT_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/* 508struct stlck_data {
482 * Try to break the lock on a boxed device. 509 struct completion done;
483 */ 510 int rc;
484int 511};
485ccw_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) 513void 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)); 524int 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));
544out_unlock: 562out_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
551void *ccw_device_get_chp_desc(struct ccw_device *cdev, int chp_no) 569void *ccw_device_get_chp_desc(struct ccw_device *cdev, int chp_no)