On Sat, 5 Oct 2013, Akira Hayakawa wrote:

> Mikulas,
> 
> > nvidia binary driver, but it may happen in other parts of the kernel too. 
> > The fact that it works in your setup doesn't mean that it is correct.
> You are right. I am convinced.
> 
> As far as I looked around the kernel code,
> it seems to be choosing kthread when one needs looping in background.
> There are other examples such as loop_thread in drivers/block/loop.c .
> 
> And done. Please git pull.
> commit b6d2d3892e09145cd0b9c5ad69ea2d8c410fdeab is tested in my environment.
> All the looping daemons are running on kthread now.
> 
> The diff between the older version (with wq) and the new version (with 
> kthread)
> is shown below. I defined fancy CREATE_DAEMON macro.
> 
> Another by-product is that
> you are no longer in need to wait for long time in dmsetup remove
> since kthread_stop() forcefully wakes the thread up.

The change seems ok. Please, also move this piece of code in flush_proc
out of the spinlock:
                        if (kthread_should_stop())
                                return 0;

It caused the workqueue warning I reported before and still causes warning 
with kthreads:
note: flush_daemon[5145] exited with preempt_count 1

I will send you next email with more bugs that I found in your code.

Mikulas

> diff --git a/Driver/dm-writeboost-daemon.c b/Driver/dm-writeboost-daemon.c
> index 6090661..65974e2 100644
> --- a/Driver/dm-writeboost-daemon.c
> +++ b/Driver/dm-writeboost-daemon.c
> @@ -10,12 +10,11 @@
>  
>  /*----------------------------------------------------------------*/
>  
> -void flush_proc(struct work_struct *work)
> +int flush_proc(void *data)
>  {
>       unsigned long flags;
>  
> -     struct wb_cache *cache =
> -             container_of(work, struct wb_cache, flush_work);
> +     struct wb_cache *cache = data;
>  
>       while (true) {
>               struct flush_job *job;
> @@ -32,8 +31,12 @@ void flush_proc(struct work_struct *work)
>                               msecs_to_jiffies(100));
>                       spin_lock_irqsave(&cache->flush_queue_lock, flags);
>  
> -                     if (cache->on_terminate)
> -                             return;
> +                     /*
> +                      * flush daemon can exit
> +                      * only if no flush job is queued.
> +                      */
> +                     if (kthread_should_stop())
> +                             return 0;
>               }
>  
>               /*
> @@ -84,6 +87,7 @@ void flush_proc(struct work_struct *work)
>  
>               kfree(job);
>       }
> +     return 0;
>  }
>  
>  /*----------------------------------------------------------------*/
> @@ -353,19 +357,15 @@ migrate_write:
>       }
>  }
>  
> -void migrate_proc(struct work_struct *work)
> +int migrate_proc(void *data)
>  {
> -     struct wb_cache *cache =
> -             container_of(work, struct wb_cache, migrate_work);
> +     struct wb_cache *cache = data;
>  
> -     while (true) {
> +     while (!kthread_should_stop()) {
>               bool allow_migrate;
>               size_t i, nr_mig_candidates, nr_mig, nr_max_batch;
>               struct segment_header *seg, *tmp;
>  
> -             if (cache->on_terminate)
> -                     return;
> -
>               /*
>                * If reserving_id > 0
>                * Migration should be immediate.
> @@ -430,6 +430,7 @@ void migrate_proc(struct work_struct *work)
>                       list_del(&seg->migrate_list);
>               }
>       }
> +     return 0;
>  }
>  
>  /*
> @@ -453,19 +454,16 @@ void wait_for_migration(struct wb_cache *cache, u64 id)
>  
>  /*----------------------------------------------------------------*/
>  
> -void modulator_proc(struct work_struct *work)
> +int modulator_proc(void *data)
>  {
> -     struct wb_cache *cache =
> -             container_of(work, struct wb_cache, modulator_work);
> +     struct wb_cache *cache = data;
>       struct wb_device *wb = cache->wb;
>  
>       struct hd_struct *hd = wb->device->bdev->bd_part;
>       unsigned long old = 0, new, util;
>       unsigned long intvl = 1000;
>  
> -     while (true) {
> -             if (cache->on_terminate)
> -                     return;
> +     while (!kthread_should_stop()) {
>  
>               new = jiffies_to_msecs(part_stat_read(hd, io_ticks));
>  
> @@ -484,6 +482,7 @@ modulator_update:
>  
>               schedule_timeout_interruptible(msecs_to_jiffies(intvl));
>       }
> +     return 0;
>  }
>  
>  /*----------------------------------------------------------------*/
> @@ -517,16 +516,12 @@ static void update_superblock_record(struct wb_cache 
> *cache)
>       kfree(buf);
>  }
>  
> -void recorder_proc(struct work_struct *work)
> +int recorder_proc(void *data)
>  {
> -     struct wb_cache *cache =
> -             container_of(work, struct wb_cache, recorder_work);
> +     struct wb_cache *cache = data;
>       unsigned long intvl;
>  
> -     while (true) {
> -             if (cache->on_terminate)
> -                     return;
> -
> +     while (!kthread_should_stop()) {
>               /* sec -> ms */
>               intvl = cache->update_record_interval * 1000;
>  
> @@ -539,20 +534,17 @@ void recorder_proc(struct work_struct *work)
>  
>               schedule_timeout_interruptible(msecs_to_jiffies(intvl));
>       }
> +     return 0;
>  }
>  
>  /*----------------------------------------------------------------*/
>  
> -void sync_proc(struct work_struct *work)
> +int sync_proc(void *data)
>  {
> -     struct wb_cache *cache =
> -             container_of(work, struct wb_cache, sync_work);
> +     struct wb_cache *cache = data;
>       unsigned long intvl;
>  
> -     while (true) {
> -             if (cache->on_terminate)
> -                     return;
> -
> +     while (!kthread_should_stop()) {
>               /* sec -> ms */
>               intvl = cache->sync_interval * 1000;
>  
> @@ -566,4 +558,5 @@ void sync_proc(struct work_struct *work)
>  
>               schedule_timeout_interruptible(msecs_to_jiffies(intvl));
>       }
> +     return 0;
>  }
> diff --git a/Driver/dm-writeboost-daemon.h b/Driver/dm-writeboost-daemon.h
> index 3bdd862..d21d66c 100644
> --- a/Driver/dm-writeboost-daemon.h
> +++ b/Driver/dm-writeboost-daemon.h
> @@ -9,7 +9,7 @@
>  
>  /*----------------------------------------------------------------*/
>  
> -void flush_proc(struct work_struct *);
> +int flush_proc(void *);
>  
>  /*----------------------------------------------------------------*/
>  
> @@ -19,20 +19,20 @@ void flush_barrier_ios(struct work_struct *);
>  
>  /*----------------------------------------------------------------*/
>  
> -void migrate_proc(struct work_struct *);
> +int migrate_proc(void *);
>  void wait_for_migration(struct wb_cache *, u64 id);
>  
>  /*----------------------------------------------------------------*/
>  
> -void modulator_proc(struct work_struct *);
> +int modulator_proc(void *);
>  
>  /*----------------------------------------------------------------*/
>  
> -void sync_proc(struct work_struct *);
> +int sync_proc(void *);
>  
>  /*----------------------------------------------------------------*/
>  
> -void recorder_proc(struct work_struct *);
> +int recorder_proc(void *);
>  
>  /*----------------------------------------------------------------*/
>  
> diff --git a/Driver/dm-writeboost-metadata.c b/Driver/dm-writeboost-metadata.c
> index 1cd9e0c..4f5fa5e 100644
> --- a/Driver/dm-writeboost-metadata.c
> +++ b/Driver/dm-writeboost-metadata.c
> @@ -994,6 +994,17 @@ void free_migration_buffer(struct wb_cache *cache)
>  
>  /*----------------------------------------------------------------*/
>  
> +#define CREATE_DAEMON(name)\
> +     cache->name##_daemon = kthread_create(name##_proc, cache,\
> +                                           #name "_daemon");\
> +     if (IS_ERR(cache->name##_daemon)) {\
> +             r = PTR_ERR(cache->name##_daemon);\
> +             cache->name##_daemon = NULL;\
> +             WBERR("couldn't spawn" #name "daemon");\
> +             goto bad_##name##_daemon;\
> +     }\
> +     wake_up_process(cache->name##_daemon);
> +
>  int __must_check resume_cache(struct wb_cache *cache, struct dm_dev *dev)
>  {
>       int r = 0;
> @@ -1010,8 +1021,6 @@ int __must_check resume_cache(struct wb_cache *cache, 
> struct dm_dev *dev)
>  
>       mutex_init(&cache->io_lock);
>  
> -     cache->on_terminate = false;
> -
>       /*
>        * (i) Harmless Initializations
>        */
> @@ -1042,12 +1051,6 @@ int __must_check resume_cache(struct wb_cache *cache, 
> struct dm_dev *dev)
>        * Recovering the cache metadata
>        * prerequires the migration daemon working.
>        */
> -     cache->migrate_wq = create_singlethread_workqueue("migratewq");
> -     if (!cache->migrate_wq) {
> -             r = -ENOMEM;
> -             WBERR();
> -             goto bad_migratewq;
> -     }
>  
>       /* Migration Daemon */
>       atomic_set(&cache->migrate_fail_count, 0);
> @@ -1070,9 +1073,7 @@ int __must_check resume_cache(struct wb_cache *cache, 
> struct dm_dev *dev)
>  
>       cache->allow_migrate = true;
>       cache->reserving_segment_id = 0;
> -     INIT_WORK(&cache->migrate_work, migrate_proc);
> -     queue_work(cache->migrate_wq, &cache->migrate_work);
> -
> +     CREATE_DAEMON(migrate);
>  
>       r = recover_cache(cache);
>       if (r) {
> @@ -1086,19 +1087,12 @@ int __must_check resume_cache(struct wb_cache *cache, 
> struct dm_dev *dev)
>        * These are only working
>        * after the logical device created.
>        */
> -     cache->flush_wq = create_singlethread_workqueue("flushwq");
> -     if (!cache->flush_wq) {
> -             r = -ENOMEM;
> -             WBERR();
> -             goto bad_flushwq;
> -     }
>  
>       /* Flush Daemon */
> -     INIT_WORK(&cache->flush_work, flush_proc);
>       spin_lock_init(&cache->flush_queue_lock);
>       INIT_LIST_HEAD(&cache->flush_queue);
>       init_waitqueue_head(&cache->flush_wait_queue);
> -     queue_work(cache->flush_wq, &cache->flush_work);
> +     CREATE_DAEMON(flush);
>  
>       /* Deferred ACK for barrier writes */
>  
> @@ -1118,29 +1112,30 @@ int __must_check resume_cache(struct wb_cache *cache, 
> struct dm_dev *dev)
>  
>       /* Migartion Modulator */
>       cache->enable_migration_modulator = true;
> -     INIT_WORK(&cache->modulator_work, modulator_proc);
> -     schedule_work(&cache->modulator_work);
> +     CREATE_DAEMON(modulator);
>  
>       /* Superblock Recorder */
>       cache->update_record_interval = 60;
> -     INIT_WORK(&cache->recorder_work, recorder_proc);
> -     schedule_work(&cache->recorder_work);
> +     CREATE_DAEMON(recorder);
>  
>       /* Dirty Synchronizer */
>       cache->sync_interval = 60;
> -     INIT_WORK(&cache->sync_work, sync_proc);
> -     schedule_work(&cache->sync_work);
> +     CREATE_DAEMON(sync);
>  
>       return 0;
>  
> -bad_flushwq:
> +bad_sync_daemon:
> +     kthread_stop(cache->recorder_daemon);
> +bad_recorder_daemon:
> +     kthread_stop(cache->modulator_daemon);
> +bad_modulator_daemon:
> +     kthread_stop(cache->flush_daemon);
> +bad_flush_daemon:
>  bad_recover:
> -     cache->on_terminate = true;
> -     cancel_work_sync(&cache->migrate_work);
> +     kthread_stop(cache->migrate_daemon);
> +bad_migrate_daemon:
>       free_migration_buffer(cache);
>  bad_alloc_migrate_buffer:
> -     destroy_workqueue(cache->migrate_wq);
> -bad_migratewq:
>       free_ht(cache);
>  bad_alloc_ht:
>       free_segment_header_array(cache);
> @@ -1153,20 +1148,21 @@ bad_init_rambuf_pool:
>  
>  void free_cache(struct wb_cache *cache)
>  {
> -     cache->on_terminate = true;
> +     /*
> +      * Must clean up all the volatile data
> +      * before termination.
> +      */
> +     flush_current_buffer(cache);
>  
> -     /* Kill in-kernel daemons */
> -     cancel_work_sync(&cache->sync_work);
> -     cancel_work_sync(&cache->recorder_work);
> -     cancel_work_sync(&cache->modulator_work);
> +     kthread_stop(cache->sync_daemon);
> +     kthread_stop(cache->recorder_daemon);
> +     kthread_stop(cache->modulator_daemon);
>  
> -     cancel_work_sync(&cache->flush_work);
> -     destroy_workqueue(cache->flush_wq);
> +     kthread_stop(cache->flush_daemon);
>  
>       cancel_work_sync(&cache->barrier_deadline_work);
>  
> -     cancel_work_sync(&cache->migrate_work);
> -     destroy_workqueue(cache->migrate_wq);
> +     kthread_stop(cache->migrate_daemon);
>       free_migration_buffer(cache);
>  
>       /* Destroy in-core structures */
> diff --git a/Driver/dm-writeboost-target.c b/Driver/dm-writeboost-target.c
> index 6aa4c7c..4b5b7aa 100644
> --- a/Driver/dm-writeboost-target.c
> +++ b/Driver/dm-writeboost-target.c
> @@ -973,12 +973,6 @@ static void writeboost_dtr(struct dm_target *ti)
>       struct wb_device *wb = ti->private;
>       struct wb_cache *cache = wb->cache;
>  
> -     /*
> -      * Synchronize all the dirty writes
> -      * before termination.
> -      */
> -     cache->sync_interval = 1;
> -
>       free_cache(cache);
>       kfree(cache);
>  
> diff --git a/Driver/dm-writeboost.h b/Driver/dm-writeboost.h
> index 74ff194..092c100 100644
> --- a/Driver/dm-writeboost.h
> +++ b/Driver/dm-writeboost.h
> @@ -16,6 +16,7 @@
>  #include <linux/list.h>
>  #include <linux/slab.h>
>  #include <linux/mutex.h>
> +#include <linux/kthread.h>
>  #include <linux/sched.h>
>  #include <linux/timer.h>
>  #include <linux/device-mapper.h>
> @@ -265,8 +266,7 @@ struct wb_cache {
>        * and flush daemon asynchronously
>        * flush them to the cache device.
>        */
> -     struct work_struct flush_work;
> -     struct workqueue_struct *flush_wq;
> +     struct task_struct *flush_daemon;
>       spinlock_t flush_queue_lock;
>       struct list_head flush_queue;
>       wait_queue_head_t flush_wait_queue;
> @@ -288,8 +288,7 @@ struct wb_cache {
>        * migrate daemon goes into migration
>        * if they are segments to migrate.
>        */
> -     struct work_struct migrate_work;
> -     struct workqueue_struct *migrate_wq;
> +     struct task_struct *migrate_daemon;
>       bool allow_migrate; /* param */
>  
>       /*
> @@ -314,7 +313,7 @@ struct wb_cache {
>        * the migration
>        * according to the load of backing store.
>        */
> -     struct work_struct modulator_work;
> +     struct task_struct *modulator_daemon;
>       bool enable_migration_modulator; /* param */
>  
>       /*
> @@ -323,7 +322,7 @@ struct wb_cache {
>        * Update the superblock record
>        * periodically.
>        */
> -     struct work_struct recorder_work;
> +     struct task_struct *recorder_daemon;
>       unsigned long update_record_interval; /* param */
>  
>       /*
> @@ -332,16 +331,9 @@ struct wb_cache {
>        * Sync the dirty writes
>        * periodically.
>        */
> -     struct work_struct sync_work;
> +     struct task_struct *sync_daemon;
>       unsigned long sync_interval; /* param */
>  
> -     /*
> -      * on_terminate is true
> -      * to notify all the background daemons to
> -      * stop their operations.
> -      */
> -     bool on_terminate;
> -
>       atomic64_t stat[STATLEN];
>  };
> 
> Akira
> 
_______________________________________________
devel mailing list
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel

Reply via email to