#include <linux/module.h>
#include <linux/slab.h>
#include <linux/sched.h>
+#include <linux/bitops.h>
/* FIXME: can we shrink this ? */
struct io_context {
int sync_search;
};
+#define BYTE_SHIFT 3
+
static int core_ctr(struct dirty_log *log, sector_t dev_size,
unsigned int argc, char **argv)
{
clog->region_size = region_size;
clog->region_count = region_count;
- bitset_size = dm_round_up((region_count + 7) >> 3,
- sizeof(*clog->clean_bits));
+ /*
+ * Work out how many words we need to hold the bitset.
+ */
+ bitset_size = dm_round_up(region_count,
+ sizeof(*clog->clean_bits) << BYTE_SHIFT);
+ bitset_size >>= BYTE_SHIFT;
+
clog->clean_bits = vmalloc(bitset_size);
if (!clog->clean_bits) {
DMWARN("couldn't allocate clean bitset");
#define NODE_SIZE L1_CACHE_BYTES
#define KEYS_PER_NODE (NODE_SIZE / sizeof(sector_t))
#define CHILDREN_PER_NODE (KEYS_PER_NODE + 1)
-#define MAX_TARGET_ARGS 64
struct dm_table {
atomic_t holders;
return 0;
}
-/*
- * highs, and targets are managed as dynamic arrays during a
- * table load.
- */
-static int alloc_targets(struct dm_table *t, unsigned int num)
-{
- sector_t *n_highs;
- struct dm_target *n_targets;
- int n = t->num_targets;
-
- /*
- * Allocate both the target array and offset array at once.
- */
- n_highs = (sector_t *) vcalloc(sizeof(struct dm_target) +
- sizeof(sector_t), num);
- if (!n_highs)
- return -ENOMEM;
- n_targets = (struct dm_target *) (n_highs + num);
-
- if (n) {
- memcpy(n_highs, t->highs, sizeof(*n_highs) * n);
- 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;
- t->highs = n_highs;
- t->targets = n_targets;
-
- return 0;
-}
int dm_table_create(struct dm_table **result, int mode, unsigned num_targets)
{
INIT_LIST_HEAD(&t->devices);
atomic_set(&t->holders, 1);
- if (!num_targets)
- num_targets = KEYS_PER_NODE;
+ num_targets = dm_round_up(num_targets, KEYS_PER_NODE);
- if (alloc_targets(t, num_targets)) {
+ /* Allocate both the target array and offset array at once. */
+ t->highs = (sector_t *) vcalloc(sizeof(struct dm_target) +
+ sizeof(sector_t), num_targets);
+ if (!t->highs) {
kfree(t);
- t = NULL;
return -ENOMEM;
}
+ memset(t->highs, -1, sizeof(*t->highs) * num_targets);
+
+ t->targets = (struct dm_target *) (t->highs + num_targets);
+ t->num_allocated = num_targets;
t->mode = mode;
*result = t;
return 0;
table_destroy(t);
}
-/*
- * Checks to see if we need to extend highs or targets.
- */
-static inline int check_space(struct dm_table *t)
-{
- if (t->num_targets >= t->num_allocated)
- return alloc_targets(t, t->num_allocated * 2);
-
- return 0;
-}
-
/*
* Convert a device path to a dev_t.
*/
return (ti->begin == (prev->begin + prev->len));
}
+/*
+ * Used to dynamically allocate the arg array.
+ */
+static char **realloc_argv(unsigned *array_size, char **old_argv)
+{
+ char **argv;
+ unsigned new_size;
+
+ new_size = *array_size ? *array_size * 2 : 64;
+ argv = kmalloc(new_size * sizeof(*argv), GFP_NOIO);
+ if (argv) {
+ memcpy(argv, old_argv, *array_size * sizeof(*argv));
+ *array_size = new_size;
+ }
+
+ kfree(old_argv);
+ return argv;
+}
+
/*
* Destructively splits up the argument list to pass to ctr.
*/
static int split_args(int *argc, char ***argvp, char *input)
{
- char *start, *end = input, *out;
- char **argv;
- int max_args = MAX_TARGET_ARGS;
+ char *start, *end = input, *out, **argv = NULL;
+ unsigned array_size = 0;
*argc = 0;
- argv = kmalloc(sizeof(*argv) * max_args, GFP_NOIO);
+ argv = realloc_argv(&array_size, argv);
if (!argv)
return -ENOMEM;
}
/* have we already filled the array ? */
- if ((*argc + 1) > max_args) {
- char **argv2;
-
- max_args *= 2;
- argv2 = kmalloc(sizeof(*argv2) * max_args, GFP_NOIO);
- if (!argv2) {
- kfree(argv);
+ if ((*argc + 1) > array_size) {
+ argv = realloc_argv(&array_size, argv);
+ if (!argv)
return -ENOMEM;
- }
-
- memcpy(argv2, argv, sizeof(*argv) * *argc);
- kfree(argv);
- argv = argv2;
}
/* we know this is whitespace */
char **argv;
struct dm_target *tgt;
- if ((r = check_space(t)))
- return r;
+ if (t->num_targets >= t->num_allocated)
+ return -ENOMEM;
tgt = t->targets + t->num_targets;
memset(tgt, 0, sizeof(*tgt));
static struct dm_daemon _kcopyd;
+#define SECTORS_PER_PAGE (PAGE_SIZE / SECTOR_SIZE)
+#define SUB_JOB_SIZE 128
+#define PAGES_PER_SUB_JOB (SUB_JOB_SIZE / SECTORS_PER_PAGE)
+#define SUB_JOB_COUNT 8
+
/*-----------------------------------------------------------------
* Each kcopyd client has its own little pool of preallocated
* pages for kcopyd io.
struct list_head pages;
unsigned int nr_pages;
unsigned int nr_free_pages;
+ unsigned int max_split;
};
static inline void __push_page(struct kcopyd_client *kc, struct page *p)
kcopyd_put_pages(kc, &new);
kc->nr_pages += nr;
+ kc->max_split = kc->nr_pages / PAGES_PER_SUB_JOB;
+ if (kc->max_split > SUB_JOB_COUNT)
+ kc->max_split = SUB_JOB_COUNT;
+
return 0;
}
return r;
}
-#define SECTORS_PER_PAGE (PAGE_SIZE / SECTOR_SIZE)
static int run_pages_job(struct kcopyd_job *job)
{
int r;
dm_daemon_wake(&_kcopyd);
}
-#define SUB_JOB_SIZE 128
static void segment_complete(int read_err,
unsigned int write_err, void *context)
{
* Create some little jobs that will do the move between
* them.
*/
-#define SPLIT_COUNT 8
static void split_job(struct kcopyd_job *job)
{
- int i;
+ int nr;
+
+ nr = dm_div_up(job->source.count, SUB_JOB_SIZE);
+ if (nr > job->kc->max_split)
+ nr = job->kc->max_split;
- atomic_set(&job->sub_jobs, SPLIT_COUNT);
- for (i = 0; i < SPLIT_COUNT; i++)
+ atomic_set(&job->sub_jobs, nr);
+ while (nr--)
segment_complete(0, 0u, job);
}
-#define SUB_JOB_THRESHOLD (SPLIT_COUNT * SUB_JOB_SIZE)
int kcopyd_copy(struct kcopyd_client *kc, struct io_region *from,
unsigned int num_dests, struct io_region *dests,
unsigned int flags, kcopyd_notify_fn fn, void *context)
job->fn = fn;
job->context = context;
- if (job->source.count < SUB_JOB_THRESHOLD)
+ if (job->source.count < SUB_JOB_SIZE)
dispatch_job(job);
else {
int r = 0;
struct kcopyd_client *kc;
- if (nr_pages * SECTORS_PER_PAGE < SUB_JOB_SIZE * SPLIT_COUNT) {
+ if (nr_pages * SECTORS_PER_PAGE < SUB_JOB_SIZE) {
DMERR("kcopyd client requested %u pages: minimum is %lu",
- nr_pages, SUB_JOB_SIZE * SPLIT_COUNT / SECTORS_PER_PAGE);
+ nr_pages, SUB_JOB_SIZE / SECTORS_PER_PAGE);
return -ENOMEM;
}
return r;
}
+/*
+ * Build up the status struct for each target
+ */
+static void retrieve_status(struct dm_table *table, struct dm_ioctl *param,
+ size_t param_size)
+{
+ unsigned int i, num_targets;
+ struct dm_target_spec *spec;
+ char *outbuf, *outptr;
+ status_type_t type;
+ size_t remaining, len, used = 0;
+
+ outptr = outbuf = get_result_buffer(param, param_size, &len);
+
+ if (param->flags & DM_STATUS_TABLE_FLAG)
+ type = STATUSTYPE_TABLE;
+ else
+ type = STATUSTYPE_INFO;
+
+ /* Get all the target info */
+ num_targets = dm_table_get_num_targets(table);
+ for (i = 0; i < num_targets; i++) {
+ struct dm_target *ti = dm_table_get_target(table, i);
+
+ remaining = len - (outptr - outbuf);
+ if (remaining < sizeof(struct dm_target_spec)) {
+ param->flags |= DM_BUFFER_FULL_FLAG;
+ break;
+ }
+
+ spec = (struct dm_target_spec *) outptr;
+
+ spec->status = 0;
+ spec->sector_start = ti->begin;
+ spec->length = ti->len;
+ strncpy(spec->target_type, ti->type->name,
+ sizeof(spec->target_type));
+
+ outptr += sizeof(struct dm_target_spec);
+ remaining = len - (outptr - outbuf);
+
+ /* Get the status/table string from the target driver */
+ if (ti->type->status) {
+ if (ti->type->status(ti, type, outptr, remaining)) {
+ param->flags |= DM_BUFFER_FULL_FLAG;
+ break;
+ }
+ } else
+ outptr[0] = '\0';
+
+ outptr += strlen(outptr) + 1;
+ used = param->data_start + (outptr - outbuf);
+
+ align_ptr(outptr);
+ spec->next = outptr - outbuf;
+ }
+
+ if (used)
+ param->data_size = used;
+
+ param->target_count = num_targets;
+}
+
+/*
+ * Wait for a device to report an event
+ */
+static int dev_wait(struct dm_ioctl *param, size_t param_size)
+{
+ int r;
+ struct mapped_device *md;
+ struct dm_table *table;
+ DECLARE_WAITQUEUE(wq, current);
+
+ md = find_device(param);
+ if (!md)
+ return -ENXIO;
+
+ /*
+ * Wait for a notification event
+ */
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (!dm_add_wait_queue(md, &wq, param->event_nr)) {
+ schedule();
+ dm_remove_wait_queue(md, &wq);
+ }
+ set_current_state(TASK_RUNNING);
+
+ /*
+ * The userland program is going to want to know what
+ * changed to trigger the event, so we may as well tell
+ * him and save an ioctl.
+ */
+ r = __dev_status(md, param);
+ if (r)
+ goto out;
+
+ table = dm_get_table(md);
+ if (table) {
+ retrieve_status(table, param, param_size);
+ dm_table_put(table);
+ }
+
+ out:
+ dm_put(md);
+ return r;
+}
+
static inline int get_mode(struct dm_ioctl *param)
{
int mode = FMODE_READ | FMODE_WRITE;
return r;
}
-/*
- * Build up the status struct for each target
- */
-static void retrieve_status(struct dm_table *table, struct dm_ioctl *param,
- size_t param_size)
-{
- unsigned int i, num_targets;
- struct dm_target_spec *spec;
- char *outbuf, *outptr;
- status_type_t type;
- size_t remaining, len, used = 0;
-
- outptr = outbuf = get_result_buffer(param, param_size, &len);
-
- if (param->flags & DM_STATUS_TABLE_FLAG)
- type = STATUSTYPE_TABLE;
- else
- type = STATUSTYPE_INFO;
-
- /* Get all the target info */
- num_targets = dm_table_get_num_targets(table);
- for (i = 0; i < num_targets; i++) {
- struct dm_target *ti = dm_table_get_target(table, i);
-
- remaining = len - (outptr - outbuf);
- if (remaining < sizeof(struct dm_target_spec)) {
- param->flags |= DM_BUFFER_FULL_FLAG;
- break;
- }
-
- spec = (struct dm_target_spec *) outptr;
-
- spec->status = 0;
- spec->sector_start = ti->begin;
- spec->length = ti->len;
- strncpy(spec->target_type, ti->type->name,
- sizeof(spec->target_type));
-
- outptr += sizeof(struct dm_target_spec);
- remaining = len - (outptr - outbuf);
-
- /* Get the status/table string from the target driver */
- if (ti->type->status) {
- if (ti->type->status(ti, type, outptr, remaining)) {
- param->flags |= DM_BUFFER_FULL_FLAG;
- break;
- }
- } else
- outptr[0] = '\0';
-
- outptr += strlen(outptr) + 1;
- used = param->data_start + (outptr - outbuf);
-
- align_ptr(outptr);
- spec->next = outptr - outbuf;
- }
-
- if (used)
- param->data_size = used;
-
- param->target_count = num_targets;
-}
-
/*
* Return the status of a device as a text string for each
* target.
return r;
}
-/*
- * Wait for a device to report an event
- */
-static int dev_wait(struct dm_ioctl *param, size_t param_size)
-{
- int r;
- struct mapped_device *md;
- struct dm_table *table;
- DECLARE_WAITQUEUE(wq, current);
-
- md = find_device(param);
- if (!md)
- return -ENXIO;
-
- /*
- * Wait for a notification event
- */
- set_current_state(TASK_INTERRUPTIBLE);
- if (!dm_add_wait_queue(md, &wq, param->event_nr)) {
- schedule();
- dm_remove_wait_queue(md, &wq);
- }
- set_current_state(TASK_RUNNING);
-
- /*
- * The userland program is going to want to know what
- * changed to trigger the event, so we may as well tell
- * him and save an ioctl.
- */
- r = __dev_status(md, param);
- if (r)
- goto out;
-
- table = dm_get_table(md);
- if (table) {
- retrieve_status(table, param, param_size);
- dm_table_put(table);
- }
-
- out:
- dm_put(md);
- return r;
-}
-
/*-----------------------------------------------------------------
* Implementation of open/close/ioctl on the special char
* device.
#define DM_VERSION_MAJOR 4
#define DM_VERSION_MINOR 0
-#define DM_VERSION_PATCHLEVEL 4
-#define DM_VERSION_EXTRA "-ioctl-cvs (2003-08-30)"
+#define DM_VERSION_PATCHLEVEL 5
+#define DM_VERSION_EXTRA "-ioctl-cvs (2003-11-18)"
/* Status bits */
#define DM_READONLY_FLAG (1 << 0) /* In/Out */