linux/drivers/md/dm-log-writes.c

// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2014 Facebook. All rights reserved.
 *
 * This file is released under the GPL.
 */

#include <linux/device-mapper.h>

#include <linux/module.h>
#include <linux/init.h>
#include <linux/blkdev.h>
#include <linux/bio.h>
#include <linux/dax.h>
#include <linux/slab.h>
#include <linux/kthread.h>
#include <linux/freezer.h>
#include <linux/uio.h>

#define DM_MSG_PREFIX

/*
 * This target will sequentially log all writes to the target device onto the
 * log device.  This is helpful for replaying writes to check for fs consistency
 * at all times.  This target provides a mechanism to mark specific events to
 * check data at a later time.  So for example you would:
 *
 * write data
 * fsync
 * dmsetup message /dev/whatever mark mymark
 * unmount /mnt/test
 *
 * Then replay the log up to mymark and check the contents of the replay to
 * verify it matches what was written.
 *
 * We log writes only after they have been flushed, this makes the log describe
 * close to the order in which the data hits the actual disk, not its cache.  So
 * for example the following sequence (W means write, C means complete)
 *
 * Wa,Wb,Wc,Cc,Ca,FLUSH,FUAd,Cb,CFLUSH,CFUAd
 *
 * Would result in the log looking like this:
 *
 * c,a,b,flush,fuad,<other writes>,<next flush>
 *
 * This is meant to help expose problems where file systems do not properly wait
 * on data being written before invoking a FLUSH.  FUA bypasses cache so once it
 * completes it is added to the log as it should be on disk.
 *
 * We treat DISCARDs as if they don't bypass cache so that they are logged in
 * order of completion along with the normal writes.  If we didn't do it this
 * way we would process all the discards first and then write all the data, when
 * in fact we want to do the data and the discard in the order that they
 * completed.
 */
#define LOG_FLUSH_FLAG
#define LOG_FUA_FLAG
#define LOG_DISCARD_FLAG
#define LOG_MARK_FLAG
#define LOG_METADATA_FLAG

#define WRITE_LOG_VERSION
#define WRITE_LOG_MAGIC
#define WRITE_LOG_SUPER_SECTOR

/*
 * The disk format for this is braindead simple.
 *
 * At byte 0 we have our super, followed by the following sequence for
 * nr_entries:
 *
 * [   1 sector    ][  entry->nr_sectors ]
 * [log_write_entry][    data written    ]
 *
 * The log_write_entry takes up a full sector so we can have arbitrary length
 * marks and it leaves us room for extra content in the future.
 */

/*
 * Basic info about the log for userspace.
 */
struct log_write_super {};

/*
 * sector - the sector we wrote.
 * nr_sectors - the number of sectors we wrote.
 * flags - flags for this log entry.
 * data_len - the size of the data in this log entry, this is for private log
 * entry stuff, the MARK data provided by userspace for example.
 */
struct log_write_entry {};

struct log_writes_c {};

struct pending_block {};

struct per_bio_data {};

static inline sector_t bio_to_dev_sectors(struct log_writes_c *lc,
					  sector_t sectors)
{}

static inline sector_t dev_to_bio_sectors(struct log_writes_c *lc,
					  sector_t sectors)
{}

static void put_pending_block(struct log_writes_c *lc)
{}

static void put_io_block(struct log_writes_c *lc)
{}

static void log_end_io(struct bio *bio)
{}

static void log_end_super(struct bio *bio)
{}

/*
 * Meant to be called if there is an error, it will free all the pages
 * associated with the block.
 */
static void free_pending_block(struct log_writes_c *lc,
			       struct pending_block *block)
{}

static int write_metadata(struct log_writes_c *lc, void *entry,
			  size_t entrylen, void *data, size_t datalen,
			  sector_t sector)
{}

static int write_inline_data(struct log_writes_c *lc, void *entry,
			     size_t entrylen, void *data, size_t datalen,
			     sector_t sector)
{}

static int log_one_block(struct log_writes_c *lc,
			 struct pending_block *block, sector_t sector)
{}

static int log_super(struct log_writes_c *lc)
{}

static inline sector_t logdev_last_sector(struct log_writes_c *lc)
{}

static int log_writes_kthread(void *arg)
{}

/*
 * Construct a log-writes mapping:
 * log-writes <dev_path> <log_dev_path>
 */
static int log_writes_ctr(struct dm_target *ti, unsigned int argc, char **argv)
{}

static int log_mark(struct log_writes_c *lc, char *data)
{}

static void log_writes_dtr(struct dm_target *ti)
{}

static void normal_map_bio(struct dm_target *ti, struct bio *bio)
{}

static int log_writes_map(struct dm_target *ti, struct bio *bio)
{}

static int normal_end_io(struct dm_target *ti, struct bio *bio,
		blk_status_t *error)
{}

/*
 * INFO format: <logged entries> <highest allocated sector>
 */
static void log_writes_status(struct dm_target *ti, status_type_t type,
			      unsigned int status_flags, char *result,
			      unsigned int maxlen)
{}

static int log_writes_prepare_ioctl(struct dm_target *ti,
				    struct block_device **bdev)
{}

static int log_writes_iterate_devices(struct dm_target *ti,
				      iterate_devices_callout_fn fn,
				      void *data)
{}

/*
 * Messages supported:
 *   mark <mark data> - specify the marked data.
 */
static int log_writes_message(struct dm_target *ti, unsigned int argc, char **argv,
			      char *result, unsigned int maxlen)
{}

static void log_writes_io_hints(struct dm_target *ti, struct queue_limits *limits)
{}

#if IS_ENABLED(CONFIG_FS_DAX)
static struct dax_device *log_writes_dax_pgoff(struct dm_target *ti,
		pgoff_t *pgoff)
{}

static long log_writes_dax_direct_access(struct dm_target *ti, pgoff_t pgoff,
		long nr_pages, enum dax_access_mode mode, void **kaddr,
		pfn_t *pfn)
{}

static int log_writes_dax_zero_page_range(struct dm_target *ti, pgoff_t pgoff,
					  size_t nr_pages)
{}

static size_t log_writes_dax_recovery_write(struct dm_target *ti,
		pgoff_t pgoff, void *addr, size_t bytes, struct iov_iter *i)
{}

#else
#define log_writes_dax_direct_access
#define log_writes_dax_zero_page_range
#define log_writes_dax_recovery_write
#endif

static struct target_type log_writes_target =;
module_dm(log_writes);

MODULE_DESCRIPTION();
MODULE_AUTHOR();
MODULE_LICENSE();