dm-log.c
来自「linux 内核源代码」· C语言 代码 · 共 740 行 · 第 1/2 页
C
740 行
/* * Copyright (C) 2003 Sistina Software * * This file is released under the LGPL. */#include <linux/init.h>#include <linux/slab.h>#include <linux/module.h>#include <linux/vmalloc.h>#include "dm-log.h"#include "dm-io.h"#define DM_MSG_PREFIX "mirror log"static LIST_HEAD(_log_types);static DEFINE_SPINLOCK(_lock);int dm_register_dirty_log_type(struct dirty_log_type *type){ spin_lock(&_lock); type->use_count = 0; list_add(&type->list, &_log_types); spin_unlock(&_lock); return 0;}int dm_unregister_dirty_log_type(struct dirty_log_type *type){ spin_lock(&_lock); if (type->use_count) DMWARN("Attempt to unregister a log type that is still in use"); else list_del(&type->list); spin_unlock(&_lock); return 0;}static struct dirty_log_type *get_type(const char *type_name){ struct dirty_log_type *type; spin_lock(&_lock); list_for_each_entry (type, &_log_types, list) if (!strcmp(type_name, type->name)) { if (!type->use_count && !try_module_get(type->module)){ spin_unlock(&_lock); return NULL; } type->use_count++; spin_unlock(&_lock); return type; } spin_unlock(&_lock); return NULL;}static void put_type(struct dirty_log_type *type){ spin_lock(&_lock); if (!--type->use_count) module_put(type->module); spin_unlock(&_lock);}struct dirty_log *dm_create_dirty_log(const char *type_name, struct dm_target *ti, unsigned int argc, char **argv){ struct dirty_log_type *type; struct dirty_log *log; log = kmalloc(sizeof(*log), GFP_KERNEL); if (!log) return NULL; type = get_type(type_name); if (!type) { kfree(log); return NULL; } log->type = type; if (type->ctr(log, ti, argc, argv)) { kfree(log); put_type(type); return NULL; } return log;}void dm_destroy_dirty_log(struct dirty_log *log){ log->type->dtr(log); put_type(log->type); kfree(log);}/*----------------------------------------------------------------- * Persistent and core logs share a lot of their implementation. * FIXME: need a reload method to be called from a resume *---------------------------------------------------------------*//* * Magic for persistent mirrors: "MiRr" */#define MIRROR_MAGIC 0x4D695272/* * The on-disk version of the metadata. */#define MIRROR_DISK_VERSION 2#define LOG_OFFSET 2struct log_header { uint32_t magic; /* * Simple, incrementing version. no backward * compatibility. */ uint32_t version; sector_t nr_regions;};struct log_c { struct dm_target *ti; int touched; uint32_t region_size; unsigned int region_count; region_t sync_count; unsigned bitset_uint32_count; uint32_t *clean_bits; uint32_t *sync_bits; uint32_t *recovering_bits; /* FIXME: this seems excessive */ int sync_search; /* Resync flag */ enum sync { DEFAULTSYNC, /* Synchronize if necessary */ NOSYNC, /* Devices known to be already in sync */ FORCESYNC, /* Force a sync to happen */ } sync; struct dm_io_request io_req; /* * Disk log fields */ int log_dev_failed; struct dm_dev *log_dev; struct log_header header; struct io_region header_location; struct log_header *disk_header;};/* * The touched member needs to be updated every time we access * one of the bitsets. */static inline int log_test_bit(uint32_t *bs, unsigned bit){ return ext2_test_bit(bit, (unsigned long *) bs) ? 1 : 0;}static inline void log_set_bit(struct log_c *l, uint32_t *bs, unsigned bit){ ext2_set_bit(bit, (unsigned long *) bs); l->touched = 1;}static inline void log_clear_bit(struct log_c *l, uint32_t *bs, unsigned bit){ ext2_clear_bit(bit, (unsigned long *) bs); l->touched = 1;}/*---------------------------------------------------------------- * Header IO *--------------------------------------------------------------*/static void header_to_disk(struct log_header *core, struct log_header *disk){ disk->magic = cpu_to_le32(core->magic); disk->version = cpu_to_le32(core->version); disk->nr_regions = cpu_to_le64(core->nr_regions);}static void header_from_disk(struct log_header *core, struct log_header *disk){ core->magic = le32_to_cpu(disk->magic); core->version = le32_to_cpu(disk->version); core->nr_regions = le64_to_cpu(disk->nr_regions);}static int rw_header(struct log_c *lc, int rw){ lc->io_req.bi_rw = rw; lc->io_req.mem.ptr.vma = lc->disk_header; lc->io_req.notify.fn = NULL; return dm_io(&lc->io_req, 1, &lc->header_location, NULL);}static int read_header(struct log_c *log){ int r; r = rw_header(log, READ); if (r) return r; header_from_disk(&log->header, log->disk_header); /* New log required? */ if (log->sync != DEFAULTSYNC || log->header.magic != MIRROR_MAGIC) { log->header.magic = MIRROR_MAGIC; log->header.version = MIRROR_DISK_VERSION; log->header.nr_regions = 0; }#ifdef __LITTLE_ENDIAN if (log->header.version == 1) log->header.version = 2;#endif if (log->header.version != MIRROR_DISK_VERSION) { DMWARN("incompatible disk log version"); return -EINVAL; } return 0;}static inline int write_header(struct log_c *log){ header_to_disk(&log->header, log->disk_header); return rw_header(log, WRITE);}/*---------------------------------------------------------------- * core log constructor/destructor * * argv contains region_size followed optionally by [no]sync *--------------------------------------------------------------*/#define BYTE_SHIFT 3static int create_log_context(struct dirty_log *log, struct dm_target *ti, unsigned int argc, char **argv, struct dm_dev *dev){ enum sync sync = DEFAULTSYNC; struct log_c *lc; uint32_t region_size; unsigned int region_count; size_t bitset_size, buf_size; int r; if (argc < 1 || argc > 2) { DMWARN("wrong number of arguments to mirror log"); return -EINVAL; } if (argc > 1) { if (!strcmp(argv[1], "sync")) sync = FORCESYNC; else if (!strcmp(argv[1], "nosync")) sync = NOSYNC; else { DMWARN("unrecognised sync argument to mirror log: %s", argv[1]); return -EINVAL; } } if (sscanf(argv[0], "%u", ®ion_size) != 1) { DMWARN("invalid region size string"); return -EINVAL; } region_count = dm_sector_div_up(ti->len, region_size); lc = kmalloc(sizeof(*lc), GFP_KERNEL); if (!lc) { DMWARN("couldn't allocate core log"); return -ENOMEM; } lc->ti = ti; lc->touched = 0; lc->region_size = region_size; lc->region_count = region_count; lc->sync = sync; /* * Work out how many "unsigned long"s we need to hold the bitset. */ bitset_size = dm_round_up(region_count, sizeof(*lc->clean_bits) << BYTE_SHIFT); bitset_size >>= BYTE_SHIFT; lc->bitset_uint32_count = bitset_size / sizeof(*lc->clean_bits); /* * Disk log? */ if (!dev) { lc->clean_bits = vmalloc(bitset_size); if (!lc->clean_bits) { DMWARN("couldn't allocate clean bitset"); kfree(lc); return -ENOMEM; } lc->disk_header = NULL; } else { lc->log_dev = dev; lc->log_dev_failed = 0; lc->header_location.bdev = lc->log_dev->bdev; lc->header_location.sector = 0; /* * Buffer holds both header and bitset. */ buf_size = dm_round_up((LOG_OFFSET << SECTOR_SHIFT) + bitset_size, ti->limits.hardsect_size); lc->header_location.count = buf_size >> SECTOR_SHIFT; lc->io_req.mem.type = DM_IO_VMA; lc->io_req.client = dm_io_client_create(dm_div_up(buf_size, PAGE_SIZE)); if (IS_ERR(lc->io_req.client)) { r = PTR_ERR(lc->io_req.client); DMWARN("couldn't allocate disk io client"); kfree(lc); return -ENOMEM; } lc->disk_header = vmalloc(buf_size); if (!lc->disk_header) { DMWARN("couldn't allocate disk log buffer"); kfree(lc); return -ENOMEM; } lc->clean_bits = (void *)lc->disk_header + (LOG_OFFSET << SECTOR_SHIFT); } memset(lc->clean_bits, -1, bitset_size); lc->sync_bits = vmalloc(bitset_size); if (!lc->sync_bits) { DMWARN("couldn't allocate sync bitset"); if (!dev) vfree(lc->clean_bits); vfree(lc->disk_header); kfree(lc); return -ENOMEM; } memset(lc->sync_bits, (sync == NOSYNC) ? -1 : 0, bitset_size); lc->sync_count = (sync == NOSYNC) ? region_count : 0;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?