aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390/cio
diff options
context:
space:
mode:
authorPeter Oberparleiter <peter.oberparleiter@de.ibm.com>2009-12-07 06:51:32 -0500
committerMartin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com>2009-12-07 06:51:32 -0500
commitd7d12ef2befac4fed0dccaddff11338b654804df (patch)
tree1563b299e609024844affbc3ebba99c0718db238 /drivers/s390/cio
parent52ef0608e3ee4a511725e443c4b572fece22b353 (diff)
[S390] cio: make steal lock procedure more robust
An Unconditional Reserve + Release operation (steal lock) for a boxed device may fail when encountering special error cases (e.g. unit checks or path errors). Fix this by using the more robust ccw_request infrastructure for performing the steal lock CCW program. Signed-off-by: Peter Oberparleiter <peter.oberparleiter@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390/cio')
-rw-r--r--drivers/s390/cio/device.h4
-rw-r--r--drivers/s390/cio/device_fsm.c55
-rw-r--r--drivers/s390/cio/device_ops.c112
-rw-r--r--drivers/s390/cio/device_pgid.c52
4 files changed, 134 insertions, 89 deletions
diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h
index 2df519bb877e..bcfe13e42638 100644
--- a/drivers/s390/cio/device.h
+++ b/drivers/s390/cio/device.h
@@ -28,6 +28,7 @@ enum dev_state {
28 DEV_STATE_DISCONNECTED_SENSE_ID, 28 DEV_STATE_DISCONNECTED_SENSE_ID,
29 DEV_STATE_CMFCHANGE, 29 DEV_STATE_CMFCHANGE,
30 DEV_STATE_CMFUPDATE, 30 DEV_STATE_CMFUPDATE,
31 DEV_STATE_STEAL_LOCK,
31 /* last element! */ 32 /* last element! */
32 NR_DEV_STATES 33 NR_DEV_STATES
33}; 34};
@@ -116,6 +117,9 @@ void ccw_device_verify_done(struct ccw_device *, int);
116void ccw_device_disband_start(struct ccw_device *); 117void ccw_device_disband_start(struct ccw_device *);
117void ccw_device_disband_done(struct ccw_device *, int); 118void ccw_device_disband_done(struct ccw_device *, int);
118 119
120void ccw_device_stlck_start(struct ccw_device *, void *, void *, void *);
121void ccw_device_stlck_done(struct ccw_device *, void *, int);
122
119int ccw_device_call_handler(struct ccw_device *); 123int ccw_device_call_handler(struct ccw_device *);
120 124
121int ccw_device_stlck(struct ccw_device *); 125int ccw_device_stlck(struct ccw_device *);
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c
index 8d565ff85e43..7d42417bc2c7 100644
--- a/drivers/s390/cio/device_fsm.c
+++ b/drivers/s390/cio/device_fsm.c
@@ -641,6 +641,23 @@ ccw_device_online_verify(struct ccw_device *cdev, enum dev_event dev_event)
641} 641}
642 642
643/* 643/*
644 * Handle path verification event in boxed state.
645 */
646static void ccw_device_boxed_verify(struct ccw_device *cdev,
647 enum dev_event dev_event)
648{
649 struct subchannel *sch = to_subchannel(cdev->dev.parent);
650
651 if (cdev->online) {
652 if (cio_enable_subchannel(sch, (u32) (addr_t) sch))
653 ccw_device_done(cdev, DEV_STATE_NOT_OPER);
654 else
655 ccw_device_online_verify(cdev, dev_event);
656 } else
657 css_schedule_eval(sch->schid);
658}
659
660/*
644 * Got an interrupt for a normal io (state online). 661 * Got an interrupt for a normal io (state online).
645 */ 662 */
646static void 663static void
@@ -817,32 +834,6 @@ ccw_device_delay_verify(struct ccw_device *cdev, enum dev_event dev_event)
817} 834}
818 835
819static void 836static void
820ccw_device_stlck_done(struct ccw_device *cdev, enum dev_event dev_event)
821{
822 struct irb *irb;
823
824 switch (dev_event) {
825 case DEV_EVENT_INTERRUPT:
826 irb = (struct irb *) __LC_IRB;
827 /* Check for unsolicited interrupt. */
828 if ((scsw_stctl(&irb->scsw) ==
829 (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) &&
830 (!scsw_cc(&irb->scsw)))
831 /* FIXME: we should restart stlck here, but this
832 * is extremely unlikely ... */
833 goto out_wakeup;
834
835 ccw_device_accumulate_irb(cdev, irb);
836 /* We don't care about basic sense etc. */
837 break;
838 default: /* timeout */
839 break;
840 }
841out_wakeup:
842 wake_up(&cdev->private->wait_q);
843}
844
845static void
846ccw_device_start_id(struct ccw_device *cdev, enum dev_event dev_event) 837ccw_device_start_id(struct ccw_device *cdev, enum dev_event dev_event)
847{ 838{
848 struct subchannel *sch; 839 struct subchannel *sch;
@@ -1010,9 +1001,9 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = {
1010 }, 1001 },
1011 [DEV_STATE_BOXED] = { 1002 [DEV_STATE_BOXED] = {
1012 [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, 1003 [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper,
1013 [DEV_EVENT_INTERRUPT] = ccw_device_stlck_done, 1004 [DEV_EVENT_INTERRUPT] = ccw_device_nop,
1014 [DEV_EVENT_TIMEOUT] = ccw_device_stlck_done, 1005 [DEV_EVENT_TIMEOUT] = ccw_device_nop,
1015 [DEV_EVENT_VERIFY] = ccw_device_nop, 1006 [DEV_EVENT_VERIFY] = ccw_device_boxed_verify,
1016 }, 1007 },
1017 /* states to wait for i/o completion before doing something */ 1008 /* states to wait for i/o completion before doing something */
1018 [DEV_STATE_TIMEOUT_KILL] = { 1009 [DEV_STATE_TIMEOUT_KILL] = {
@@ -1052,6 +1043,12 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = {
1052 [DEV_EVENT_TIMEOUT] = ccw_device_update_cmfblock, 1043 [DEV_EVENT_TIMEOUT] = ccw_device_update_cmfblock,
1053 [DEV_EVENT_VERIFY] = ccw_device_update_cmfblock, 1044 [DEV_EVENT_VERIFY] = ccw_device_update_cmfblock,
1054 }, 1045 },
1046 [DEV_STATE_STEAL_LOCK] = {
1047 [DEV_EVENT_NOTOPER] = ccw_device_request_event,
1048 [DEV_EVENT_INTERRUPT] = ccw_device_request_event,
1049 [DEV_EVENT_TIMEOUT] = ccw_device_request_event,
1050 [DEV_EVENT_VERIFY] = ccw_device_nop,
1051 },
1055}; 1052};
1056 1053
1057EXPORT_SYMBOL_GPL(ccw_device_set_timeout); 1054EXPORT_SYMBOL_GPL(ccw_device_set_timeout);
diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c
index d4be16acebe4..6da84543dfe9 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>
@@ -504,74 +505,65 @@ __u8 ccw_device_get_path_mask(struct ccw_device *cdev)
504 return sch->lpm; 505 return sch->lpm;
505} 506}
506 507
507/* 508struct stlck_data {
508 * Try to break the lock on a boxed device. 509 struct completion done;
509 */ 510 int rc;
510int 511};
511ccw_device_stlck(struct ccw_device *cdev)
512{
513 void *buf, *buf2;
514 unsigned long flags;
515 struct subchannel *sch;
516 int ret;
517 512
518 if (!cdev) 513void ccw_device_stlck_done(struct ccw_device *cdev, void *data, int rc)
519 return -ENODEV; 514{
515 struct stlck_data *sdata = data;
520 516
521 if (cdev->drv && !cdev->private->options.force) 517 sdata->rc = rc;
522 return -EINVAL; 518 complete(&sdata->done);
519}
523 520
524 sch = to_subchannel(cdev->dev.parent); 521/*
525 522 * Perform unconditional reserve + release.
526 CIO_TRACE_EVENT(2, "stl lock"); 523 */
527 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;
528 530
529 buf = kmalloc(32*sizeof(char), GFP_DMA|GFP_KERNEL); 531 /* Check if steal lock operation is valid for this device. */
530 if (!buf) 532 if (cdev->drv) {
531 return -ENOMEM; 533 if (!cdev->private->options.force)
532 buf2 = kmalloc(32*sizeof(char), GFP_DMA|GFP_KERNEL); 534 return -EINVAL;
533 if (!buf2) {
534 kfree(buf);
535 return -ENOMEM;
536 } 535 }
537 spin_lock_irqsave(sch->lock, flags); 536 buffer = kzalloc(64, GFP_DMA | GFP_KERNEL);
538 ret = cio_enable_subchannel(sch, (u32)(addr_t)sch); 537 if (!buffer)
539 if (ret) 538 return -ENOMEM;
540 goto out_unlock; 539 init_completion(&data.done);
541 /* 540 data.rc = -EIO;
542 * Setup ccw. We chain an unconditional reserve and a release so we 541 spin_lock_irq(sch->lock);
543 * only break the lock. 542 rc = cio_enable_subchannel(sch, (u32) (addr_t) sch);
544 */ 543 if (rc)
545 cdev->private->iccws[0].cmd_code = CCW_CMD_STLCK;
546 cdev->private->iccws[0].cda = (__u32) __pa(buf);
547 cdev->private->iccws[0].count = 32;
548 cdev->private->iccws[0].flags = CCW_FLAG_CC;
549 cdev->private->iccws[1].cmd_code = CCW_CMD_RELEASE;
550 cdev->private->iccws[1].cda = (__u32) __pa(buf2);
551 cdev->private->iccws[1].count = 32;
552 cdev->private->iccws[1].flags = 0;
553 ret = cio_start(sch, cdev->private->iccws, 0);
554 if (ret) {
555 cio_disable_subchannel(sch); //FIXME: return code?
556 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);
557 } 556 }
558 cdev->private->irb.scsw.cmd.actl |= SCSW_ACTL_START_PEND; 557 rc = data.rc;
559 spin_unlock_irqrestore(sch->lock, flags); 558 /* Check results. */
560 wait_event(cdev->private->wait_q, 559 spin_lock_irq(sch->lock);
561 cdev->private->irb.scsw.cmd.actl == 0); 560 cio_disable_subchannel(sch);
562 spin_lock_irqsave(sch->lock, flags); 561 cdev->private->state = DEV_STATE_BOXED;
563 cio_disable_subchannel(sch); //FIXME: return code?
564 if ((cdev->private->irb.scsw.cmd.dstat !=
565 (DEV_STAT_CHN_END|DEV_STAT_DEV_END)) ||
566 (cdev->private->irb.scsw.cmd.cstat != 0))
567 ret = -EIO;
568 /* Clear irb. */
569 memset(&cdev->private->irb, 0, sizeof(struct irb));
570out_unlock: 562out_unlock:
571 kfree(buf); 563 spin_unlock_irq(sch->lock);
572 kfree(buf2); 564 kfree(buffer);
573 spin_unlock_irqrestore(sch->lock, flags); 565
574 return ret; 566 return rc;
575} 567}
576 568
577void *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)
diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c
index 4d54abd82b8c..5bcefeaff744 100644
--- a/drivers/s390/cio/device_pgid.c
+++ b/drivers/s390/cio/device_pgid.c
@@ -507,3 +507,55 @@ void ccw_device_disband_start(struct ccw_device *cdev)
507 spid_build_cp(cdev, fn); 507 spid_build_cp(cdev, fn);
508 ccw_request_start(cdev); 508 ccw_request_start(cdev);
509} 509}
510
511static void stlck_build_cp(struct ccw_device *cdev, void *buf1, void *buf2)
512{
513 struct ccw_request *req = &cdev->private->req;
514 struct ccw1 *cp = cdev->private->iccws;
515
516 cp[0].cmd_code = CCW_CMD_STLCK;
517 cp[0].cda = (u32) (addr_t) buf1;
518 cp[0].count = 32;
519 cp[0].flags = CCW_FLAG_CC;
520 cp[1].cmd_code = CCW_CMD_RELEASE;
521 cp[1].cda = (u32) (addr_t) buf2;
522 cp[1].count = 32;
523 cp[1].flags = 0;
524 req->cp = cp;
525}
526
527static void stlck_callback(struct ccw_device *cdev, void *data, int rc)
528{
529 ccw_device_stlck_done(cdev, data, rc);
530}
531
532/**
533 * ccw_device_stlck_start - perform unconditional release
534 * @cdev: ccw device
535 * @data: data pointer to be passed to ccw_device_stlck_done
536 * @buf1: data pointer used in channel program
537 * @buf2: data pointer used in channel program
538 *
539 * Execute a channel program on @cdev to release an existing PGID reservation.
540 * When finished, call ccw_device_stlck_done with a return code specifying the
541 * result.
542 */
543void ccw_device_stlck_start(struct ccw_device *cdev, void *data, void *buf1,
544 void *buf2)
545{
546 struct subchannel *sch = to_subchannel(cdev->dev.parent);
547 struct ccw_request *req = &cdev->private->req;
548
549 CIO_TRACE_EVENT(4, "stlck");
550 CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id));
551 /* Request setup. */
552 memset(req, 0, sizeof(*req));
553 req->timeout = PGID_TIMEOUT;
554 req->maxretries = PGID_RETRIES;
555 req->lpm = sch->schib.pmcw.pam & sch->opm;
556 req->data = data;
557 req->callback = stlck_callback;
558 stlck_build_cp(cdev, buf1, buf2);
559 ccw_request_start(cdev);
560}
561