/*
* Copyright (C) 2001, 2002 Sistina Software (UK) Limited.
- * Copyright (C) 2004 - 2005 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2004 - 2006 Red Hat, Inc. All rights reserved.
*
* This file is released under the GPL.
*/
#include <linux/init.h>
#include <linux/wait.h>
#include <linux/slab.h>
-#include <linux/devfs_fs_kernel.h>
#include <linux/dm-ioctl.h>
#include <linux/hdreg.h>
#include <asm/uaccess.h>
+#define DM_MSG_PREFIX "ioctl"
#define DM_DRIVER_EMAIL "dm-devel@redhat.com"
/*-----------------------------------------------------------------
static struct list_head _name_buckets[NUM_BUCKETS];
static struct list_head _uuid_buckets[NUM_BUCKETS];
-static void dm_hash_remove_all(void);
+static void dm_hash_remove_all(int keep_open_devices);
/*
* Guards access to both hash tables.
{
init_buckets(_name_buckets);
init_buckets(_uuid_buckets);
- devfs_mk_dir(DM_DIR);
return 0;
}
static void dm_hash_exit(void)
{
- dm_hash_remove_all();
- devfs_remove(DM_DIR);
+ dm_hash_remove_all(0);
}
/*-----------------------------------------------------------------
}
}
-/*
- * devfs stuff.
- */
-static int register_with_devfs(struct hash_cell *hc)
-{
- struct gendisk *disk = dm_disk(hc->md);
-
- devfs_mk_bdev(MKDEV(disk->major, disk->first_minor),
- S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP,
- DM_DIR "/%s", hc->name);
- return 0;
-}
-
-static int unregister_with_devfs(struct hash_cell *hc)
-{
- devfs_remove(DM_DIR"/%s", hc->name);
- return 0;
-}
-
/*
* The kdev_t and uuid of a device can never change once it is
* initially inserted.
}
list_add(&cell->uuid_list, _uuid_buckets + hash_str(uuid));
}
- register_with_devfs(cell);
dm_get(md);
dm_set_mdptr(md, cell);
up_write(&_hash_lock);
/* remove from the dev hash */
list_del(&hc->uuid_list);
list_del(&hc->name_list);
- unregister_with_devfs(hc);
dm_set_mdptr(hc->md, NULL);
table = dm_get_table(hc->md);
free_cell(hc);
}
-static void dm_hash_remove_all(void)
+static void dm_hash_remove_all(int keep_open_devices)
{
- int i;
+ int i, dev_skipped, dev_removed;
struct hash_cell *hc;
struct list_head *tmp, *n;
down_write(&_hash_lock);
+
+retry:
+ dev_skipped = dev_removed = 0;
for (i = 0; i < NUM_BUCKETS; i++) {
list_for_each_safe (tmp, n, _name_buckets + i) {
hc = list_entry(tmp, struct hash_cell, name_list);
+
+ if (keep_open_devices &&
+ dm_lock_for_deletion(hc->md)) {
+ dev_skipped++;
+ continue;
+ }
__hash_remove(hc);
+ dev_removed = 1;
}
}
+
+ /*
+ * Some mapped devices may be using other mapped devices, so if any
+ * still exist, repeat until we make no further progress.
+ */
+ if (dev_skipped) {
+ if (dev_removed)
+ goto retry;
+
+ DMWARN("remove_all left %d open device(s)", dev_skipped);
+ }
+
up_write(&_hash_lock);
}
/*
* rename and move the name cell.
*/
- unregister_with_devfs(hc);
-
list_del(&hc->name_list);
old_name = hc->name;
hc->name = new_name;
list_add(&hc->name_list, _name_buckets + hash_str(new_name));
- /* rename the device node in devfs */
- register_with_devfs(hc);
-
/*
* Wake up any dm event waiters.
*/
static int remove_all(struct dm_ioctl *param, size_t param_size)
{
- dm_hash_remove_all();
+ dm_hash_remove_all(1);
param->data_size = 0;
return 0;
}
{
struct gendisk *disk = dm_disk(md);
struct dm_table *table;
- struct block_device *bdev;
param->flags &= ~(DM_SUSPEND_FLAG | DM_READONLY_FLAG |
DM_ACTIVE_PRESENT_FLAG);
param->dev = huge_encode_dev(MKDEV(disk->major, disk->first_minor));
- if (!(param->flags & DM_SKIP_BDGET_FLAG)) {
- bdev = bdget_disk(disk, 0);
- if (!bdev)
- return -ENXIO;
-
- /*
- * Yes, this will be out of date by the time it gets back
- * to userland, but it is still very useful for
- * debugging.
- */
- param->open_count = bdev->bd_openers;
- bdput(bdev);
- } else
- param->open_count = -1;
+ /*
+ * Yes, this will be out of date by the time it gets back
+ * to userland, but it is still very useful for
+ * debugging.
+ */
+ param->open_count = dm_open_count(md);
if (disk->policy)
param->flags |= DM_READONLY_FLAG;
static int dev_create(struct dm_ioctl *param, size_t param_size)
{
- int r;
+ int r, m = DM_ANY_MINOR;
struct mapped_device *md;
r = check_name(param->name);
return r;
if (param->flags & DM_PERSISTENT_DEV_FLAG)
- r = dm_create_with_minor(MINOR(huge_decode_dev(param->dev)), &md);
- else
- r = dm_create(&md);
+ m = MINOR(huge_decode_dev(param->dev));
+ r = dm_create(m, &md);
if (r)
return r;
{
struct hash_cell *hc;
struct mapped_device *md;
+ int r;
down_write(&_hash_lock);
hc = __find_device_hash_cell(param);
md = hc->md;
+ /*
+ * Ensure the device is not open and nothing further can open it.
+ */
+ r = dm_lock_for_deletion(md);
+ if (r) {
+ DMWARN("unable to remove open device %s", hc->name);
+ up_write(&_hash_lock);
+ dm_put(md);
+ return r;
+ }
+
__hash_remove(hc);
up_write(&_hash_lock);
dm_put(md);
static struct miscdevice _dm_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = DM_NAME,
- .devfs_name = "mapper/control",
.fops = &_ctl_fops
};