Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[PATCH] dm snapshot: fix kcopyd destructor

Before removing a snapshot, wait for the completion of any kcopyd jobs using
it.

Do this by maintaining a count (nr_jobs) of how many outstanding jobs each
kcopyd_client has.

The snapshot destructor first unregisters the snapshot so that no new kcopyd
jobs (created by writes to the origin) will reference that particular
snapshot. kcopyd_client_destroy() is now run next to wait for the completion
of any outstanding jobs before the snapshot exception structures (that those
jobs reference) are freed.

Signed-off-by: Alasdair G Kergon <agk@redhat.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

authored by

Alasdair G Kergon and committed by
Linus Torvalds
138728dc 969429b5

+21 -2
+5 -1
drivers/md/dm-snap.c
··· 559 559 { 560 560 struct dm_snapshot *s = (struct dm_snapshot *) ti->private; 561 561 562 + /* Prevent further origin writes from using this snapshot. */ 563 + /* After this returns there can be no new kcopyd jobs. */ 562 564 unregister_snapshot(s); 565 + 566 + kcopyd_client_destroy(s->kcopyd_client); 563 567 564 568 exit_exception_table(&s->pending, pending_cache); 565 569 exit_exception_table(&s->complete, exception_cache); ··· 573 569 574 570 dm_put_device(ti, s->origin); 575 571 dm_put_device(ti, s->cow); 576 - kcopyd_client_destroy(s->kcopyd_client); 572 + 577 573 kfree(s); 578 574 } 579 575
+16 -1
drivers/md/kcopyd.c
··· 44 44 struct page_list *pages; 45 45 unsigned int nr_pages; 46 46 unsigned int nr_free_pages; 47 + 48 + wait_queue_head_t destroyq; 49 + atomic_t nr_jobs; 47 50 }; 48 51 49 52 static struct page_list *alloc_pl(void) ··· 295 292 int read_err = job->read_err; 296 293 unsigned int write_err = job->write_err; 297 294 kcopyd_notify_fn fn = job->fn; 295 + struct kcopyd_client *kc = job->kc; 298 296 299 - kcopyd_put_pages(job->kc, job->pages); 297 + kcopyd_put_pages(kc, job->pages); 300 298 mempool_free(job, _job_pool); 301 299 fn(read_err, write_err, context); 300 + 301 + if (atomic_dec_and_test(&kc->nr_jobs)) 302 + wake_up(&kc->destroyq); 303 + 302 304 return 0; 303 305 } 304 306 ··· 438 430 */ 439 431 static void dispatch_job(struct kcopyd_job *job) 440 432 { 433 + atomic_inc(&job->kc->nr_jobs); 441 434 push(&_pages_jobs, job); 442 435 wake(); 443 436 } ··· 678 669 return r; 679 670 } 680 671 672 + init_waitqueue_head(&kc->destroyq); 673 + atomic_set(&kc->nr_jobs, 0); 674 + 681 675 client_add(kc); 682 676 *result = kc; 683 677 return 0; ··· 688 676 689 677 void kcopyd_client_destroy(struct kcopyd_client *kc) 690 678 { 679 + /* Wait for completion of all jobs submitted by this client. */ 680 + wait_event(kc->destroyq, !atomic_read(&kc->nr_jobs)); 681 + 691 682 dm_io_put(kc->nr_pages); 692 683 client_free_pages(kc); 693 684 client_del(kc);