Devices with active channel measurement are included in a list. When a
device is removed without deactivating channel measurement first the
list_head is freed but still used. Fix this by making sure that
channel measurement is deactivated during device deregistration.
For devices that we deregister because they are no longer accessible
deactivating channel measurement will fail. In this case we can report
success because the FW will no longer access the measurement block.
In addition to these steps keep an extra device reference while
channel measurement is active.
Signed-off-by: Sebastian Ott <sebott@linux.vnet.ibm.com>
Reviewed-by: Cornelia Huck <cornelia.huck@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
static int set_schib(struct ccw_device *cdev, u32 mme, int mbfc,
unsigned long address)
{
static int set_schib(struct ccw_device *cdev, u32 mme, int mbfc,
unsigned long address)
{
- struct subchannel *sch;
-
- sch = to_subchannel(cdev->dev.parent);
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
+ int ret;
sch->config.mme = mme;
sch->config.mbfc = mbfc;
sch->config.mme = mme;
sch->config.mbfc = mbfc;
else
sch->config.mbi = address;
else
sch->config.mbi = address;
- return cio_commit_config(sch);
+ ret = cio_commit_config(sch);
+ if (!mme && ret == -ENODEV) {
+ /*
+ * The task was to disable measurement block updates but
+ * the subchannel is already gone. Report success.
+ */
+ ret = 0;
+ }
+ return ret;
}
struct set_schib_struct {
}
struct set_schib_struct {
spin_lock_irq(cdev->ccwlock);
priv = cdev->private;
spin_lock_irq(cdev->ccwlock);
priv = cdev->private;
-
- if (list_empty(&priv->cmb_list)) {
- /* already freed */
- goto out;
- }
-
cmb_data = priv->cmb;
priv->cmb = NULL;
if (cmb_data)
cmb_data = priv->cmb;
priv->cmb = NULL;
if (cmb_data)
free_pages((unsigned long)cmb_area.mem, get_order(size));
cmb_area.mem = NULL;
}
free_pages((unsigned long)cmb_area.mem, get_order(size));
cmb_area.mem = NULL;
}
spin_unlock_irq(cdev->ccwlock);
spin_unlock(&cmb_area.lock);
}
spin_unlock_irq(cdev->ccwlock);
spin_unlock(&cmb_area.lock);
}
int ret;
device_lock(&cdev->dev);
int ret;
device_lock(&cdev->dev);
+ get_device(&cdev->dev);
ret = cmbops->alloc(cdev);
if (ret)
goto out;
ret = cmbops->alloc(cdev);
if (ret)
goto out;
cmbops->free(cdev);
}
out:
cmbops->free(cdev);
}
out:
+ if (ret)
+ put_device(&cdev->dev);
+
device_unlock(&cdev->dev);
return ret;
}
device_unlock(&cdev->dev);
return ret;
}
sysfs_remove_group(&cdev->dev.kobj, cmbops->attr_group);
cmbops->free(cdev);
sysfs_remove_group(&cdev->dev.kobj, cmbops->attr_group);
cmbops->free(cdev);
+ put_device(&cdev->dev);
cdev->drv = NULL;
cdev->private->int_class = IRQIO_CIO;
spin_unlock_irq(cdev->ccwlock);
cdev->drv = NULL;
cdev->private->int_class = IRQIO_CIO;
spin_unlock_irq(cdev->ccwlock);