* [RFC/Patch 1/5] driver core: Introduce device_find_child().
[not found] <20061107163730.0c8f3dad@gondolin.boeblingen.de.ibm.com>
@ 2006-11-07 15:55 ` Cornelia Huck
2006-11-07 15:55 ` [RFC/Patch 2/5] driver core: Introduce device_move(): move a device to a new parent Cornelia Huck
` (3 subsequent siblings)
4 siblings, 0 replies; 5+ messages in thread
From: Cornelia Huck @ 2006-11-07 15:55 UTC (permalink / raw)
To: linux-kernel
From: Cornelia Huck <cornelia.huck@de.ibm.com>
Introduce device_find_child() to match device_for_each_child().
Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
---
drivers/base/core.c | 32 ++++++++++++++++++++++++++++++++
include/linux/device.h | 2 ++
2 files changed, 34 insertions(+)
--- linux-2.6-CH.orig/drivers/base/core.c
+++ linux-2.6-CH/drivers/base/core.c
@@ -749,12 +749,44 @@ int device_for_each_child(struct device
return error;
}
+/**
+ * device_find_child - device iterator for locating a particular device.
+ * @parent: parent struct device
+ * @data: Data to pass to match function
+ * @match: Callback function to check device
+ *
+ * This is similar to the device_for_each_child() function above, but it
+ * returns a reference to a device that is 'found' for later use, as
+ * determined by the @match callback.
+ *
+ * The callback should return 0 if the device doesn't match and non-zero
+ * if it does. If the callback returns non-zero, this function will
+ * return to the caller and not iterate over any more devices.
+ */
+struct device * device_find_child(struct device *parent, void *data,
+ int (*match)(struct device *, void *))
+{
+ struct klist_iter i;
+ struct device *child;
+
+ if (!parent)
+ return NULL;
+
+ klist_iter_init(&parent->klist_children, &i);
+ while ((child = next_device(&i)))
+ if (match(child, data) && get_device(child))
+ break;
+ klist_iter_exit(&i);
+ return child;
+}
+
int __init devices_init(void)
{
return subsystem_register(&devices_subsys);
}
EXPORT_SYMBOL_GPL(device_for_each_child);
+EXPORT_SYMBOL_GPL(device_find_child);
EXPORT_SYMBOL_GPL(device_initialize);
EXPORT_SYMBOL_GPL(device_add);
--- linux-2.6-CH.orig/include/linux/device.h
+++ linux-2.6-CH/include/linux/device.h
@@ -420,6 +420,8 @@ extern int __must_check device_add(struc
extern void device_del(struct device * dev);
extern int device_for_each_child(struct device *, void *,
int (*fn)(struct device *, void *));
+extern struct device *device_find_child(struct device *, void *data,
+ int (*match)(struct device *, void *));
extern int device_rename(struct device *dev, char *new_name);
/*
^ permalink raw reply [flat|nested] 5+ messages in thread
* [RFC/Patch 2/5] driver core: Introduce device_move(): move a device to a new parent.
[not found] <20061107163730.0c8f3dad@gondolin.boeblingen.de.ibm.com>
2006-11-07 15:55 ` [RFC/Patch 1/5] driver core: Introduce device_find_child() Cornelia Huck
@ 2006-11-07 15:55 ` Cornelia Huck
2006-11-07 15:55 ` [RFC/Patch 3/5] s390: Prepatory cleanup in common I/O layer Cornelia Huck
` (2 subsequent siblings)
4 siblings, 0 replies; 5+ messages in thread
From: Cornelia Huck @ 2006-11-07 15:55 UTC (permalink / raw)
To: linux-kernel
From: Cornelia Huck <cornelia.huck@de.ibm.com>
Provide a function device_move() to move a device to a new parent device. Add
auxilliary functions kobject_move() and sysfs_move_dir().
Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
---
drivers/base/core.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++
fs/sysfs/dir.c | 45 +++++++++++++++++++++++
include/linux/device.h | 1
include/linux/kobject.h | 1
include/linux/sysfs.h | 3 +
lib/kobject.c | 30 +++++++++++++++
6 files changed, 172 insertions(+)
--- linux-2.6-CH.orig/drivers/base/core.c
+++ linux-2.6-CH/drivers/base/core.c
@@ -953,3 +953,95 @@ int device_rename(struct device *dev, ch
return error;
}
+
+
+static int device_move_class_links(struct device *dev,
+ struct device *old_parent,
+ struct device *new_parent)
+{
+#ifdef CONFIG_SYSFS_DEPRECATED
+ int error;
+ char *class_name;
+
+ class_name = make_class_name(dev->class->name, &dev->kobj);
+ if (!class_name) {
+ error = PTR_ERR(class_name);
+ class_name = NULL;
+ goto out;
+ }
+ if (old_parent) {
+ sysfs_remove_link(&dev->kobj, "device");
+ sysfs_remove_link(&old_parent->kobj, class_name);
+ }
+ error = sysfs_create_link(&dev->kobj, &new_parent->kobj, "device");
+ if (error)
+ goto out;
+ error = sysfs_create_link(&new_parent->kobj, &dev->kobj, class_name);
+ if (error)
+ sysfs_remove_link(&dev->kobj, "device");
+out:
+ kfree(class_name);
+ return error;
+#else
+ return 0;
+#endif
+}
+
+/**
+ * device_move - moves a device to a new parent
+ * @dev: the pointer to the struct device to be moved
+ * @new_parent: the new parent of the device
+ */
+int device_move(struct device *dev, struct device *new_parent)
+{
+ int error;
+ struct device *old_parent;
+
+ dev = get_device(dev);
+ if (!dev)
+ return -EINVAL;
+
+ if (!device_is_registered(dev)) {
+ error = -EINVAL;
+ goto out;
+ }
+ new_parent = get_device(new_parent);
+ if (!new_parent) {
+ error = -EINVAL;
+ goto out;
+ }
+ pr_debug("DEVICE: moving '%s' to '%s'\n", dev->bus_id,
+ new_parent->bus_id);
+ error = kobject_move(&dev->kobj, &new_parent->kobj);
+ if (error) {
+ put_device(new_parent);
+ goto out;
+ }
+ old_parent = dev->parent;
+ dev->parent = new_parent;
+ if (old_parent)
+ klist_del(&dev->knode_parent);
+ klist_add_tail(&dev->knode_parent, &new_parent->klist_children);
+ if (!dev->class)
+ goto out_put;
+ error = device_move_class_links(dev, old_parent, new_parent);
+ if (error) {
+ /* We ignore errors on cleanup since we're hosed anyway... */
+ device_move_class_links(dev, new_parent, old_parent);
+ if (!kobject_move(&dev->kobj, &old_parent->kobj)) {
+ klist_del(&dev->knode_parent);
+ if (old_parent)
+ klist_add_tail(&dev->knode_parent,
+ &old_parent->klist_children);
+ }
+ put_device(new_parent);
+ goto out;
+ }
+out_put:
+ put_device(old_parent);
+out:
+ put_device(dev);
+ return error;
+}
+
+EXPORT_SYMBOL_GPL(device_move);
--- linux-2.6-CH.orig/fs/sysfs/dir.c
+++ linux-2.6-CH/fs/sysfs/dir.c
@@ -372,6 +372,51 @@ int sysfs_rename_dir(struct kobject * ko
return error;
}
+int sysfs_move_dir(struct kobject *kobj, struct kobject *new_parent)
+{
+ struct dentry *old_parent_dentry, *new_parent_dentry, *new_dentry;
+ struct sysfs_dirent *new_parent_sd, *sd;
+ int error;
+
+ if (!new_parent)
+ return -EINVAL;
+
+ old_parent_dentry = kobj->parent ?
+ kobj->parent->dentry : sysfs_mount->mnt_sb->s_root;
+ new_parent_dentry = new_parent->dentry;
+
+again:
+ mutex_lock(&old_parent_dentry->d_inode->i_mutex);
+ if (!mutex_trylock(&new_parent_dentry->d_inode->i_mutex)) {
+ mutex_unlock(&old_parent_dentry->d_inode->i_mutex);
+ goto again;
+ }
+
+ new_parent_sd = new_parent_dentry->d_fsdata;
+ sd = kobj->dentry->d_fsdata;
+
+ new_dentry = lookup_one_len(kobj->name, new_parent_dentry,
+ strlen(kobj->name));
+ if (IS_ERR(new_dentry)) {
+ error = PTR_ERR(new_dentry);
+ goto out;
+ } else
+ error = 0;
+ d_add(new_dentry, NULL);
+ d_move(kobj->dentry, new_dentry);
+ dput(new_dentry);
+
+ /* Remove from old parent's list and insert into new parent's list. */
+ list_del_init(&sd->s_sibling);
+ list_add(&sd->s_sibling, &new_parent_sd->s_children);
+
+out:
+ mutex_unlock(&new_parent_dentry->d_inode->i_mutex);
+ mutex_unlock(&old_parent_dentry->d_inode->i_mutex);
+
+ return error;
+}
+
static int sysfs_dir_open(struct inode *inode, struct file *file)
{
struct dentry * dentry = file->f_path.dentry;
--- linux-2.6-CH.orig/include/linux/device.h
+++ linux-2.6-CH/include/linux/device.h
@@ -423,6 +423,7 @@ extern int device_for_each_child(struct
extern struct device *device_find_child(struct device *, void *data,
int (*match)(struct device *, void *));
extern int device_rename(struct device *dev, char *new_name);
+extern int device_move(struct device *dev, struct device *new_parent);
/*
* Manual binding of a device to driver. See drivers/base/bus.c
--- linux-2.6-CH.orig/include/linux/kobject.h
+++ linux-2.6-CH/include/linux/kobject.h
@@ -76,6 +76,7 @@ extern int __must_check kobject_add(stru
extern void kobject_del(struct kobject *);
extern int __must_check kobject_rename(struct kobject *, const char *new_name);
+extern int __must_check kobject_move(struct kobject *, struct kobject *);
extern int __must_check kobject_register(struct kobject *);
extern void kobject_unregister(struct kobject *);
--- linux-2.6-CH.orig/include/linux/sysfs.h
+++ linux-2.6-CH/include/linux/sysfs.h
@@ -97,6 +97,9 @@ extern int __must_check
sysfs_rename_dir(struct kobject *, const char *new_name);
extern int __must_check
+sysfs_move_dir(struct kobject *, struct kobject *);
+
+extern int __must_check
sysfs_create_file(struct kobject *, const struct attribute *);
extern int __must_check
--- linux-2.6-CH.orig/lib/kobject.c
+++ linux-2.6-CH/lib/kobject.c
@@ -355,6 +355,36 @@ int kobject_rename(struct kobject * kobj
}
/**
+ * kobject_move - move object to another parent
+ * @kobj: object in question.
+ * @new_parent: object's new parent
+ */
+
+int kobject_move(struct kobject *kobj, struct kobject *new_parent)
+{
+ int error;
+ struct kobject *old_parent;
+
+ kobj = kobject_get(kobj);
+ if (!kobj)
+ return -EINVAL;
+ new_parent = kobject_get(new_parent);
+ if (!new_parent) {
+ error = -EINVAL;
+ goto out;
+ }
+ error = sysfs_move_dir(kobj, new_parent);
+ if (error)
+ goto out;
+ old_parent = kobj->parent;
+ kobj->parent = new_parent;
+ kobject_put(old_parent);
+out:
+ kobject_put(kobj);
+ return error;
+}
+
+/**
* kobject_del - unlink kobject from hierarchy.
* @kobj: object.
*/
^ permalink raw reply [flat|nested] 5+ messages in thread
* [RFC/Patch 3/5] s390: Prepatory cleanup in common I/O layer.
[not found] <20061107163730.0c8f3dad@gondolin.boeblingen.de.ibm.com>
2006-11-07 15:55 ` [RFC/Patch 1/5] driver core: Introduce device_find_child() Cornelia Huck
2006-11-07 15:55 ` [RFC/Patch 2/5] driver core: Introduce device_move(): move a device to a new parent Cornelia Huck
@ 2006-11-07 15:55 ` Cornelia Huck
2006-11-07 15:55 ` [RFC/Patch 4/5] s390: Make the per-subchannel lock dynamic Cornelia Huck
2006-11-07 15:56 ` [RFC/Patch 5/5] s390: Dynamic subchannel mapping of ccw devices Cornelia Huck
4 siblings, 0 replies; 5+ messages in thread
From: Cornelia Huck @ 2006-11-07 15:55 UTC (permalink / raw)
To: linux-kernel
From: Cornelia Huck <cornelia.huck@de.ibm.com>
Some preparations for the dynamic subchannel mapping patch.
- Move adding subchannel attributes to css_register_subchannel().
- Don't call device_trigger_reprobe() for non-operational devices.
- Introduce io_subchannel_create_ccwdev().
Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
---
drivers/s390/cio/css.c | 13 +++--
drivers/s390/cio/css.h | 2
drivers/s390/cio/device.c | 102 +++++++++++++++++++++++++-----------------
drivers/s390/cio/device_fsm.c | 3 -
4 files changed, 75 insertions(+), 45 deletions(-)
--- linux-2.6-CH.orig/drivers/s390/cio/css.c
+++ linux-2.6-CH/drivers/s390/cio/css.c
@@ -135,14 +135,19 @@ css_register_subchannel(struct subchanne
sch->dev.parent = &css[0]->device;
sch->dev.bus = &css_bus_type;
sch->dev.release = &css_subchannel_release;
-
+
/* make it known to the system */
ret = css_sch_device_register(sch);
- if (ret)
+ if (ret) {
printk (KERN_WARNING "%s: could not register %s\n",
__func__, sch->dev.bus_id);
- else
- css_get_ssd_info(sch);
+ return ret;
+ }
+ css_get_ssd_info(sch);
+ ret = subchannel_add_files(&sch->dev);
+ if (ret)
+ printk(KERN_WARNING "%s: could not add attributes to %s\n",
+ __func__, sch->dev.bus_id);
return ret;
}
--- linux-2.6-CH.orig/drivers/s390/cio/css.h
+++ linux-2.6-CH/drivers/s390/cio/css.h
@@ -184,4 +184,6 @@ extern int need_rescan;
extern struct workqueue_struct *slow_path_wq;
extern struct work_struct slow_path_work;
+
+int subchannel_add_files (struct device *);
#endif
--- linux-2.6-CH.orig/drivers/s390/cio/device.c
+++ linux-2.6-CH/drivers/s390/cio/device.c
@@ -294,6 +294,16 @@ online_show (struct device *dev, struct
return sprintf(buf, cdev->online ? "1\n" : "0\n");
}
+static void ccw_device_unregister(void *data)
+{
+ struct ccw_device *cdev;
+
+ cdev = (struct ccw_device *)data;
+ if (test_and_clear_bit(1, &cdev->private->registered))
+ device_unregister(&cdev->dev);
+ put_device(&cdev->dev);
+}
+
static void
ccw_device_remove_disconnected(struct ccw_device *cdev)
{
@@ -498,8 +508,7 @@ static struct attribute_group subch_attr
.attrs = subch_attrs,
};
-static inline int
-subchannel_add_files (struct device *dev)
+int subchannel_add_files (struct device *dev)
{
return sysfs_create_group(&dev->kobj, &subch_attr_group);
}
@@ -673,6 +682,54 @@ ccw_device_release(struct device *dev)
kfree(cdev);
}
+static struct ccw_device * io_subchannel_allocate_dev(struct subchannel *sch)
+{
+ struct ccw_device *cdev;
+
+ cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
+ if (cdev) {
+ cdev->private = kzalloc(sizeof(struct ccw_device_private),
+ GFP_KERNEL | GFP_DMA);
+ if (cdev->private)
+ return cdev;
+ }
+ kfree(cdev);
+ return ERR_PTR(-ENOMEM);
+}
+
+static int io_subchannel_initialize_dev(struct subchannel *sch,
+ struct ccw_device *cdev)
+{
+ atomic_set(&cdev->private->onoff, 0);
+ cdev->dev.parent = &sch->dev;
+ cdev->dev.release = ccw_device_release;
+ INIT_LIST_HEAD(&cdev->private->kick_work.entry);
+ /* Do first half of device_register. */
+ device_initialize(&cdev->dev);
+ if (!get_device(&sch->dev)) {
+ if (cdev->dev.release)
+ cdev->dev.release(&cdev->dev);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static struct ccw_device * io_subchannel_create_ccwdev(struct subchannel *sch)
+{
+ struct ccw_device *cdev;
+ int ret;
+
+ cdev = io_subchannel_allocate_dev(sch);
+ if (!IS_ERR(cdev)) {
+ ret = io_subchannel_initialize_dev(sch, cdev);
+ if (ret) {
+ kfree(cdev);
+ cdev = ERR_PTR(ret);
+ }
+ }
+ return cdev;
+}
+
/*
* Register recognized device.
*/
@@ -707,11 +764,6 @@ io_subchannel_register(void *data)
wake_up(&ccw_device_init_wq);
return;
}
-
- ret = subchannel_add_files(cdev->dev.parent);
- if (ret)
- printk(KERN_WARNING "%s: could not add attributes to %s\n",
- __func__, sch->dev.bus_id);
put_device(&cdev->dev);
out:
cdev->private->flags.recog_done = 1;
@@ -831,7 +883,6 @@ io_subchannel_probe (struct subchannel *
cdev = sch->dev.driver_data;
device_initialize(&cdev->dev);
ccw_device_register(cdev);
- subchannel_add_files(&sch->dev);
/*
* Check if the device is already online. If it is
* the reference count needs to be corrected
@@ -844,27 +895,9 @@ io_subchannel_probe (struct subchannel *
get_device(&cdev->dev);
return 0;
}
- cdev = kzalloc (sizeof(*cdev), GFP_KERNEL);
- if (!cdev)
- return -ENOMEM;
- cdev->private = kzalloc(sizeof(struct ccw_device_private),
- GFP_KERNEL | GFP_DMA);
- if (!cdev->private) {
- kfree(cdev);
- return -ENOMEM;
- }
- atomic_set(&cdev->private->onoff, 0);
- cdev->dev.parent = &sch->dev;
- cdev->dev.release = ccw_device_release;
- INIT_LIST_HEAD(&cdev->private->kick_work.entry);
- /* Do first half of device_register. */
- device_initialize(&cdev->dev);
-
- if (!get_device(&sch->dev)) {
- if (cdev->dev.release)
- cdev->dev.release(&cdev->dev);
- return -ENODEV;
- }
+ cdev = io_subchannel_create_ccwdev(sch);
+ if (IS_ERR(cdev))
+ return PTR_ERR(cdev);
rc = io_subchannel_recog(cdev, sch);
if (rc) {
@@ -878,17 +911,6 @@ io_subchannel_probe (struct subchannel *
return rc;
}
-static void
-ccw_device_unregister(void *data)
-{
- struct ccw_device *cdev;
-
- cdev = (struct ccw_device *)data;
- if (test_and_clear_bit(1, &cdev->private->registered))
- device_unregister(&cdev->dev);
- put_device(&cdev->dev);
-}
-
static int
io_subchannel_remove (struct subchannel *sch)
{
--- linux-2.6-CH.orig/drivers/s390/cio/device_fsm.c
+++ linux-2.6-CH/drivers/s390/cio/device_fsm.c
@@ -1077,7 +1077,8 @@ device_trigger_reprobe(struct subchannel
/* Update some values. */
if (stsch(sch->schid, &sch->schib))
return;
-
+ if (!sch->schib.pmcw.dnv)
+ return;
/*
* The pim, pam, pom values may not be accurate, but they are the best
* we have before performing device selection :/
^ permalink raw reply [flat|nested] 5+ messages in thread
* [RFC/Patch 4/5] s390: Make the per-subchannel lock dynamic.
[not found] <20061107163730.0c8f3dad@gondolin.boeblingen.de.ibm.com>
` (2 preceding siblings ...)
2006-11-07 15:55 ` [RFC/Patch 3/5] s390: Prepatory cleanup in common I/O layer Cornelia Huck
@ 2006-11-07 15:55 ` Cornelia Huck
2006-11-07 15:56 ` [RFC/Patch 5/5] s390: Dynamic subchannel mapping of ccw devices Cornelia Huck
4 siblings, 0 replies; 5+ messages in thread
From: Cornelia Huck @ 2006-11-07 15:55 UTC (permalink / raw)
To: linux-kernel
From: Cornelia Huck <cornelia.huck@de.ibm.com>
Convert the subchannel lock to a pointer to a lock. Needed for the dynamic
subchannel mapping patch.
Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
---
drivers/s390/cio/chsc.c | 28 ++++++++++----------
drivers/s390/cio/cio.c | 58 +++++++++++++++++++++++++++++-------------
drivers/s390/cio/cio.h | 4 ++
drivers/s390/cio/css.c | 22 ++++++++-------
drivers/s390/cio/device.c | 21 ++++++++++-----
drivers/s390/cio/device_ops.c | 28 ++++++++++----------
6 files changed, 98 insertions(+), 63 deletions(-)
--- linux-2.6-CH.orig/drivers/s390/cio/chsc.c
+++ linux-2.6-CH/drivers/s390/cio/chsc.c
@@ -183,7 +183,7 @@ css_get_ssd_info(struct subchannel *sch)
page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!page)
return -ENOMEM;
- spin_lock_irq(&sch->lock);
+ spin_lock_irq(sch->lock);
ret = chsc_get_sch_desc_irq(sch, page);
if (ret) {
static int cio_chsc_err_msg;
@@ -197,7 +197,7 @@ css_get_ssd_info(struct subchannel *sch)
cio_chsc_err_msg = 1;
}
}
- spin_unlock_irq(&sch->lock);
+ spin_unlock_irq(sch->lock);
free_page((unsigned long)page);
if (!ret) {
int j, chpid, mask;
@@ -233,7 +233,7 @@ s390_subchannel_remove_chpid(struct devi
if (j >= 8)
return 0;
- spin_lock_irq(&sch->lock);
+ spin_lock_irq(sch->lock);
stsch(sch->schid, &schib);
if (!schib.pmcw.dnv)
@@ -263,10 +263,10 @@ s390_subchannel_remove_chpid(struct devi
else if (sch->lpm == mask)
goto out_unreg;
out_unlock:
- spin_unlock_irq(&sch->lock);
+ spin_unlock_irq(sch->lock);
return 0;
out_unreg:
- spin_unlock_irq(&sch->lock);
+ spin_unlock_irq(sch->lock);
sch->lpm = 0;
if (css_enqueue_subchannel_slow(sch->schid)) {
css_clear_subchannel_slow_list();
@@ -376,12 +376,12 @@ __s390_process_res_acc(struct subchannel
/* Check if a subchannel is newly available. */
return s390_process_res_acc_new_sch(schid);
- spin_lock_irq(&sch->lock);
+ spin_lock_irq(sch->lock);
chp_mask = s390_process_res_acc_sch(res_data, sch);
if (chp_mask == 0) {
- spin_unlock_irq(&sch->lock);
+ spin_unlock_irq(sch->lock);
put_device(&sch->dev);
return 0;
}
@@ -395,7 +395,7 @@ __s390_process_res_acc(struct subchannel
else if (sch->driver && sch->driver->verify)
sch->driver->verify(&sch->dev);
- spin_unlock_irq(&sch->lock);
+ spin_unlock_irq(sch->lock);
put_device(&sch->dev);
return 0;
}
@@ -633,21 +633,21 @@ __chp_add(struct subchannel_id schid, vo
if (!sch)
/* Check if the subchannel is now available. */
return __chp_add_new_sch(schid);
- spin_lock_irq(&sch->lock);
+ spin_lock_irq(sch->lock);
for (i=0; i<8; i++) {
mask = 0x80 >> i;
if ((sch->schib.pmcw.pim & mask) &&
(sch->schib.pmcw.chpid[i] == chp->id)) {
if (stsch(sch->schid, &sch->schib) != 0) {
/* Endgame. */
- spin_unlock_irq(&sch->lock);
+ spin_unlock_irq(sch->lock);
return -ENXIO;
}
break;
}
}
if (i==8) {
- spin_unlock_irq(&sch->lock);
+ spin_unlock_irq(sch->lock);
return 0;
}
sch->lpm = ((sch->schib.pmcw.pim &
@@ -658,7 +658,7 @@ __chp_add(struct subchannel_id schid, vo
if (sch->driver && sch->driver->verify)
sch->driver->verify(&sch->dev);
- spin_unlock_irq(&sch->lock);
+ spin_unlock_irq(sch->lock);
put_device(&sch->dev);
return 0;
}
@@ -731,7 +731,7 @@ __s390_subchannel_vary_chpid(struct subc
if (!sch->ssd_info.valid)
return;
- spin_lock_irqsave(&sch->lock, flags);
+ spin_lock_irqsave(sch->lock, flags);
old_lpm = sch->lpm;
for (chp = 0; chp < 8; chp++) {
if (sch->ssd_info.chpid[chp] != chpid)
@@ -760,7 +760,7 @@ __s390_subchannel_vary_chpid(struct subc
}
break;
}
- spin_unlock_irqrestore(&sch->lock, flags);
+ spin_unlock_irqrestore(sch->lock, flags);
}
static int
--- linux-2.6-CH.orig/drivers/s390/cio/cio.c
+++ linux-2.6-CH/drivers/s390/cio/cio.c
@@ -141,11 +141,11 @@ cio_tpi(void)
return 1;
local_bh_disable();
irq_enter ();
- spin_lock(&sch->lock);
+ spin_lock(sch->lock);
memcpy (&sch->schib.scsw, &irb->scsw, sizeof (struct scsw));
if (sch->driver && sch->driver->irq)
sch->driver->irq(&sch->dev);
- spin_unlock(&sch->lock);
+ spin_unlock(sch->lock);
irq_exit ();
_local_bh_enable();
return 1;
@@ -494,6 +494,15 @@ cio_disable_subchannel (struct subchanne
return ret;
}
+static int cio_create_sch_lock(struct subchannel *sch)
+{
+ sch->lock = kmalloc(sizeof(spinlock_t), GFP_KERNEL);
+ if (!sch->lock)
+ return -ENOMEM;
+ spin_lock_init(sch->lock);
+ return 0;
+}
+
/*
* cio_validate_subchannel()
*
@@ -511,6 +520,7 @@ cio_validate_subchannel (struct subchann
{
char dbf_txt[15];
int ccode;
+ int err;
sprintf (dbf_txt, "valsch%x", schid.sch_no);
CIO_TRACE_EVENT (4, dbf_txt);
@@ -518,9 +528,15 @@ cio_validate_subchannel (struct subchann
/* Nuke all fields. */
memset(sch, 0, sizeof(struct subchannel));
- spin_lock_init(&sch->lock);
+ sch->schid = schid;
+ if (cio_is_console(schid)) {
+ sch->lock = cio_get_console_lock();
+ } else {
+ err = cio_create_sch_lock(sch);
+ if (err)
+ goto out;
+ }
mutex_init(&sch->reg_mutex);
-
/* Set a name for the subchannel */
snprintf (sch->dev.bus_id, BUS_ID_SIZE, "0.%x.%04x", schid.ssid,
schid.sch_no);
@@ -532,10 +548,10 @@ cio_validate_subchannel (struct subchann
* is not valid.
*/
ccode = stsch_err (schid, &sch->schib);
- if (ccode)
- return (ccode == 3) ? -ENXIO : ccode;
-
- sch->schid = schid;
+ if (ccode) {
+ err = (ccode == 3) ? -ENXIO : ccode;
+ goto out;
+ }
/* Copy subchannel type from path management control word. */
sch->st = sch->schib.pmcw.st;
@@ -548,14 +564,16 @@ cio_validate_subchannel (struct subchann
"non-I/O subchannel type %04X\n",
sch->schid.ssid, sch->schid.sch_no, sch->st);
/* We stop here for non-io subchannels. */
- return sch->st;
+ err = sch->st;
+ goto out;
}
/* Initialization for io subchannels. */
- if (!sch->schib.pmcw.dnv)
+ if (!sch->schib.pmcw.dnv) {
/* io subchannel but device number is invalid. */
- return -ENODEV;
-
+ err = -ENODEV;
+ goto out;
+ }
/* Devno is valid. */
if (is_blacklisted (sch->schid.ssid, sch->schib.pmcw.dev)) {
/*
@@ -565,7 +583,8 @@ cio_validate_subchannel (struct subchann
CIO_MSG_EVENT(0, "Blacklisted device detected "
"at devno %04X, subchannel set %x\n",
sch->schib.pmcw.dev, sch->schid.ssid);
- return -ENODEV;
+ err = -ENODEV;
+ goto out;
}
sch->opm = 0xff;
if (!cio_is_console(sch->schid))
@@ -593,6 +612,11 @@ cio_validate_subchannel (struct subchann
if ((sch->lpm & (sch->lpm - 1)) != 0)
sch->schib.pmcw.mp = 1; /* multipath mode */
return 0;
+out:
+ if (!cio_is_console(schid))
+ kfree(sch->lock);
+ sch->lock = NULL;
+ return err;
}
/*
@@ -635,7 +659,7 @@ do_IRQ (struct pt_regs *regs)
}
sch = (struct subchannel *)(unsigned long)tpi_info->intparm;
if (sch)
- spin_lock(&sch->lock);
+ spin_lock(sch->lock);
/* Store interrupt response block to lowcore. */
if (tsch (tpi_info->schid, irb) == 0 && sch) {
/* Keep subchannel information word up to date. */
@@ -646,7 +670,7 @@ do_IRQ (struct pt_regs *regs)
sch->driver->irq(&sch->dev);
}
if (sch)
- spin_unlock(&sch->lock);
+ spin_unlock(sch->lock);
/*
* Are more interrupts pending?
* If so, the tpi instruction will update the lowcore
@@ -685,10 +709,10 @@ wait_cons_dev (void)
__ctl_load (cr6, 6, 6);
do {
- spin_unlock(&console_subchannel.lock);
+ spin_unlock(console_subchannel.lock);
if (!cio_tpi())
cpu_relax();
- spin_lock(&console_subchannel.lock);
+ spin_lock(console_subchannel.lock);
} while (console_subchannel.schib.scsw.actl != 0);
/*
* restore previous isc value
--- linux-2.6-CH.orig/drivers/s390/cio/cio.h
+++ linux-2.6-CH/drivers/s390/cio/cio.h
@@ -87,7 +87,7 @@ struct orb {
/* subchannel data structure used by I/O subroutines */
struct subchannel {
struct subchannel_id schid;
- spinlock_t lock; /* subchannel lock */
+ spinlock_t *lock; /* subchannel lock */
struct mutex reg_mutex;
enum {
SUBCHANNEL_TYPE_IO = 0,
@@ -137,9 +137,11 @@ extern struct subchannel *cio_probe_cons
extern void cio_release_console(void);
extern int cio_is_console(struct subchannel_id);
extern struct subchannel *cio_get_console_subchannel(void);
+extern spinlock_t * cio_get_console_lock(void);
#else
#define cio_is_console(schid) 0
#define cio_get_console_subchannel() NULL
+#define cio_get_console_lock() NULL;
#endif
extern int cio_show_msg;
--- linux-2.6-CH.orig/drivers/s390/cio/css.c
+++ linux-2.6-CH/drivers/s390/cio/css.c
@@ -91,9 +91,9 @@ css_free_subchannel(struct subchannel *s
/* Reset intparm to zeroes. */
sch->schib.pmcw.intparm = 0;
cio_modify(sch);
+ kfree(sch->lock);
kfree(sch);
}
-
}
static void
@@ -102,8 +102,10 @@ css_subchannel_release(struct device *de
struct subchannel *sch;
sch = to_subchannel(dev);
- if (!cio_is_console(sch->schid))
+ if (!cio_is_console(sch->schid)) {
+ kfree(sch->lock);
kfree(sch);
+ }
}
extern int css_get_ssd_info(struct subchannel *sch);
@@ -206,18 +208,18 @@ static int css_evaluate_known_subchannel
unsigned long flags;
enum { NONE, UNREGISTER, UNREGISTER_PROBE, REPROBE } action;
- spin_lock_irqsave(&sch->lock, flags);
+ spin_lock_irqsave(sch->lock, flags);
disc = device_is_disconnected(sch);
if (disc && slow) {
/* Disconnected devices are evaluated directly only.*/
- spin_unlock_irqrestore(&sch->lock, flags);
+ spin_unlock_irqrestore(sch->lock, flags);
return 0;
}
/* No interrupt after machine check - kill pending timers. */
device_kill_pending_timer(sch);
if (!disc && !slow) {
/* Non-disconnected devices are evaluated on the slow path. */
- spin_unlock_irqrestore(&sch->lock, flags);
+ spin_unlock_irqrestore(sch->lock, flags);
return -EAGAIN;
}
event = css_get_subchannel_status(sch);
@@ -242,9 +244,9 @@ static int css_evaluate_known_subchannel
/* Ask driver what to do with device. */
action = UNREGISTER;
if (sch->driver && sch->driver->notify) {
- spin_unlock_irqrestore(&sch->lock, flags);
+ spin_unlock_irqrestore(sch->lock, flags);
ret = sch->driver->notify(&sch->dev, event);
- spin_lock_irqsave(&sch->lock, flags);
+ spin_lock_irqsave(sch->lock, flags);
if (ret)
action = NONE;
}
@@ -269,9 +271,9 @@ static int css_evaluate_known_subchannel
case UNREGISTER:
case UNREGISTER_PROBE:
/* Unregister device (will use subchannel lock). */
- spin_unlock_irqrestore(&sch->lock, flags);
+ spin_unlock_irqrestore(sch->lock, flags);
css_sch_device_unregister(sch);
- spin_lock_irqsave(&sch->lock, flags);
+ spin_lock_irqsave(sch->lock, flags);
/* Reset intparm to zeroes. */
sch->schib.pmcw.intparm = 0;
@@ -283,7 +285,7 @@ static int css_evaluate_known_subchannel
default:
break;
}
- spin_unlock_irqrestore(&sch->lock, flags);
+ spin_unlock_irqrestore(sch->lock, flags);
/* Probe if necessary. */
if (action == UNREGISTER_PROBE)
ret = css_probe_device(sch->schid);
--- linux-2.6-CH.orig/drivers/s390/cio/device.c
+++ linux-2.6-CH/drivers/s390/cio/device.c
@@ -754,9 +754,9 @@ io_subchannel_register(void *data)
printk (KERN_WARNING "%s: could not register %s\n",
__func__, cdev->dev.bus_id);
put_device(&cdev->dev);
- spin_lock_irqsave(&sch->lock, flags);
+ spin_lock_irqsave(sch->lock, flags);
sch->dev.driver_data = NULL;
- spin_unlock_irqrestore(&sch->lock, flags);
+ spin_unlock_irqrestore(sch->lock, flags);
kfree (cdev->private);
kfree (cdev);
put_device(&sch->dev);
@@ -837,7 +837,7 @@ io_subchannel_recog(struct ccw_device *c
sch->dev.driver_data = cdev;
sch->driver = &io_subchannel_driver;
- cdev->ccwlock = &sch->lock;
+ cdev->ccwlock = sch->lock;
/* Init private data. */
priv = cdev->private;
@@ -857,9 +857,9 @@ io_subchannel_recog(struct ccw_device *c
atomic_inc(&ccw_device_init_count);
/* Start async. device sensing. */
- spin_lock_irq(&sch->lock);
+ spin_lock_irq(sch->lock);
rc = ccw_device_recognition(cdev);
- spin_unlock_irq(&sch->lock);
+ spin_unlock_irq(sch->lock);
if (rc) {
if (atomic_dec_and_test(&ccw_device_init_count))
wake_up(&ccw_device_init_wq);
@@ -901,9 +901,9 @@ io_subchannel_probe (struct subchannel *
rc = io_subchannel_recog(cdev, sch);
if (rc) {
- spin_lock_irqsave(&sch->lock, flags);
+ spin_lock_irqsave(sch->lock, flags);
sch->dev.driver_data = NULL;
- spin_unlock_irqrestore(&sch->lock, flags);
+ spin_unlock_irqrestore(sch->lock, flags);
if (cdev->dev.release)
cdev->dev.release(&cdev->dev);
}
@@ -1010,6 +1010,13 @@ static struct ccw_device console_cdev;
static struct ccw_device_private console_private;
static int console_cdev_in_use;
+static DEFINE_SPINLOCK(ccw_console_lock);
+
+spinlock_t * cio_get_console_lock(void)
+{
+ return &ccw_console_lock;
+}
+
static int
ccw_device_console_enable (struct ccw_device *cdev, struct subchannel *sch)
{
--- linux-2.6-CH.orig/drivers/s390/cio/device_ops.c
+++ linux-2.6-CH/drivers/s390/cio/device_ops.c
@@ -316,9 +316,9 @@ __ccw_device_retry_loop(struct ccw_devic
ccw_device_set_timeout(cdev, 0);
if (ret == -EBUSY) {
/* Try again later. */
- spin_unlock_irq(&sch->lock);
+ spin_unlock_irq(sch->lock);
msleep(10);
- spin_lock_irq(&sch->lock);
+ spin_lock_irq(sch->lock);
continue;
}
if (ret != 0)
@@ -326,12 +326,12 @@ __ccw_device_retry_loop(struct ccw_devic
break;
/* Wait for end of request. */
cdev->private->intparm = magic;
- spin_unlock_irq(&sch->lock);
+ spin_unlock_irq(sch->lock);
wait_event(cdev->private->wait_q,
(cdev->private->intparm == -EIO) ||
(cdev->private->intparm == -EAGAIN) ||
(cdev->private->intparm == 0));
- spin_lock_irq(&sch->lock);
+ spin_lock_irq(sch->lock);
/* Check at least for channel end / device end */
if (cdev->private->intparm == -EIO) {
/* Non-retryable error. */
@@ -342,9 +342,9 @@ __ccw_device_retry_loop(struct ccw_devic
/* Success. */
break;
/* Try again later. */
- spin_unlock_irq(&sch->lock);
+ spin_unlock_irq(sch->lock);
msleep(10);
- spin_lock_irq(&sch->lock);
+ spin_lock_irq(sch->lock);
} while (1);
return ret;
@@ -389,7 +389,7 @@ read_dev_chars (struct ccw_device *cdev,
return ret;
}
- spin_lock_irq(&sch->lock);
+ spin_lock_irq(sch->lock);
/* Save interrupt handler. */
handler = cdev->handler;
/* Temporarily install own handler. */
@@ -406,7 +406,7 @@ read_dev_chars (struct ccw_device *cdev,
/* Restore interrupt handler. */
cdev->handler = handler;
- spin_unlock_irq(&sch->lock);
+ spin_unlock_irq(sch->lock);
clear_normalized_cda (rdc_ccw);
kfree(rdc_ccw);
@@ -463,7 +463,7 @@ read_conf_data_lpm (struct ccw_device *c
rcd_ccw->count = ciw->count;
rcd_ccw->flags = CCW_FLAG_SLI;
- spin_lock_irq(&sch->lock);
+ spin_lock_irq(sch->lock);
/* Save interrupt handler. */
handler = cdev->handler;
/* Temporarily install own handler. */
@@ -480,7 +480,7 @@ read_conf_data_lpm (struct ccw_device *c
/* Restore interrupt handler. */
cdev->handler = handler;
- spin_unlock_irq(&sch->lock);
+ spin_unlock_irq(sch->lock);
/*
* on success we update the user input parms
@@ -537,7 +537,7 @@ ccw_device_stlck(struct ccw_device *cdev
kfree(buf);
return -ENOMEM;
}
- spin_lock_irqsave(&sch->lock, flags);
+ spin_lock_irqsave(sch->lock, flags);
ret = cio_enable_subchannel(sch, 3);
if (ret)
goto out_unlock;
@@ -559,9 +559,9 @@ ccw_device_stlck(struct ccw_device *cdev
goto out_unlock;
}
cdev->private->irb.scsw.actl |= SCSW_ACTL_START_PEND;
- spin_unlock_irqrestore(&sch->lock, flags);
+ spin_unlock_irqrestore(sch->lock, flags);
wait_event(cdev->private->wait_q, cdev->private->irb.scsw.actl == 0);
- spin_lock_irqsave(&sch->lock, flags);
+ spin_lock_irqsave(sch->lock, flags);
cio_disable_subchannel(sch); //FIXME: return code?
if ((cdev->private->irb.scsw.dstat !=
(DEV_STAT_CHN_END|DEV_STAT_DEV_END)) ||
@@ -572,7 +572,7 @@ ccw_device_stlck(struct ccw_device *cdev
out_unlock:
kfree(buf);
kfree(buf2);
- spin_unlock_irqrestore(&sch->lock, flags);
+ spin_unlock_irqrestore(sch->lock, flags);
return ret;
}
^ permalink raw reply [flat|nested] 5+ messages in thread
* [RFC/Patch 5/5] s390: Dynamic subchannel mapping of ccw devices.
[not found] <20061107163730.0c8f3dad@gondolin.boeblingen.de.ibm.com>
` (3 preceding siblings ...)
2006-11-07 15:55 ` [RFC/Patch 4/5] s390: Make the per-subchannel lock dynamic Cornelia Huck
@ 2006-11-07 15:56 ` Cornelia Huck
4 siblings, 0 replies; 5+ messages in thread
From: Cornelia Huck @ 2006-11-07 15:56 UTC (permalink / raw)
To: linux-kernel
From: Cornelia Huck <cornelia.huck@de.ibm.com>
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: Cornelia Huck <cornelia.huck@de.ibm.com>
---
drivers/s390/cio/cio.c | 6
drivers/s390/cio/cio.h | 2
drivers/s390/cio/css.c | 37 ++++-
drivers/s390/cio/css.h | 4
drivers/s390/cio/device.c | 294 +++++++++++++++++++++++++++++++++++-------
drivers/s390/cio/device.h | 2
drivers/s390/cio/device_fsm.c | 17 +-
7 files changed, 306 insertions(+), 56 deletions(-)
--- linux-2.6-CH.orig/drivers/s390/cio/cio.c
+++ linux-2.6-CH/drivers/s390/cio/cio.c
@@ -413,6 +413,8 @@ cio_enable_subchannel (struct subchannel
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;
@@ -460,6 +462,8 @@ cio_disable_subchannel (struct subchanne
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;
@@ -494,7 +498,7 @@ cio_disable_subchannel (struct subchanne
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)
--- linux-2.6-CH.orig/drivers/s390/cio/css.c
+++ linux-2.6-CH/drivers/s390/cio/css.c
@@ -581,12 +581,24 @@ css_cm_enable_store(struct device *dev,
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;
@@ -594,6 +606,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;
}
/*
@@ -630,10 +643,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,
@@ -641,6 +656,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;
@@ -648,13 +666,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);
@@ -666,6 +690,11 @@ out:
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
--- linux-2.6-CH.orig/drivers/s390/cio/css.h
+++ linux-2.6-CH/drivers/s390/cio/css.h
@@ -157,6 +157,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)
@@ -182,6 +184,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;
--- linux-2.6-CH.orig/drivers/s390/cio/device.c
+++ linux-2.6-CH/drivers/s390/cio/device.c
@@ -23,6 +23,7 @@
#include <asm/param.h> /* HZ */
#include "cio.h"
+#include "cio_debug.h"
#include "css.h"
#include "device.h"
#include "ioasm.h"
@@ -294,6 +295,11 @@ online_show (struct device *dev, struct
return sprintf(buf, cdev->online ? "1\n" : "0\n");
}
+int ccw_device_is_orphan(struct ccw_device *cdev)
+{
+ return sch_is_pseudo_sch(to_subchannel(cdev->dev.parent));
+}
+
static void ccw_device_unregister(void *data)
{
struct ccw_device *cdev;
@@ -308,10 +314,23 @@ static void
ccw_device_remove_disconnected(struct ccw_device *cdev)
{
struct subchannel *sch;
+ unsigned long flags;
/*
* Forced offline in disconnected state means
* 'throw away device'.
*/
+ if (ccw_device_is_orphan(cdev)) {
+ /* Deregister ccw device. */
+ spin_lock_irqsave(cdev->ccwlock, flags);
+ cdev->private->state = DEV_STATE_NOT_OPER;
+ spin_unlock_irqrestore(cdev->ccwlock, flags);
+ if (get_device(&cdev->dev)) {
+ PREPARE_WORK(&cdev->private->kick_work,
+ ccw_device_unregister, cdev);
+ queue_work(ccw_device_work, &cdev->private->kick_work);
+ }
+ return ;
+ }
sch = to_subchannel(cdev->dev.parent);
css_sch_device_unregister(sch);
/* Reset intparm to zeroes. */
@@ -472,6 +491,8 @@ available_show (struct device *dev, stru
struct ccw_device *cdev = to_ccwdev(dev);
struct subchannel *sch;
+ if (ccw_device_is_orphan(cdev))
+ return sprintf(buf, "no device\n");
switch (cdev->private->state) {
case DEV_STATE_BOXED:
return sprintf(buf, "boxed\n");
@@ -572,11 +593,10 @@ match_devno(struct device * dev, void *
cdev = to_ccwdev(dev);
if ((cdev->private->state == DEV_STATE_DISCONNECTED) &&
+ !ccw_device_is_orphan(cdev) &&
ccw_dev_id_is_equal(&cdev->private->dev_id, &d->dev_id) &&
- (cdev != d->sibling)) {
- cdev->private->state = DEV_STATE_NOT_OPER;
+ (cdev != d->sibling))
return 1;
- }
return 0;
}
@@ -593,6 +613,28 @@ static struct ccw_device * get_disc_ccwd
return dev ? to_ccwdev(dev) : NULL;
}
+static int match_orphan(struct device *dev, void *data)
+{
+ struct ccw_dev_id *dev_id;
+ struct ccw_device *cdev;
+
+ dev_id = data;
+ cdev = to_ccwdev(dev);
+ return ccw_dev_id_is_equal(&cdev->private->dev_id, dev_id);
+}
+
+static struct ccw_device *
+get_orphaned_ccwdev_by_dev_id(struct channel_subsystem *css,
+ struct ccw_dev_id *dev_id)
+{
+ struct device *dev;
+
+ dev = device_find_child(&css->pseudo_subchannel->dev, dev_id,
+ match_orphan);
+
+ return dev ? to_ccwdev(dev) : NULL;
+}
+
static void
ccw_device_add_changed(void *data)
{
@@ -611,62 +653,18 @@ ccw_device_add_changed(void *data)
}
}
-extern int css_get_ssd_info(struct subchannel *sch);
-
void
ccw_device_do_unreg_rereg(void *data)
{
struct ccw_device *cdev;
struct subchannel *sch;
- int need_rename;
cdev = data;
sch = to_subchannel(cdev->dev.parent);
- if (cdev->private->dev_id.devno != sch->schib.pmcw.dev) {
- /*
- * The device number has changed. This is usually only when
- * a device has been detached under VM and then re-appeared
- * on another subchannel because of a different attachment
- * order than before. Ideally, we should should just switch
- * subchannels, but unfortunately, this is not possible with
- * the current implementation.
- * Instead, we search for the old subchannel for this device
- * number and deregister so there are no collisions with the
- * newly registered ccw_device.
- * FIXME: Find another solution so the block layer doesn't
- * get possibly sick...
- */
- struct ccw_device *other_cdev;
- struct ccw_dev_id dev_id;
- need_rename = 1;
- dev_id.devno = sch->schib.pmcw.dev;
- dev_id.ssid = sch->schid.ssid;
- other_cdev = get_disc_ccwdev_by_dev_id(&dev_id, cdev);
- if (other_cdev) {
- struct subchannel *other_sch;
-
- other_sch = to_subchannel(other_cdev->dev.parent);
- if (get_device(&other_sch->dev)) {
- stsch(other_sch->schid, &other_sch->schib);
- if (other_sch->schib.pmcw.dnv) {
- other_sch->schib.pmcw.intparm = 0;
- cio_modify(other_sch);
- }
- css_sch_device_unregister(other_sch);
- }
- }
- /* Update ssd info here. */
- css_get_ssd_info(sch);
- cdev->private->dev_id.devno = sch->schib.pmcw.dev;
- } else
- need_rename = 0;
device_remove_files(&cdev->dev);
if (test_and_clear_bit(1, &cdev->private->registered))
device_del(&cdev->dev);
- if (need_rename)
- snprintf (cdev->dev.bus_id, BUS_ID_SIZE, "0.%x.%04x",
- sch->schid.ssid, sch->schib.pmcw.dev);
PREPARE_WORK(&cdev->private->kick_work,
ccw_device_add_changed, cdev);
queue_work(ccw_device_work, &cdev->private->kick_work);
@@ -730,6 +728,129 @@ static struct ccw_device * io_subchannel
return cdev;
}
+static int io_subchannel_recog(struct ccw_device *, struct subchannel *);
+
+static void sch_attach_device(struct subchannel *sch,
+ struct ccw_device *cdev)
+{
+ spin_lock_irq(sch->lock);
+ sch->dev.driver_data = cdev;
+ cdev->private->schid = sch->schid;
+ cdev->ccwlock = sch->lock;
+ device_trigger_reprobe(sch);
+ spin_unlock_irq(sch->lock);
+}
+
+static void sch_attach_disconnected_device(struct subchannel *sch,
+ struct ccw_device *cdev)
+{
+ struct subchannel *other_sch;
+ int ret;
+
+ other_sch = to_subchannel(get_device(cdev->dev.parent));
+ ret = device_move(&cdev->dev, &sch->dev);
+ if (ret) {
+ CIO_MSG_EVENT(2, "Moving disconnected device 0.%x.%04x failed "
+ "(ret=%d)!\n", cdev->private->dev_id.ssid,
+ cdev->private->dev_id.devno, ret);
+ put_device(&other_sch->dev);
+ return;
+ }
+ other_sch->dev.driver_data = NULL;
+ /* No need to keep a subchannel without ccw device around. */
+ css_sch_device_unregister(other_sch);
+ put_device(&other_sch->dev);
+ sch_attach_device(sch, cdev);
+}
+
+static void sch_attach_orphaned_device(struct subchannel *sch,
+ struct ccw_device *cdev)
+{
+ int ret;
+
+ /* Try to move the ccw device to its new subchannel. */
+ ret = device_move(&cdev->dev, &sch->dev);
+ if (ret) {
+ CIO_MSG_EVENT(0, "Moving device 0.%x.%04x from orphanage "
+ "failed (ret=%d)!\n",
+ cdev->private->dev_id.ssid,
+ cdev->private->dev_id.devno, ret);
+ return;
+ }
+ sch_attach_device(sch, cdev);
+}
+
+static void sch_create_and_recog_new_device(struct subchannel *sch)
+{
+ struct ccw_device *cdev;
+
+ /* Need to allocate a new ccw device. */
+ cdev = io_subchannel_create_ccwdev(sch);
+ if (IS_ERR(cdev)) {
+ /* OK, we did everything we could... */
+ css_sch_device_unregister(sch);
+ return;
+ }
+ spin_lock_irq(sch->lock);
+ sch->dev.driver_data = cdev;
+ spin_unlock_irq(sch->lock);
+ /* Start recognition for the new ccw device. */
+ if (io_subchannel_recog(cdev, sch)) {
+ spin_lock_irq(sch->lock);
+ sch->dev.driver_data = NULL;
+ spin_unlock_irq(sch->lock);
+ if (cdev->dev.release)
+ cdev->dev.release(&cdev->dev);
+ css_sch_device_unregister(sch);
+ }
+}
+
+
+void ccw_device_move_to_orphanage(void *data)
+{
+ struct ccw_device *cdev;
+ struct ccw_device *replacing_cdev;
+ struct subchannel *sch;
+ int ret;
+ struct channel_subsystem *css;
+ struct ccw_dev_id dev_id;
+
+ cdev = (struct ccw_device *) data;
+ sch = to_subchannel(cdev->dev.parent);
+ css = to_css(sch->dev.parent);
+ dev_id.devno = sch->schib.pmcw.dev;
+ dev_id.ssid = sch->schid.ssid;
+
+ /*
+ * Move the orphaned ccw device to the orphanage so the replacing
+ * ccw device can take its place on the subchannel.
+ */
+ ret = device_move(&cdev->dev, &css->pseudo_subchannel->dev);
+ if (ret) {
+ CIO_MSG_EVENT(0, "Moving device 0.%x.%04x to orphanage failed "
+ "(ret=%d)!\n", cdev->private->dev_id.ssid,
+ cdev->private->dev_id.devno, ret);
+ return;
+ }
+ cdev->ccwlock = css->pseudo_subchannel->lock;
+ /*
+ * Search for the replacing ccw device
+ * - among the disconnected devices
+ * - in the orphanage
+ */
+ replacing_cdev = get_disc_ccwdev_by_dev_id(&dev_id, cdev);
+ if (replacing_cdev) {
+ sch_attach_disconnected_device(sch, replacing_cdev);
+ return;
+ }
+ replacing_cdev = get_orphaned_ccwdev_by_dev_id(css, &dev_id);
+ if (replacing_cdev) {
+ sch_attach_orphaned_device(sch, replacing_cdev);
+ return;
+ }
+ sch_create_and_recog_new_device(sch);
+}
+
/*
* Register recognized device.
*/
@@ -867,12 +988,61 @@ io_subchannel_recog(struct ccw_device *c
return rc;
}
+struct device_pair_struct {
+ struct ccw_device *cdev;
+ struct subchannel *sch;
+};
+
+static void ccw_device_move_to_sch(void *data)
+{
+ struct device_pair_struct *device_pair;
+ int rc;
+ struct subchannel *sch;
+ struct ccw_device *cdev;
+ struct subchannel *former_parent;
+
+ device_pair = data;
+ sch = device_pair->sch;
+ cdev = device_pair->cdev;
+ former_parent = ccw_device_is_orphan(cdev) ?
+ NULL : to_subchannel(get_device(cdev->dev.parent));
+ mutex_lock(&sch->reg_mutex);
+ /* Try to move the ccw device to its new subchannel. */
+ rc = device_move(&cdev->dev, &sch->dev);
+ mutex_unlock(&sch->reg_mutex);
+ if (rc) {
+ CIO_MSG_EVENT(2, "Moving device 0.%x.%04x to subchannel "
+ "0.%x.%04x failed (ret=%d)!\n",
+ cdev->private->dev_id.ssid,
+ cdev->private->dev_id.devno, sch->schid.ssid,
+ sch->schid.sch_no, rc);
+ css_sch_device_unregister(sch);
+ goto out;
+ }
+ if (former_parent) {
+ spin_lock_irq(former_parent->lock);
+ former_parent->dev.driver_data = NULL;
+ spin_unlock_irq(former_parent->lock);
+ css_sch_device_unregister(former_parent);
+ /* Reset intparm to zeroes. */
+ former_parent->schib.pmcw.intparm = 0;
+ cio_modify(former_parent);
+ }
+ sch_attach_device(sch, cdev);
+out:
+ if (former_parent)
+ put_device(&former_parent->dev);
+ put_device(&cdev->dev);
+ kfree(device_pair);
+}
+
static int
io_subchannel_probe (struct subchannel *sch)
{
struct ccw_device *cdev;
int rc;
unsigned long flags;
+ struct ccw_dev_id dev_id;
if (sch->dev.driver_data) {
/*
@@ -895,6 +1065,36 @@ io_subchannel_probe (struct subchannel *
get_device(&cdev->dev);
return 0;
}
+ /*
+ * First check if a fitting device may be found amongst the
+ * disconnected devices or in the orphanage.
+ */
+ dev_id.devno = sch->schib.pmcw.dev;
+ dev_id.ssid = sch->schid.ssid;
+ cdev = get_disc_ccwdev_by_dev_id(&dev_id, NULL);
+ if (!cdev)
+ cdev = get_orphaned_ccwdev_by_dev_id(to_css(sch->dev.parent),
+ &dev_id);
+ if (cdev) {
+ /*
+ * Schedule moving the device until when we have a registered
+ * subchannel to move to and succeed the probe. We can
+ * unregister later again, when the probe is through.
+ */
+ struct device_pair_struct *device_pair;
+
+ device_pair = kmalloc(sizeof(*device_pair), GFP_KERNEL);
+ if (!device_pair) {
+ put_device(&cdev->dev);
+ return -ENOMEM;
+ }
+ device_pair->cdev = cdev;
+ device_pair->sch = sch;
+ PREPARE_WORK(&cdev->private->kick_work,
+ ccw_device_move_to_sch, device_pair);
+ queue_work(slow_path_wq, &cdev->private->kick_work);
+ return 0;
+ }
cdev = io_subchannel_create_ccwdev(sch);
if (IS_ERR(cdev))
return PTR_ERR(cdev);
--- linux-2.6-CH.orig/drivers/s390/cio/device.h
+++ linux-2.6-CH/drivers/s390/cio/device.h
@@ -80,6 +80,8 @@ int ccw_device_cancel_halt_clear(struct
void ccw_device_do_unreg_rereg(void *);
void ccw_device_call_sch_unregister(void *);
+void ccw_device_move_to_orphanage(void *);
+int ccw_device_is_orphan(struct ccw_device *);
int ccw_device_recognition(struct ccw_device *);
int ccw_device_online(struct ccw_device *);
--- linux-2.6-CH.orig/drivers/s390/cio/device_fsm.c
+++ linux-2.6-CH/drivers/s390/cio/device_fsm.c
@@ -165,13 +165,12 @@ ccw_device_handle_oper(struct ccw_device
/*
* 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, cdev);
queue_work(ccw_device_work, &cdev->private->kick_work);
@@ -653,6 +652,10 @@ ccw_device_offline(struct ccw_device *cd
{
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;
@@ -1092,7 +1095,13 @@ device_trigger_reprobe(struct subchannel
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, cdev);
+ queue_work(ccw_device_work, &cdev->private->kick_work);
+ } else
+ ccw_device_start_id(cdev, 0);
}
static void
--- linux-2.6-CH.orig/drivers/s390/cio/cio.h
+++ linux-2.6-CH/drivers/s390/cio/cio.h
@@ -131,6 +131,8 @@ extern int cio_set_options (struct subch
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);
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2006-11-07 15:56 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
[not found] <20061107163730.0c8f3dad@gondolin.boeblingen.de.ibm.com>
2006-11-07 15:55 ` [RFC/Patch 1/5] driver core: Introduce device_find_child() Cornelia Huck
2006-11-07 15:55 ` [RFC/Patch 2/5] driver core: Introduce device_move(): move a device to a new parent Cornelia Huck
2006-11-07 15:55 ` [RFC/Patch 3/5] s390: Prepatory cleanup in common I/O layer Cornelia Huck
2006-11-07 15:55 ` [RFC/Patch 4/5] s390: Make the per-subchannel lock dynamic Cornelia Huck
2006-11-07 15:56 ` [RFC/Patch 5/5] s390: Dynamic subchannel mapping of ccw devices Cornelia Huck
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®