From: Mikulas Patocka <mpatocka@redhat.com> Date: Mon, 1 Sep 2008 18:00:18 -0400 Subject: [md] dm kcopyd: private mempool Message-id: Pine.LNX.4.64.0809011757100.12369@hs20-bc2-1.build.redhat.com O-Subject: [RHEL 5.3 PATCH 2/2] bz460845 Deadlock with nested LVMs Bugzilla: 460845 RH-Acked-by: Alasdair G Kergon <agk@redhat.com> This is another patch for the same bug --- kcopyd uses shared mempool of reserved memory for all clients and it could cause deadlocks with nested LVM (if the upper layer drains the mempool and the lower layer has no memory to make progress). This patch changes it to per-client mempool. This is backported upstream patch. Upstream commit: 08d8757a4d52d21d825b9170af36f2696d1da1a8 (2.6.26-rc1) Testing: compiled on a workstation and tested dm-raid1 and dm-snapshot. diff --git a/drivers/md/kcopyd.c b/drivers/md/kcopyd.c index 6ebc27c..b079d04 100644 --- a/drivers/md/kcopyd.c +++ b/drivers/md/kcopyd.c @@ -42,6 +42,8 @@ struct kcopyd_client { #ifndef __GENKSYMS__ struct dm_io_client *io_client; + mempool_t *job_pool; + struct workqueue_struct *kcopyd_wq; struct work_struct kcopyd_work; @@ -221,7 +223,6 @@ struct kcopyd_job { #define MIN_JOBS 512 static kmem_cache_t *_job_cache; -static mempool_t *_job_pool; static int jobs_init(void) { @@ -232,20 +233,12 @@ static int jobs_init(void) if (!_job_cache) return -ENOMEM; - _job_pool = mempool_create_slab_pool(MIN_JOBS, _job_cache); - if (!_job_pool) { - kmem_cache_destroy(_job_cache); - return -ENOMEM; - } - return 0; } static void jobs_exit(void) { - mempool_destroy(_job_pool); kmem_cache_destroy(_job_cache); - _job_pool = NULL; _job_cache = NULL; } @@ -298,7 +291,7 @@ static int run_complete_job(struct kcopyd_job *job) struct kcopyd_client *kc = job->kc; kcopyd_put_pages(kc, job->pages); - mempool_free(job, _job_pool); + mempool_free(job, kc->job_pool); fn(read_err, write_err, context); if (atomic_dec_and_test(&kc->nr_jobs)) @@ -489,7 +482,8 @@ static void segment_complete(int read_err, if (count) { int i; - struct kcopyd_job *sub_job = mempool_alloc(_job_pool, GFP_NOIO); + struct kcopyd_job *sub_job = mempool_alloc(job->kc->job_pool, + GFP_NOIO); *sub_job = *job; sub_job->source.sector += progress; @@ -513,7 +507,7 @@ static void segment_complete(int read_err, * after we've completed. */ job->fn(read_err, write_err, job->context); - mempool_free(job, _job_pool); + mempool_free(job, job->kc->job_pool); } } @@ -540,7 +534,7 @@ int kcopyd_copy(struct kcopyd_client *kc, struct io_region *from, /* * Allocate a new job. */ - job = mempool_alloc(_job_pool, GFP_NOIO); + job = mempool_alloc(kc->job_pool, GFP_NOIO); /* * set up for the read. @@ -666,10 +660,19 @@ int kcopyd_client_create(unsigned int nr_pages, struct kcopyd_client **result) INIT_LIST_HEAD(&kc->io_jobs); INIT_LIST_HEAD(&kc->pages_jobs); + kc->job_pool = mempool_create_slab_pool(MIN_JOBS, _job_cache); + if (!kc->job_pool) { + r = -ENOMEM; + kfree(kc); + kcopyd_exit(); + return r; + } + INIT_WORK(&kc->kcopyd_work, do_work, kc); kc->kcopyd_wq = create_singlethread_workqueue("kcopyd"); if (!kc->kcopyd_wq) { r = -ENOMEM; + mempool_destroy(kc->job_pool); kfree(kc); kcopyd_exit(); return r; @@ -680,6 +683,7 @@ int kcopyd_client_create(unsigned int nr_pages, struct kcopyd_client **result) r = client_alloc_pages(kc, nr_pages); if (r) { destroy_workqueue(kc->kcopyd_wq); + mempool_destroy(kc->job_pool); kfree(kc); kcopyd_exit(); return r; @@ -690,6 +694,7 @@ int kcopyd_client_create(unsigned int nr_pages, struct kcopyd_client **result) r = PTR_ERR(kc->io_client); client_free_pages(kc); destroy_workqueue(kc->kcopyd_wq); + mempool_destroy(kc->job_pool); kfree(kc); kcopyd_exit(); return r; @@ -715,6 +720,7 @@ void kcopyd_client_destroy(struct kcopyd_client *kc) dm_io_client_destroy(kc->io_client); client_free_pages(kc); client_del(kc); + mempool_destroy(kc->job_pool); kfree(kc); kcopyd_exit(); }