Commit d7b5a4c9 authored by Cornelia Huck's avatar Cornelia Huck Committed by Martin Schwidefsky

[S390] Support for disconnected devices reappearing on another subchannel.

- create a 'pseudo_subchannel' per channel subsystem (the 'orphanage')
- use the orphanage as a shelter for ccw_devices that can't remain on the same
  subchannel
Signed-off-by: default avatarCornelia Huck <cornelia.huck@de.ibm.com>
Cc: Greg KH <greg@kroah.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 2ec22984
......@@ -415,6 +415,8 @@ cio_enable_subchannel (struct subchannel *sch, unsigned int isc)
CIO_TRACE_EVENT (2, "ensch");
CIO_TRACE_EVENT (2, sch->dev.bus_id);
if (sch_is_pseudo_sch(sch))
return -EINVAL;
ccode = stsch (sch->schid, &sch->schib);
if (ccode)
return -ENODEV;
......@@ -462,6 +464,8 @@ cio_disable_subchannel (struct subchannel *sch)
CIO_TRACE_EVENT (2, "dissch");
CIO_TRACE_EVENT (2, sch->dev.bus_id);
if (sch_is_pseudo_sch(sch))
return 0;
ccode = stsch (sch->schid, &sch->schib);
if (ccode == 3) /* Not operational. */
return -ENODEV;
......@@ -496,7 +500,7 @@ cio_disable_subchannel (struct subchannel *sch)
return ret;
}
static int cio_create_sch_lock(struct subchannel *sch)
int cio_create_sch_lock(struct subchannel *sch)
{
sch->lock = kmalloc(sizeof(spinlock_t), GFP_KERNEL);
if (!sch->lock)
......
......@@ -131,6 +131,8 @@ extern int cio_set_options (struct subchannel *, int);
extern int cio_get_options (struct subchannel *);
extern int cio_modify (struct subchannel *);
int cio_create_sch_lock(struct subchannel *);
/* Use with care. */
#ifdef CONFIG_CCW_CONSOLE
extern struct subchannel *cio_probe_console(void);
......
......@@ -580,12 +580,24 @@ css_cm_enable_store(struct device *dev, struct device_attribute *attr,
static DEVICE_ATTR(cm_enable, 0644, css_cm_enable_show, css_cm_enable_store);
static inline void __init
setup_css(int nr)
static inline int __init setup_css(int nr)
{
u32 tod_high;
int ret;
memset(css[nr], 0, sizeof(struct channel_subsystem));
css[nr]->pseudo_subchannel =
kzalloc(sizeof(*css[nr]->pseudo_subchannel), GFP_KERNEL);
if (!css[nr]->pseudo_subchannel)
return -ENOMEM;
css[nr]->pseudo_subchannel->dev.parent = &css[nr]->device;
css[nr]->pseudo_subchannel->dev.release = css_subchannel_release;
sprintf(css[nr]->pseudo_subchannel->dev.bus_id, "defunct");
ret = cio_create_sch_lock(css[nr]->pseudo_subchannel);
if (ret) {
kfree(css[nr]->pseudo_subchannel);
return ret;
}
mutex_init(&css[nr]->mutex);
css[nr]->valid = 1;
css[nr]->cssid = nr;
......@@ -593,6 +605,7 @@ setup_css(int nr)
css[nr]->device.release = channel_subsystem_release;
tod_high = (u32) (get_clock() >> 32);
css_generate_pgid(css[nr], tod_high);
return 0;
}
/*
......@@ -629,10 +642,12 @@ init_channel_subsystem (void)
ret = -ENOMEM;
goto out_unregister;
}
setup_css(i);
ret = device_register(&css[i]->device);
ret = setup_css(i);
if (ret)
goto out_free;
ret = device_register(&css[i]->device);
if (ret)
goto out_free_all;
if (css_characteristics_avail &&
css_chsc_characteristics.secm) {
ret = device_create_file(&css[i]->device,
......@@ -640,6 +655,9 @@ init_channel_subsystem (void)
if (ret)
goto out_device;
}
ret = device_register(&css[i]->pseudo_subchannel->dev);
if (ret)
goto out_file;
}
css_init_done = 1;
......@@ -647,13 +665,19 @@ init_channel_subsystem (void)
for_each_subchannel(__init_channel_subsystem, NULL);
return 0;
out_file:
device_remove_file(&css[i]->device, &dev_attr_cm_enable);
out_device:
device_unregister(&css[i]->device);
out_free_all:
kfree(css[i]->pseudo_subchannel->lock);
kfree(css[i]->pseudo_subchannel);
out_free:
kfree(css[i]);
out_unregister:
while (i > 0) {
i--;
device_unregister(&css[i]->pseudo_subchannel->dev);
if (css_characteristics_avail && css_chsc_characteristics.secm)
device_remove_file(&css[i]->device,
&dev_attr_cm_enable);
......@@ -665,6 +689,11 @@ init_channel_subsystem (void)
return ret;
}
int sch_is_pseudo_sch(struct subchannel *sch)
{
return sch == to_css(sch->dev.parent)->pseudo_subchannel;
}
/*
* find a driver for a subchannel. They identify by the subchannel
* type with the exception that the console subchannel driver has its own
......
......@@ -160,6 +160,8 @@ struct channel_subsystem {
int cm_enabled;
void *cub_addr1;
void *cub_addr2;
/* for orphaned ccw devices */
struct subchannel *pseudo_subchannel;
};
#define to_css(dev) container_of(dev, struct channel_subsystem, device)
......@@ -187,6 +189,8 @@ void css_clear_subchannel_slow_list(void);
int css_slow_subchannels_exist(void);
extern int need_rescan;
int sch_is_pseudo_sch(struct subchannel *);
extern struct workqueue_struct *slow_path_wq;
extern struct work_struct slow_path_work;
......
This diff is collapsed.
......@@ -80,6 +80,8 @@ int ccw_device_cancel_halt_clear(struct ccw_device *);
void ccw_device_do_unreg_rereg(struct work_struct *);
void ccw_device_call_sch_unregister(struct work_struct *);
void ccw_device_move_to_orphanage(struct work_struct *);
int ccw_device_is_orphan(struct ccw_device *);
int ccw_device_recognition(struct ccw_device *);
int ccw_device_online(struct ccw_device *);
......
......@@ -186,13 +186,12 @@ ccw_device_handle_oper(struct ccw_device *cdev)
/*
* Check if cu type and device type still match. If
* not, it is certainly another device and we have to
* de- and re-register. Also check here for non-matching devno.
* de- and re-register.
*/
if (cdev->id.cu_type != cdev->private->senseid.cu_type ||
cdev->id.cu_model != cdev->private->senseid.cu_model ||
cdev->id.dev_type != cdev->private->senseid.dev_type ||
cdev->id.dev_model != cdev->private->senseid.dev_model ||
cdev->private->dev_id.devno != sch->schib.pmcw.dev) {
cdev->id.dev_model != cdev->private->senseid.dev_model) {
PREPARE_WORK(&cdev->private->kick_work,
ccw_device_do_unreg_rereg);
queue_work(ccw_device_work, &cdev->private->kick_work);
......@@ -676,6 +675,10 @@ ccw_device_offline(struct ccw_device *cdev)
{
struct subchannel *sch;
if (ccw_device_is_orphan(cdev)) {
ccw_device_done(cdev, DEV_STATE_OFFLINE);
return 0;
}
sch = to_subchannel(cdev->dev.parent);
if (stsch(sch->schid, &sch->schib) || !sch->schib.pmcw.dnv)
return -ENODEV;
......@@ -1121,7 +1124,13 @@ device_trigger_reprobe(struct subchannel *sch)
sch->schib.pmcw.mp = 1;
sch->schib.pmcw.intparm = (__u32)(unsigned long)sch;
/* We should also udate ssd info, but this has to wait. */
ccw_device_start_id(cdev, 0);
/* Check if this is another device which appeared on the same sch. */
if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) {
PREPARE_WORK(&cdev->private->kick_work,
ccw_device_move_to_orphanage);
queue_work(ccw_device_work, &cdev->private->kick_work);
} else
ccw_device_start_id(cdev, 0);
}
static void
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment