aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390/cio
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/s390/cio')
-rw-r--r--drivers/s390/cio/device.c43
-rw-r--r--drivers/s390/cio/device.h1
-rw-r--r--drivers/s390/cio/device_fsm.c31
-rw-r--r--drivers/s390/cio/qdio_main.c43
4 files changed, 49 insertions, 69 deletions
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index c4d2f667a2f6..35441fa16be1 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -310,8 +310,6 @@ static void ccw_device_remove_orphan_cb(struct work_struct *work)
put_device(&cdev->dev);
}
-static void ccw_device_call_sch_unregister(struct work_struct *work);
-
static void
ccw_device_remove_disconnected(struct ccw_device *cdev)
{
@@ -335,11 +333,10 @@ ccw_device_remove_disconnected(struct ccw_device *cdev)
spin_unlock_irqrestore(cdev->ccwlock, flags);
PREPARE_WORK(&cdev->private->kick_work,
ccw_device_remove_orphan_cb);
+ queue_work(slow_path_wq, &cdev->private->kick_work);
} else
/* Deregister subchannel, which will kill the ccw device. */
- PREPARE_WORK(&cdev->private->kick_work,
- ccw_device_call_sch_unregister);
- queue_work(slow_path_wq, &cdev->private->kick_work);
+ ccw_device_schedule_sch_unregister(cdev);
}
/**
@@ -471,7 +468,7 @@ static int online_store_recog_and_online(struct ccw_device *cdev)
int ret;
/* Do device recognition, if needed. */
- if (cdev->id.cu_type == 0) {
+ if (cdev->private->state == DEV_STATE_BOXED) {
ret = ccw_device_recognition(cdev);
if (ret) {
CIO_MSG_EVENT(0, "Couldn't start recognition "
@@ -482,17 +479,21 @@ static int online_store_recog_and_online(struct ccw_device *cdev)
}
wait_event(cdev->private->wait_q,
cdev->private->flags.recog_done);
+ if (cdev->private->state != DEV_STATE_OFFLINE)
+ /* recognition failed */
+ return -EAGAIN;
}
if (cdev->drv && cdev->drv->set_online)
ccw_device_set_online(cdev);
return 0;
}
+
static int online_store_handle_online(struct ccw_device *cdev, int force)
{
int ret;
ret = online_store_recog_and_online(cdev);
- if (ret)
+ if (ret && !force)
return ret;
if (force && cdev->private->state == DEV_STATE_BOXED) {
ret = ccw_device_stlck(cdev);
@@ -500,7 +501,9 @@ static int online_store_handle_online(struct ccw_device *cdev, int force)
return ret;
if (cdev->id.cu_type == 0)
cdev->private->state = DEV_STATE_NOT_OPER;
- online_store_recog_and_online(cdev);
+ ret = online_store_recog_and_online(cdev);
+ if (ret)
+ return ret;
}
return 0;
}
@@ -512,7 +515,11 @@ static ssize_t online_store (struct device *dev, struct device_attribute *attr,
int force, ret;
unsigned long i;
- if (atomic_cmpxchg(&cdev->private->onoff, 0, 1) != 0)
+ if ((cdev->private->state != DEV_STATE_OFFLINE &&
+ cdev->private->state != DEV_STATE_ONLINE &&
+ cdev->private->state != DEV_STATE_BOXED &&
+ cdev->private->state != DEV_STATE_DISCONNECTED) ||
+ atomic_cmpxchg(&cdev->private->onoff, 0, 1) != 0)
return -EAGAIN;
if (cdev->drv && !try_module_get(cdev->drv->owner)) {
@@ -1014,6 +1021,13 @@ static void ccw_device_call_sch_unregister(struct work_struct *work)
put_device(&sch->dev);
}
+void ccw_device_schedule_sch_unregister(struct ccw_device *cdev)
+{
+ PREPARE_WORK(&cdev->private->kick_work,
+ ccw_device_call_sch_unregister);
+ queue_work(slow_path_wq, &cdev->private->kick_work);
+}
+
/*
* subchannel recognition done. Called from the state machine.
*/
@@ -1025,19 +1039,17 @@ io_subchannel_recog_done(struct ccw_device *cdev)
return;
}
switch (cdev->private->state) {
+ case DEV_STATE_BOXED:
+ /* Device did not respond in time. */
case DEV_STATE_NOT_OPER:
cdev->private->flags.recog_done = 1;
/* Remove device found not operational. */
if (!get_device(&cdev->dev))
break;
- PREPARE_WORK(&cdev->private->kick_work,
- ccw_device_call_sch_unregister);
- queue_work(slow_path_wq, &cdev->private->kick_work);
+ ccw_device_schedule_sch_unregister(cdev);
if (atomic_dec_and_test(&ccw_device_init_count))
wake_up(&ccw_device_init_wq);
break;
- case DEV_STATE_BOXED:
- /* Device did not respond in time. */
case DEV_STATE_OFFLINE:
/*
* We can't register the device in interrupt context so
@@ -1551,8 +1563,7 @@ static int purge_fn(struct device *dev, void *data)
goto out;
CIO_MSG_EVENT(3, "ccw: purging 0.%x.%04x\n", priv->dev_id.ssid,
priv->dev_id.devno);
- PREPARE_WORK(&cdev->private->kick_work, ccw_device_call_sch_unregister);
- queue_work(slow_path_wq, &cdev->private->kick_work);
+ ccw_device_schedule_sch_unregister(cdev);
out:
/* Abort loop in case of pending signal. */
diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h
index 85e01846ca65..f1cbbd94ad4e 100644
--- a/drivers/s390/cio/device.h
+++ b/drivers/s390/cio/device.h
@@ -87,6 +87,7 @@ int ccw_device_is_orphan(struct ccw_device *);
int ccw_device_recognition(struct ccw_device *);
int ccw_device_online(struct ccw_device *);
int ccw_device_offline(struct ccw_device *);
+void ccw_device_schedule_sch_unregister(struct ccw_device *);
int ccw_purge_blacklisted(void);
/* Function prototypes for device status and basic sense stuff. */
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c
index 87b4bfca080f..e46049261561 100644
--- a/drivers/s390/cio/device_fsm.c
+++ b/drivers/s390/cio/device_fsm.c
@@ -256,13 +256,12 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
old_lpm = 0;
if (sch->lpm != old_lpm)
__recover_lost_chpids(sch, old_lpm);
- if (cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID) {
- if (state == DEV_STATE_NOT_OPER) {
- cdev->private->flags.recog_done = 1;
- cdev->private->state = DEV_STATE_DISCONNECTED;
- return;
- }
- /* Boxed devices don't need extra treatment. */
+ if (cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID &&
+ (state == DEV_STATE_NOT_OPER || state == DEV_STATE_BOXED)) {
+ cdev->private->flags.recog_done = 1;
+ cdev->private->state = DEV_STATE_DISCONNECTED;
+ wake_up(&cdev->private->wait_q);
+ return;
}
notify = 0;
same_dev = 0; /* Keep the compiler quiet... */
@@ -274,7 +273,7 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
sch->schid.ssid, sch->schid.sch_no);
break;
case DEV_STATE_OFFLINE:
- if (cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID) {
+ if (cdev->online) {
same_dev = ccw_device_handle_oper(cdev);
notify = 1;
}
@@ -307,12 +306,17 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
" subchannel 0.%x.%04x\n",
cdev->private->dev_id.devno,
sch->schid.ssid, sch->schid.sch_no);
+ if (cdev->id.cu_type != 0) { /* device was recognized before */
+ cdev->private->flags.recog_done = 1;
+ cdev->private->state = DEV_STATE_BOXED;
+ wake_up(&cdev->private->wait_q);
+ return;
+ }
break;
}
cdev->private->state = state;
io_subchannel_recog_done(cdev);
- if (state != DEV_STATE_NOT_OPER)
- wake_up(&cdev->private->wait_q);
+ wake_up(&cdev->private->wait_q);
}
/*
@@ -390,10 +394,13 @@ ccw_device_done(struct ccw_device *cdev, int state)
cdev->private->state = state;
-
- if (state == DEV_STATE_BOXED)
+ if (state == DEV_STATE_BOXED) {
CIO_MSG_EVENT(0, "Boxed device %04x on subchannel %04x\n",
cdev->private->dev_id.devno, sch->schid.sch_no);
+ if (cdev->online && !ccw_device_notify(cdev, CIO_BOXED))
+ ccw_device_schedule_sch_unregister(cdev);
+ cdev->private->flags.donotify = 0;
+ }
if (cdev->private->flags.donotify) {
cdev->private->flags.donotify = 0;
diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c
index 9e8a2914259b..accd957454e7 100644
--- a/drivers/s390/cio/qdio_main.c
+++ b/drivers/s390/cio/qdio_main.c
@@ -881,42 +881,6 @@ no_handler:
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_STOPPED);
}
-static void qdio_call_shutdown(struct work_struct *work)
-{
- struct ccw_device_private *priv;
- struct ccw_device *cdev;
-
- priv = container_of(work, struct ccw_device_private, kick_work);
- cdev = priv->cdev;
- qdio_shutdown(cdev, QDIO_FLAG_CLEANUP_USING_CLEAR);
- put_device(&cdev->dev);
-}
-
-static void qdio_int_error(struct ccw_device *cdev)
-{
- struct qdio_irq *irq_ptr = cdev->private->qdio_data;
-
- switch (irq_ptr->state) {
- case QDIO_IRQ_STATE_INACTIVE:
- case QDIO_IRQ_STATE_CLEANUP:
- qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR);
- break;
- case QDIO_IRQ_STATE_ESTABLISHED:
- case QDIO_IRQ_STATE_ACTIVE:
- qdio_set_state(irq_ptr, QDIO_IRQ_STATE_STOPPED);
- if (get_device(&cdev->dev)) {
- /* Can't call shutdown from interrupt context. */
- PREPARE_WORK(&cdev->private->kick_work,
- qdio_call_shutdown);
- queue_work(ccw_device_work, &cdev->private->kick_work);
- }
- break;
- default:
- WARN_ON(1);
- }
- wake_up(&cdev->private->wait_q);
-}
-
static int qdio_establish_check_errors(struct ccw_device *cdev, int cstat,
int dstat)
{
@@ -973,10 +937,8 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm,
switch (PTR_ERR(irb)) {
case -EIO:
DBF_ERROR("%4x IO error", irq_ptr->schid.sch_no);
- return;
- case -ETIMEDOUT:
- DBF_ERROR("%4x IO timeout", irq_ptr->schid.sch_no);
- qdio_int_error(cdev);
+ qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR);
+ wake_up(&cdev->private->wait_q);
return;
default:
WARN_ON(1);
@@ -1001,7 +963,6 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm,
case QDIO_IRQ_STATE_ACTIVE:
if (cstat & SCHN_STAT_PCI) {
qdio_int_handler_pci(irq_ptr);
- /* no state change so no need to wake up wait_q */
return;
}
if ((cstat & ~SCHN_STAT_PCI) || dstat) {