in cio/device.c [1422:1542]
static int io_subchannel_sch_event(struct subchannel *sch, int process)
{
unsigned long flags;
struct ccw_device *cdev;
struct ccw_dev_id dev_id;
enum io_sch_action action;
int rc = -EAGAIN;
spin_lock_irqsave(sch->lock, flags);
if (!device_is_registered(&sch->dev))
goto out_unlock;
if (work_pending(&sch->todo_work))
goto out_unlock;
cdev = sch_get_cdev(sch);
if (cdev && work_pending(&cdev->private->todo_work))
goto out_unlock;
action = sch_get_action(sch);
CIO_MSG_EVENT(2, "event: sch 0.%x.%04x, process=%d, action=%d\n",
sch->schid.ssid, sch->schid.sch_no, process,
action);
/* Perform immediate actions while holding the lock. */
switch (action) {
case IO_SCH_REPROBE:
/* Trigger device recognition. */
ccw_device_trigger_reprobe(cdev);
rc = 0;
goto out_unlock;
case IO_SCH_VERIFY:
/* Trigger path verification. */
io_subchannel_verify(sch);
rc = 0;
goto out_unlock;
case IO_SCH_DISC:
ccw_device_set_disconnected(cdev);
rc = 0;
goto out_unlock;
case IO_SCH_ORPH_UNREG:
case IO_SCH_ORPH_ATTACH:
ccw_device_set_disconnected(cdev);
break;
case IO_SCH_UNREG_ATTACH:
case IO_SCH_UNREG:
if (!cdev)
break;
if (cdev->private->state == DEV_STATE_SENSE_ID) {
/*
* Note: delayed work triggered by this event
* and repeated calls to sch_event are synchronized
* by the above check for work_pending(cdev).
*/
dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
} else
ccw_device_set_notoper(cdev);
break;
case IO_SCH_NOP:
rc = 0;
goto out_unlock;
default:
break;
}
spin_unlock_irqrestore(sch->lock, flags);
/* All other actions require process context. */
if (!process)
goto out;
/* Handle attached ccw device. */
switch (action) {
case IO_SCH_ORPH_UNREG:
case IO_SCH_ORPH_ATTACH:
/* Move ccw device to orphanage. */
rc = ccw_device_move_to_orph(cdev);
if (rc)
goto out;
break;
case IO_SCH_UNREG_ATTACH:
spin_lock_irqsave(sch->lock, flags);
sch_set_cdev(sch, NULL);
spin_unlock_irqrestore(sch->lock, flags);
/* Unregister ccw device. */
ccw_device_unregister(cdev);
break;
default:
break;
}
/* Handle subchannel. */
switch (action) {
case IO_SCH_ORPH_UNREG:
case IO_SCH_UNREG:
css_sch_device_unregister(sch);
break;
case IO_SCH_ORPH_ATTACH:
case IO_SCH_UNREG_ATTACH:
case IO_SCH_ATTACH:
dev_id.ssid = sch->schid.ssid;
dev_id.devno = sch->schib.pmcw.dev;
cdev = get_ccwdev_by_dev_id(&dev_id);
if (!cdev) {
sch_create_and_recog_new_device(sch);
break;
}
rc = ccw_device_move_to_sch(cdev, sch);
if (rc) {
/* Release reference from get_ccwdev_by_dev_id() */
put_device(&cdev->dev);
goto out;
}
spin_lock_irqsave(sch->lock, flags);
ccw_device_trigger_reprobe(cdev);
spin_unlock_irqrestore(sch->lock, flags);
/* Release reference from get_ccwdev_by_dev_id() */
put_device(&cdev->dev);
break;
default:
break;
}
return 0;
out_unlock:
spin_unlock_irqrestore(sch->lock, flags);
out:
return rc;
}