On Fri, Dec 09, 2022 at 03:23:27PM +0100, Sergei Shtepa wrote: > +static int snapimage_kthread_worker_fn(void *param); Any chance to order th code so that you can avoid forward declarations wherever possible. > +static inline int snapimage_start_worker(struct snapimage *snapimage) > +{ > + struct task_struct *task; > + > + spin_lock_init(&snapimage->queue_lock); > + bio_list_init(&snapimage->queue); > + > + task = kthread_create(snapimage_kthread_worker_fn, > + snapimage, > + BLK_SNAP_IMAGE_NAME "%d", > + MINOR(snapimage->image_dev_id)); > + if (IS_ERR(task)) > + return -ENOMEM; > + > + snapimage->worker = get_task_struct(task); There's not need to grab and put an extra reference to the task_struct, the kthread code already holds one. > + while (!kthread_should_stop()) { > + bio = get_bio_from_queue(snapimage); > + if (!bio) { > + schedule_timeout_interruptible(HZ / 100); Do you need a timeout sleep here? All new arrivals should properly wake the thread. > + continue; > + } > + > + snapimage_process_bio(snapimage, bio); > + } > + > + while ((bio = get_bio_from_queue(snapimage))) > + snapimage_process_bio(snapimage, bio); It seems like the loop could be simplified to: for (;; { while ((bio = get_bio_from_queue(snapimage))) snapimage_process_bio(snapimage, bio); if (kthread_should_stop()) break; schedule(); } return 0; > +static void snapimage_submit_bio(struct bio *bio) > +{ > + struct snapimage *snapimage = bio->bi_bdev->bd_disk->private_data; > + gfp_t gfp = GFP_NOIO; > + > + if (bio->bi_opf & REQ_NOWAIT) > + gfp |= GFP_NOWAIT; gfp isn't actually used anywhere. > + if (snapimage->is_ready) { > + spin_lock(&snapimage->queue_lock); > + bio_list_add(&snapimage->queue, bio); > + spin_unlock(&snapimage->queue_lock); > + > + wake_up_process(snapimage->worker); > + } else > + bio_io_error(bio); > +} I think you can do away with the is_ready flag entirely by just calling del_gendisk early enough. See below. > +void snapimage_free(struct snapimage *snapimage) > +{ > + pr_info("Snapshot image disk [%u:%u] delete\n", > + MAJOR(snapimage->image_dev_id), MINOR(snapimage->image_dev_id)); > + > + blk_mq_freeze_queue(snapimage->disk->queue); > + snapimage->is_ready = false; > + blk_mq_unfreeze_queue(snapimage->disk->queue); > + > + snapimage_stop_worker(snapimage); > + > + del_gendisk(snapimage->disk); Just move the snapimage_stop_worker after del_gendisk here, and you should be fine. > + put_disk(snapimage->disk); > + > + diff_area_put(snapimage->diff_area); > + cbt_map_put(snapimage->cbt_map); > + > + ida_free(&snapimage_devt_ida, MINOR(snapimage->image_dev_id)); > + kfree(snapimage); .. and then implement the free_disk block_device operation, and move all code after the put_disk into it, which means all your data structures are alive until the very last gendisk reference goes away. > +#ifdef GENHD_FL_NO_PART_SCAN > + disk->flags |= GENHD_FL_NO_PART_SCAN; > +#else > + disk->flags |= GENHD_FL_NO_PART; > +#endif GENHD_FL_NO_PART_SCAN is gone, so this ifdef goes away. > + disk->major = _major; > + disk->first_minor = minor; > + disk->minors = 1; /* One disk has only one partition */ No new driver should manage the major/minor numbers manually, as the block layer just assigns a dev_t by default if you don't se these. > + wake_up_process(snapimage->worker); I don't think this wake_up is needed - the first queue bio should wake the helper thread up. > +int snapimage_init(void) > +{ > + int ret = 0; > + > + ret = register_blkdev(0, BLK_SNAP_IMAGE_NAME); > + if (ret < 0) { > + pr_err("Failed to register snapshot image block device\n"); > + return ret; > + } > + > + _major = ret; > + pr_info("Snapshot image block device major %d was registered\n", > + _major); > + > + return 0; > +} > + > +void snapimage_done(void) > +{ > + unregister_blkdev(_major, BLK_SNAP_IMAGE_NAME); > + pr_info("Snapshot image block device [%d] was unregistered\n", _major); > +} And with block layer managed dev_t all this boilerplate can go away as well. > +#include <linux/blk_types.h> > +#include <linux/blkdev.h> > +#include <linux/blk-mq.h> If you already include blk-mq.h, there is no need for the other two headers as they are already implictly included.