freebsd-nq/sys/geom/union/g_union.h
Kirk McKusick c7996ddf80 Create a new GEOM utility, gunion(8).
The gunion(8) utility is used to track changes to a read-only disk on
a writable disk. Logically, a writable disk is placed over a read-only
disk. Write requests are intercepted and stored on the writable
disk. Read requests are first checked to see if they have been
written on the top (writable disk) and if found are returned. If
they have not been written on the top disk, then they are read from
the lower disk.

The gunion(8) utility can be especially useful if you have a large
disk with a corrupted filesystem that you are unsure of how to
repair. You can use gunion(8) to place another disk over the corrupted
disk and then attempt to repair the filesystem. If the repair fails,
you can revert all the changes in the upper disk and be back to the
unchanged state of the lower disk thus allowing you to try another
approach to repairing it. If the repair is successful you can commit
all the writes recorded on the top disk to the lower disk.

Another use of the gunion(8) utility is to try out upgrades to your
system. Place the upper disk over the disk holding your filesystem
that is to be upgraded and then run the upgrade on it. If it works,
commit it; if it fails, revert the upgrade.

Further details can be found in the gunion(8) manual page.

Reviewed by: Chuck Silvers, kib (earlier version)
tested by:   Peter Holm
Differential Revision: https://reviews.freebsd.org/D32697
2022-02-28 16:36:08 -08:00

145 lines
5.6 KiB
C

/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2022 Marshall Kirk McKusick <mckusick@mckusick.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef _G_UNION_H_
#define _G_UNION_H_
#define G_UNION_CLASS_NAME "UNION"
#define G_UNION_VERSION 1
#define G_UNION_SUFFIX ".union"
/*
* Special flag to instruct gunion to passthrough the underlying provider's
* physical path
*/
#define G_UNION_PHYSPATH_PASSTHROUGH "\255"
#ifdef _KERNEL
#define G_UNION_DEBUG(lvl, ...) \
_GEOM_DEBUG("GEOM_UNION", g_union_debug, (lvl), NULL, __VA_ARGS__)
#define G_UNION_LOGREQLVL(lvl, bp, ...) \
_GEOM_DEBUG("GEOM_UNION", g_union_debug, (lvl), (bp), __VA_ARGS__)
#define G_UNION_LOGREQ(bp, ...) G_UNION_LOGREQLVL(3, (bp), __VA_ARGS__)
TAILQ_HEAD(wiplist, g_union_wip);
/*
* State maintained by each instance of a UNION GEOM.
*/
struct g_union_softc {
struct rwlock sc_rwlock; /* writemap lock */
uint64_t **sc_writemap_root; /* root of write map */
uint64_t *sc_leafused; /* 1 => leaf has allocation */
uint64_t sc_map_size; /* size of write map */
long sc_root_size; /* entries in root node */
long sc_leaf_size; /* entries in leaf node */
long sc_bits_per_leaf; /* bits per leaf node entry */
long sc_writemap_memory; /* memory used by writemap */
off_t sc_offset; /* starting offset in lower */
off_t sc_size; /* size of union geom */
off_t sc_sectorsize; /* sector size of geom */
struct g_consumer *sc_uppercp; /* upper-level provider */
struct g_consumer *sc_lowercp; /* lower-level provider */
struct wiplist sc_wiplist; /* I/O work-in-progress list */
long sc_flags; /* see flags below */
long sc_reads; /* number of reads done */
long sc_wrotebytes; /* number of bytes written */
long sc_writes; /* number of writes done */
long sc_readbytes; /* number of bytes read */
long sc_deletes; /* number of deletes done */
long sc_getattrs; /* number of getattrs done */
long sc_flushes; /* number of flushes done */
long sc_cmd0s; /* number of cmd0's done */
long sc_cmd1s; /* number of cmd1's done */
long sc_cmd2s; /* number of cmd2's done */
long sc_speedups; /* number of speedups done */
long sc_readcurrentread; /* reads current with read */
long sc_readblockwrite; /* writes blocked by read */
long sc_writeblockread; /* reads blocked by write */
long sc_writeblockwrite; /* writes blocked by write */
};
/*
* Structure to track work-in-progress I/O operations.
*
* Used to prevent overlapping I/O operations from running concurrently.
* Created for each I/O operation.
*
* In usual case of no overlap it is linked to sc_wiplist and started.
* If found to overlap an I/O on sc_wiplist, it is not started and is
* linked to wip_waiting list of the I/O that it overlaps. When an I/O
* completes, it restarts all the I/O operations on its wip_waiting list.
*/
struct g_union_wip {
struct wiplist wip_waiting; /* list of I/Os waiting on me */
TAILQ_ENTRY(g_union_wip) wip_next; /* pending or active I/O list */
struct bio *wip_bp; /* bio for this I/O */
struct g_union_softc *wip_sc; /* g_union's softc */
off_t wip_start; /* starting offset of I/O */
off_t wip_end; /* ending offset of I/O */
long wip_numios; /* BIO_READs in progress */
long wip_error; /* merged I/O errors */
};
/*
* UNION flags
*/
#define DOING_COMMIT 0x00000001 /* a commit command is in progress */
#define DOING_COMMIT_BITNUM 0 /* a commit command is in progress */
#define BITS_PER_ENTRY (sizeof(uint64_t) * NBBY)
#define G_RLOCK(sc) rw_rlock(&(sc)->sc_rwlock)
#define G_RUNLOCK(sc) rw_runlock(&(sc)->sc_rwlock)
#define G_WLOCK(sc) rw_wlock(&(sc)->sc_rwlock)
#define G_WUNLOCK(sc) rw_wunlock(&(sc)->sc_rwlock)
#define G_WLOCKOWNED(sc) rw_assert(&(sc)->sc_rwlock, RA_WLOCKED)
/*
* The writelock is held while a commit operation is in progress.
* While held union device may not be used or in use.
* Returns == 0 if lock was successfully obtained.
*/
static inline int
g_union_get_writelock(struct g_union_softc *sc)
{
return (atomic_testandset_long(&sc->sc_flags, DOING_COMMIT_BITNUM));
}
static inline void
g_union_rel_writelock(struct g_union_softc *sc)
{
long ret __diagused;
ret = atomic_testandclear_long(&sc->sc_flags, DOING_COMMIT_BITNUM);
KASSERT(ret != 0, ("UNION GEOM releasing unheld lock"));
}
#endif /* _KERNEL */
#endif /* _G_UNION_H_ */