// SPDX-License-Identifier: GPL-2.0
/*
* random utiility code, for bcache but in theory not specific to bcache
*
* Copyright 2010, 2011 Kent Overstreet <kent.overstreet@gmail.com>
* Copyright 2012 Google, Inc.
*/
#include <linux/bio.h>
#include <linux/blkdev.h>
#include <linux/ctype.h>
#include <linux/debugfs.h>
#include <linux/freezer.h>
#include <linux/kthread.h>
#include <linux/log2.h>
#include <linux/math64.h>
#include <linux/percpu.h>
#include <linux/preempt.h>
#include <linux/random.h>
#include <linux/seq_file.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/sched/clock.h>
#include "eytzinger.h"
#include "util.h"
static const char si_units[] = "?kMGTPEZY";
static int __bch2_strtoh(const char *cp, u64 *res,
u64 t_max, bool t_signed)
{
bool positive = *cp != '-';
unsigned u;
u64 v = 0;
if (*cp == '+' || *cp == '-')
cp++;
if (!isdigit(*cp))
return -EINVAL;
do {
if (v > U64_MAX / 10)
return -ERANGE;
v *= 10;
if (v > U64_MAX - (*cp - '0'))
return -ERANGE;
v += *cp - '0';
cp++;
} while (isdigit(*cp));
for (u = 1; u < strlen(si_units); u++)
if (*cp == si_units[u]) {
cp++;
goto got_unit;
}
u = 0;
got_unit:
if (*cp == '\n')
cp++;
if (*cp)
return -EINVAL;
if (fls64(v) + u * 10 > 64)
return -ERANGE;
v <<= u * 10;
if (positive) {
if (v > t_max)
return -ERANGE;
} else {
if (v && !t_signed)
return -ERANGE;
if (v > t_max + 1)
return -ERANGE;
v = -v;
}
*res = v;
return 0;
}
#define STRTO_H(name, type) \
int bch2_ ## name ## _h(const char *cp, type *res) \
{ \
u64 v; \
int ret = __bch2_strtoh(cp, &v, ANYSINT_MAX(type), \
ANYSINT_MAX(type) != ((type) ~0ULL)); \
*res = v; \
return ret; \
}
STRTO_H(strtoint, int)
STRTO_H(strtouint, unsigned int)
STRTO_H(strtoll, long long)
STRTO_H(strtoull, unsigned long long)
STRTO_H(strtou64, u64)
void bch2_hprint(struct printbuf *buf, s64 v)
{
int u, t = 0;
for (u = 0; v >= 1024 || v <= -1024; u++) {
t = v & ~(~0U << 10);
v >>= 10;
}
pr_buf(buf, "%lli", v);
/*
* 103 is magic: t is in the range [-1023, 1023] and we want
* to turn it into [-9, 9]
*/
if (u && v < 100 && v > -100)
pr_buf(buf, ".%i", t / 103);
if (u)
pr_buf(buf, "%c", si_units[u]);
}
void bch2_string_opt_to_text(struct printbuf *out,
const char * const list[],
size_t selected)
{
size_t i;
for (i = 0; list[i]; i++)
pr_buf(out, i == selected ? "[%s] " : "%s ", list[i]);
}
void bch2_flags_to_text(struct printbuf *out,
const char * const list[], u64 flags)
{
unsigned bit, nr = 0;
bool first = true;
if (out->pos != out->end)
*out->pos = '\0';
while (list[nr])
nr++;
while (flags && (bit = __ffs(flags)) < nr) {
if (!first)
pr_buf(out, ",");
first = false;
pr_buf(out, "%s", list[bit]);
flags ^= 1 << bit;
}
}
u64 bch2_read_flag_list(char *opt, const char * const list[])
{
u64 ret = 0;
char *p, *s, *d = kstrndup(opt, PAGE_SIZE - 1, GFP_KERNEL);
if (!d)
return -ENOMEM;
s = strim(d);
while ((p = strsep(&s, ","))) {
int flag = match_string(list, -1, p);
if (flag < 0) {
ret = -1;
break;
}
ret |= 1 << flag;
}
kfree(d);
return ret;
}
bool bch2_is_zero(const void *_p, size_t n)
{
const char *p = _p;
size_t i;
for (i = 0; i < n; i++)
if (p[i])
return false;
return true;
}
/* time stats: */
#ifndef CONFIG_BCACHEFS_NO_LATENCY_ACCT
static void bch2_quantiles_update(struct bch2_quantiles *q, u64 v)
{
unsigned i = 0;
while (i < ARRAY_SIZE(q->entries)) {
struct bch2_quantile_entry *e = q->entries + i;
if (unlikely(!e->step)) {
e->m = v;
e->step = max_t(unsigned, v / 2, 1024);
} else if (e->m > v) {
e->m = e->m >= e->step
? e->m - e->step
: 0;
} else if (e->m < v) {
e->m = e->m + e->step > e->m
? e->m + e->step
: U32_MAX;
}
i