|
@@ -432,6 +432,9 @@ static void init_once(void *foo)
|
|
|
mutex_init(&bdev->bd_mutex);
|
|
|
INIT_LIST_HEAD(&bdev->bd_inodes);
|
|
|
INIT_LIST_HEAD(&bdev->bd_list);
|
|
|
+#ifdef CONFIG_SYSFS
|
|
|
+ INIT_LIST_HEAD(&bdev->bd_holder_disks);
|
|
|
+#endif
|
|
|
inode_init_once(&ei->vfs_inode);
|
|
|
/* Initialize mutex for freeze. */
|
|
|
mutex_init(&bdev->bd_fsfreeze_mutex);
|
|
@@ -779,6 +782,23 @@ static struct block_device *bd_start_claiming(struct block_device *bdev,
|
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_SYSFS
|
|
|
+struct bd_holder_disk {
|
|
|
+ struct list_head list;
|
|
|
+ struct gendisk *disk;
|
|
|
+ int refcnt;
|
|
|
+};
|
|
|
+
|
|
|
+static struct bd_holder_disk *bd_find_holder_disk(struct block_device *bdev,
|
|
|
+ struct gendisk *disk)
|
|
|
+{
|
|
|
+ struct bd_holder_disk *holder;
|
|
|
+
|
|
|
+ list_for_each_entry(holder, &bdev->bd_holder_disks, list)
|
|
|
+ if (holder->disk == disk)
|
|
|
+ return holder;
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
static int add_symlink(struct kobject *from, struct kobject *to)
|
|
|
{
|
|
|
return sysfs_create_link(from, to, kobject_name(to));
|
|
@@ -794,6 +814,8 @@ static void del_symlink(struct kobject *from, struct kobject *to)
|
|
|
* @bdev: the claimed slave bdev
|
|
|
* @disk: the holding disk
|
|
|
*
|
|
|
+ * DON'T USE THIS UNLESS YOU'RE ALREADY USING IT.
|
|
|
+ *
|
|
|
* This functions creates the following sysfs symlinks.
|
|
|
*
|
|
|
* - from "slaves" directory of the holder @disk to the claimed @bdev
|
|
@@ -817,47 +839,83 @@ static void del_symlink(struct kobject *from, struct kobject *to)
|
|
|
*/
|
|
|
int bd_link_disk_holder(struct block_device *bdev, struct gendisk *disk)
|
|
|
{
|
|
|
+ struct bd_holder_disk *holder;
|
|
|
int ret = 0;
|
|
|
|
|
|
mutex_lock(&bdev->bd_mutex);
|
|
|
|
|
|
- WARN_ON_ONCE(!bdev->bd_holder || bdev->bd_holder_disk);
|
|
|
+ WARN_ON_ONCE(!bdev->bd_holder);
|
|
|
|
|
|
/* FIXME: remove the following once add_disk() handles errors */
|
|
|
if (WARN_ON(!disk->slave_dir || !bdev->bd_part->holder_dir))
|
|
|
goto out_unlock;
|
|
|
|
|
|
- ret = add_symlink(disk->slave_dir, &part_to_dev(bdev->bd_part)->kobj);
|
|
|
- if (ret)
|
|
|
+ holder = bd_find_holder_disk(bdev, disk);
|
|
|
+ if (holder) {
|
|
|
+ holder->refcnt++;
|
|
|
goto out_unlock;
|
|
|
+ }
|
|
|
|
|
|
- ret = add_symlink(bdev->bd_part->holder_dir, &disk_to_dev(disk)->kobj);
|
|
|
- if (ret) {
|
|
|
- del_symlink(disk->slave_dir, &part_to_dev(bdev->bd_part)->kobj);
|
|
|
+ holder = kzalloc(sizeof(*holder), GFP_KERNEL);
|
|
|
+ if (!holder) {
|
|
|
+ ret = -ENOMEM;
|
|
|
goto out_unlock;
|
|
|
}
|
|
|
|
|
|
- bdev->bd_holder_disk = disk;
|
|
|
+ INIT_LIST_HEAD(&holder->list);
|
|
|
+ holder->disk = disk;
|
|
|
+ holder->refcnt = 1;
|
|
|
+
|
|
|
+ ret = add_symlink(disk->slave_dir, &part_to_dev(bdev->bd_part)->kobj);
|
|
|
+ if (ret)
|
|
|
+ goto out_free;
|
|
|
+
|
|
|
+ ret = add_symlink(bdev->bd_part->holder_dir, &disk_to_dev(disk)->kobj);
|
|
|
+ if (ret)
|
|
|
+ goto out_del;
|
|
|
+
|
|
|
+ list_add(&holder->list, &bdev->bd_holder_disks);
|
|
|
+ goto out_unlock;
|
|
|
+
|
|
|
+out_del:
|
|
|
+ del_symlink(disk->slave_dir, &part_to_dev(bdev->bd_part)->kobj);
|
|
|
+out_free:
|
|
|
+ kfree(holder);
|
|
|
out_unlock:
|
|
|
mutex_unlock(&bdev->bd_mutex);
|
|
|
return ret;
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(bd_link_disk_holder);
|
|
|
|
|
|
-static void bd_unlink_disk_holder(struct block_device *bdev)
|
|
|
+/**
|
|
|
+ * bd_unlink_disk_holder - destroy symlinks created by bd_link_disk_holder()
|
|
|
+ * @bdev: the calimed slave bdev
|
|
|
+ * @disk: the holding disk
|
|
|
+ *
|
|
|
+ * DON'T USE THIS UNLESS YOU'RE ALREADY USING IT.
|
|
|
+ *
|
|
|
+ * CONTEXT:
|
|
|
+ * Might sleep.
|
|
|
+ */
|
|
|
+void bd_unlink_disk_holder(struct block_device *bdev, struct gendisk *disk)
|
|
|
{
|
|
|
- struct gendisk *disk = bdev->bd_holder_disk;
|
|
|
+ struct bd_holder_disk *holder;
|
|
|
|
|
|
- bdev->bd_holder_disk = NULL;
|
|
|
- if (!disk)
|
|
|
- return;
|
|
|
+ mutex_lock(&bdev->bd_mutex);
|
|
|
|
|
|
- del_symlink(disk->slave_dir, &part_to_dev(bdev->bd_part)->kobj);
|
|
|
- del_symlink(bdev->bd_part->holder_dir, &disk_to_dev(disk)->kobj);
|
|
|
+ holder = bd_find_holder_disk(bdev, disk);
|
|
|
+
|
|
|
+ if (!WARN_ON_ONCE(holder == NULL) && !--holder->refcnt) {
|
|
|
+ del_symlink(disk->slave_dir, &part_to_dev(bdev->bd_part)->kobj);
|
|
|
+ del_symlink(bdev->bd_part->holder_dir,
|
|
|
+ &disk_to_dev(disk)->kobj);
|
|
|
+ list_del_init(&holder->list);
|
|
|
+ kfree(holder);
|
|
|
+ }
|
|
|
+
|
|
|
+ mutex_unlock(&bdev->bd_mutex);
|
|
|
}
|
|
|
-#else
|
|
|
-static inline void bd_unlink_disk_holder(struct block_device *bdev)
|
|
|
-{ }
|
|
|
+EXPORT_SYMBOL_GPL(bd_unlink_disk_holder);
|
|
|
#endif
|
|
|
|
|
|
/**
|
|
@@ -1380,7 +1438,6 @@ int blkdev_put(struct block_device *bdev, fmode_t mode)
|
|
|
* unblock evpoll if it was a write holder.
|
|
|
*/
|
|
|
if (bdev_free) {
|
|
|
- bd_unlink_disk_holder(bdev);
|
|
|
if (bdev->bd_write_holder) {
|
|
|
disk_unblock_events(bdev->bd_disk);
|
|
|
bdev->bd_write_holder = false;
|