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) ...@@ -415,6 +415,8 @@ cio_enable_subchannel (struct subchannel *sch, unsigned int isc)
CIO_TRACE_EVENT (2, "ensch"); CIO_TRACE_EVENT (2, "ensch");
CIO_TRACE_EVENT (2, sch->dev.bus_id); CIO_TRACE_EVENT (2, sch->dev.bus_id);
if (sch_is_pseudo_sch(sch))
return -EINVAL;
ccode = stsch (sch->schid, &sch->schib); ccode = stsch (sch->schid, &sch->schib);
if (ccode) if (ccode)
return -ENODEV; return -ENODEV;
...@@ -462,6 +464,8 @@ cio_disable_subchannel (struct subchannel *sch) ...@@ -462,6 +464,8 @@ cio_disable_subchannel (struct subchannel *sch)
CIO_TRACE_EVENT (2, "dissch"); CIO_TRACE_EVENT (2, "dissch");
CIO_TRACE_EVENT (2, sch->dev.bus_id); CIO_TRACE_EVENT (2, sch->dev.bus_id);
if (sch_is_pseudo_sch(sch))
return 0;
ccode = stsch (sch->schid, &sch->schib); ccode = stsch (sch->schid, &sch->schib);
if (ccode == 3) /* Not operational. */ if (ccode == 3) /* Not operational. */
return -ENODEV; return -ENODEV;
...@@ -496,7 +500,7 @@ cio_disable_subchannel (struct subchannel *sch) ...@@ -496,7 +500,7 @@ cio_disable_subchannel (struct subchannel *sch)
return ret; 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); sch->lock = kmalloc(sizeof(spinlock_t), GFP_KERNEL);
if (!sch->lock) if (!sch->lock)
......
...@@ -131,6 +131,8 @@ extern int cio_set_options (struct subchannel *, int); ...@@ -131,6 +131,8 @@ extern int cio_set_options (struct subchannel *, int);
extern int cio_get_options (struct subchannel *); extern int cio_get_options (struct subchannel *);
extern int cio_modify (struct subchannel *); extern int cio_modify (struct subchannel *);
int cio_create_sch_lock(struct subchannel *);
/* Use with care. */ /* Use with care. */
#ifdef CONFIG_CCW_CONSOLE #ifdef CONFIG_CCW_CONSOLE
extern struct subchannel *cio_probe_console(void); extern struct subchannel *cio_probe_console(void);
......
...@@ -580,12 +580,24 @@ css_cm_enable_store(struct device *dev, struct device_attribute *attr, ...@@ -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 DEVICE_ATTR(cm_enable, 0644, css_cm_enable_show, css_cm_enable_store);
static inline void __init static inline int __init setup_css(int nr)
setup_css(int nr)
{ {
u32 tod_high; u32 tod_high;
int ret;
memset(css[nr], 0, sizeof(struct channel_subsystem)); 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); mutex_init(&css[nr]->mutex);
css[nr]->valid = 1; css[nr]->valid = 1;
css[nr]->cssid = nr; css[nr]->cssid = nr;
...@@ -593,6 +605,7 @@ setup_css(int nr) ...@@ -593,6 +605,7 @@ setup_css(int nr)
css[nr]->device.release = channel_subsystem_release; css[nr]->device.release = channel_subsystem_release;
tod_high = (u32) (get_clock() >> 32); tod_high = (u32) (get_clock() >> 32);
css_generate_pgid(css[nr], tod_high); css_generate_pgid(css[nr], tod_high);
return 0;
} }
/* /*
...@@ -629,10 +642,12 @@ init_channel_subsystem (void) ...@@ -629,10 +642,12 @@ init_channel_subsystem (void)
ret = -ENOMEM; ret = -ENOMEM;
goto out_unregister; goto out_unregister;
} }
setup_css(i); ret = setup_css(i);
ret = device_register(&css[i]->device);
if (ret) if (ret)
goto out_free; goto out_free;
ret = device_register(&css[i]->device);
if (ret)
goto out_free_all;
if (css_characteristics_avail && if (css_characteristics_avail &&
css_chsc_characteristics.secm) { css_chsc_characteristics.secm) {
ret = device_create_file(&css[i]->device, ret = device_create_file(&css[i]->device,
...@@ -640,6 +655,9 @@ init_channel_subsystem (void) ...@@ -640,6 +655,9 @@ init_channel_subsystem (void)
if (ret) if (ret)
goto out_device; goto out_device;
} }
ret = device_register(&css[i]->pseudo_subchannel->dev);
if (ret)
goto out_file;
} }
css_init_done = 1; css_init_done = 1;
...@@ -647,13 +665,19 @@ init_channel_subsystem (void) ...@@ -647,13 +665,19 @@ init_channel_subsystem (void)
for_each_subchannel(__init_channel_subsystem, NULL); for_each_subchannel(__init_channel_subsystem, NULL);
return 0; return 0;
out_file:
device_remove_file(&css[i]->device, &dev_attr_cm_enable);
out_device: out_device:
device_unregister(&css[i]->device); device_unregister(&css[i]->device);
out_free_all:
kfree(css[i]->pseudo_subchannel->lock);
kfree(css[i]->pseudo_subchannel);
out_free: out_free:
kfree(css[i]); kfree(css[i]);
out_unregister: out_unregister:
while (i > 0) { while (i > 0) {
i--; i--;
device_unregister(&css[i]->pseudo_subchannel->dev);
if (css_characteristics_avail && css_chsc_characteristics.secm) if (css_characteristics_avail && css_chsc_characteristics.secm)
device_remove_file(&css[i]->device, device_remove_file(&css[i]->device,
&dev_attr_cm_enable); &dev_attr_cm_enable);
...@@ -665,6 +689,11 @@ init_channel_subsystem (void) ...@@ -665,6 +689,11 @@ init_channel_subsystem (void)
return ret; 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 * find a driver for a subchannel. They identify by the subchannel
* type with the exception that the console subchannel driver has its own * type with the exception that the console subchannel driver has its own
......
...@@ -160,6 +160,8 @@ struct channel_subsystem { ...@@ -160,6 +160,8 @@ struct channel_subsystem {
int cm_enabled; int cm_enabled;
void *cub_addr1; void *cub_addr1;
void *cub_addr2; void *cub_addr2;
/* for orphaned ccw devices */
struct subchannel *pseudo_subchannel;
}; };
#define to_css(dev) container_of(dev, struct channel_subsystem, device) #define to_css(dev) container_of(dev, struct channel_subsystem, device)
...@@ -187,6 +189,8 @@ void css_clear_subchannel_slow_list(void); ...@@ -187,6 +189,8 @@ void css_clear_subchannel_slow_list(void);
int css_slow_subchannels_exist(void); int css_slow_subchannels_exist(void);
extern int need_rescan; extern int need_rescan;
int sch_is_pseudo_sch(struct subchannel *);
extern struct workqueue_struct *slow_path_wq; extern struct workqueue_struct *slow_path_wq;
extern struct work_struct slow_path_work; extern struct work_struct slow_path_work;
......
This diff is collapsed.
...@@ -80,6 +80,8 @@ int ccw_device_cancel_halt_clear(struct ccw_device *); ...@@ -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_do_unreg_rereg(struct work_struct *);
void ccw_device_call_sch_unregister(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_recognition(struct ccw_device *);
int ccw_device_online(struct ccw_device *); int ccw_device_online(struct ccw_device *);
......
...@@ -186,13 +186,12 @@ ccw_device_handle_oper(struct ccw_device *cdev) ...@@ -186,13 +186,12 @@ ccw_device_handle_oper(struct ccw_device *cdev)
/* /*
* Check if cu type and device type still match. If * Check if cu type and device type still match. If
* not, it is certainly another device and we have to * 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 || if (cdev->id.cu_type != cdev->private->senseid.cu_type ||
cdev->id.cu_model != cdev->private->senseid.cu_model || cdev->id.cu_model != cdev->private->senseid.cu_model ||
cdev->id.dev_type != cdev->private->senseid.dev_type || cdev->id.dev_type != cdev->private->senseid.dev_type ||
cdev->id.dev_model != cdev->private->senseid.dev_model || cdev->id.dev_model != cdev->private->senseid.dev_model) {
cdev->private->dev_id.devno != sch->schib.pmcw.dev) {
PREPARE_WORK(&cdev->private->kick_work, PREPARE_WORK(&cdev->private->kick_work,
ccw_device_do_unreg_rereg); ccw_device_do_unreg_rereg);
queue_work(ccw_device_work, &cdev->private->kick_work); queue_work(ccw_device_work, &cdev->private->kick_work);
...@@ -676,6 +675,10 @@ ccw_device_offline(struct ccw_device *cdev) ...@@ -676,6 +675,10 @@ ccw_device_offline(struct ccw_device *cdev)
{ {
struct subchannel *sch; struct subchannel *sch;
if (ccw_device_is_orphan(cdev)) {
ccw_device_done(cdev, DEV_STATE_OFFLINE);
return 0;
}
sch = to_subchannel(cdev->dev.parent); sch = to_subchannel(cdev->dev.parent);
if (stsch(sch->schid, &sch->schib) || !sch->schib.pmcw.dnv) if (stsch(sch->schid, &sch->schib) || !sch->schib.pmcw.dnv)
return -ENODEV; return -ENODEV;
...@@ -1121,7 +1124,13 @@ device_trigger_reprobe(struct subchannel *sch) ...@@ -1121,7 +1124,13 @@ device_trigger_reprobe(struct subchannel *sch)
sch->schib.pmcw.mp = 1; sch->schib.pmcw.mp = 1;
sch->schib.pmcw.intparm = (__u32)(unsigned long)sch; sch->schib.pmcw.intparm = (__u32)(unsigned long)sch;
/* We should also udate ssd info, but this has to wait. */ /* 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 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