Commit 197cec28 authored by Julian Wiedmann's avatar Julian Wiedmann Committed by Vasily Gorbik
Browse files

s390/ccwgroup: release the cdevs from within dev->release()



Wiring up the cdevs is an essential part of the gdev creation. So
release them during the gdev destruction, ie. on the last put_device().
This could cause us to hold on to the cdevs a tiny bit longer, but
that's not a real concern.

As we have much less certainty of what context this put_device() is
executed from, switch to irqsave locking.

Signed-off-by: default avatarJulian Wiedmann <jwi@linux.ibm.com>
Signed-off-by: default avatarVasily Gorbik <gor@linux.ibm.com>
parent 95c09f03
Loading
Loading
Loading
Loading
+17 −32
Original line number Diff line number Diff line
@@ -45,27 +45,6 @@ static void __ccwgroup_remove_symlinks(struct ccwgroup_device *gdev)
	}
}

/*
 * Remove references from ccw devices to ccw group device and from
 * ccw group device to ccw devices.
 */
static void __ccwgroup_remove_cdev_refs(struct ccwgroup_device *gdev)
{
	struct ccw_device *cdev;
	int i;

	for (i = 0; i < gdev->count; i++) {
		cdev = gdev->cdev[i];
		if (!cdev)
			continue;
		spin_lock_irq(cdev->ccwlock);
		dev_set_drvdata(&cdev->dev, NULL);
		spin_unlock_irq(cdev->ccwlock);
		gdev->cdev[i] = NULL;
		put_device(&cdev->dev);
	}
}

/**
 * ccwgroup_set_online() - enable a ccwgroup device
 * @gdev: target ccwgroup device
@@ -175,7 +154,6 @@ static void ccwgroup_ungroup(struct ccwgroup_device *gdev)
	if (device_is_registered(&gdev->dev)) {
		__ccwgroup_remove_symlinks(gdev);
		device_unregister(&gdev->dev);
		__ccwgroup_remove_cdev_refs(gdev);
	}
	mutex_unlock(&gdev->reg_mutex);
}
@@ -228,7 +206,23 @@ static void ccwgroup_ungroup_workfn(struct work_struct *work)

static void ccwgroup_release(struct device *dev)
{
	kfree(to_ccwgroupdev(dev));
	struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
	unsigned int i;

	for (i = 0; i < gdev->count; i++) {
		struct ccw_device *cdev = gdev->cdev[i];
		unsigned long flags;

		if (cdev) {
			spin_lock_irqsave(cdev->ccwlock, flags);
			if (dev_get_drvdata(&cdev->dev) == gdev)
				dev_set_drvdata(&cdev->dev, NULL);
			spin_unlock_irqrestore(cdev->ccwlock, flags);
			put_device(&cdev->dev);
		}
	}

	kfree(gdev);
}

static int __ccwgroup_create_symlinks(struct ccwgroup_device *gdev)
@@ -396,15 +390,6 @@ int ccwgroup_create_dev(struct device *parent, struct ccwgroup_driver *gdrv,
	mutex_unlock(&gdev->reg_mutex);
	return 0;
error:
	for (i = 0; i < num_devices; i++)
		if (gdev->cdev[i]) {
			spin_lock_irq(gdev->cdev[i]->ccwlock);
			if (dev_get_drvdata(&gdev->cdev[i]->dev) == gdev)
				dev_set_drvdata(&gdev->cdev[i]->dev, NULL);
			spin_unlock_irq(gdev->cdev[i]->ccwlock);
			put_device(&gdev->cdev[i]->dev);
			gdev->cdev[i] = NULL;
		}
	mutex_unlock(&gdev->reg_mutex);
	put_device(&gdev->dev);
	return rc;