blob: aba5b0b1b05061a0363b621a247effcdc3b68ea6 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2015 Red Hat, Inc.
* All Rights Reserved.
*/
#include "libxfs.h"
#include "command.h"
#include "init.h"
#include "output.h"
#include "libxlog.h"
#include "logformat.h"
#define MAX_LSUNIT 256 * 1024 /* max log buf. size */
static int
logformat_f(int argc, char **argv)
{
xfs_daddr_t head_blk;
xfs_daddr_t tail_blk;
int logversion;
int lsunit = -1;
int cycle = -1;
int error;
int c;
logversion = xfs_has_logv2(mp) ? 2 : 1;
while ((c = getopt(argc, argv, "c:s:")) != EOF) {
switch (c) {
case 'c':
cycle = strtol(optarg, NULL, 0);
if (cycle == 0) {
dbprintf("invalid cycle\n");
return -1;
}
break;
case 's':
lsunit = strtol(optarg, NULL, 0);
/*
* The log stripe unit must be block aligned and no
* larger than 256k.
*/
if (lsunit > 1 &&
(lsunit % mp->m_sb.sb_blocksize ||
(logversion == 2 && lsunit > MAX_LSUNIT))) {
dbprintf("invalid log stripe unit\n");
return -1;
}
break;
default:
dbprintf("invalid option\n");
return -1;
}
}
/*
* Check whether the log is dirty. This also determines the current log
* cycle if we have to use it by default below.
*/
memset(mp->m_log, 0, sizeof(struct xlog));
mp->m_log->l_mp = mp;
mp->m_log->l_dev = mp->m_logdev_targp;
mp->m_log->l_logBBsize = XFS_FSB_TO_BB(mp, mp->m_sb.sb_logblocks);
mp->m_log->l_logBBstart = XFS_FSB_TO_DADDR(mp, mp->m_sb.sb_logstart);
mp->m_log->l_sectBBsize = BBSIZE;
if (xfs_has_sector(mp))
mp->m_log->l_sectBBsize <<= (mp->m_sb.sb_logsectlog - BBSHIFT);
mp->m_log->l_sectBBsize = BTOBB(mp->m_log->l_sectBBsize);
error = xlog_find_tail(mp->m_log, &head_blk, &tail_blk);
if (error) {
dbprintf("could not find log head/tail\n");
return -1;
}
if (head_blk != tail_blk) {
dbprintf(_(
"The log is dirty. Please mount to replay the log.\n"));
return -1;
}
/*
* Use the current cycle and/or log stripe unit if either is not
* provided by the user.
*/
if (cycle < 0)
cycle = mp->m_log->l_curr_cycle;
if (lsunit < 0)
lsunit = mp->m_sb.sb_logsunit;
dbprintf("Formatting the log to cycle %d, stripe unit %d bytes.\n",
cycle, lsunit);
error = -libxfs_log_clear(mp->m_logdev_targp, NULL,
mp->m_log->l_logBBstart,
mp->m_log->l_logBBsize,
&mp->m_sb.sb_uuid, logversion, lsunit,
XLOG_FMT, cycle, false);
if (error) {
dbprintf("error formatting log - %d\n", error);
return error;
}
return 0;
}
static void
logformat_help(void)
{
dbprintf(_(
"\n"
" The 'logformat' command reformats (clears) the log to the specified log\n"
" cycle and log stripe unit. If the log cycle is not specified, the log is\n"
" reformatted to the current cycle. If the log stripe unit is not specified,\n"
" the stripe unit from the filesystem superblock is used.\n"
"\n"
));
}
static const struct cmdinfo logformat_cmd = {
.name = "logformat",
.altname = NULL,
.cfunc = logformat_f,
.argmin = 0,
.argmax = 4,
.canpush = 0,
.args = N_("[-c cycle] [-s sunit]"),
.oneline = N_("reformat the log"),
.help = logformat_help,
};
void
logformat_init(void)
{
if (!expert_mode)
return;
add_command(&logformat_cmd);
}
static void
print_logres(
int i,
struct xfs_trans_res *res)
{
dbprintf(_("type %d logres %u logcount %d flags 0x%x\n"),
i, res->tr_logres, res->tr_logcount, res->tr_logflags);
}
static int
logres_f(
int argc,
char **argv)
{
struct xfs_trans_res resv;
struct xfs_trans_res *res;
struct xfs_trans_res *end_res;
int i;
res = (struct xfs_trans_res *)M_RES(mp);
end_res = (struct xfs_trans_res *)(M_RES(mp) + 1);
for (i = 0; res < end_res; i++, res++)
print_logres(i, res);
libxfs_log_get_max_trans_res(mp, &resv);
dbprintf(_("minlogsize logres %u logcount %d\n"),
resv.tr_logres, resv.tr_logcount);
return 0;
}
static void
logres_help(void)
{
dbprintf(_(
"\n"
" The 'logres' command prints information about all log reservation types.\n"
" This includes the reservation space, the intended transaction roll count,\n"
" and the reservation flags, if any.\n"
"\n"
));
}
static const struct cmdinfo logres_cmd = {
.name = "logres",
.altname = NULL,
.cfunc = logres_f,
.argmin = 0,
.argmax = 0,
.canpush = 0,
.args = NULL,
.oneline = N_("dump log reservations"),
.help = logres_help,
};
STATIC void
untorn_cow_limits(
struct xfs_mount *mp,
unsigned int logres,
unsigned int desired_max)
{
const unsigned int efi = xfs_efi_log_space(1);
const unsigned int efd = xfs_efd_log_space(1);
const unsigned int rui = xfs_rui_log_space(1);
const unsigned int rud = xfs_rud_log_space();
const unsigned int cui = xfs_cui_log_space(1);
const unsigned int cud = xfs_cud_log_space();
const unsigned int bui = xfs_bui_log_space(1);
const unsigned int bud = xfs_bud_log_space();
/*
* Maximum overhead to complete an untorn write ioend in software:
* remove data fork extent + remove cow fork extent + map extent into
* data fork.
*
* tx0: Creates a BUI and a CUI and that's all it needs.
*
* tx1: Roll to finish the BUI. Need space for the BUD, an RUI, and
* enough space to relog the CUI (== CUI + CUD).
*
* tx2: Roll again to finish the RUI. Need space for the RUD and space
* to relog the CUI.
*
* tx3: Roll again, need space for the CUD and possibly a new EFI.
*
* tx4: Roll again, need space for an EFD.
*
* If the extent referenced by the pair of BUI/CUI items is not the one
* being currently processed, then we need to reserve space to relog
* both items.
*/
const unsigned int tx0 = bui + cui;
const unsigned int tx1 = bud + rui + cui + cud;
const unsigned int tx2 = rud + cui + cud;
const unsigned int tx3 = cud + efi;
const unsigned int tx4 = efd;
const unsigned int relog = bui + bud + cui + cud;
const unsigned int per_intent = max(max3(tx0, tx1, tx2),
max3(tx3, tx4, relog));
/* Overhead to finish one step of each intent item type */
const unsigned int f1 = libxfs_calc_finish_efi_reservation(mp, 1);
const unsigned int f2 = libxfs_calc_finish_rui_reservation(mp, 1);
const unsigned int f3 = libxfs_calc_finish_cui_reservation(mp, 1);
const unsigned int f4 = libxfs_calc_finish_bui_reservation(mp, 1);
/* We only finish one item per transaction in a chain */
const unsigned int step_size = max(f4, max3(f1, f2, f3));
if (desired_max) {
dbprintf(
"desired_max: %u\nstep_size: %u\nper_intent: %u\nlogres: %u\n",
desired_max, step_size, per_intent,
(desired_max * per_intent) + step_size);
} else if (logres) {
dbprintf(
"logres: %u\nstep_size: %u\nper_intent: %u\nmax_awu: %u\n",
logres, step_size, per_intent,
logres >= step_size ? (logres - step_size) / per_intent : 0);
}
}
static void
untorn_write_max_help(void)
{
dbprintf(_(
"\n"
" The 'untorn_write_max' command computes either the log reservation needed to\n"
" complete an untorn write of a given block count; or the maximum number of\n"
" blocks that can be completed given a specific log reservation.\n"
"\n"
));
}
static int
untorn_write_max_f(
int argc,
char **argv)
{
unsigned int logres = 0;
unsigned int desired_max = 0;
int c;
while ((c = getopt(argc, argv, "l:b:")) != EOF) {
switch (c) {
case 'l':
logres = atoi(optarg);
break;
case 'b':
desired_max = atoi(optarg);
break;
default:
untorn_write_max_help();
return 0;
}
}
if (!logres && !desired_max) {
dbprintf("untorn_write_max needs -l or -b option\n");
return 0;
}
if (xfs_has_reflink(mp))
untorn_cow_limits(mp, logres, desired_max);
else
dbprintf("untorn write emulation not supported\n");
return 0;
}
static const struct cmdinfo untorn_write_max_cmd = {
.name = "untorn_write_max",
.altname = NULL,
.cfunc = untorn_write_max_f,
.argmin = 0,
.argmax = -1,
.canpush = 0,
.args = NULL,
.oneline = N_("compute untorn write max"),
.help = logres_help,
};
void
logres_init(void)
{
add_command(&logres_cmd);
add_command(&untorn_write_max_cmd);
}