162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2003 Sistina Software 462306a36Sopenharmony_ci * Copyright (C) 2004-2008 Red Hat, Inc. All rights reserved. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * This file is released under the LGPL. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/init.h> 1062306a36Sopenharmony_ci#include <linux/slab.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/vmalloc.h> 1362306a36Sopenharmony_ci#include <linux/dm-io.h> 1462306a36Sopenharmony_ci#include <linux/dm-dirty-log.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/device-mapper.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define DM_MSG_PREFIX "dirty region log" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistatic LIST_HEAD(_log_types); 2162306a36Sopenharmony_cistatic DEFINE_SPINLOCK(_lock); 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic struct dm_dirty_log_type *__find_dirty_log_type(const char *name) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci struct dm_dirty_log_type *log_type; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci list_for_each_entry(log_type, &_log_types, list) 2862306a36Sopenharmony_ci if (!strcmp(name, log_type->name)) 2962306a36Sopenharmony_ci return log_type; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci return NULL; 3262306a36Sopenharmony_ci} 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic struct dm_dirty_log_type *_get_dirty_log_type(const char *name) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci struct dm_dirty_log_type *log_type; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci spin_lock(&_lock); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci log_type = __find_dirty_log_type(name); 4162306a36Sopenharmony_ci if (log_type && !try_module_get(log_type->module)) 4262306a36Sopenharmony_ci log_type = NULL; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci spin_unlock(&_lock); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci return log_type; 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/* 5062306a36Sopenharmony_ci * get_type 5162306a36Sopenharmony_ci * @type_name 5262306a36Sopenharmony_ci * 5362306a36Sopenharmony_ci * Attempt to retrieve the dm_dirty_log_type by name. If not already 5462306a36Sopenharmony_ci * available, attempt to load the appropriate module. 5562306a36Sopenharmony_ci * 5662306a36Sopenharmony_ci * Log modules are named "dm-log-" followed by the 'type_name'. 5762306a36Sopenharmony_ci * Modules may contain multiple types. 5862306a36Sopenharmony_ci * This function will first try the module "dm-log-<type_name>", 5962306a36Sopenharmony_ci * then truncate 'type_name' on the last '-' and try again. 6062306a36Sopenharmony_ci * 6162306a36Sopenharmony_ci * For example, if type_name was "clustered-disk", it would search 6262306a36Sopenharmony_ci * 'dm-log-clustered-disk' then 'dm-log-clustered'. 6362306a36Sopenharmony_ci * 6462306a36Sopenharmony_ci * Returns: dirty_log_type* on success, NULL on failure 6562306a36Sopenharmony_ci */ 6662306a36Sopenharmony_cistatic struct dm_dirty_log_type *get_type(const char *type_name) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci char *p, *type_name_dup; 6962306a36Sopenharmony_ci struct dm_dirty_log_type *log_type; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci if (!type_name) 7262306a36Sopenharmony_ci return NULL; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci log_type = _get_dirty_log_type(type_name); 7562306a36Sopenharmony_ci if (log_type) 7662306a36Sopenharmony_ci return log_type; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci type_name_dup = kstrdup(type_name, GFP_KERNEL); 7962306a36Sopenharmony_ci if (!type_name_dup) { 8062306a36Sopenharmony_ci DMWARN("No memory left to attempt log module load for \"%s\"", 8162306a36Sopenharmony_ci type_name); 8262306a36Sopenharmony_ci return NULL; 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci while (request_module("dm-log-%s", type_name_dup) || 8662306a36Sopenharmony_ci !(log_type = _get_dirty_log_type(type_name))) { 8762306a36Sopenharmony_ci p = strrchr(type_name_dup, '-'); 8862306a36Sopenharmony_ci if (!p) 8962306a36Sopenharmony_ci break; 9062306a36Sopenharmony_ci p[0] = '\0'; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (!log_type) 9462306a36Sopenharmony_ci DMWARN("Module for logging type \"%s\" not found.", type_name); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci kfree(type_name_dup); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci return log_type; 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic void put_type(struct dm_dirty_log_type *type) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci if (!type) 10462306a36Sopenharmony_ci return; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci spin_lock(&_lock); 10762306a36Sopenharmony_ci if (!__find_dirty_log_type(type->name)) 10862306a36Sopenharmony_ci goto out; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci module_put(type->module); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ciout: 11362306a36Sopenharmony_ci spin_unlock(&_lock); 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ciint dm_dirty_log_type_register(struct dm_dirty_log_type *type) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci int r = 0; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci spin_lock(&_lock); 12162306a36Sopenharmony_ci if (!__find_dirty_log_type(type->name)) 12262306a36Sopenharmony_ci list_add(&type->list, &_log_types); 12362306a36Sopenharmony_ci else 12462306a36Sopenharmony_ci r = -EEXIST; 12562306a36Sopenharmony_ci spin_unlock(&_lock); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci return r; 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ciEXPORT_SYMBOL(dm_dirty_log_type_register); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ciint dm_dirty_log_type_unregister(struct dm_dirty_log_type *type) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci spin_lock(&_lock); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (!__find_dirty_log_type(type->name)) { 13662306a36Sopenharmony_ci spin_unlock(&_lock); 13762306a36Sopenharmony_ci return -EINVAL; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci list_del(&type->list); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci spin_unlock(&_lock); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci return 0; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ciEXPORT_SYMBOL(dm_dirty_log_type_unregister); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistruct dm_dirty_log *dm_dirty_log_create(const char *type_name, 14962306a36Sopenharmony_ci struct dm_target *ti, 15062306a36Sopenharmony_ci int (*flush_callback_fn)(struct dm_target *ti), 15162306a36Sopenharmony_ci unsigned int argc, char **argv) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci struct dm_dirty_log_type *type; 15462306a36Sopenharmony_ci struct dm_dirty_log *log; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci log = kmalloc(sizeof(*log), GFP_KERNEL); 15762306a36Sopenharmony_ci if (!log) 15862306a36Sopenharmony_ci return NULL; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci type = get_type(type_name); 16162306a36Sopenharmony_ci if (!type) { 16262306a36Sopenharmony_ci kfree(log); 16362306a36Sopenharmony_ci return NULL; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci log->flush_callback_fn = flush_callback_fn; 16762306a36Sopenharmony_ci log->type = type; 16862306a36Sopenharmony_ci if (type->ctr(log, ti, argc, argv)) { 16962306a36Sopenharmony_ci kfree(log); 17062306a36Sopenharmony_ci put_type(type); 17162306a36Sopenharmony_ci return NULL; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci return log; 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ciEXPORT_SYMBOL(dm_dirty_log_create); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_civoid dm_dirty_log_destroy(struct dm_dirty_log *log) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci log->type->dtr(log); 18162306a36Sopenharmony_ci put_type(log->type); 18262306a36Sopenharmony_ci kfree(log); 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ciEXPORT_SYMBOL(dm_dirty_log_destroy); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci/* 18762306a36Sopenharmony_ci *--------------------------------------------------------------- 18862306a36Sopenharmony_ci * Persistent and core logs share a lot of their implementation. 18962306a36Sopenharmony_ci * FIXME: need a reload method to be called from a resume 19062306a36Sopenharmony_ci *--------------------------------------------------------------- 19162306a36Sopenharmony_ci */ 19262306a36Sopenharmony_ci/* 19362306a36Sopenharmony_ci * Magic for persistent mirrors: "MiRr" 19462306a36Sopenharmony_ci */ 19562306a36Sopenharmony_ci#define MIRROR_MAGIC 0x4D695272 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci/* 19862306a36Sopenharmony_ci * The on-disk version of the metadata. 19962306a36Sopenharmony_ci */ 20062306a36Sopenharmony_ci#define MIRROR_DISK_VERSION 2 20162306a36Sopenharmony_ci#define LOG_OFFSET 2 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistruct log_header_disk { 20462306a36Sopenharmony_ci __le32 magic; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci /* 20762306a36Sopenharmony_ci * Simple, incrementing version. no backward 20862306a36Sopenharmony_ci * compatibility. 20962306a36Sopenharmony_ci */ 21062306a36Sopenharmony_ci __le32 version; 21162306a36Sopenharmony_ci __le64 nr_regions; 21262306a36Sopenharmony_ci} __packed; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistruct log_header_core { 21562306a36Sopenharmony_ci uint32_t magic; 21662306a36Sopenharmony_ci uint32_t version; 21762306a36Sopenharmony_ci uint64_t nr_regions; 21862306a36Sopenharmony_ci}; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistruct log_c { 22162306a36Sopenharmony_ci struct dm_target *ti; 22262306a36Sopenharmony_ci int touched_dirtied; 22362306a36Sopenharmony_ci int touched_cleaned; 22462306a36Sopenharmony_ci int flush_failed; 22562306a36Sopenharmony_ci uint32_t region_size; 22662306a36Sopenharmony_ci unsigned int region_count; 22762306a36Sopenharmony_ci region_t sync_count; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci unsigned int bitset_uint32_count; 23062306a36Sopenharmony_ci uint32_t *clean_bits; 23162306a36Sopenharmony_ci uint32_t *sync_bits; 23262306a36Sopenharmony_ci uint32_t *recovering_bits; /* FIXME: this seems excessive */ 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci int sync_search; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci /* Resync flag */ 23762306a36Sopenharmony_ci enum sync { 23862306a36Sopenharmony_ci DEFAULTSYNC, /* Synchronize if necessary */ 23962306a36Sopenharmony_ci NOSYNC, /* Devices known to be already in sync */ 24062306a36Sopenharmony_ci FORCESYNC, /* Force a sync to happen */ 24162306a36Sopenharmony_ci } sync; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci struct dm_io_request io_req; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci /* 24662306a36Sopenharmony_ci * Disk log fields 24762306a36Sopenharmony_ci */ 24862306a36Sopenharmony_ci int log_dev_failed; 24962306a36Sopenharmony_ci int log_dev_flush_failed; 25062306a36Sopenharmony_ci struct dm_dev *log_dev; 25162306a36Sopenharmony_ci struct log_header_core header; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci struct dm_io_region header_location; 25462306a36Sopenharmony_ci struct log_header_disk *disk_header; 25562306a36Sopenharmony_ci}; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci/* 25862306a36Sopenharmony_ci * The touched member needs to be updated every time we access 25962306a36Sopenharmony_ci * one of the bitsets. 26062306a36Sopenharmony_ci */ 26162306a36Sopenharmony_cistatic inline int log_test_bit(uint32_t *bs, unsigned int bit) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci return test_bit_le(bit, bs) ? 1 : 0; 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_cistatic inline void log_set_bit(struct log_c *l, 26762306a36Sopenharmony_ci uint32_t *bs, unsigned int bit) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci __set_bit_le(bit, bs); 27062306a36Sopenharmony_ci l->touched_cleaned = 1; 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic inline void log_clear_bit(struct log_c *l, 27462306a36Sopenharmony_ci uint32_t *bs, unsigned int bit) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci __clear_bit_le(bit, bs); 27762306a36Sopenharmony_ci l->touched_dirtied = 1; 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci/* 28162306a36Sopenharmony_ci *--------------------------------------------------------------- 28262306a36Sopenharmony_ci * Header IO 28362306a36Sopenharmony_ci *-------------------------------------------------------------- 28462306a36Sopenharmony_ci */ 28562306a36Sopenharmony_cistatic void header_to_disk(struct log_header_core *core, struct log_header_disk *disk) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci disk->magic = cpu_to_le32(core->magic); 28862306a36Sopenharmony_ci disk->version = cpu_to_le32(core->version); 28962306a36Sopenharmony_ci disk->nr_regions = cpu_to_le64(core->nr_regions); 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cistatic void header_from_disk(struct log_header_core *core, struct log_header_disk *disk) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci core->magic = le32_to_cpu(disk->magic); 29562306a36Sopenharmony_ci core->version = le32_to_cpu(disk->version); 29662306a36Sopenharmony_ci core->nr_regions = le64_to_cpu(disk->nr_regions); 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic int rw_header(struct log_c *lc, enum req_op op) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci lc->io_req.bi_opf = op; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci return dm_io(&lc->io_req, 1, &lc->header_location, NULL, IOPRIO_DEFAULT); 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cistatic int flush_header(struct log_c *lc) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci struct dm_io_region null_location = { 30962306a36Sopenharmony_ci .bdev = lc->header_location.bdev, 31062306a36Sopenharmony_ci .sector = 0, 31162306a36Sopenharmony_ci .count = 0, 31262306a36Sopenharmony_ci }; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci lc->io_req.bi_opf = REQ_OP_WRITE | REQ_PREFLUSH; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci return dm_io(&lc->io_req, 1, &null_location, NULL, IOPRIO_DEFAULT); 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_cistatic int read_header(struct log_c *log) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci int r; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci r = rw_header(log, REQ_OP_READ); 32462306a36Sopenharmony_ci if (r) 32562306a36Sopenharmony_ci return r; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci header_from_disk(&log->header, log->disk_header); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci /* New log required? */ 33062306a36Sopenharmony_ci if (log->sync != DEFAULTSYNC || log->header.magic != MIRROR_MAGIC) { 33162306a36Sopenharmony_ci log->header.magic = MIRROR_MAGIC; 33262306a36Sopenharmony_ci log->header.version = MIRROR_DISK_VERSION; 33362306a36Sopenharmony_ci log->header.nr_regions = 0; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci#ifdef __LITTLE_ENDIAN 33762306a36Sopenharmony_ci if (log->header.version == 1) 33862306a36Sopenharmony_ci log->header.version = 2; 33962306a36Sopenharmony_ci#endif 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if (log->header.version != MIRROR_DISK_VERSION) { 34262306a36Sopenharmony_ci DMWARN("incompatible disk log version"); 34362306a36Sopenharmony_ci return -EINVAL; 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci return 0; 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistatic int _check_region_size(struct dm_target *ti, uint32_t region_size) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci if (region_size < 2 || region_size > ti->len) 35262306a36Sopenharmony_ci return 0; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci if (!is_power_of_2(region_size)) 35562306a36Sopenharmony_ci return 0; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci return 1; 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci/* 36162306a36Sopenharmony_ci *-------------------------------------------------------------- 36262306a36Sopenharmony_ci * core log constructor/destructor 36362306a36Sopenharmony_ci * 36462306a36Sopenharmony_ci * argv contains region_size followed optionally by [no]sync 36562306a36Sopenharmony_ci *-------------------------------------------------------------- 36662306a36Sopenharmony_ci */ 36762306a36Sopenharmony_ci#define BYTE_SHIFT 3 36862306a36Sopenharmony_cistatic int create_log_context(struct dm_dirty_log *log, struct dm_target *ti, 36962306a36Sopenharmony_ci unsigned int argc, char **argv, 37062306a36Sopenharmony_ci struct dm_dev *dev) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci enum sync sync = DEFAULTSYNC; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci struct log_c *lc; 37562306a36Sopenharmony_ci uint32_t region_size; 37662306a36Sopenharmony_ci unsigned int region_count; 37762306a36Sopenharmony_ci size_t bitset_size, buf_size; 37862306a36Sopenharmony_ci int r; 37962306a36Sopenharmony_ci char dummy; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci if (argc < 1 || argc > 2) { 38262306a36Sopenharmony_ci DMWARN("wrong number of arguments to dirty region log"); 38362306a36Sopenharmony_ci return -EINVAL; 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci if (argc > 1) { 38762306a36Sopenharmony_ci if (!strcmp(argv[1], "sync")) 38862306a36Sopenharmony_ci sync = FORCESYNC; 38962306a36Sopenharmony_ci else if (!strcmp(argv[1], "nosync")) 39062306a36Sopenharmony_ci sync = NOSYNC; 39162306a36Sopenharmony_ci else { 39262306a36Sopenharmony_ci DMWARN("unrecognised sync argument to dirty region log: %s", argv[1]); 39362306a36Sopenharmony_ci return -EINVAL; 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (sscanf(argv[0], "%u%c", ®ion_size, &dummy) != 1 || 39862306a36Sopenharmony_ci !_check_region_size(ti, region_size)) { 39962306a36Sopenharmony_ci DMWARN("invalid region size %s", argv[0]); 40062306a36Sopenharmony_ci return -EINVAL; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci region_count = dm_sector_div_up(ti->len, region_size); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci lc = kmalloc(sizeof(*lc), GFP_KERNEL); 40662306a36Sopenharmony_ci if (!lc) { 40762306a36Sopenharmony_ci DMWARN("couldn't allocate core log"); 40862306a36Sopenharmony_ci return -ENOMEM; 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci lc->ti = ti; 41262306a36Sopenharmony_ci lc->touched_dirtied = 0; 41362306a36Sopenharmony_ci lc->touched_cleaned = 0; 41462306a36Sopenharmony_ci lc->flush_failed = 0; 41562306a36Sopenharmony_ci lc->region_size = region_size; 41662306a36Sopenharmony_ci lc->region_count = region_count; 41762306a36Sopenharmony_ci lc->sync = sync; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci /* 42062306a36Sopenharmony_ci * Work out how many "unsigned long"s we need to hold the bitset. 42162306a36Sopenharmony_ci */ 42262306a36Sopenharmony_ci bitset_size = dm_round_up(region_count, BITS_PER_LONG); 42362306a36Sopenharmony_ci bitset_size >>= BYTE_SHIFT; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci lc->bitset_uint32_count = bitset_size / sizeof(*lc->clean_bits); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci /* 42862306a36Sopenharmony_ci * Disk log? 42962306a36Sopenharmony_ci */ 43062306a36Sopenharmony_ci if (!dev) { 43162306a36Sopenharmony_ci lc->clean_bits = vmalloc(bitset_size); 43262306a36Sopenharmony_ci if (!lc->clean_bits) { 43362306a36Sopenharmony_ci DMWARN("couldn't allocate clean bitset"); 43462306a36Sopenharmony_ci kfree(lc); 43562306a36Sopenharmony_ci return -ENOMEM; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci lc->disk_header = NULL; 43862306a36Sopenharmony_ci } else { 43962306a36Sopenharmony_ci lc->log_dev = dev; 44062306a36Sopenharmony_ci lc->log_dev_failed = 0; 44162306a36Sopenharmony_ci lc->log_dev_flush_failed = 0; 44262306a36Sopenharmony_ci lc->header_location.bdev = lc->log_dev->bdev; 44362306a36Sopenharmony_ci lc->header_location.sector = 0; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci /* 44662306a36Sopenharmony_ci * Buffer holds both header and bitset. 44762306a36Sopenharmony_ci */ 44862306a36Sopenharmony_ci buf_size = 44962306a36Sopenharmony_ci dm_round_up((LOG_OFFSET << SECTOR_SHIFT) + bitset_size, 45062306a36Sopenharmony_ci bdev_logical_block_size(lc->header_location.bdev)); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci if (buf_size > bdev_nr_bytes(dev->bdev)) { 45362306a36Sopenharmony_ci DMWARN("log device %s too small: need %llu bytes", 45462306a36Sopenharmony_ci dev->name, (unsigned long long)buf_size); 45562306a36Sopenharmony_ci kfree(lc); 45662306a36Sopenharmony_ci return -EINVAL; 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci lc->header_location.count = buf_size >> SECTOR_SHIFT; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci lc->io_req.mem.type = DM_IO_VMA; 46262306a36Sopenharmony_ci lc->io_req.notify.fn = NULL; 46362306a36Sopenharmony_ci lc->io_req.client = dm_io_client_create(); 46462306a36Sopenharmony_ci if (IS_ERR(lc->io_req.client)) { 46562306a36Sopenharmony_ci r = PTR_ERR(lc->io_req.client); 46662306a36Sopenharmony_ci DMWARN("couldn't allocate disk io client"); 46762306a36Sopenharmony_ci kfree(lc); 46862306a36Sopenharmony_ci return r; 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci lc->disk_header = vmalloc(buf_size); 47262306a36Sopenharmony_ci if (!lc->disk_header) { 47362306a36Sopenharmony_ci DMWARN("couldn't allocate disk log buffer"); 47462306a36Sopenharmony_ci dm_io_client_destroy(lc->io_req.client); 47562306a36Sopenharmony_ci kfree(lc); 47662306a36Sopenharmony_ci return -ENOMEM; 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci lc->io_req.mem.ptr.vma = lc->disk_header; 48062306a36Sopenharmony_ci lc->clean_bits = (void *)lc->disk_header + 48162306a36Sopenharmony_ci (LOG_OFFSET << SECTOR_SHIFT); 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci memset(lc->clean_bits, -1, bitset_size); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci lc->sync_bits = vmalloc(bitset_size); 48762306a36Sopenharmony_ci if (!lc->sync_bits) { 48862306a36Sopenharmony_ci DMWARN("couldn't allocate sync bitset"); 48962306a36Sopenharmony_ci if (!dev) 49062306a36Sopenharmony_ci vfree(lc->clean_bits); 49162306a36Sopenharmony_ci else 49262306a36Sopenharmony_ci dm_io_client_destroy(lc->io_req.client); 49362306a36Sopenharmony_ci vfree(lc->disk_header); 49462306a36Sopenharmony_ci kfree(lc); 49562306a36Sopenharmony_ci return -ENOMEM; 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci memset(lc->sync_bits, (sync == NOSYNC) ? -1 : 0, bitset_size); 49862306a36Sopenharmony_ci lc->sync_count = (sync == NOSYNC) ? region_count : 0; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci lc->recovering_bits = vzalloc(bitset_size); 50162306a36Sopenharmony_ci if (!lc->recovering_bits) { 50262306a36Sopenharmony_ci DMWARN("couldn't allocate sync bitset"); 50362306a36Sopenharmony_ci vfree(lc->sync_bits); 50462306a36Sopenharmony_ci if (!dev) 50562306a36Sopenharmony_ci vfree(lc->clean_bits); 50662306a36Sopenharmony_ci else 50762306a36Sopenharmony_ci dm_io_client_destroy(lc->io_req.client); 50862306a36Sopenharmony_ci vfree(lc->disk_header); 50962306a36Sopenharmony_ci kfree(lc); 51062306a36Sopenharmony_ci return -ENOMEM; 51162306a36Sopenharmony_ci } 51262306a36Sopenharmony_ci lc->sync_search = 0; 51362306a36Sopenharmony_ci log->context = lc; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci return 0; 51662306a36Sopenharmony_ci} 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_cistatic int core_ctr(struct dm_dirty_log *log, struct dm_target *ti, 51962306a36Sopenharmony_ci unsigned int argc, char **argv) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci return create_log_context(log, ti, argc, argv, NULL); 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_cistatic void destroy_log_context(struct log_c *lc) 52562306a36Sopenharmony_ci{ 52662306a36Sopenharmony_ci vfree(lc->sync_bits); 52762306a36Sopenharmony_ci vfree(lc->recovering_bits); 52862306a36Sopenharmony_ci kfree(lc); 52962306a36Sopenharmony_ci} 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_cistatic void core_dtr(struct dm_dirty_log *log) 53262306a36Sopenharmony_ci{ 53362306a36Sopenharmony_ci struct log_c *lc = log->context; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci vfree(lc->clean_bits); 53662306a36Sopenharmony_ci destroy_log_context(lc); 53762306a36Sopenharmony_ci} 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci/* 54062306a36Sopenharmony_ci *--------------------------------------------------------------------- 54162306a36Sopenharmony_ci * disk log constructor/destructor 54262306a36Sopenharmony_ci * 54362306a36Sopenharmony_ci * argv contains log_device region_size followed optionally by [no]sync 54462306a36Sopenharmony_ci *--------------------------------------------------------------------- 54562306a36Sopenharmony_ci */ 54662306a36Sopenharmony_cistatic int disk_ctr(struct dm_dirty_log *log, struct dm_target *ti, 54762306a36Sopenharmony_ci unsigned int argc, char **argv) 54862306a36Sopenharmony_ci{ 54962306a36Sopenharmony_ci int r; 55062306a36Sopenharmony_ci struct dm_dev *dev; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci if (argc < 2 || argc > 3) { 55362306a36Sopenharmony_ci DMWARN("wrong number of arguments to disk dirty region log"); 55462306a36Sopenharmony_ci return -EINVAL; 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci r = dm_get_device(ti, argv[0], dm_table_get_mode(ti->table), &dev); 55862306a36Sopenharmony_ci if (r) 55962306a36Sopenharmony_ci return r; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci r = create_log_context(log, ti, argc - 1, argv + 1, dev); 56262306a36Sopenharmony_ci if (r) { 56362306a36Sopenharmony_ci dm_put_device(ti, dev); 56462306a36Sopenharmony_ci return r; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci return 0; 56862306a36Sopenharmony_ci} 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_cistatic void disk_dtr(struct dm_dirty_log *log) 57162306a36Sopenharmony_ci{ 57262306a36Sopenharmony_ci struct log_c *lc = log->context; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci dm_put_device(lc->ti, lc->log_dev); 57562306a36Sopenharmony_ci vfree(lc->disk_header); 57662306a36Sopenharmony_ci dm_io_client_destroy(lc->io_req.client); 57762306a36Sopenharmony_ci destroy_log_context(lc); 57862306a36Sopenharmony_ci} 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_cistatic void fail_log_device(struct log_c *lc) 58162306a36Sopenharmony_ci{ 58262306a36Sopenharmony_ci if (lc->log_dev_failed) 58362306a36Sopenharmony_ci return; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci lc->log_dev_failed = 1; 58662306a36Sopenharmony_ci dm_table_event(lc->ti->table); 58762306a36Sopenharmony_ci} 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_cistatic int disk_resume(struct dm_dirty_log *log) 59062306a36Sopenharmony_ci{ 59162306a36Sopenharmony_ci int r; 59262306a36Sopenharmony_ci unsigned int i; 59362306a36Sopenharmony_ci struct log_c *lc = log->context; 59462306a36Sopenharmony_ci size_t size = lc->bitset_uint32_count * sizeof(uint32_t); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci /* read the disk header */ 59762306a36Sopenharmony_ci r = read_header(lc); 59862306a36Sopenharmony_ci if (r) { 59962306a36Sopenharmony_ci DMWARN("%s: Failed to read header on dirty region log device", 60062306a36Sopenharmony_ci lc->log_dev->name); 60162306a36Sopenharmony_ci fail_log_device(lc); 60262306a36Sopenharmony_ci /* 60362306a36Sopenharmony_ci * If the log device cannot be read, we must assume 60462306a36Sopenharmony_ci * all regions are out-of-sync. If we simply return 60562306a36Sopenharmony_ci * here, the state will be uninitialized and could 60662306a36Sopenharmony_ci * lead us to return 'in-sync' status for regions 60762306a36Sopenharmony_ci * that are actually 'out-of-sync'. 60862306a36Sopenharmony_ci */ 60962306a36Sopenharmony_ci lc->header.nr_regions = 0; 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci /* set or clear any new bits -- device has grown */ 61362306a36Sopenharmony_ci if (lc->sync == NOSYNC) 61462306a36Sopenharmony_ci for (i = lc->header.nr_regions; i < lc->region_count; i++) 61562306a36Sopenharmony_ci /* FIXME: amazingly inefficient */ 61662306a36Sopenharmony_ci log_set_bit(lc, lc->clean_bits, i); 61762306a36Sopenharmony_ci else 61862306a36Sopenharmony_ci for (i = lc->header.nr_regions; i < lc->region_count; i++) 61962306a36Sopenharmony_ci /* FIXME: amazingly inefficient */ 62062306a36Sopenharmony_ci log_clear_bit(lc, lc->clean_bits, i); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci /* clear any old bits -- device has shrunk */ 62362306a36Sopenharmony_ci for (i = lc->region_count; i % BITS_PER_LONG; i++) 62462306a36Sopenharmony_ci log_clear_bit(lc, lc->clean_bits, i); 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci /* copy clean across to sync */ 62762306a36Sopenharmony_ci memcpy(lc->sync_bits, lc->clean_bits, size); 62862306a36Sopenharmony_ci lc->sync_count = memweight(lc->clean_bits, 62962306a36Sopenharmony_ci lc->bitset_uint32_count * sizeof(uint32_t)); 63062306a36Sopenharmony_ci lc->sync_search = 0; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci /* set the correct number of regions in the header */ 63362306a36Sopenharmony_ci lc->header.nr_regions = lc->region_count; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci header_to_disk(&lc->header, lc->disk_header); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci /* write the new header */ 63862306a36Sopenharmony_ci r = rw_header(lc, REQ_OP_WRITE); 63962306a36Sopenharmony_ci if (!r) { 64062306a36Sopenharmony_ci r = flush_header(lc); 64162306a36Sopenharmony_ci if (r) 64262306a36Sopenharmony_ci lc->log_dev_flush_failed = 1; 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci if (r) { 64562306a36Sopenharmony_ci DMWARN("%s: Failed to write header on dirty region log device", 64662306a36Sopenharmony_ci lc->log_dev->name); 64762306a36Sopenharmony_ci fail_log_device(lc); 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci return r; 65162306a36Sopenharmony_ci} 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_cistatic uint32_t core_get_region_size(struct dm_dirty_log *log) 65462306a36Sopenharmony_ci{ 65562306a36Sopenharmony_ci struct log_c *lc = log->context; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci return lc->region_size; 65862306a36Sopenharmony_ci} 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_cistatic int core_resume(struct dm_dirty_log *log) 66162306a36Sopenharmony_ci{ 66262306a36Sopenharmony_ci struct log_c *lc = log->context; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci lc->sync_search = 0; 66562306a36Sopenharmony_ci return 0; 66662306a36Sopenharmony_ci} 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_cistatic int core_is_clean(struct dm_dirty_log *log, region_t region) 66962306a36Sopenharmony_ci{ 67062306a36Sopenharmony_ci struct log_c *lc = log->context; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci return log_test_bit(lc->clean_bits, region); 67362306a36Sopenharmony_ci} 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_cistatic int core_in_sync(struct dm_dirty_log *log, region_t region, int block) 67662306a36Sopenharmony_ci{ 67762306a36Sopenharmony_ci struct log_c *lc = log->context; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci return log_test_bit(lc->sync_bits, region); 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_cistatic int core_flush(struct dm_dirty_log *log) 68362306a36Sopenharmony_ci{ 68462306a36Sopenharmony_ci /* no op */ 68562306a36Sopenharmony_ci return 0; 68662306a36Sopenharmony_ci} 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_cistatic int disk_flush(struct dm_dirty_log *log) 68962306a36Sopenharmony_ci{ 69062306a36Sopenharmony_ci int r, i; 69162306a36Sopenharmony_ci struct log_c *lc = log->context; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci /* only write if the log has changed */ 69462306a36Sopenharmony_ci if (!lc->touched_cleaned && !lc->touched_dirtied) 69562306a36Sopenharmony_ci return 0; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci if (lc->touched_cleaned && log->flush_callback_fn && 69862306a36Sopenharmony_ci log->flush_callback_fn(lc->ti)) { 69962306a36Sopenharmony_ci /* 70062306a36Sopenharmony_ci * At this point it is impossible to determine which 70162306a36Sopenharmony_ci * regions are clean and which are dirty (without 70262306a36Sopenharmony_ci * re-reading the log off disk). So mark all of them 70362306a36Sopenharmony_ci * dirty. 70462306a36Sopenharmony_ci */ 70562306a36Sopenharmony_ci lc->flush_failed = 1; 70662306a36Sopenharmony_ci for (i = 0; i < lc->region_count; i++) 70762306a36Sopenharmony_ci log_clear_bit(lc, lc->clean_bits, i); 70862306a36Sopenharmony_ci } 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci r = rw_header(lc, REQ_OP_WRITE); 71162306a36Sopenharmony_ci if (r) 71262306a36Sopenharmony_ci fail_log_device(lc); 71362306a36Sopenharmony_ci else { 71462306a36Sopenharmony_ci if (lc->touched_dirtied) { 71562306a36Sopenharmony_ci r = flush_header(lc); 71662306a36Sopenharmony_ci if (r) { 71762306a36Sopenharmony_ci lc->log_dev_flush_failed = 1; 71862306a36Sopenharmony_ci fail_log_device(lc); 71962306a36Sopenharmony_ci } else 72062306a36Sopenharmony_ci lc->touched_dirtied = 0; 72162306a36Sopenharmony_ci } 72262306a36Sopenharmony_ci lc->touched_cleaned = 0; 72362306a36Sopenharmony_ci } 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci return r; 72662306a36Sopenharmony_ci} 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_cistatic void core_mark_region(struct dm_dirty_log *log, region_t region) 72962306a36Sopenharmony_ci{ 73062306a36Sopenharmony_ci struct log_c *lc = log->context; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci log_clear_bit(lc, lc->clean_bits, region); 73362306a36Sopenharmony_ci} 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_cistatic void core_clear_region(struct dm_dirty_log *log, region_t region) 73662306a36Sopenharmony_ci{ 73762306a36Sopenharmony_ci struct log_c *lc = log->context; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci if (likely(!lc->flush_failed)) 74062306a36Sopenharmony_ci log_set_bit(lc, lc->clean_bits, region); 74162306a36Sopenharmony_ci} 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_cistatic int core_get_resync_work(struct dm_dirty_log *log, region_t *region) 74462306a36Sopenharmony_ci{ 74562306a36Sopenharmony_ci struct log_c *lc = log->context; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci if (lc->sync_search >= lc->region_count) 74862306a36Sopenharmony_ci return 0; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci do { 75162306a36Sopenharmony_ci *region = find_next_zero_bit_le(lc->sync_bits, 75262306a36Sopenharmony_ci lc->region_count, 75362306a36Sopenharmony_ci lc->sync_search); 75462306a36Sopenharmony_ci lc->sync_search = *region + 1; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci if (*region >= lc->region_count) 75762306a36Sopenharmony_ci return 0; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci } while (log_test_bit(lc->recovering_bits, *region)); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci log_set_bit(lc, lc->recovering_bits, *region); 76262306a36Sopenharmony_ci return 1; 76362306a36Sopenharmony_ci} 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_cistatic void core_set_region_sync(struct dm_dirty_log *log, region_t region, 76662306a36Sopenharmony_ci int in_sync) 76762306a36Sopenharmony_ci{ 76862306a36Sopenharmony_ci struct log_c *lc = log->context; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci log_clear_bit(lc, lc->recovering_bits, region); 77162306a36Sopenharmony_ci if (in_sync) { 77262306a36Sopenharmony_ci log_set_bit(lc, lc->sync_bits, region); 77362306a36Sopenharmony_ci lc->sync_count++; 77462306a36Sopenharmony_ci } else if (log_test_bit(lc->sync_bits, region)) { 77562306a36Sopenharmony_ci lc->sync_count--; 77662306a36Sopenharmony_ci log_clear_bit(lc, lc->sync_bits, region); 77762306a36Sopenharmony_ci } 77862306a36Sopenharmony_ci} 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_cistatic region_t core_get_sync_count(struct dm_dirty_log *log) 78162306a36Sopenharmony_ci{ 78262306a36Sopenharmony_ci struct log_c *lc = log->context; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci return lc->sync_count; 78562306a36Sopenharmony_ci} 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci#define DMEMIT_SYNC \ 78862306a36Sopenharmony_ci do { \ 78962306a36Sopenharmony_ci if (lc->sync != DEFAULTSYNC) \ 79062306a36Sopenharmony_ci DMEMIT("%ssync ", lc->sync == NOSYNC ? "no" : ""); \ 79162306a36Sopenharmony_ci } while (0) 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_cistatic int core_status(struct dm_dirty_log *log, status_type_t status, 79462306a36Sopenharmony_ci char *result, unsigned int maxlen) 79562306a36Sopenharmony_ci{ 79662306a36Sopenharmony_ci int sz = 0; 79762306a36Sopenharmony_ci struct log_c *lc = log->context; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci switch (status) { 80062306a36Sopenharmony_ci case STATUSTYPE_INFO: 80162306a36Sopenharmony_ci DMEMIT("1 %s", log->type->name); 80262306a36Sopenharmony_ci break; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci case STATUSTYPE_TABLE: 80562306a36Sopenharmony_ci DMEMIT("%s %u %u ", log->type->name, 80662306a36Sopenharmony_ci lc->sync == DEFAULTSYNC ? 1 : 2, lc->region_size); 80762306a36Sopenharmony_ci DMEMIT_SYNC; 80862306a36Sopenharmony_ci break; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci case STATUSTYPE_IMA: 81162306a36Sopenharmony_ci *result = '\0'; 81262306a36Sopenharmony_ci break; 81362306a36Sopenharmony_ci } 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci return sz; 81662306a36Sopenharmony_ci} 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_cistatic int disk_status(struct dm_dirty_log *log, status_type_t status, 81962306a36Sopenharmony_ci char *result, unsigned int maxlen) 82062306a36Sopenharmony_ci{ 82162306a36Sopenharmony_ci int sz = 0; 82262306a36Sopenharmony_ci struct log_c *lc = log->context; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci switch (status) { 82562306a36Sopenharmony_ci case STATUSTYPE_INFO: 82662306a36Sopenharmony_ci DMEMIT("3 %s %s %c", log->type->name, lc->log_dev->name, 82762306a36Sopenharmony_ci lc->log_dev_flush_failed ? 'F' : 82862306a36Sopenharmony_ci lc->log_dev_failed ? 'D' : 82962306a36Sopenharmony_ci 'A'); 83062306a36Sopenharmony_ci break; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci case STATUSTYPE_TABLE: 83362306a36Sopenharmony_ci DMEMIT("%s %u %s %u ", log->type->name, 83462306a36Sopenharmony_ci lc->sync == DEFAULTSYNC ? 2 : 3, lc->log_dev->name, 83562306a36Sopenharmony_ci lc->region_size); 83662306a36Sopenharmony_ci DMEMIT_SYNC; 83762306a36Sopenharmony_ci break; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci case STATUSTYPE_IMA: 84062306a36Sopenharmony_ci *result = '\0'; 84162306a36Sopenharmony_ci break; 84262306a36Sopenharmony_ci } 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci return sz; 84562306a36Sopenharmony_ci} 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_cistatic struct dm_dirty_log_type _core_type = { 84862306a36Sopenharmony_ci .name = "core", 84962306a36Sopenharmony_ci .module = THIS_MODULE, 85062306a36Sopenharmony_ci .ctr = core_ctr, 85162306a36Sopenharmony_ci .dtr = core_dtr, 85262306a36Sopenharmony_ci .resume = core_resume, 85362306a36Sopenharmony_ci .get_region_size = core_get_region_size, 85462306a36Sopenharmony_ci .is_clean = core_is_clean, 85562306a36Sopenharmony_ci .in_sync = core_in_sync, 85662306a36Sopenharmony_ci .flush = core_flush, 85762306a36Sopenharmony_ci .mark_region = core_mark_region, 85862306a36Sopenharmony_ci .clear_region = core_clear_region, 85962306a36Sopenharmony_ci .get_resync_work = core_get_resync_work, 86062306a36Sopenharmony_ci .set_region_sync = core_set_region_sync, 86162306a36Sopenharmony_ci .get_sync_count = core_get_sync_count, 86262306a36Sopenharmony_ci .status = core_status, 86362306a36Sopenharmony_ci}; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_cistatic struct dm_dirty_log_type _disk_type = { 86662306a36Sopenharmony_ci .name = "disk", 86762306a36Sopenharmony_ci .module = THIS_MODULE, 86862306a36Sopenharmony_ci .ctr = disk_ctr, 86962306a36Sopenharmony_ci .dtr = disk_dtr, 87062306a36Sopenharmony_ci .postsuspend = disk_flush, 87162306a36Sopenharmony_ci .resume = disk_resume, 87262306a36Sopenharmony_ci .get_region_size = core_get_region_size, 87362306a36Sopenharmony_ci .is_clean = core_is_clean, 87462306a36Sopenharmony_ci .in_sync = core_in_sync, 87562306a36Sopenharmony_ci .flush = disk_flush, 87662306a36Sopenharmony_ci .mark_region = core_mark_region, 87762306a36Sopenharmony_ci .clear_region = core_clear_region, 87862306a36Sopenharmony_ci .get_resync_work = core_get_resync_work, 87962306a36Sopenharmony_ci .set_region_sync = core_set_region_sync, 88062306a36Sopenharmony_ci .get_sync_count = core_get_sync_count, 88162306a36Sopenharmony_ci .status = disk_status, 88262306a36Sopenharmony_ci}; 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_cistatic int __init dm_dirty_log_init(void) 88562306a36Sopenharmony_ci{ 88662306a36Sopenharmony_ci int r; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci r = dm_dirty_log_type_register(&_core_type); 88962306a36Sopenharmony_ci if (r) 89062306a36Sopenharmony_ci DMWARN("couldn't register core log"); 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci r = dm_dirty_log_type_register(&_disk_type); 89362306a36Sopenharmony_ci if (r) { 89462306a36Sopenharmony_ci DMWARN("couldn't register disk type"); 89562306a36Sopenharmony_ci dm_dirty_log_type_unregister(&_core_type); 89662306a36Sopenharmony_ci } 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci return r; 89962306a36Sopenharmony_ci} 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_cistatic void __exit dm_dirty_log_exit(void) 90262306a36Sopenharmony_ci{ 90362306a36Sopenharmony_ci dm_dirty_log_type_unregister(&_disk_type); 90462306a36Sopenharmony_ci dm_dirty_log_type_unregister(&_core_type); 90562306a36Sopenharmony_ci} 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_cimodule_init(dm_dirty_log_init); 90862306a36Sopenharmony_cimodule_exit(dm_dirty_log_exit); 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ciMODULE_DESCRIPTION(DM_NAME " dirty region log"); 91162306a36Sopenharmony_ciMODULE_AUTHOR("Joe Thornber, Heinz Mauelshagen <dm-devel@redhat.com>"); 91262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 913