linux/drivers/mtd/mtdswap.c

// SPDX-License-Identifier: GPL-2.0-only
/*
 * Swap block device support for MTDs
 * Turns an MTD device into a swap device with block wear leveling
 *
 * Copyright © 2007,2011 Nokia Corporation. All rights reserved.
 *
 * Authors: Jarkko Lavinen <[email protected]>
 *
 * Based on Richard Purdie's earlier implementation in 2007. Background
 * support and lock-less operation written by Adrian Hunter.
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/blktrans.h>
#include <linux/rbtree.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/blkdev.h>
#include <linux/swap.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/device.h>
#include <linux/math64.h>

#define MTDSWAP_PREFIX

/*
 * The number of free eraseblocks when GC should stop
 */
#define CLEAN_BLOCK_THRESHOLD

/*
 * Number of free eraseblocks below which GC can also collect low frag
 * blocks.
 */
#define LOW_FRAG_GC_THRESHOLD

/*
 * Wear level cost amortization. We want to do wear leveling on the background
 * without disturbing gc too much. This is made by defining max GC frequency.
 * Frequency value 6 means 1/6 of the GC passes will pick an erase block based
 * on the biggest wear difference rather than the biggest dirtiness.
 *
 * The lower freq2 should be chosen so that it makes sure the maximum erase
 * difference will decrease even if a malicious application is deliberately
 * trying to make erase differences large.
 */
#define MAX_ERASE_DIFF
#define COLLECT_NONDIRTY_BASE
#define COLLECT_NONDIRTY_FREQ1
#define COLLECT_NONDIRTY_FREQ2

#define PAGE_UNDEF
#define BLOCK_UNDEF
#define BLOCK_ERROR
#define BLOCK_MAX

#define EBLOCK_BAD
#define EBLOCK_NOMAGIC
#define EBLOCK_BITFLIP
#define EBLOCK_FAILED
#define EBLOCK_READERR
#define EBLOCK_IDX_SHIFT

struct swap_eb {};

#define MTDSWAP_ECNT_MIN(rbroot)
#define MTDSWAP_ECNT_MAX(rbroot)

struct mtdswap_tree {};

enum {};

struct mtdswap_dev {};

struct mtdswap_oobdata {} __packed;

#define MTDSWAP_MAGIC_CLEAN
#define MTDSWAP_MAGIC_DIRTY
#define MTDSWAP_TYPE_CLEAN
#define MTDSWAP_TYPE_DIRTY
#define MTDSWAP_OOBSIZE

#define MTDSWAP_ERASE_RETRIES
#define MTDSWAP_IO_RETRIES

enum {};

/*
 * In the worst case mtdswap_writesect() has allocated the last clean
 * page from the current block and is then pre-empted by the GC
 * thread. The thread can consume a full erase block when moving a
 * block.
 */
#define MIN_SPARE_EBLOCKS
#define MIN_ERASE_BLOCKS

#define TREE_ROOT(d, name)
#define TREE_EMPTY(d, name)
#define TREE_NONEMPTY(d, name)
#define TREE_COUNT(d, name)

#define MTDSWAP_MBD_TO_MTDSWAP(dev)

static char partitions[128] =;
module_param_string();
MODULE_PARM_DESC();

static unsigned int spare_eblocks =;
module_param(spare_eblocks, uint, 0444);
MODULE_PARM_DESC();

static bool header; /* false */
module_param(header, bool, 0444);
MODULE_PARM_DESC();

static int mtdswap_gc(struct mtdswap_dev *d, unsigned int background);

static loff_t mtdswap_eb_offset(struct mtdswap_dev *d, struct swap_eb *eb)
{}

static void mtdswap_eb_detach(struct mtdswap_dev *d, struct swap_eb *eb)
{}

static void __mtdswap_rb_add(struct rb_root *root, struct swap_eb *eb)
{}

static void mtdswap_rb_add(struct mtdswap_dev *d, struct swap_eb *eb, int idx)
{}

static struct rb_node *mtdswap_rb_index(struct rb_root *root, unsigned int idx)
{}

static int mtdswap_handle_badblock(struct mtdswap_dev *d, struct swap_eb *eb)
{}

static int mtdswap_handle_write_error(struct mtdswap_dev *d, struct swap_eb *eb)
{}

static int mtdswap_read_oob(struct mtdswap_dev *d, loff_t from,
			struct mtd_oob_ops *ops)
{}

static int mtdswap_read_markers(struct mtdswap_dev *d, struct swap_eb *eb)
{}

static int mtdswap_write_marker(struct mtdswap_dev *d, struct swap_eb *eb,
				u16 marker)
{}

/*
 * Are there any erase blocks without MAGIC_CLEAN header, presumably
 * because power was cut off after erase but before header write? We
 * need to guestimate the erase count.
 */
static void mtdswap_check_counts(struct mtdswap_dev *d)
{}

static void mtdswap_scan_eblks(struct mtdswap_dev *d)
{}

/*
 * Place eblk into a tree corresponding to its number of active blocks
 * it contains.
 */
static void mtdswap_store_eb(struct mtdswap_dev *d, struct swap_eb *eb)
{}

static int mtdswap_erase_block(struct mtdswap_dev *d, struct swap_eb *eb)
{}

static int mtdswap_map_free_block(struct mtdswap_dev *d, unsigned int page,
				unsigned int *block)
{}

static unsigned int mtdswap_free_page_cnt(struct mtdswap_dev *d)
{}

static unsigned int mtdswap_enough_free_pages(struct mtdswap_dev *d)
{}

static int mtdswap_write_block(struct mtdswap_dev *d, char *buf,
			unsigned int page, unsigned int *bp, int gc_context)
{}

static int mtdswap_move_block(struct mtdswap_dev *d, unsigned int oldblock,
		unsigned int *newblock)
{}

static int mtdswap_gc_eblock(struct mtdswap_dev *d, struct swap_eb *eb)
{}

static int __mtdswap_choose_gc_tree(struct mtdswap_dev *d)
{}

static int mtdswap_wlfreq(unsigned int maxdiff)
{}

static int mtdswap_choose_wl_tree(struct mtdswap_dev *d)
{}

static int mtdswap_choose_gc_tree(struct mtdswap_dev *d,
				unsigned int background)
{}

static struct swap_eb *mtdswap_pick_gc_eblk(struct mtdswap_dev *d,
					unsigned int background)
{}

static unsigned int mtdswap_test_patt(unsigned int i)
{}

static unsigned int mtdswap_eblk_passes(struct mtdswap_dev *d,
					struct swap_eb *eb)
{}

static int mtdswap_gc(struct mtdswap_dev *d, unsigned int background)
{}

static void mtdswap_background(struct mtd_blktrans_dev *dev)
{}

static void mtdswap_cleanup(struct mtdswap_dev *d)
{}

static int mtdswap_flush(struct mtd_blktrans_dev *dev)
{}

static unsigned int mtdswap_badblocks(struct mtd_info *mtd, uint64_t size)
{}

static int mtdswap_writesect(struct mtd_blktrans_dev *dev,
			unsigned long page, char *buf)
{}

/* Provide a dummy swap header for the kernel */
static int mtdswap_auto_header(struct mtdswap_dev *d, char *buf)
{}

static int mtdswap_readsect(struct mtd_blktrans_dev *dev,
			unsigned long page, char *buf)
{}

static int mtdswap_discard(struct mtd_blktrans_dev *dev, unsigned long first,
			unsigned nr_pages)
{}

static int mtdswap_show(struct seq_file *s, void *data)
{}
DEFINE_SHOW_ATTRIBUTE();

static int mtdswap_add_debugfs(struct mtdswap_dev *d)
{}

static int mtdswap_init(struct mtdswap_dev *d, unsigned int eblocks,
			unsigned int spare_cnt)
{}

static void mtdswap_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
{}

static void mtdswap_remove_dev(struct mtd_blktrans_dev *dev)
{}

static struct mtd_blktrans_ops mtdswap_ops =;

module_mtd_blktrans();

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