mirror of
git://sourceware.org/git/lvm2.git
synced 2025-01-03 05:18:29 +03:00
o Initial merge attempt
There are still a few odd things going on, so more debugging remains to be done.
This commit is contained in:
parent
b546cd6d69
commit
a9a572e8a1
@ -15,7 +15,8 @@
|
||||
#ifndef DEVICE_MAPPER_H
|
||||
#define DEVICE_MAPPER_H
|
||||
|
||||
#include <linux/major.h>
|
||||
#define DM_DIR "device-mapper"
|
||||
#define DM_MAX_TYPE_NAME 16
|
||||
|
||||
struct dm_table;
|
||||
struct dm_dev;
|
||||
@ -40,6 +41,7 @@ typedef char *(*dm_print_fn)(void *context);
|
||||
* (ie. opened/closed).
|
||||
*/
|
||||
int dm_table_get_device(struct dm_table *t, const char *path,
|
||||
offset_t start, offset_t len,
|
||||
struct dm_dev **result);
|
||||
void dm_table_put_device(struct dm_table *table, struct dm_dev *d);
|
||||
|
||||
@ -59,19 +61,6 @@ struct target_type {
|
||||
int dm_register_target(struct target_type *t);
|
||||
int dm_unregister_target(struct target_type *t);
|
||||
|
||||
static inline char *next_token(char **p)
|
||||
{
|
||||
static const char *delim = " \t";
|
||||
char *r;
|
||||
|
||||
do {
|
||||
r = strsep(p, delim);
|
||||
} while(r && *r == 0);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
#endif /* DEVICE_MAPPER_H */
|
||||
|
||||
/*
|
||||
|
331
driver/device-mapper/dm-ioctl.c
Normal file
331
driver/device-mapper/dm-ioctl.c
Normal file
@ -0,0 +1,331 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
|
||||
#include "dm.h"
|
||||
#include <linux/dm-ioctl.h>
|
||||
|
||||
static void free_params(struct dm_ioctl *p)
|
||||
{
|
||||
vfree(p);
|
||||
}
|
||||
|
||||
static int copy_params(struct dm_ioctl *user, struct dm_ioctl **result)
|
||||
{
|
||||
struct dm_ioctl tmp, *dmi;
|
||||
|
||||
if (copy_from_user(&tmp, user, sizeof(tmp)))
|
||||
return -EFAULT;
|
||||
|
||||
if (!(dmi = vmalloc(tmp.data_size)))
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(dmi, user, tmp.data_size))
|
||||
return -EFAULT;
|
||||
|
||||
*result = dmi;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* check a string doesn't overrun the chunk of
|
||||
* memory we copied from userland.
|
||||
*/
|
||||
static int valid_str(char *str, void *end)
|
||||
{
|
||||
while (((void *) str < end) && *str)
|
||||
str++;
|
||||
|
||||
return *str ? 0 : 1;
|
||||
}
|
||||
|
||||
static int first_target(struct dm_ioctl *a, void *end,
|
||||
struct dm_target_spec **spec, char **params)
|
||||
{
|
||||
*spec = (struct dm_target_spec *) (a + 1);
|
||||
*params = (char *) (*spec + 1);
|
||||
|
||||
return valid_str(*params, end);
|
||||
}
|
||||
|
||||
static int next_target(struct dm_target_spec *last, void *end,
|
||||
struct dm_target_spec **spec, char **params)
|
||||
{
|
||||
*spec = (struct dm_target_spec *)
|
||||
(((unsigned char *) last) + last->next);
|
||||
*params = (char *) (*spec + 1);
|
||||
|
||||
return valid_str(*params, end);
|
||||
}
|
||||
|
||||
void err_fn(const char *message, void *private)
|
||||
{
|
||||
printk(KERN_WARNING "%s\n", message);
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks to see if there's a gap in the table.
|
||||
* Returns true iff there is a gap.
|
||||
*/
|
||||
static int gap(struct dm_table *table, struct dm_target_spec *spec)
|
||||
{
|
||||
if (!table->num_targets)
|
||||
return (spec->sector_start > 0) ? 1 : 0;
|
||||
|
||||
if (spec->sector_start != table->highs[table->num_targets - 1] + 1)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int populate_table(struct dm_table *table, struct dm_ioctl *args)
|
||||
{
|
||||
int i = 0, r, first = 1;
|
||||
struct dm_target_spec *spec;
|
||||
char *params;
|
||||
struct target_type *ttype;
|
||||
void *context, *end;
|
||||
offset_t high = 0;
|
||||
|
||||
if (!args->target_count) {
|
||||
WARN("No targets specified");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
end = ((void *) args) + args->data_size;
|
||||
|
||||
#define PARSE_ERROR(msg) {err_fn(msg, NULL); return -EINVAL;}
|
||||
|
||||
for (i = 0; i < args->target_count; i++) {
|
||||
|
||||
r = first ? first_target(args, end, &spec, ¶ms) :
|
||||
next_target(spec, end, &spec, ¶ms);
|
||||
|
||||
if (!r)
|
||||
PARSE_ERROR("unable to find target");
|
||||
|
||||
/* lookup the target type */
|
||||
if (!(ttype = dm_get_target_type(spec->target_type)))
|
||||
PARSE_ERROR("unable to find target type");
|
||||
|
||||
if (gap(table, spec))
|
||||
PARSE_ERROR("gap in target ranges");
|
||||
|
||||
/* build the target */
|
||||
if (ttype->ctr(table, spec->sector_start, spec->length, params,
|
||||
&context))
|
||||
PARSE_ERROR(context);
|
||||
|
||||
/* add the target to the table */
|
||||
high = spec->sector_start + (spec->length - 1);
|
||||
if (dm_table_add_target(table, high, ttype, context))
|
||||
PARSE_ERROR("internal error adding target to table");
|
||||
|
||||
first = 0;
|
||||
}
|
||||
|
||||
#undef PARSE_ERROR
|
||||
|
||||
r = dm_table_complete(table);
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Copies device info back to user space, used by
|
||||
* the create and info ioctls.
|
||||
*/
|
||||
static int info(const char *name, struct dm_ioctl *user)
|
||||
{
|
||||
struct dm_ioctl param;
|
||||
struct mapped_device *md = dm_get(name);
|
||||
|
||||
if (!md) {
|
||||
param.exists = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
param.data_size = 0;
|
||||
strncpy(param.name, md->name, sizeof(param.name));
|
||||
param.exists = 1;
|
||||
param.suspend = md->suspended;
|
||||
param.open_count = md->use_count;
|
||||
param.major = MAJOR(md->dev);
|
||||
param.minor = MINOR(md->dev);
|
||||
param.target_count = md->map->num_targets;
|
||||
|
||||
out:
|
||||
return copy_to_user(user, ¶m, sizeof(param));
|
||||
}
|
||||
|
||||
static int create(struct dm_ioctl *param, struct dm_ioctl *user)
|
||||
{
|
||||
int r;
|
||||
struct mapped_device *md;
|
||||
struct dm_table *t;
|
||||
|
||||
t = dm_table_create();
|
||||
r = PTR_ERR(t);
|
||||
if (IS_ERR(t))
|
||||
goto bad;
|
||||
|
||||
if ((r = populate_table(t, param)))
|
||||
goto bad;
|
||||
|
||||
md = dm_create(param->name, param->minor, t);
|
||||
r = PTR_ERR(md);
|
||||
if (IS_ERR(md))
|
||||
goto bad;
|
||||
|
||||
if ((r = info(param->name, user))) {
|
||||
dm_destroy(md);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
bad:
|
||||
dm_table_destroy(t);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int remove(struct dm_ioctl *param)
|
||||
{
|
||||
struct mapped_device *md = dm_get(param->name);
|
||||
|
||||
if (!md)
|
||||
return -ENXIO;
|
||||
|
||||
return dm_destroy(md);
|
||||
}
|
||||
|
||||
static int suspend(struct dm_ioctl *param)
|
||||
{
|
||||
struct mapped_device *md = dm_get(param->name);
|
||||
|
||||
if (!md)
|
||||
return -ENXIO;
|
||||
|
||||
return param->suspend ? dm_suspend(md) : dm_resume(md);
|
||||
}
|
||||
|
||||
static int reload(struct dm_ioctl *param)
|
||||
{
|
||||
int r;
|
||||
struct mapped_device *md = dm_get(param->name);
|
||||
struct dm_table *t;
|
||||
|
||||
if (!md)
|
||||
return -ENXIO;
|
||||
|
||||
t = dm_table_create();
|
||||
if (IS_ERR(t))
|
||||
return PTR_ERR(t);
|
||||
|
||||
if ((r = populate_table(t, param))) {
|
||||
dm_table_destroy(t);
|
||||
return r;
|
||||
}
|
||||
|
||||
if ((r = dm_swap_table(md, t))) {
|
||||
dm_table_destroy(t);
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ctl_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
/* only root can open this */
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EACCES;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ctl_close(struct inode *inode, struct file *file)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int ctl_ioctl(struct inode *inode, struct file *file,
|
||||
uint command, ulong a)
|
||||
{
|
||||
int r;
|
||||
struct dm_ioctl *p;
|
||||
|
||||
if ((r = copy_params((struct dm_ioctl *) a, &p)))
|
||||
return r;
|
||||
|
||||
switch (command) {
|
||||
case DM_CREATE:
|
||||
r = create(p, (struct dm_ioctl *) a);
|
||||
break;
|
||||
|
||||
case DM_REMOVE:
|
||||
r = remove(p);
|
||||
break;
|
||||
|
||||
case DM_SUSPEND:
|
||||
r = suspend(p);
|
||||
break;
|
||||
|
||||
case DM_RELOAD:
|
||||
r = reload(p);
|
||||
break;
|
||||
|
||||
case DM_INFO:
|
||||
r = info(p->name, (struct dm_ioctl *) a);
|
||||
break;
|
||||
|
||||
default:
|
||||
WARN("dm_ctl_ioctl: unknown command 0x%x\n", command);
|
||||
r = -EINVAL;
|
||||
}
|
||||
|
||||
free_params(p);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
static struct file_operations _ctl_fops = {
|
||||
open: ctl_open,
|
||||
release: ctl_close,
|
||||
ioctl: ctl_ioctl,
|
||||
owner: THIS_MODULE,
|
||||
};
|
||||
|
||||
|
||||
static devfs_handle_t _ctl_handle;
|
||||
|
||||
int dm_interface_init(void)
|
||||
{
|
||||
int r;
|
||||
|
||||
if ((r = devfs_register_chrdev(DM_CHAR_MAJOR, DM_DIR,
|
||||
&_ctl_fops)) < 0) {
|
||||
WARN("devfs_register_chrdev failed for dm control dev");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
_ctl_handle = devfs_register(0 , DM_DIR "/control", 0,
|
||||
DM_CHAR_MAJOR, 0,
|
||||
S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP,
|
||||
&_ctl_fops, NULL);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void dm_interface_exit(void)
|
||||
{
|
||||
// FIXME: remove control device
|
||||
|
||||
if (devfs_unregister_chrdev(DM_CHAR_MAJOR, DM_DIR) < 0)
|
||||
WARN("devfs_unregister_chrdev failed for dm control device");
|
||||
}
|
||||
|
57
driver/device-mapper/dm-ioctl.h
Normal file
57
driver/device-mapper/dm-ioctl.h
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#ifndef _DM_IOCTL_H
|
||||
#define _DM_IOCTL_H
|
||||
|
||||
#include "device-mapper.h"
|
||||
|
||||
/*
|
||||
* Implements a traditional ioctl interface to the
|
||||
* device mapper. Yuck.
|
||||
*/
|
||||
|
||||
struct dm_target_spec {
|
||||
int32_t status; /* used when reading from kernel only */
|
||||
unsigned long long sector_start;
|
||||
unsigned long long length;
|
||||
|
||||
char target_type[DM_MAX_TYPE_NAME];
|
||||
|
||||
unsigned long next; /* offset in bytes to next target_spec */
|
||||
|
||||
/*
|
||||
* Parameter string starts immediately
|
||||
* after this object. Be careful to add
|
||||
* padding after string to ensure correct
|
||||
* alignment of subsequent dm_target_spec.
|
||||
*/
|
||||
};
|
||||
|
||||
struct dm_ioctl {
|
||||
unsigned long data_size; /* the size of this structure */
|
||||
char name[DM_NAME_LEN];
|
||||
|
||||
int exists; /* out */
|
||||
int suspend; /* in/out */
|
||||
int open_count; /* out */
|
||||
int major; /* out */
|
||||
int minor; /* in/out */
|
||||
|
||||
int target_count; /* in/out */
|
||||
};
|
||||
|
||||
/* FIXME: find own numbers, 109 is pinched from LVM */
|
||||
#define DM_IOCTL 0xfd
|
||||
#define DM_CHAR_MAJOR 124
|
||||
|
||||
#define DM_CREATE _IOWR(DM_IOCTL, 0x00, struct dm_ioctl)
|
||||
#define DM_REMOVE _IOW(DM_IOCTL, 0x01, struct dm_ioctl)
|
||||
#define DM_SUSPEND _IOW(DM_IOCTL, 0x02, struct dm_ioctl)
|
||||
#define DM_RELOAD _IOWR(DM_IOCTL, 0x03, struct dm_ioctl)
|
||||
#define DM_INFO _IOWR(DM_IOCTL, 0x04, struct dm_ioctl)
|
||||
|
||||
#endif
|
@ -24,6 +24,18 @@ struct linear_c {
|
||||
struct dm_dev *dev;
|
||||
};
|
||||
|
||||
static inline char *next_token(char **p)
|
||||
{
|
||||
static const char *delim = " \t";
|
||||
char *r;
|
||||
|
||||
do {
|
||||
r = strsep(p, delim);
|
||||
} while(r && *r == 0);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* construct a linear mapping.
|
||||
* <dev_path> <offset>
|
||||
@ -55,7 +67,7 @@ static int linear_ctr(struct dm_table *t, offset_t b, offset_t l,
|
||||
goto bad;
|
||||
|
||||
*context = "Cannot get target device";
|
||||
r = dm_table_get_device(t, path, &lc->dev);
|
||||
r = dm_table_get_device(t, path, start, l, &lc->dev);
|
||||
if (r)
|
||||
goto bad_free;
|
||||
|
||||
|
185
driver/device-mapper/dm-stripe.c
Normal file
185
driver/device-mapper/dm-stripe.c
Normal file
@ -0,0 +1,185 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/device-mapper.h>
|
||||
|
||||
#include "dm.h"
|
||||
|
||||
struct stripe {
|
||||
struct dm_dev *dev;
|
||||
offset_t physical_start;
|
||||
};
|
||||
|
||||
struct stripe_c {
|
||||
offset_t logical_start;
|
||||
uint32_t stripes;
|
||||
|
||||
/* The size of this target / num. stripes */
|
||||
uint32_t stripe_width;
|
||||
|
||||
/* eg, we stripe in 64k chunks */
|
||||
uint32_t chunk_shift;
|
||||
offset_t chunk_mask;
|
||||
|
||||
struct stripe stripe[0];
|
||||
};
|
||||
|
||||
|
||||
static inline struct stripe_c *alloc_context(int stripes)
|
||||
{
|
||||
size_t len = sizeof(struct stripe_c) +
|
||||
(sizeof(struct stripe) * stripes);
|
||||
return kmalloc(len, GFP_KERNEL);
|
||||
}
|
||||
|
||||
/*
|
||||
* parses a single <dev> <sector> pair.
|
||||
*/
|
||||
static int get_stripe(struct dm_table *t, struct stripe_c *sc,
|
||||
int stripe, char *args)
|
||||
{
|
||||
int n, r;
|
||||
char path[256]; /* FIXME: buffer overrun risk */
|
||||
unsigned long start;
|
||||
|
||||
if (sscanf(args, "%s %lu %n", path, &start, &n) != 2)
|
||||
return -EINVAL;
|
||||
|
||||
if ((r = dm_table_get_device(t, path, start, sc->stripe_width,
|
||||
&sc->stripe[stripe].dev)))
|
||||
return -ENXIO;
|
||||
|
||||
sc->stripe[stripe].physical_start = start;
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
* construct a striped mapping.
|
||||
* <number of stripes> <chunk size (2^^n)> [<dev_path> <offset>]+
|
||||
*/
|
||||
static int stripe_ctr(struct dm_table *t, offset_t b, offset_t l,
|
||||
char *args, void **context)
|
||||
{
|
||||
struct stripe_c *sc;
|
||||
uint32_t stripes;
|
||||
uint32_t chunk_size;
|
||||
int n, i;
|
||||
|
||||
*context = "couldn't parse <stripes> <chunk size>";
|
||||
if (sscanf(args, "%u %u %n", &stripes, &chunk_size, &n) != 2) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*context = "target length is not divisable by the number of stripes";
|
||||
if (l % stripes) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*context = "couldn't allocate memory for striped context";
|
||||
if (!(sc = alloc_context(stripes))) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
sc->logical_start = b;
|
||||
sc->stripes = stripes;
|
||||
sc->stripe_width = l / stripes;
|
||||
|
||||
/*
|
||||
* chunk_size is a power of two. We only
|
||||
* that power and the mask.
|
||||
*/
|
||||
*context = "invalid chunk size";
|
||||
if (!chunk_size) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sc->chunk_mask = chunk_size - 1;
|
||||
for (sc->chunk_shift = 0; chunk_size; sc->chunk_shift++)
|
||||
chunk_size >>= 1;
|
||||
sc->chunk_shift--;
|
||||
|
||||
/*
|
||||
* Get the stripe destinations.
|
||||
*/
|
||||
for (i = 0; i < stripes; i++) {
|
||||
args += n;
|
||||
n = get_stripe(t, sc, i, args);
|
||||
|
||||
*context = "couldn't parse stripe destination";
|
||||
if (n < 0) {
|
||||
kfree(sc);
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
*context = sc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stripe_dtr(struct dm_table *t, void *c)
|
||||
{
|
||||
unsigned int i;
|
||||
struct stripe_c *sc = (struct stripe_c *) c;
|
||||
|
||||
for (i = 0; i < sc->stripes; i++)
|
||||
dm_table_put_device(t, sc->stripe[i].dev);
|
||||
|
||||
kfree(sc);
|
||||
}
|
||||
|
||||
static int stripe_map(struct buffer_head *bh, int rw, void *context)
|
||||
{
|
||||
struct stripe_c *sc = (struct stripe_c *) context;
|
||||
|
||||
offset_t offset = bh->b_rsector - sc->logical_start;
|
||||
uint32_t chunk = (uint32_t) (offset >> sc->chunk_shift);
|
||||
uint32_t stripe = chunk % sc->stripes; /* 32bit modulus */
|
||||
chunk = chunk / sc->stripes;
|
||||
|
||||
bh->b_rdev = sc->stripe[stripe].dev->dev;
|
||||
bh->b_rsector = sc->stripe[stripe].physical_start +
|
||||
(chunk << sc->chunk_shift) +
|
||||
(offset & sc->chunk_mask);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct target_type stripe_target = {
|
||||
name: "striped",
|
||||
module: THIS_MODULE,
|
||||
ctr: stripe_ctr,
|
||||
dtr: stripe_dtr,
|
||||
map: stripe_map,
|
||||
};
|
||||
|
||||
static int __init stripe_init(void)
|
||||
{
|
||||
int r;
|
||||
|
||||
if ((r = dm_register_target(&stripe_target)) < 0)
|
||||
WARN("linear target register failed");
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void __exit stripe_exit(void)
|
||||
{
|
||||
if (dm_unregister_target(&stripe_target))
|
||||
WARN("striped target unregister failed");
|
||||
}
|
||||
|
||||
module_init(stripe_init);
|
||||
module_exit(stripe_exit);
|
||||
|
||||
MODULE_AUTHOR("Joe Thornber <thornber@sistina.com>");
|
||||
MODULE_DESCRIPTION("Device Mapper: Striped mapping");
|
||||
MODULE_LICENSE("GPL");
|
@ -1,19 +1,14 @@
|
||||
/*
|
||||
* dm-table.c
|
||||
*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Changelog
|
||||
*
|
||||
* 16/08/2001 - First version [Joe Thornber]
|
||||
*/
|
||||
|
||||
#include "dm.h"
|
||||
|
||||
#include <linux/blkdev.h>
|
||||
|
||||
|
||||
/* ceiling(n / size) * size */
|
||||
static inline ulong round_up(ulong n, ulong size)
|
||||
{
|
||||
@ -96,6 +91,7 @@ static int alloc_targets(struct dm_table *t, int num)
|
||||
memcpy(n_targets, t->targets, sizeof(*n_targets) * n);
|
||||
}
|
||||
|
||||
memset(n_highs + n , -1, sizeof(*n_highs) * (num - n));
|
||||
vfree(t->highs);
|
||||
|
||||
t->num_allocated = num;
|
||||
@ -110,7 +106,7 @@ struct dm_table *dm_table_create(void)
|
||||
struct dm_table *t = kmalloc(sizeof(struct dm_table), GFP_NOIO);
|
||||
|
||||
if (!t)
|
||||
return 0;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
memset(t, 0, sizeof(*t));
|
||||
INIT_LIST_HEAD(&t->devices);
|
||||
@ -119,7 +115,7 @@ struct dm_table *dm_table_create(void)
|
||||
begin with */
|
||||
if (alloc_targets(t, KEYS_PER_NODE)) {
|
||||
kfree(t);
|
||||
t = 0;
|
||||
t = ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
return t;
|
||||
@ -144,12 +140,14 @@ void dm_table_destroy(struct dm_table *t)
|
||||
if (t->depth >= 2)
|
||||
vfree(t->index[t->depth - 2]);
|
||||
|
||||
|
||||
/* free the targets */
|
||||
for (i = 0; i < t->num_targets; i++) {
|
||||
struct target *tgt = &t->targets[i];
|
||||
|
||||
if (tgt->type->dtr)
|
||||
tgt->type->dtr(t, tgt->private);
|
||||
|
||||
dm_put_target_type(t->targets[i].type);
|
||||
}
|
||||
|
||||
vfree(t->highs);
|
||||
@ -218,21 +216,74 @@ static struct dm_dev *find_device(struct list_head *l, kdev_t dev)
|
||||
{
|
||||
struct list_head *tmp;
|
||||
|
||||
for (tmp = l->next; tmp != l; tmp = tmp->next) {
|
||||
|
||||
list_for_each(tmp, l) {
|
||||
struct dm_dev *dd = list_entry(tmp, struct dm_dev, list);
|
||||
if (dd->dev == dev)
|
||||
return dd;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* open a device so we can use it as a map
|
||||
* destination.
|
||||
*/
|
||||
static int open_dev(struct dm_dev *d)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (d->bd)
|
||||
BUG();
|
||||
|
||||
if (!(d->bd = bdget(kdev_t_to_nr(d->dev))))
|
||||
return -ENOMEM;
|
||||
|
||||
if ((err = blkdev_get(d->bd, FMODE_READ|FMODE_WRITE, 0, BDEV_FILE)))
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* close a device that we've been using.
|
||||
*/
|
||||
static void close_dev(struct dm_dev *d)
|
||||
{
|
||||
if (!d->bd)
|
||||
return;
|
||||
|
||||
blkdev_put(d->bd, BDEV_FILE);
|
||||
d->bd = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* If possible (ie. blk_size[major] is set), this
|
||||
* checks an area of a destination device is
|
||||
* valid.
|
||||
*/
|
||||
static int check_device_area(kdev_t dev, offset_t start, offset_t len)
|
||||
{
|
||||
int *sizes;
|
||||
offset_t dev_size;
|
||||
|
||||
if (!(sizes = blk_size[MAJOR(dev)]) || !(dev_size = sizes[MINOR(dev)]))
|
||||
/* we don't know the device details,
|
||||
* so give the benefit of the doubt */
|
||||
return 1;
|
||||
|
||||
/* convert to 512-byte sectors */
|
||||
dev_size <<= 1;
|
||||
|
||||
return ((start < dev_size) && (len <= (dev_size - start)));
|
||||
}
|
||||
|
||||
/*
|
||||
* add a device to the list, or just increment the
|
||||
* usage count if it's already present.
|
||||
*/
|
||||
int dm_table_get_device(struct dm_table *t, const char *path,
|
||||
offset_t start, offset_t len,
|
||||
struct dm_dev **result)
|
||||
{
|
||||
int r;
|
||||
@ -251,14 +302,28 @@ int dm_table_get_device(struct dm_table *t, const char *path,
|
||||
|
||||
dd->dev = dev;
|
||||
dd->bd = 0;
|
||||
|
||||
if ((r = open_dev(dd))) {
|
||||
kfree(dd);
|
||||
return r;
|
||||
}
|
||||
|
||||
atomic_set(&dd->count, 0);
|
||||
list_add(&dd->list, &t->devices);
|
||||
}
|
||||
atomic_inc(&dd->count);
|
||||
|
||||
if (!check_device_area(dd->dev, start, len)) {
|
||||
WARN("device '%s' not large enough for target", path);
|
||||
dm_table_put_device(t, dd);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*result = dd;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* decrement a devices use count and remove it if
|
||||
* neccessary.
|
||||
@ -266,6 +331,7 @@ int dm_table_get_device(struct dm_table *t, const char *path,
|
||||
void dm_table_put_device(struct dm_table *t, struct dm_dev *dd)
|
||||
{
|
||||
if (atomic_dec_and_test(&dd->count)) {
|
||||
close_dev(dd);
|
||||
list_del(&dd->list);
|
||||
kfree(dd);
|
||||
}
|
||||
@ -307,7 +373,8 @@ static int setup_indexes(struct dm_table *t)
|
||||
|
||||
/* set up internal nodes, bottom-up */
|
||||
for (i = t->depth - 2, total = 0; i >= 0; i--) {
|
||||
t->index[i] = indexes + (KEYS_PER_NODE * t->counts[i]);
|
||||
t->index[i] = indexes;
|
||||
indexes += (KEYS_PER_NODE * t->counts[i]);
|
||||
setup_btree_index(i, t);
|
||||
}
|
||||
|
||||
|
@ -1,15 +1,9 @@
|
||||
/*
|
||||
* dm-target.c
|
||||
*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 16/08/2001 - First Version [Joe Thornber]
|
||||
*/
|
||||
|
||||
#include "dm.h"
|
||||
#include <linux/kmod.h>
|
||||
|
||||
@ -30,14 +24,14 @@ static inline struct tt_internal *__find_target_type(const char *name)
|
||||
struct list_head *tmp;
|
||||
struct tt_internal *ti;
|
||||
|
||||
for(tmp = _targets.next; tmp != &_targets; tmp = tmp->next) {
|
||||
|
||||
list_for_each(tmp, &_targets) {
|
||||
ti = list_entry(tmp, struct tt_internal, list);
|
||||
|
||||
if (!strcmp(name, ti->tt.name))
|
||||
return ti;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct tt_internal *get_target_type(const char *name)
|
||||
@ -78,7 +72,7 @@ struct target_type *dm_get_target_type(const char *name)
|
||||
ti = get_target_type(name);
|
||||
}
|
||||
|
||||
return ti ? &ti->tt : 0;
|
||||
return ti ? &ti->tt : NULL;
|
||||
}
|
||||
|
||||
void dm_put_target_type(struct target_type *t)
|
||||
@ -126,18 +120,24 @@ int dm_register_target(struct target_type *t)
|
||||
|
||||
int dm_unregister_target(struct target_type *t)
|
||||
{
|
||||
struct tt_internal *ti = (struct tt_internal *) t;
|
||||
int rv = -ETXTBSY;
|
||||
struct tt_internal *ti;
|
||||
|
||||
write_lock(&_lock);
|
||||
if (ti->use == 0) {
|
||||
if (!(ti = __find_target_type(t->name))) {
|
||||
write_unlock(&_lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ti->use) {
|
||||
write_unlock(&_lock);
|
||||
return -ETXTBSY;
|
||||
}
|
||||
|
||||
list_del(&ti->list);
|
||||
kfree(ti);
|
||||
rv = 0;
|
||||
}
|
||||
write_unlock(&_lock);
|
||||
|
||||
return rv;
|
||||
write_unlock(&_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -147,7 +147,7 @@ int dm_unregister_target(struct target_type *t)
|
||||
static int io_err_ctr(struct dm_table *t, offset_t b, offset_t l,
|
||||
char *args, void **context)
|
||||
{
|
||||
*context = 0;
|
||||
*context = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1,35 +1,14 @@
|
||||
/*
|
||||
* device-mapper.c
|
||||
*
|
||||
* Copyright (C) 2001 Sistina Software
|
||||
*
|
||||
* This software is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with GNU CC; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Changelog
|
||||
*
|
||||
* 14/08/2001 - First Version [Joe Thornber]
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#include "dm.h"
|
||||
#include "dmfs.h"
|
||||
|
||||
#include <linux/blkpg.h>
|
||||
#include <linux/blk.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/blkpg.h>
|
||||
#include <linux/kmod.h>
|
||||
|
||||
/* we only need this for the lv_bmap struct definition, not happy */
|
||||
@ -37,10 +16,11 @@
|
||||
|
||||
#define MAX_DEVICES 64
|
||||
#define DEFAULT_READ_AHEAD 64
|
||||
#define DEVICE_NAME "device-mapper"
|
||||
|
||||
static const char *_name = DEVICE_NAME;
|
||||
static int _version[3] = {0, 1, 0};
|
||||
static int major = 0;
|
||||
const char *_name = "device-mapper";
|
||||
int _version[3] = {0, 1, 0};
|
||||
|
||||
struct io_hook {
|
||||
struct mapped_device *md;
|
||||
@ -51,14 +31,14 @@ struct io_hook {
|
||||
void *context;
|
||||
};
|
||||
|
||||
kmem_cache_t *_io_hook_cache;
|
||||
static kmem_cache_t *_io_hook_cache;
|
||||
|
||||
#define rl down_read(&_dev_lock)
|
||||
#define ru up_read(&_dev_lock)
|
||||
#define wl down_write(&_dev_lock)
|
||||
#define wu up_write(&_dev_lock)
|
||||
|
||||
struct rw_semaphore _dev_lock;
|
||||
static struct rw_semaphore _dev_lock;
|
||||
static struct mapped_device *_devs[MAX_DEVICES];
|
||||
|
||||
/* block device arrays */
|
||||
@ -66,7 +46,6 @@ static int _block_size[MAX_DEVICES];
|
||||
static int _blksize_size[MAX_DEVICES];
|
||||
static int _hardsect_size[MAX_DEVICES];
|
||||
|
||||
const char *_fs_dir = "device-mapper";
|
||||
static devfs_handle_t _dev_dir;
|
||||
|
||||
static int request(request_queue_t *q, int rw, struct buffer_head *bh);
|
||||
@ -88,13 +67,13 @@ static int __init dm_init(void)
|
||||
if (!_io_hook_cache)
|
||||
goto err;
|
||||
|
||||
ret = dmfs_init();
|
||||
if (ret < 0)
|
||||
goto err_kmem_cache_destroy;
|
||||
|
||||
ret = dm_target_init();
|
||||
if (ret)
|
||||
goto err_kmem_cache_destroy;
|
||||
if (ret < 0)
|
||||
goto err_cache_free;
|
||||
|
||||
ret = dm_interface_init();
|
||||
if (ret < 0)
|
||||
goto err_cache_free;
|
||||
|
||||
ret = devfs_register_blkdev(major, _name, &dm_blk_dops);
|
||||
if (ret < 0)
|
||||
@ -109,11 +88,9 @@ static int __init dm_init(void)
|
||||
blksize_size[major] = _blksize_size;
|
||||
hardsect_size[major] = _hardsect_size;
|
||||
|
||||
ret = -EIO;
|
||||
|
||||
blk_queue_make_request(BLK_DEFAULT_QUEUE(major), request);
|
||||
|
||||
_dev_dir = devfs_mk_dir(0, _fs_dir, NULL);
|
||||
_dev_dir = devfs_mk_dir(0, DM_DIR, NULL);
|
||||
|
||||
printk(KERN_INFO "%s %d.%d.%d initialised\n", _name,
|
||||
_version[0], _version[1], _version[2]);
|
||||
@ -121,7 +98,8 @@ static int __init dm_init(void)
|
||||
|
||||
err_blkdev:
|
||||
printk(KERN_ERR "%s -- register_blkdev failed\n", _name);
|
||||
err_kmem_cache_destroy:
|
||||
dm_interface_exit();
|
||||
err_cache_free:
|
||||
kmem_cache_destroy(_io_hook_cache);
|
||||
err:
|
||||
return ret;
|
||||
@ -129,11 +107,12 @@ err:
|
||||
|
||||
static void __exit dm_exit(void)
|
||||
{
|
||||
dmfs_exit();
|
||||
dm_interface_exit();
|
||||
|
||||
if (kmem_cache_destroy(_io_hook_cache))
|
||||
WARN("it looks like there are still some io_hooks allocated");
|
||||
|
||||
_io_hook_cache = NULL;
|
||||
|
||||
if (devfs_unregister_blkdev(major, _name) < 0)
|
||||
printk(KERN_ERR "%s -- unregister_blkdev failed\n", _name);
|
||||
@ -147,45 +126,6 @@ static void __exit dm_exit(void)
|
||||
_version[0], _version[1], _version[2]);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HOTPLUG
|
||||
static void dm_sbin_hotplug(struct mapped_device *md, char *action, int minor)
|
||||
{
|
||||
int i;
|
||||
char *argv[3];
|
||||
char *envp[7];
|
||||
char name[DM_NAME_LEN + 16];
|
||||
char dev_major[16], dev_minor[16];
|
||||
|
||||
if (!hotplug_path[0])
|
||||
return;
|
||||
|
||||
if (!current->fs->root)
|
||||
return;
|
||||
|
||||
sprintf(name, "DMNAME=%s\n", md->name);
|
||||
sprintf(dev_major, "MAJOR=%d", major);
|
||||
sprintf(dev_minor, "MINOR=%d", minor);
|
||||
|
||||
i = 0;
|
||||
argv[i++] = hotplug_path;
|
||||
argv[i++] = "devmap";
|
||||
argv[i] = 0;
|
||||
|
||||
i = 0;
|
||||
envp[i++] = "HOME=/";
|
||||
envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
|
||||
envp[i++] = name;
|
||||
envp[i++] = action;
|
||||
envp[i++] = dev_minor;
|
||||
envp[i++] = dev_major;
|
||||
envp[i] = 0;
|
||||
|
||||
call_usermodehelper(argv[0], argv, envp);
|
||||
}
|
||||
#else
|
||||
#define dm_sbin_hotplug(md, action, minor) do { } while(0)
|
||||
#endif /* CONFIG_HOTPLUG */
|
||||
|
||||
/*
|
||||
* block device functions
|
||||
*/
|
||||
@ -200,7 +140,7 @@ static int dm_blk_open(struct inode *inode, struct file *file)
|
||||
wl;
|
||||
md = _devs[minor];
|
||||
|
||||
if (!md || !is_active(md)) {
|
||||
if (!md) {
|
||||
wu;
|
||||
return -ENXIO;
|
||||
}
|
||||
@ -208,6 +148,7 @@ static int dm_blk_open(struct inode *inode, struct file *file)
|
||||
md->use_count++;
|
||||
wu;
|
||||
|
||||
MOD_INC_USE_COUNT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -230,11 +171,12 @@ static int dm_blk_close(struct inode *inode, struct file *file)
|
||||
md->use_count--;
|
||||
wu;
|
||||
|
||||
MOD_DEC_USE_COUNT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* In 512-byte units */
|
||||
#define VOLUME_SIZE(minor) (_block_size[(minor)] >> 1)
|
||||
#define VOLUME_SIZE(minor) (_block_size[(minor)] << 1)
|
||||
|
||||
static int dm_blk_ioctl(struct inode *inode, struct file *file,
|
||||
uint command, ulong a)
|
||||
@ -358,7 +300,7 @@ static int queue_io(struct mapped_device *md, struct buffer_head *bh, int rw)
|
||||
return -ENOMEM;
|
||||
|
||||
wl;
|
||||
if (test_bit(DM_ACTIVE, &md->state)) {
|
||||
if (!md->suspended) {
|
||||
wu;
|
||||
return 0;
|
||||
}
|
||||
@ -449,12 +391,19 @@ static int request(request_queue_t *q, int rw, struct buffer_head *bh)
|
||||
rl;
|
||||
md = _devs[minor];
|
||||
|
||||
if (!md || !md->map)
|
||||
if (!md)
|
||||
goto bad;
|
||||
|
||||
/* if we're suspended we have to queue this io for later */
|
||||
if (!test_bit(DM_ACTIVE, &md->state)) {
|
||||
/*
|
||||
* If we're suspended we have to queue
|
||||
* this io for later.
|
||||
*/
|
||||
while (md->suspended) {
|
||||
ru;
|
||||
|
||||
if (rw == READA)
|
||||
goto bad_no_lock;
|
||||
|
||||
r = queue_io(md, bh, rw);
|
||||
|
||||
if (r < 0)
|
||||
@ -463,7 +412,13 @@ static int request(request_queue_t *q, int rw, struct buffer_head *bh)
|
||||
else if (r > 0)
|
||||
return 0; /* deferred successfully */
|
||||
|
||||
rl; /* FIXME: there's still a race here */
|
||||
/*
|
||||
* We're in a while loop, because
|
||||
* someone could suspend before we
|
||||
* get to the following read
|
||||
* lock
|
||||
*/
|
||||
rl;
|
||||
}
|
||||
|
||||
if (!__map_buffer(md, bh, rw, __find_node(md->map, bh)))
|
||||
@ -501,8 +456,7 @@ static int do_bmap(kdev_t dev, unsigned long block,
|
||||
struct target *t;
|
||||
|
||||
rl;
|
||||
if ((minor >= MAX_DEVICES) || !(md = _devs[minor]) ||
|
||||
!test_bit(DM_ACTIVE, &md->state)) {
|
||||
if ((minor >= MAX_DEVICES) || !(md = _devs[minor]) || md->suspended) {
|
||||
r = -ENXIO;
|
||||
goto out;
|
||||
}
|
||||
@ -611,7 +565,7 @@ static struct mapped_device *alloc_dev(int minor)
|
||||
|
||||
md->dev = MKDEV(major, minor);
|
||||
md->name[0] = '\0';
|
||||
md->state = 0;
|
||||
md->suspended = 0;
|
||||
|
||||
init_waitqueue_head(&md->wait);
|
||||
|
||||
@ -621,84 +575,9 @@ static struct mapped_device *alloc_dev(int minor)
|
||||
return md;
|
||||
}
|
||||
|
||||
/*
|
||||
* open a device so we can use it as a map
|
||||
* destination.
|
||||
*/
|
||||
static int open_dev(struct dm_dev *d)
|
||||
static void free_dev(struct mapped_device *md)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (d->bd)
|
||||
BUG();
|
||||
|
||||
if (!(d->bd = bdget(kdev_t_to_nr(d->dev))))
|
||||
return -ENOMEM;
|
||||
|
||||
if ((err = blkdev_get(d->bd, FMODE_READ|FMODE_WRITE, 0, BDEV_FILE))) {
|
||||
bdput(d->bd);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* close a device that we've been using.
|
||||
*/
|
||||
static void close_dev(struct dm_dev *d)
|
||||
{
|
||||
if (!d->bd)
|
||||
return;
|
||||
|
||||
blkdev_put(d->bd, BDEV_FILE);
|
||||
bdput(d->bd);
|
||||
d->bd = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Close a list of devices.
|
||||
*/
|
||||
static void close_devices(struct list_head *devices)
|
||||
{
|
||||
struct list_head *tmp;
|
||||
|
||||
for (tmp = devices->next; tmp != devices; tmp = tmp->next) {
|
||||
struct dm_dev *dd = list_entry(tmp, struct dm_dev, list);
|
||||
close_dev(dd);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Open a list of devices.
|
||||
*/
|
||||
static int open_devices(struct list_head *devices)
|
||||
{
|
||||
int r = 0;
|
||||
struct list_head *tmp;
|
||||
|
||||
for (tmp = devices->next; tmp != devices; tmp = tmp->next) {
|
||||
struct dm_dev *dd = list_entry(tmp, struct dm_dev, list);
|
||||
if ((r = open_dev(dd)))
|
||||
goto bad;
|
||||
}
|
||||
return 0;
|
||||
|
||||
bad:
|
||||
close_devices(devices);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
struct mapped_device *dm_find_by_minor(int minor)
|
||||
{
|
||||
struct mapped_device *md;
|
||||
|
||||
rl;
|
||||
md = _devs[minor];
|
||||
ru;
|
||||
|
||||
return md;
|
||||
kfree(md);
|
||||
}
|
||||
|
||||
static int register_device(struct mapped_device *md)
|
||||
@ -718,10 +597,95 @@ static int unregister_device(struct mapped_device *md)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* the hardsect size for a mapped device is the
|
||||
* smallest hard sect size from the devices it
|
||||
* maps onto.
|
||||
*/
|
||||
static int __find_hardsect_size(struct list_head *devices)
|
||||
{
|
||||
int result = INT_MAX, size;
|
||||
struct list_head *tmp;
|
||||
|
||||
list_for_each(tmp, devices) {
|
||||
struct dm_dev *dd = list_entry(tmp, struct dm_dev, list);
|
||||
size = get_hardsect_size(dd->dev);
|
||||
if (size < result)
|
||||
result = size;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Bind a table to the device.
|
||||
*/
|
||||
static int __bind(struct mapped_device *md, struct dm_table *t)
|
||||
{
|
||||
int minor = MINOR(md->dev);
|
||||
|
||||
md->map = t;
|
||||
|
||||
if (!t->num_targets) {
|
||||
_block_size[minor] = 0;
|
||||
_blksize_size[minor] = BLOCK_SIZE;
|
||||
_hardsect_size[minor] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* in k */
|
||||
_block_size[minor] = (t->highs[t->num_targets - 1] + 1) >> 1;
|
||||
|
||||
_blksize_size[minor] = BLOCK_SIZE;
|
||||
_hardsect_size[minor] = __find_hardsect_size(&t->devices);
|
||||
register_disk(NULL, md->dev, 1, &dm_blk_dops, _block_size[minor]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __unbind(struct mapped_device *md)
|
||||
{
|
||||
int minor = MINOR(md->dev);
|
||||
|
||||
dm_table_destroy(md->map);
|
||||
md->map = NULL;
|
||||
|
||||
_block_size[minor] = 0;
|
||||
_blksize_size[minor] = 0;
|
||||
_hardsect_size[minor] = 0;
|
||||
}
|
||||
|
||||
|
||||
static struct mapped_device *__get_by_name(const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_DEVICES; i++)
|
||||
if (_devs[i] && !strcmp(_devs[i]->name, name))
|
||||
return _devs[i];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int check_name(const char *name)
|
||||
{
|
||||
if (strchr(name, '/')) {
|
||||
WARN("invalid device name");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (__get_by_name(name)) {
|
||||
WARN("device name already in use");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* constructor for a new device
|
||||
*/
|
||||
struct mapped_device *dm_create(const char *name, int minor)
|
||||
struct mapped_device *dm_create(const char *name, int minor,
|
||||
struct dm_table *table)
|
||||
{
|
||||
int r;
|
||||
struct mapped_device *md;
|
||||
@ -733,28 +697,47 @@ struct mapped_device *dm_create(const char *name, int minor)
|
||||
return ERR_PTR(-ENXIO);
|
||||
|
||||
wl;
|
||||
if (!check_name(name)) {
|
||||
wu;
|
||||
free_dev(md);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
strcpy(md->name, name);
|
||||
_devs[minor] = md;
|
||||
if ((r = register_device(md))) {
|
||||
wu;
|
||||
free_dev(md);
|
||||
return ERR_PTR(r);
|
||||
}
|
||||
|
||||
if ((r = __bind(md, table))) {
|
||||
wu;
|
||||
free_dev(md);
|
||||
return ERR_PTR(r);
|
||||
}
|
||||
wu;
|
||||
|
||||
dm_sbin_hotplug(md, "ACTION=create", minor);
|
||||
|
||||
return md;
|
||||
}
|
||||
|
||||
/*
|
||||
* destructor for the device. md->map is
|
||||
* deliberately not destroyed, dm-fs should manage
|
||||
* table objects.
|
||||
* Destructor for the device. You cannot destroy
|
||||
* a suspended device.
|
||||
*/
|
||||
int dm_remove(struct mapped_device *md)
|
||||
int dm_destroy(struct mapped_device *md)
|
||||
{
|
||||
int minor, r;
|
||||
|
||||
rl;
|
||||
if (md->suspended || md->use_count) {
|
||||
ru;
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
fsync_dev(md->dev);
|
||||
ru;
|
||||
|
||||
wl;
|
||||
if (md->use_count) {
|
||||
wu;
|
||||
@ -768,131 +751,59 @@ int dm_remove(struct mapped_device *md)
|
||||
|
||||
minor = MINOR(md->dev);
|
||||
_devs[minor] = 0;
|
||||
__unbind(md);
|
||||
|
||||
wu;
|
||||
|
||||
dm_sbin_hotplug(md, "ACTION=remove", minor);
|
||||
kfree(md);
|
||||
free_dev(md);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* the hardsect size for a mapped device is the
|
||||
* smallest hard sect size from the devices it
|
||||
* maps onto.
|
||||
*/
|
||||
static int __find_hardsect_size(struct list_head *devices)
|
||||
{
|
||||
int result = INT_MAX, size;
|
||||
struct list_head *tmp;
|
||||
|
||||
for (tmp = devices->next; tmp != devices; tmp = tmp->next) {
|
||||
struct dm_dev *dd = list_entry(tmp, struct dm_dev, list);
|
||||
size = get_hardsect_size(dd->dev);
|
||||
if (size < result)
|
||||
result = size;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Bind a table to the device.
|
||||
*/
|
||||
void __bind(struct mapped_device *md, struct dm_table *t)
|
||||
{
|
||||
int minor = MINOR(md->dev);
|
||||
|
||||
md->map = t;
|
||||
|
||||
/* in k */
|
||||
_block_size[minor] = (t->highs[t->num_targets - 1] + 1) >> 1;
|
||||
|
||||
_blksize_size[minor] = BLOCK_SIZE;
|
||||
_hardsect_size[minor] = __find_hardsect_size(&t->devices);
|
||||
register_disk(NULL, md->dev, 1, &dm_blk_dops, _block_size[minor]);
|
||||
}
|
||||
|
||||
/*
|
||||
* requeue the deferred buffer_heads by calling
|
||||
* generic_make_request.
|
||||
*/
|
||||
static void __flush_deferred_io(struct mapped_device *md)
|
||||
static void flush_deferred_io(struct deferred_io *c)
|
||||
{
|
||||
struct deferred_io *c, *n;
|
||||
struct deferred_io *n;
|
||||
|
||||
for (c = md->deferred, md->deferred = 0; c; c = n) {
|
||||
while (c) {
|
||||
n = c->next;
|
||||
generic_make_request(c->rw, c->bh);
|
||||
free_deferred(c);
|
||||
c = n;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* make the device available for use, if was
|
||||
* previously suspended rather than newly created
|
||||
* then all queued io is flushed
|
||||
* Swap in a new table (destroying old one).
|
||||
*/
|
||||
int dm_activate(struct mapped_device *md, struct dm_table *table)
|
||||
int dm_swap_table(struct mapped_device *md, struct dm_table *table)
|
||||
{
|
||||
int r;
|
||||
|
||||
/* check that the mapping has at least been loaded. */
|
||||
if (!table->num_targets)
|
||||
return -EINVAL;
|
||||
|
||||
wl;
|
||||
|
||||
/* you must be deactivated first */
|
||||
if (is_active(md)) {
|
||||
/* device must be suspended */
|
||||
if (!md->suspended) {
|
||||
wu;
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
__bind(md, table);
|
||||
__unbind(md);
|
||||
|
||||
if ((r = open_devices(&md->map->devices))) {
|
||||
if ((r = __bind(md, table))) {
|
||||
wu;
|
||||
return r;
|
||||
}
|
||||
|
||||
set_bit(DM_ACTIVE, &md->state);
|
||||
__flush_deferred_io(md);
|
||||
wu;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Deactivate the device, the device must not be
|
||||
* opened by anyone.
|
||||
*/
|
||||
int dm_deactivate(struct mapped_device *md)
|
||||
{
|
||||
rl;
|
||||
if (md->use_count) {
|
||||
ru;
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
fsync_dev(md->dev);
|
||||
|
||||
ru;
|
||||
|
||||
wl;
|
||||
if (md->use_count) {
|
||||
/* drat, somebody got in quick ... */
|
||||
wu;
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
if (md->map)
|
||||
close_devices(&md->map->devices);
|
||||
md->map = NULL;
|
||||
clear_bit(DM_ACTIVE, &md->state);
|
||||
wu;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We need to be able to change a mapping table
|
||||
@ -903,17 +814,17 @@ int dm_deactivate(struct mapped_device *md)
|
||||
* flush any in flight buffer_heads and ensure
|
||||
* that any further io gets deferred.
|
||||
*/
|
||||
void dm_suspend(struct mapped_device *md)
|
||||
int dm_suspend(struct mapped_device *md)
|
||||
{
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
|
||||
wl;
|
||||
if (!is_active(md)) {
|
||||
if (md->suspended) {
|
||||
wu;
|
||||
return;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
clear_bit(DM_ACTIVE, &md->state);
|
||||
md->suspended = 1;
|
||||
wu;
|
||||
|
||||
/* wait for all the pending io to flush */
|
||||
@ -931,10 +842,43 @@ void dm_suspend(struct mapped_device *md)
|
||||
|
||||
current->state = TASK_RUNNING;
|
||||
remove_wait_queue(&md->wait, &wait);
|
||||
close_devices(&md->map->devices);
|
||||
|
||||
md->map = 0;
|
||||
wu;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dm_resume(struct mapped_device *md)
|
||||
{
|
||||
struct deferred_io *def;
|
||||
|
||||
wl;
|
||||
if (!md->suspended) {
|
||||
wu;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
md->suspended = 0;
|
||||
def = md->deferred;
|
||||
md->deferred = NULL;
|
||||
wu;
|
||||
|
||||
flush_deferred_io(def);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Search for a device with a particular name.
|
||||
*/
|
||||
struct mapped_device *dm_get(const char *name)
|
||||
{
|
||||
struct mapped_device *md;
|
||||
|
||||
rl;
|
||||
md = __get_by_name(name);
|
||||
ru;
|
||||
|
||||
return md;
|
||||
}
|
||||
|
||||
struct block_device_operations dm_blk_dops = {
|
||||
@ -951,13 +895,8 @@ module_init(dm_init);
|
||||
module_exit(dm_exit);
|
||||
|
||||
MODULE_PARM(major, "i");
|
||||
MODULE_PARM_DESC(major, "The major device number of the device-mapper");
|
||||
MODULE_PARM_DESC(major, "The major number of the device mapper");
|
||||
MODULE_DESCRIPTION("device-mapper driver");
|
||||
MODULE_AUTHOR("Joe Thornber <thornber@btconnect.com>");
|
||||
MODULE_AUTHOR("Joe Thornber <thornber@sistina.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
|
@ -124,13 +124,6 @@
|
||||
#define KEYS_PER_NODE (NODE_SIZE / sizeof(offset_t))
|
||||
#define CHILDREN_PER_NODE (KEYS_PER_NODE + 1)
|
||||
#define DM_NAME_LEN 128
|
||||
#define MAX_TARGET_LINE 256
|
||||
|
||||
enum {
|
||||
DM_BOUND = 0, /* device has been bound to a table */
|
||||
DM_ACTIVE, /* device is running */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* list of devices that a metadevice uses
|
||||
@ -188,7 +181,7 @@ struct mapped_device {
|
||||
char name[DM_NAME_LEN];
|
||||
|
||||
int use_count;
|
||||
int state;
|
||||
int suspended;
|
||||
|
||||
/* a list of io's that arrived while we were suspended */
|
||||
atomic_t pending;
|
||||
@ -211,15 +204,11 @@ void dm_put_target_type(struct target_type *t);
|
||||
|
||||
/* dm.c */
|
||||
struct mapped_device *dm_find_by_minor(int minor);
|
||||
|
||||
struct mapped_device *dm_create(const char *name, int minor);
|
||||
int dm_remove(struct mapped_device *md);
|
||||
|
||||
int dm_activate(struct mapped_device *md, struct dm_table *t);
|
||||
int dm_deactivate(struct mapped_device *md);
|
||||
|
||||
void dm_suspend(struct mapped_device *md);
|
||||
|
||||
struct mapped_device *dm_get(const char *name);
|
||||
struct mapped_device *dm_create(const char *name, int minor, struct dm_table *);int dm_destroy(struct mapped_device *md);
|
||||
int dm_swap_table(struct mapped_device *md, struct dm_table *t);
|
||||
int dm_suspend(struct mapped_device *md);
|
||||
int dm_resume(struct mapped_device *md);
|
||||
|
||||
/* dm-table.c */
|
||||
struct dm_table *dm_table_create(void);
|
||||
@ -248,9 +237,7 @@ static inline offset_t *get_node(struct dm_table *t, int l, int n)
|
||||
return t->index[l] + (n * KEYS_PER_NODE);
|
||||
}
|
||||
|
||||
static inline int is_active(struct mapped_device *md)
|
||||
{
|
||||
return test_bit(DM_ACTIVE, &md->state);
|
||||
}
|
||||
int dm_interface_init(void) __init;
|
||||
void dm_interface_exit(void) __exit;
|
||||
|
||||
#endif
|
||||
|
@ -40,8 +40,8 @@ struct dmfs_inode_info {
|
||||
extern struct inode *dmfs_create_table(struct inode *, int, struct seq_operations *, int);
|
||||
extern struct seq_operations dmfs_error_seq_ops;
|
||||
extern struct seq_operations dmfs_status_seq_ops;
|
||||
extern struct seq_operations dmfs_active_seq_ops;
|
||||
extern ssize_t dmfs_active_write(struct file *file, const char *buf, size_t size, loff_t *ppos);
|
||||
extern struct seq_operations dmfs_suspend_seq_ops;
|
||||
extern ssize_t dmfs_suspend_write(struct file *file, const char *buf, size_t size, loff_t *ppos);
|
||||
|
||||
static int dmfs_seq_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
@ -58,12 +58,12 @@ static int dmfs_no_fsync(struct file *file, struct dentry *dentry, int datasync)
|
||||
return 0;
|
||||
};
|
||||
|
||||
static struct file_operations dmfs_active_file_operations = {
|
||||
static struct file_operations dmfs_suspend_file_operations = {
|
||||
open: dmfs_seq_open,
|
||||
read: seq_read,
|
||||
llseek: seq_lseek,
|
||||
release: seq_release,
|
||||
write: dmfs_active_write,
|
||||
write: dmfs_suspend_write,
|
||||
fsync: dmfs_no_fsync,
|
||||
};
|
||||
|
||||
@ -98,11 +98,11 @@ static struct inode *dmfs_create_device(struct inode *dir, int mode, struct seq_
|
||||
return inode;
|
||||
}
|
||||
|
||||
static struct inode *dmfs_create_active(struct inode *dir, int mode, struct seq_operations *seq_ops, int dev)
|
||||
static struct inode *dmfs_create_suspend(struct inode *dir, int mode, struct seq_operations *seq_ops, int dev)
|
||||
{
|
||||
struct inode *inode = dmfs_create_seq_ro(dir, mode, seq_ops, dev);
|
||||
if (inode) {
|
||||
inode->i_fop = &dmfs_active_file_operations;
|
||||
inode->i_fop = &dmfs_suspend_file_operations;
|
||||
}
|
||||
return inode;
|
||||
}
|
||||
@ -123,7 +123,7 @@ static struct dmfs_inode_info dmfs_ii[] = {
|
||||
{ "error", dmfs_create_seq_ro, &dmfs_error_seq_ops, DT_REG },
|
||||
{ "status", dmfs_create_seq_ro, &dmfs_status_seq_ops, DT_REG },
|
||||
{ "device", dmfs_create_device, NULL, DT_BLK },
|
||||
{ "active", dmfs_create_active, &dmfs_active_seq_ops, DT_REG },
|
||||
{ "suspend", dmfs_create_suspend, &dmfs_suspend_seq_ops, DT_REG },
|
||||
};
|
||||
|
||||
#define NR_DMFS_II (sizeof(dmfs_ii)/sizeof(struct dmfs_inode_info))
|
||||
@ -205,21 +205,32 @@ struct inode *dmfs_create_lv(struct super_block *sb, int mode, struct dentry *de
|
||||
struct mapped_device *md;
|
||||
const char *name = dentry->d_name.name;
|
||||
char tmp_name[DM_NAME_LEN + 1];
|
||||
struct dm_table *table;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
if (inode) {
|
||||
table = dm_table_create();
|
||||
ret = PTR_ERR(table);
|
||||
if (!IS_ERR(table)) {
|
||||
ret = dm_table_complete(table);
|
||||
if (ret == 0) {
|
||||
inode->i_fop = &dmfs_lv_file_operations;
|
||||
inode->i_op = &dmfs_lv_inode_operations;
|
||||
memcpy(tmp_name, name, dentry->d_name.len);
|
||||
tmp_name[dentry->d_name.len] = 0;
|
||||
md = dm_create(tmp_name, -1);
|
||||
if (IS_ERR(md)) {
|
||||
iput(inode);
|
||||
return ERR_PTR(PTR_ERR(md));
|
||||
}
|
||||
md = dm_create(tmp_name, -1, table);
|
||||
if (!IS_ERR(md)) {
|
||||
DMFS_I(inode)->md = md;
|
||||
return inode;
|
||||
}
|
||||
ret = PTR_ERR(md);
|
||||
}
|
||||
dm_table_destroy(table);
|
||||
}
|
||||
iput(inode);
|
||||
}
|
||||
|
||||
return inode;
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
|
||||
|
@ -55,12 +55,12 @@ static int dmfs_root_mkdir(struct inode *dir, struct dentry *dentry, int mode)
|
||||
return -EINVAL;
|
||||
|
||||
inode = dmfs_create_lv(dir->i_sb, mode, dentry);
|
||||
if (inode) {
|
||||
if (!IS_ERR(inode)) {
|
||||
d_instantiate(dentry, inode);
|
||||
dget(dentry);
|
||||
rv = 0;
|
||||
return 0;
|
||||
}
|
||||
return rv;
|
||||
return PTR_ERR(inode);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -99,8 +99,9 @@ static int dmfs_root_rmdir(struct inode *dir, struct dentry *dentry)
|
||||
|
||||
if (empty(dentry)) {
|
||||
struct inode *inode = dentry->d_inode;
|
||||
ret = dm_deactivate(DMFS_I(inode)->md);
|
||||
ret = dm_destroy(DMFS_I(inode)->md);
|
||||
if (ret == 0) {
|
||||
DMFS_I(inode)->md = NULL;
|
||||
inode->i_nlink--;
|
||||
dput(dentry);
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <linux/config.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kmod.h>
|
||||
|
||||
#include "dmfs.h"
|
||||
#include "dm.h"
|
||||
@ -46,9 +47,7 @@ static void dmfs_delete_inode(struct inode *inode)
|
||||
|
||||
if (dmi) {
|
||||
if (dmi->md)
|
||||
dm_remove(dmi->md);
|
||||
if (dmi->dentry)
|
||||
dput(dmi->dentry);
|
||||
BUG();
|
||||
if (!list_empty(&dmi->errors))
|
||||
dmfs_zap_errors(inode);
|
||||
kfree(dmi);
|
||||
@ -130,7 +129,7 @@ struct inode *dmfs_new_private_inode(struct super_block *sb, int mode)
|
||||
static DECLARE_FSTYPE(dmfs_fstype, "dmfs", dmfs_read_super, FS_SINGLE);
|
||||
static struct vfsmount *dmfs_mnt;
|
||||
|
||||
int __init dmfs_init(void)
|
||||
int __init dm_interface_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@ -149,7 +148,7 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int __exit dmfs_exit(void)
|
||||
void __exit dm_interface_exit(void)
|
||||
{
|
||||
MOD_INC_USE_COUNT; /* So that it lands up being zero */
|
||||
|
||||
@ -157,6 +156,5 @@ int __exit dmfs_exit(void)
|
||||
|
||||
unregister_filesystem(&dmfs_fstype);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* dmfs-active.c
|
||||
* dmfs-suspend.c
|
||||
*
|
||||
* Copyright (C) 2001 Sistina Software
|
||||
*
|
||||
@ -51,22 +51,22 @@ static void s_stop(struct seq_file *s, void *v)
|
||||
static int s_show(struct seq_file *s, void *v)
|
||||
{
|
||||
struct dmfs_i *dmi = s->context;
|
||||
char msg[3] = "0\n";
|
||||
if (is_active(dmi->md)) {
|
||||
msg[1] = '1';
|
||||
char msg[3] = "1\n";
|
||||
if (dmi->md->suspended == 0) {
|
||||
msg[0] = '0';
|
||||
}
|
||||
seq_puts(s, msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct seq_operations dmfs_active_seq_ops = {
|
||||
struct seq_operations dmfs_suspend_seq_ops = {
|
||||
start: s_start,
|
||||
next: s_next,
|
||||
stop: s_stop,
|
||||
show: s_show,
|
||||
};
|
||||
|
||||
ssize_t dmfs_active_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
|
||||
ssize_t dmfs_suspend_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct inode *dir = file->f_dentry->d_parent->d_inode;
|
||||
struct dmfs_i *dmi = DMFS_I(dir);
|
||||
@ -80,19 +80,12 @@ ssize_t dmfs_active_write(struct file *file, const char *buf, size_t count, loff
|
||||
return -EINVAL;
|
||||
|
||||
down(&dmi->sem);
|
||||
written = count;
|
||||
if (is_active(dmi->md)) {
|
||||
if (buf[0] == '0')
|
||||
dm_deactivate(dmi->md);
|
||||
} else {
|
||||
if (buf[0] == '1') {
|
||||
if (dmi->md->map) {
|
||||
dm_activate(dmi->md, dmi->md->map);
|
||||
} else {
|
||||
written = -EPERM;
|
||||
}
|
||||
}
|
||||
}
|
||||
written = dm_resume(dmi->md);
|
||||
if (buf[0] == '1')
|
||||
written = dm_suspend(dmi->md);
|
||||
if (written >= 0)
|
||||
written = count;
|
||||
up(&dmi->sem);
|
||||
|
||||
out:
|
@ -50,7 +50,7 @@ static char *err_table[] = {
|
||||
"Missing/Invalid size argument",
|
||||
"Missing target type"
|
||||
};
|
||||
printk("dmfs_parse_line: (%s)\n", str);
|
||||
/* printk("dmfs_parse_line: (%s)\n", str); */
|
||||
|
||||
rv = sscanf(str, "%d %d %32s%n", &start, &size, target, &pos);
|
||||
if (rv < 3) {
|
||||
@ -72,9 +72,11 @@ static char *err_table[] = {
|
||||
rv = ttype->ctr(t, start, size, str, &context);
|
||||
msg = context;
|
||||
if (rv == 0) {
|
||||
#if 0
|
||||
printk("dmfs_parse: %u %u %s %s\n", start, size,
|
||||
ttype->name,
|
||||
ttype->print ? ttype->print(context) : "-");
|
||||
#endif
|
||||
msg = "Error adding target to table";
|
||||
high = start + (size - 1);
|
||||
if (dm_table_add_target(t, high, ttype, context) == 0)
|
||||
@ -228,17 +230,15 @@ static int dmfs_table_release(struct inode *inode, struct file *f)
|
||||
|
||||
if (table) {
|
||||
struct mapped_device *md = dmi->md;
|
||||
int need_activate = 0;
|
||||
int need_resume = 0;
|
||||
|
||||
if (is_active(md)) {
|
||||
dm_deactivate(md);
|
||||
need_activate = 1;
|
||||
if (md->suspended == 0) {
|
||||
dm_suspend(md);
|
||||
need_resume = 1;
|
||||
}
|
||||
if (md->map) {
|
||||
dm_table_destroy(md->map);
|
||||
}
|
||||
if (need_activate) {
|
||||
dm_activate(md, table);
|
||||
dm_swap_table(md, table);
|
||||
if (need_resume) {
|
||||
dm_resume(md);
|
||||
}
|
||||
}
|
||||
up(&dmi->sem);
|
||||
|
@ -4,7 +4,6 @@
|
||||
struct dmfs_i {
|
||||
struct semaphore sem;
|
||||
struct mapped_device *md;
|
||||
struct dentry *dentry;
|
||||
struct list_head errors;
|
||||
int status;
|
||||
};
|
||||
@ -12,14 +11,11 @@ struct dmfs_i {
|
||||
#define DMFS_I(inode) ((struct dmfs_i *)(inode)->u.generic_ip)
|
||||
|
||||
|
||||
extern int dmfs_init(void) __init;
|
||||
extern int dmfs_exit(void) __exit;
|
||||
extern struct inode *dmfs_new_inode(struct super_block *sb, int mode);
|
||||
extern struct inode *dmfs_new_private_inode(struct super_block *sb, int mode);
|
||||
|
||||
struct inode *dmfs_new_inode(struct super_block *sb, int mode);
|
||||
struct inode *dmfs_new_private_inode(struct super_block *sb, int mode);
|
||||
|
||||
void dmfs_add_error(struct inode *inode, unsigned num, char *str);
|
||||
void dmfs_zap_errors(struct inode *inode);
|
||||
extern void dmfs_add_error(struct inode *inode, unsigned num, char *str);
|
||||
extern void dmfs_zap_errors(struct inode *inode);
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user