%PDF- %PDF-
Direktori : /usr/src/blksnap-6.3.0.73/ |
Current File : //usr/src/blksnap-6.3.0.73/snapimage.c |
// SPDX-License-Identifier: GPL-2.0 #define pr_fmt(fmt) KBUILD_MODNAME "-snapimage: " fmt #include <linux/slab.h> #include <linux/cdrom.h> #include <linux/blk-mq.h> #ifdef STANDALONE_BDEVFILTER #include "blk_snap.h" #else #include <linux/blk_snap.h> #endif #include "memory_checker.h" #include "snapimage.h" #include "diff_area.h" #include "chunk.h" #include "cbt_map.h" #ifdef STANDALONE_BDEVFILTER #include "log.h" #include "log_histogram.h" #endif #define SNAPIMAGE_MAX_DEVICES 2048 static unsigned int _major; static DEFINE_IDR(_minor_idr); static DEFINE_SPINLOCK(_minor_lock); static void free_minor(int minor) { spin_lock(&_minor_lock); idr_remove(&_minor_idr, minor); spin_unlock(&_minor_lock); } static int new_minor(int *minor, void *ptr) { int ret; idr_preload(GFP_KERNEL); spin_lock(&_minor_lock); ret = idr_alloc(&_minor_idr, ptr, 0, 1 << MINORBITS, GFP_NOWAIT); spin_unlock(&_minor_lock); idr_preload_end(); if (ret < 0) return ret; *minor = ret; return 0; } static inline void snapimage_unprepare_worker(struct snapimage *snapimage) { kthread_flush_worker(&snapimage->worker); kthread_stop(snapimage->worker_task); } static int snapimage_kthread_worker_fn(void *worker_ptr) { current->flags |= PF_LOCAL_THROTTLE | PF_MEMALLOC_NOIO; return kthread_worker_fn(worker_ptr); } static inline int snapimage_prepare_worker(struct snapimage *snapimage) { struct task_struct *task; kthread_init_worker(&snapimage->worker); task = kthread_run(snapimage_kthread_worker_fn, &snapimage->worker, BLK_SNAP_IMAGE_NAME "%d", MINOR(snapimage->image_dev_id)); if (IS_ERR(task)) return -ENOMEM; set_user_nice(task, MIN_NICE); snapimage->worker_task = task; return 0; } struct snapimage_cmd { struct kthread_work work; }; static void snapimage_queue_work(struct kthread_work *work) { struct snapimage_cmd *cmd = container_of(work, struct snapimage_cmd, work); struct request *rq = blk_mq_rq_from_pdu(cmd); struct snapimage *snapimage = rq->q->queuedata; blk_status_t status = BLK_STS_OK; struct bio_vec bvec; struct req_iterator iter; struct diff_area_image_ctx io_ctx; sector_t pos = blk_rq_pos(rq); diff_area_throttling_io(snapimage->diff_area); diff_area_image_ctx_init(&io_ctx, snapimage->diff_area, op_is_write(req_op(rq))); #ifdef STANDALONE_BDEVFILTER log_histogram_add(&snapimage->diff_area->read_hg, blk_rq_bytes(rq)); #endif rq_for_each_segment(bvec, rq, iter) { #ifdef STANDALONE_BDEVFILTER sector_t len_sect = bvec.bv_len >> SECTOR_SHIFT; #endif status = diff_area_image_io(&io_ctx, &bvec, &pos); if (unlikely(status != BLK_STS_OK)) break; #ifdef STANDALONE_BDEVFILTER atomic64_add(len_sect, io_ctx.is_write ? &snapimage->diff_area->stat_image_written : &snapimage->diff_area->stat_image_read); #endif } diff_area_image_ctx_done(&io_ctx); blk_mq_end_request(rq, status); } static int snapimage_init_request(struct blk_mq_tag_set *set, struct request *rq, unsigned int hctx_idx, unsigned int numa_node) { struct snapimage_cmd *cmd = blk_mq_rq_to_pdu(rq); kthread_init_work(&cmd->work, snapimage_queue_work); return 0; } /* * Cannot fall asleep in the context of this function, as we are under * rwsem lockdown. */ static blk_status_t snapimage_queue_rq(struct blk_mq_hw_ctx *hctx, const struct blk_mq_queue_data *bd) { int ret; struct request *rq = bd->rq; struct snapimage *snapimage = rq->q->queuedata; struct snapimage_cmd *cmd = blk_mq_rq_to_pdu(rq); if (unlikely(diff_area_is_corrupted(snapimage->diff_area))) return BLK_STS_NOSPC; if (op_is_write(req_op(rq))) { ret = cbt_map_set_both(snapimage->cbt_map, blk_rq_pos(rq), blk_rq_sectors(rq)); if (unlikely(ret)) return BLK_STS_IOERR; } blk_mq_start_request(rq); kthread_queue_work(&snapimage->worker, &cmd->work); return BLK_STS_OK; } static const struct blk_mq_ops mq_ops = { .queue_rq = snapimage_queue_rq, .init_request = snapimage_init_request, }; const struct block_device_operations bd_ops = { .owner = THIS_MODULE, }; static inline int snapimage_alloc_tag_set(struct snapimage *snapimage) { struct blk_mq_tag_set *set = &snapimage->tag_set; set->ops = &mq_ops; set->nr_hw_queues = 1; set->nr_maps = 1; set->queue_depth = 128; set->numa_node = NUMA_NO_NODE; set->flags = BLK_MQ_F_SHOULD_MERGE | BLK_MQ_F_STACKING; set->cmd_size = sizeof(struct snapimage_cmd); set->driver_data = snapimage; return blk_mq_alloc_tag_set(set); } 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_stop_hw_queues(snapimage->disk->queue); blk_mq_quiesce_queue(snapimage->disk->queue); snapimage_unprepare_worker(snapimage); del_gendisk(snapimage->disk); #ifdef HAVE_BLK_MQ_ALLOC_DISK #ifdef HAVE_BLK_CLEANUP_DISK blk_cleanup_disk(snapimage->disk); #else put_disk(snapimage->disk); #endif #else blk_cleanup_queue(snapimage->queue); put_disk(snapimage->disk); #endif blk_mq_free_tag_set(&snapimage->tag_set); diff_area_put(snapimage->diff_area); cbt_map_put(snapimage->cbt_map); free_minor(MINOR(snapimage->image_dev_id)); kfree(snapimage); memory_object_dec(memory_object_snapimage); } struct snapimage *snapimage_create(struct diff_area *diff_area, struct cbt_map *cbt_map) { int ret = 0; int minor; struct snapimage *snapimage = NULL; struct gendisk *disk; #ifndef HAVE_BLK_MQ_ALLOC_DISK struct request_queue *queue; #endif snapimage = kzalloc(sizeof(struct snapimage), GFP_KERNEL); if (snapimage == NULL) return ERR_PTR(-ENOMEM); memory_object_inc(memory_object_snapimage); ret = new_minor(&minor, snapimage); if (ret) { pr_err("Failed to allocate minor for snapshot image device. errno=%d\n", abs(ret)); goto fail_free_image; } snapimage->capacity = cbt_map->device_capacity; snapimage->image_dev_id = MKDEV(_major, minor); pr_info("Create snapshot image device [%u:%u] for original device [%u:%u]\n", MAJOR(snapimage->image_dev_id), MINOR(snapimage->image_dev_id), MAJOR(diff_area->orig_bdev->bd_dev), MINOR(diff_area->orig_bdev->bd_dev)); ret = snapimage_prepare_worker(snapimage); if (ret) { pr_err("Failed to prepare worker thread. errno=%d\n", abs(ret)); goto fail_free_minor; } ret = snapimage_alloc_tag_set(snapimage); if (ret) { pr_err("Failed to allocate tag set. errno=%d\n", abs(ret)); goto fail_free_worker; } #ifdef HAVE_BLK_MQ_ALLOC_DISK #if defined(HAVE_BDEV_QUEUE_LIMITS) disk = blk_mq_alloc_disk(&snapimage->tag_set, NULL, snapimage); #else disk = blk_mq_alloc_disk(&snapimage->tag_set, snapimage); #endif if (IS_ERR(disk)) { ret = PTR_ERR(disk); pr_err("Failed to allocate disk. errno=%d\n", abs(ret)); goto fail_free_tagset; } #else queue = blk_mq_init_queue(&snapimage->tag_set); if (IS_ERR(queue)) { ret = PTR_ERR(queue); pr_err("Failed to allocate queue. errno=%d\n", abs(ret)); goto fail_free_tagset; } disk = alloc_disk(1); if (!disk) { pr_err("Failed to allocate disk\n"); goto fail_free_queue; } disk->queue = queue; snapimage->queue = queue; snapimage->queue->queuedata = snapimage; blk_queue_bounce_limit(snapimage->queue, BLK_BOUNCE_HIGH); #endif #ifdef HAVE_BLK_QUEUE_MAX_HW_SECTORS #ifdef BLK_DEF_MAX_SECTORS_CAP blk_queue_max_hw_sectors(disk->queue, BLK_DEF_MAX_SECTORS_CAP); #else blk_queue_max_hw_sectors(disk->queue, BLK_DEF_MAX_SECTORS); #endif #endif blk_queue_flag_set(QUEUE_FLAG_NOMERGES, disk->queue); if (snprintf(disk->disk_name, DISK_NAME_LEN, "%s%d", BLK_SNAP_IMAGE_NAME, minor) < 0) { pr_err("Unable to set disk name for snapshot image device: invalid minor %u\n", minor); ret = -EINVAL; goto fail_cleanup_disk; } pr_debug("Snapshot image disk name [%s]\n", disk->disk_name); disk->flags = 0; #ifdef GENHD_FL_NO_PART_SCAN disk->flags |= GENHD_FL_NO_PART_SCAN; #else disk->flags |= GENHD_FL_NO_PART; #endif disk->major = _major; disk->first_minor = minor; disk->minors = 1; /* One disk has only one partition */ disk->fops = &bd_ops; disk->private_data = snapimage; snapimage->disk = disk; set_capacity(disk, snapimage->capacity); pr_debug("Snapshot image device capacity %lld bytes\n", (u64)(snapimage->capacity << SECTOR_SHIFT)); diff_area_get(diff_area); snapimage->diff_area = diff_area; cbt_map_get(cbt_map); snapimage->cbt_map = cbt_map; #ifdef HAVE_ADD_DISK_RESULT ret = add_disk(disk); if (ret) { pr_err("Failed to add disk [%s] for snapshot image device\n", disk->disk_name); goto fail_cleanup_disk; } #else add_disk(disk); #endif return snapimage; fail_cleanup_disk: #ifdef HAVE_BLK_MQ_ALLOC_DISK #ifdef HAVE_BLK_CLEANUP_DISK blk_cleanup_disk(disk); #else put_disk(disk); #endif #else del_gendisk(disk); fail_free_queue: blk_cleanup_queue(queue); #endif fail_free_tagset: blk_mq_free_tag_set(&snapimage->tag_set); fail_free_worker: snapimage_unprepare_worker(snapimage); fail_free_minor: free_minor(minor); fail_free_image: kfree(snapimage); memory_object_dec(memory_object_snapimage); return ERR_PTR(ret); } int snapimage_init(void) { int mj = 0; mj = register_blkdev(mj, BLK_SNAP_IMAGE_NAME); if (mj < 0) { pr_err("Failed to register snapshot image block device. errno=%d\n", abs(mj)); return mj; } _major = mj; 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); idr_destroy(&_minor_idr); } int snapimage_major(void) { return _major; } #ifdef BLK_SNAP_DEBUG_SECTOR_STATE int snapimage_get_chunk_state(struct snapimage *snapimage, sector_t sector, struct blk_snap_sector_state *state) { int ret; ret = diff_area_get_sector_state(snapimage->diff_area, sector, &state->chunk_state); if (ret) return ret; ret = cbt_map_get_sector_state(snapimage->cbt_map, sector, &state->snap_number_prev, &state->snap_number_curr); if (ret) return ret; { char buf[SECTOR_SIZE]; ret = diff_area_get_sector_image(snapimage->diff_area, sector, buf /*&state->buf*/); if (ret) return ret; pr_info("sector #%llu", sector); print_hex_dump(KERN_INFO, "data header: ", DUMP_PREFIX_OFFSET, 32, 1, buf, 96, true); } return 0; } #endif