Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b11b06d9 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* git://git.kernel.org/pub/scm/linux/kernel/git/agk/linux-2.6-dm:
  dm kcopyd: return client directly and not through a pointer
  dm kcopyd: reserve fewer pages
  dm io: use fixed initial mempool size
  dm kcopyd: alloc pages from the main page allocator
  dm kcopyd: add gfp parm to alloc_pl
  dm kcopyd: remove superfluous page allocation spinlock
  dm kcopyd: preallocate sub jobs to avoid deadlock
  dm kcopyd: avoid pointless job splitting
  dm mpath: do not fail paths after integrity errors
  dm table: reject devices without request fns
  dm table: allow targets to support discards internally
parents f1d1c9fa fa34ce73
Loading
Loading
Loading
Loading
+5 −22
Original line number Diff line number Diff line
@@ -19,6 +19,8 @@
#define DM_MSG_PREFIX "io"

#define DM_IO_MAX_REGIONS	BITS_PER_LONG
#define MIN_IOS		16
#define MIN_BIOS	16

struct dm_io_client {
	mempool_t *pool;
@@ -40,34 +42,22 @@ struct io {

static struct kmem_cache *_dm_io_cache;

/*
 * io contexts are only dynamically allocated for asynchronous
 * io.  Since async io is likely to be the majority of io we'll
 * have the same number of io contexts as bios! (FIXME: must reduce this).
 */

static unsigned int pages_to_ios(unsigned int pages)
{
	return 4 * pages;	/* too many ? */
}

/*
 * Create a client with mempool and bioset.
 */
struct dm_io_client *dm_io_client_create(unsigned num_pages)
struct dm_io_client *dm_io_client_create(void)
{
	unsigned ios = pages_to_ios(num_pages);
	struct dm_io_client *client;

	client = kmalloc(sizeof(*client), GFP_KERNEL);
	if (!client)
		return ERR_PTR(-ENOMEM);

	client->pool = mempool_create_slab_pool(ios, _dm_io_cache);
	client->pool = mempool_create_slab_pool(MIN_IOS, _dm_io_cache);
	if (!client->pool)
		goto bad;

	client->bios = bioset_create(16, 0);
	client->bios = bioset_create(MIN_BIOS, 0);
	if (!client->bios)
		goto bad;

@@ -81,13 +71,6 @@ struct dm_io_client *dm_io_client_create(unsigned num_pages)
}
EXPORT_SYMBOL(dm_io_client_create);

int dm_io_client_resize(unsigned num_pages, struct dm_io_client *client)
{
	return mempool_resize(client->pool, pages_to_ios(num_pages),
			      GFP_KERNEL);
}
EXPORT_SYMBOL(dm_io_client_resize);

void dm_io_client_destroy(struct dm_io_client *client)
{
	mempool_destroy(client->pool);
+98 −70
Original line number Diff line number Diff line
@@ -27,15 +27,19 @@

#include "dm.h"

#define SUB_JOB_SIZE	128
#define SPLIT_COUNT	8
#define MIN_JOBS	8
#define RESERVE_PAGES	(DIV_ROUND_UP(SUB_JOB_SIZE << SECTOR_SHIFT, PAGE_SIZE))

/*-----------------------------------------------------------------
 * Each kcopyd client has its own little pool of preallocated
 * pages for kcopyd io.
 *---------------------------------------------------------------*/
struct dm_kcopyd_client {
	spinlock_t lock;
	struct page_list *pages;
	unsigned int nr_pages;
	unsigned int nr_free_pages;
	unsigned nr_reserved_pages;
	unsigned nr_free_pages;

	struct dm_io_client *io_client;

@@ -67,15 +71,18 @@ static void wake(struct dm_kcopyd_client *kc)
	queue_work(kc->kcopyd_wq, &kc->kcopyd_work);
}

static struct page_list *alloc_pl(void)
/*
 * Obtain one page for the use of kcopyd.
 */
static struct page_list *alloc_pl(gfp_t gfp)
{
	struct page_list *pl;

	pl = kmalloc(sizeof(*pl), GFP_KERNEL);
	pl = kmalloc(sizeof(*pl), gfp);
	if (!pl)
		return NULL;

	pl->page = alloc_page(GFP_KERNEL);
	pl->page = alloc_page(gfp);
	if (!pl->page) {
		kfree(pl);
		return NULL;
@@ -90,41 +97,56 @@ static void free_pl(struct page_list *pl)
	kfree(pl);
}

static int kcopyd_get_pages(struct dm_kcopyd_client *kc,
			    unsigned int nr, struct page_list **pages)
/*
 * Add the provided pages to a client's free page list, releasing
 * back to the system any beyond the reserved_pages limit.
 */
static void kcopyd_put_pages(struct dm_kcopyd_client *kc, struct page_list *pl)
{
	struct page_list *pl;
	struct page_list *next;

	spin_lock(&kc->lock);
	if (kc->nr_free_pages < nr) {
		spin_unlock(&kc->lock);
		return -ENOMEM;
	do {
		next = pl->next;

		if (kc->nr_free_pages >= kc->nr_reserved_pages)
			free_pl(pl);
		else {
			pl->next = kc->pages;
			kc->pages = pl;
			kc->nr_free_pages++;
		}

	kc->nr_free_pages -= nr;
	for (*pages = pl = kc->pages; --nr; pl = pl->next)
		;
		pl = next;
	} while (pl);
}

	kc->pages = pl->next;
	pl->next = NULL;
static int kcopyd_get_pages(struct dm_kcopyd_client *kc,
			    unsigned int nr, struct page_list **pages)
{
	struct page_list *pl;

	spin_unlock(&kc->lock);
	*pages = NULL;

	return 0;
	do {
		pl = alloc_pl(__GFP_NOWARN | __GFP_NORETRY);
		if (unlikely(!pl)) {
			/* Use reserved pages */
			pl = kc->pages;
			if (unlikely(!pl))
				goto out_of_memory;
			kc->pages = pl->next;
			kc->nr_free_pages--;
		}
		pl->next = *pages;
		*pages = pl;
	} while (--nr);

static void kcopyd_put_pages(struct dm_kcopyd_client *kc, struct page_list *pl)
{
	struct page_list *cursor;

	spin_lock(&kc->lock);
	for (cursor = pl; cursor->next; cursor = cursor->next)
		kc->nr_free_pages++;
	return 0;

	kc->nr_free_pages++;
	cursor->next = kc->pages;
	kc->pages = pl;
	spin_unlock(&kc->lock);
out_of_memory:
	if (*pages)
		kcopyd_put_pages(kc, *pages);
	return -ENOMEM;
}

/*
@@ -141,13 +163,16 @@ static void drop_pages(struct page_list *pl)
	}
}

static int client_alloc_pages(struct dm_kcopyd_client *kc, unsigned int nr)
/*
 * Allocate and reserve nr_pages for the use of a specific client.
 */
static int client_reserve_pages(struct dm_kcopyd_client *kc, unsigned nr_pages)
{
	unsigned int i;
	unsigned i;
	struct page_list *pl = NULL, *next;

	for (i = 0; i < nr; i++) {
		next = alloc_pl();
	for (i = 0; i < nr_pages; i++) {
		next = alloc_pl(GFP_KERNEL);
		if (!next) {
			if (pl)
				drop_pages(pl);
@@ -157,17 +182,18 @@ static int client_alloc_pages(struct dm_kcopyd_client *kc, unsigned int nr)
		pl = next;
	}

	kc->nr_reserved_pages += nr_pages;
	kcopyd_put_pages(kc, pl);
	kc->nr_pages += nr;

	return 0;
}

static void client_free_pages(struct dm_kcopyd_client *kc)
{
	BUG_ON(kc->nr_free_pages != kc->nr_pages);
	BUG_ON(kc->nr_free_pages != kc->nr_reserved_pages);
	drop_pages(kc->pages);
	kc->pages = NULL;
	kc->nr_free_pages = kc->nr_pages = 0;
	kc->nr_free_pages = kc->nr_reserved_pages = 0;
}

/*-----------------------------------------------------------------
@@ -216,16 +242,17 @@ struct kcopyd_job {
	struct mutex lock;
	atomic_t sub_jobs;
	sector_t progress;
};

/* FIXME: this should scale with the number of pages */
#define MIN_JOBS 512
	struct kcopyd_job *master_job;
};

static struct kmem_cache *_job_cache;

int __init dm_kcopyd_init(void)
{
	_job_cache = KMEM_CACHE(kcopyd_job, 0);
	_job_cache = kmem_cache_create("kcopyd_job",
				sizeof(struct kcopyd_job) * (SPLIT_COUNT + 1),
				__alignof__(struct kcopyd_job), 0, NULL);
	if (!_job_cache)
		return -ENOMEM;

@@ -299,6 +326,11 @@ static int run_complete_job(struct kcopyd_job *job)

	if (job->pages)
		kcopyd_put_pages(kc, job->pages);
	/*
	 * If this is the master job, the sub jobs have already
	 * completed so we can free everything.
	 */
	if (job->master_job == job)
		mempool_free(job, kc->job_pool);
	fn(read_err, write_err, context);

@@ -460,14 +492,14 @@ static void dispatch_job(struct kcopyd_job *job)
	wake(kc);
}

#define SUB_JOB_SIZE 128
static void segment_complete(int read_err, unsigned long write_err,
			     void *context)
{
	/* FIXME: tidy this function */
	sector_t progress = 0;
	sector_t count = 0;
	struct kcopyd_job *job = (struct kcopyd_job *) context;
	struct kcopyd_job *sub_job = (struct kcopyd_job *) context;
	struct kcopyd_job *job = sub_job->master_job;
	struct dm_kcopyd_client *kc = job->kc;

	mutex_lock(&job->lock);
@@ -498,8 +530,6 @@ static void segment_complete(int read_err, unsigned long write_err,

	if (count) {
		int i;
		struct kcopyd_job *sub_job = mempool_alloc(kc->job_pool,
							   GFP_NOIO);

		*sub_job = *job;
		sub_job->source.sector += progress;
@@ -511,7 +541,7 @@ static void segment_complete(int read_err, unsigned long write_err,
		}

		sub_job->fn = segment_complete;
		sub_job->context = job;
		sub_job->context = sub_job;
		dispatch_job(sub_job);

	} else if (atomic_dec_and_test(&job->sub_jobs)) {
@@ -531,19 +561,19 @@ static void segment_complete(int read_err, unsigned long write_err,
}

/*
 * Create some little jobs that will do the move between
 * them.
 * Create some sub jobs to share the work between them.
 */
#define SPLIT_COUNT 8
static void split_job(struct kcopyd_job *job)
static void split_job(struct kcopyd_job *master_job)
{
	int i;

	atomic_inc(&job->kc->nr_jobs);
	atomic_inc(&master_job->kc->nr_jobs);

	atomic_set(&job->sub_jobs, SPLIT_COUNT);
	for (i = 0; i < SPLIT_COUNT; i++)
		segment_complete(0, 0u, job);
	atomic_set(&master_job->sub_jobs, SPLIT_COUNT);
	for (i = 0; i < SPLIT_COUNT; i++) {
		master_job[i + 1].master_job = master_job;
		segment_complete(0, 0u, &master_job[i + 1]);
	}
}

int dm_kcopyd_copy(struct dm_kcopyd_client *kc, struct dm_io_region *from,
@@ -553,7 +583,8 @@ int dm_kcopyd_copy(struct dm_kcopyd_client *kc, struct dm_io_region *from,
	struct kcopyd_job *job;

	/*
	 * Allocate a new job.
	 * Allocate an array of jobs consisting of one master job
	 * followed by SPLIT_COUNT sub jobs.
	 */
	job = mempool_alloc(kc->job_pool, GFP_NOIO);

@@ -577,10 +608,10 @@ int dm_kcopyd_copy(struct dm_kcopyd_client *kc, struct dm_io_region *from,

	job->fn = fn;
	job->context = context;
	job->master_job = job;

	if (job->source.count < SUB_JOB_SIZE)
	if (job->source.count <= SUB_JOB_SIZE)
		dispatch_job(job);

	else {
		mutex_init(&job->lock);
		job->progress = 0;
@@ -606,17 +637,15 @@ int kcopyd_cancel(struct kcopyd_job *job, int block)
/*-----------------------------------------------------------------
 * Client setup
 *---------------------------------------------------------------*/
int dm_kcopyd_client_create(unsigned int nr_pages,
			    struct dm_kcopyd_client **result)
struct dm_kcopyd_client *dm_kcopyd_client_create(void)
{
	int r = -ENOMEM;
	struct dm_kcopyd_client *kc;

	kc = kmalloc(sizeof(*kc), GFP_KERNEL);
	if (!kc)
		return -ENOMEM;
		return ERR_PTR(-ENOMEM);

	spin_lock_init(&kc->lock);
	spin_lock_init(&kc->job_lock);
	INIT_LIST_HEAD(&kc->complete_jobs);
	INIT_LIST_HEAD(&kc->io_jobs);
@@ -633,12 +662,12 @@ int dm_kcopyd_client_create(unsigned int nr_pages,
		goto bad_workqueue;

	kc->pages = NULL;
	kc->nr_pages = kc->nr_free_pages = 0;
	r = client_alloc_pages(kc, nr_pages);
	kc->nr_reserved_pages = kc->nr_free_pages = 0;
	r = client_reserve_pages(kc, RESERVE_PAGES);
	if (r)
		goto bad_client_pages;

	kc->io_client = dm_io_client_create(nr_pages);
	kc->io_client = dm_io_client_create();
	if (IS_ERR(kc->io_client)) {
		r = PTR_ERR(kc->io_client);
		goto bad_io_client;
@@ -647,8 +676,7 @@ int dm_kcopyd_client_create(unsigned int nr_pages,
	init_waitqueue_head(&kc->destroyq);
	atomic_set(&kc->nr_jobs, 0);

	*result = kc;
	return 0;
	return kc;

bad_io_client:
	client_free_pages(kc);
@@ -659,7 +687,7 @@ int dm_kcopyd_client_create(unsigned int nr_pages,
bad_slab:
	kfree(kc);

	return r;
	return ERR_PTR(r);
}
EXPORT_SYMBOL(dm_kcopyd_client_create);

+1 −2
Original line number Diff line number Diff line
@@ -449,8 +449,7 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti,

		lc->io_req.mem.type = DM_IO_VMA;
		lc->io_req.notify.fn = NULL;
		lc->io_req.client = dm_io_client_create(dm_div_up(buf_size,
								   PAGE_SIZE));
		lc->io_req.client = dm_io_client_create();
		if (IS_ERR(lc->io_req.client)) {
			r = PTR_ERR(lc->io_req.client);
			DMWARN("couldn't allocate disk io client");
+1 −1
Original line number Diff line number Diff line
@@ -1290,7 +1290,7 @@ static int do_end_io(struct multipath *m, struct request *clone,
	if (!error && !clone->errors)
		return 0;	/* I/O complete */

	if (error == -EOPNOTSUPP || error == -EREMOTEIO)
	if (error == -EOPNOTSUPP || error == -EREMOTEIO || error == -EILSEQ)
		return error;

	if (mpio->pgpath)
+5 −5
Original line number Diff line number Diff line
@@ -22,8 +22,6 @@
#define DM_MSG_PREFIX "raid1"

#define MAX_RECOVERY 1	/* Maximum number of regions recovered in parallel. */
#define DM_IO_PAGES 64
#define DM_KCOPYD_PAGES 64

#define DM_RAID1_HANDLE_ERRORS 0x01
#define errors_handled(p)	((p)->features & DM_RAID1_HANDLE_ERRORS)
@@ -887,7 +885,7 @@ static struct mirror_set *alloc_context(unsigned int nr_mirrors,
		return NULL;
	}

	ms->io_client = dm_io_client_create(DM_IO_PAGES);
	ms->io_client = dm_io_client_create();
	if (IS_ERR(ms->io_client)) {
		ti->error = "Error creating dm_io client";
		mempool_destroy(ms->read_record_pool);
@@ -1117,9 +1115,11 @@ static int mirror_ctr(struct dm_target *ti, unsigned int argc, char **argv)
		goto err_destroy_wq;
	}

	r = dm_kcopyd_client_create(DM_KCOPYD_PAGES, &ms->kcopyd_client);
	if (r)
	ms->kcopyd_client = dm_kcopyd_client_create();
	if (IS_ERR(ms->kcopyd_client)) {
		r = PTR_ERR(ms->kcopyd_client);
		goto err_destroy_wq;
	}

	wakeup_mirrord(ms);
	return 0;
Loading