+// SPDX-License-Identifier: GPL-2.0
/*
* random utiility code, for bcache but in theory not specific to bcache
*
#include "eytzinger.h"
#include "util.h"
-#define simple_strtoint(c, end, base) simple_strtol(c, end, base)
-#define simple_strtouint(c, end, base) simple_strtoul(c, end, base)
+static const char si_units[] = "?kMGTPEZY";
-#define STRTO_H(name, type) \
-int bch2_ ## name ## _h(const char *cp, type *res) \
-{ \
- int u = 0; \
- char *e; \
- type i = simple_ ## name(cp, &e, 10); \
- \
- switch (tolower(*e)) { \
- default: \
- return -EINVAL; \
- case 'y': \
- case 'z': \
- u++; \
- case 'e': \
- u++; \
- case 'p': \
- u++; \
- case 't': \
- u++; \
- case 'g': \
- u++; \
- case 'm': \
- u++; \
- case 'k': \
- u++; \
- if (e++ == cp) \
- return -EINVAL; \
- case '\n': \
- case '\0': \
- if (*e == '\n') \
- e++; \
- } \
- \
- if (*e) \
- return -EINVAL; \
- \
- while (u--) { \
- if ((type) ~0 > 0 && \
- (type) ~0 / 1024 <= i) \
- return -EINVAL; \
- if ((i > 0 && ANYSINT_MAX(type) / 1024 < i) || \
- (i < 0 && -ANYSINT_MAX(type) / 1024 > i)) \
- return -EINVAL; \
- i *= 1024; \
- } \
- \
- *res = i; \
- return 0; \
-} \
+/* string_get_size units: */
+static const char *const units_2[] = {
+ "B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"
+};
+static const char *const units_10[] = {
+ "B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"
+};
-STRTO_H(strtoint, int)
-STRTO_H(strtouint, unsigned int)
-STRTO_H(strtoll, long long)
-STRTO_H(strtoull, unsigned long long)
+static int parse_u64(const char *cp, u64 *res)
+{
+ const char *start = cp;
+ u64 v = 0;
+
+ if (!isdigit(*cp))
+ return -EINVAL;
-ssize_t bch2_hprint(char *buf, s64 v)
+ 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));
+
+ *res = v;
+ return cp - start;
+}
+
+static int bch2_pow(u64 n, u64 p, u64 *res)
{
- static const char units[] = "?kMGTPEZY";
- char dec[4] = "";
- int u, t = 0;
+ *res = 1;
- for (u = 0; v >= 1024 || v <= -1024; u++) {
- t = v & ~(~0U << 10);
- v >>= 10;
+ while (p--) {
+ if (*res > div_u64(U64_MAX, n))
+ return -ERANGE;
+ *res *= n;
}
+ return 0;
+}
+
+static int parse_unit_suffix(const char *cp, u64 *res)
+{
+ const char *start = cp;
+ u64 base = 1024;
+ unsigned u;
+ int ret;
+
+ if (*cp == ' ')
+ cp++;
+
+ for (u = 1; u < strlen(si_units); u++)
+ if (*cp == si_units[u]) {
+ cp++;
+ goto got_unit;
+ }
- if (!u)
- return sprintf(buf, "%lli", v);
+ for (u = 0; u < ARRAY_SIZE(units_2); u++)
+ if (!strncmp(cp, units_2[u], strlen(units_2[u]))) {
+ cp += strlen(units_2[u]);
+ goto got_unit;
+ }
+
+ for (u = 0; u < ARRAY_SIZE(units_10); u++)
+ if (!strncmp(cp, units_10[u], strlen(units_10[u]))) {
+ cp += strlen(units_10[u]);
+ base = 1000;
+ goto got_unit;
+ }
- /*
- * 103 is magic: t is in the range [-1023, 1023] and we want
- * to turn it into [-9, 9]
- */
- if (v < 100 && v > -100)
- scnprintf(dec, sizeof(dec), ".%i", t / 103);
+ *res = 1;
+ return 0;
+got_unit:
+ ret = bch2_pow(base, u, res);
+ if (ret)
+ return ret;
- return sprintf(buf, "%lli%s%c", v, dec, units[u]);
+ return cp - start;
}
-ssize_t bch2_scnprint_string_list(char *buf, size_t size,
- const char * const list[],
- size_t selected)
+#define parse_or_ret(cp, _f) \
+do { \
+ int ret = _f; \
+ if (ret < 0) \
+ return ret; \
+ cp += ret; \
+} while (0)
+
+static int __bch2_strtou64_h(const char *cp, u64 *res)
{
- char *out = buf;
- size_t i;
+ const char *start = cp;
+ u64 v = 0, b, f_n = 0, f_d = 1;
+ int ret;
+
+ parse_or_ret(cp, parse_u64(cp, &v));
+
+ if (*cp == '.') {
+ cp++;
+ ret = parse_u64(cp, &f_n);
+ if (ret < 0)
+ return ret;
+ cp += ret;
+
+ ret = bch2_pow(10, ret, &f_d);
+ if (ret)
+ return ret;
+ }
+
+ parse_or_ret(cp, parse_unit_suffix(cp, &b));
- if (size)
- *out = '\0';
+ if (v > div_u64(U64_MAX, b))
+ return -ERANGE;
+ v *= b;
- for (i = 0; list[i]; i++)
- out += scnprintf(out, buf + size - out,
- i == selected ? "[%s] " : "%s ", list[i]);
+ if (f_n > div_u64(U64_MAX, b))
+ return -ERANGE;
- if (out != buf)
- *--out = '\0';
+ f_n = div_u64(f_n * b, f_d);
+ if (v + f_n < v)
+ return -ERANGE;
+ v += f_n;
- return out - buf;
+ *res = v;
+ return cp - start;
}
-ssize_t bch2_scnprint_flag_list(char *buf, size_t size,
- const char * const list[], u64 flags)
+static int __bch2_strtoh(const char *cp, u64 *res,
+ u64 t_max, bool t_signed)
{
- char *out = buf, *end = buf + size;
- unsigned bit, nr = 0;
+ bool positive = *cp != '-';
+ u64 v = 0;
- while (list[nr])
- nr++;
+ if (*cp == '+' || *cp == '-')
+ cp++;
- if (size)
- *out = '\0';
+ parse_or_ret(cp, __bch2_strtou64_h(cp, &v));
- while (flags && (bit = __ffs(flags)) < nr) {
- out += scnprintf(out, end - out, "%s,", list[bit]);
- flags ^= 1 << bit;
+ if (*cp == '\n')
+ cp++;
+ if (*cp)
+ return -EINVAL;
+
+ if (positive) {
+ if (v > t_max)
+ return -ERANGE;
+ } else {
+ if (v && !t_signed)
+ return -ERANGE;
+
+ if (v > t_max + 1)
+ return -ERANGE;
+ v = -v;
}
- if (out != buf)
- *--out = '\0';
+ *res = v;
+ return 0;
+}
- return out - buf;
+#define STRTO_H(name, type) \
+int bch2_ ## name ## _h(const char *cp, type *res) \
+{ \
+ u64 v = 0; \
+ 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)
+
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);
+ char *p, *s, *d = kstrdup(opt, GFP_KERNEL);
if (!d)
return -ENOMEM;
return u;
}
-static size_t pr_time_units(char *buf, size_t len, u64 ns)
+static void pr_time_units(struct printbuf *out, u64 ns)
{
const struct time_unit *u = pick_time_units(ns);
- return scnprintf(buf, len, "%llu %s", div_u64(ns, u->nsecs), u->name);
+ prt_printf(out, "%llu %s", div_u64(ns, u->nsecs), u->name);
}
-size_t bch2_time_stats_print(struct time_stats *stats, char *buf, size_t len)
+void bch2_time_stats_to_text(struct printbuf *out, struct time_stats *stats)
{
- char *out = buf, *end = buf + len;
const struct time_unit *u;
u64 freq = READ_ONCE(stats->average_frequency);
u64 q, last_q = 0;
int i;
- out += scnprintf(out, end - out, "count:\t\t%llu\n",
+ prt_printf(out, "count:\t\t%llu\n",
stats->count);
- out += scnprintf(out, end - out, "rate:\t\t%llu/sec\n",
- freq ? div64_u64(NSEC_PER_SEC, freq) : 0);
+ prt_printf(out, "rate:\t\t%llu/sec\n",
+ freq ? div64_u64(NSEC_PER_SEC, freq) : 0);
- out += scnprintf(out, end - out, "frequency:\t");
- out += pr_time_units(out, end - out, freq);
+ prt_printf(out, "frequency:\t");
+ pr_time_units(out, freq);
- out += scnprintf(out, end - out, "\navg duration:\t");
- out += pr_time_units(out, end - out, stats->average_duration);
+ prt_printf(out, "\navg duration:\t");
+ pr_time_units(out, stats->average_duration);
- out += scnprintf(out, end - out, "\nmax duration:\t");
- out += pr_time_units(out, end - out, stats->max_duration);
+ prt_printf(out, "\nmax duration:\t");
+ pr_time_units(out, stats->max_duration);
i = eytzinger0_first(NR_QUANTILES);
u = pick_time_units(stats->quantiles.entries[i].m);
- out += scnprintf(out, end - out, "\nquantiles (%s):\t", u->name);
+ prt_printf(out, "\nquantiles (%s):\t", u->name);
eytzinger0_for_each(i, NR_QUANTILES) {
bool is_last = eytzinger0_next(i, NR_QUANTILES) == -1;
q = max(stats->quantiles.entries[i].m, last_q);
- out += scnprintf(out, end - out, "%llu%s",
- div_u64(q, u->nsecs),
- is_last ? "\n" : " ");
+ prt_printf(out, "%llu%s",
+ div_u64(q, u->nsecs),
+ is_last ? "\n" : " ");
last_q = q;
}
-
- return out - buf;
}
void bch2_time_stats_exit(struct time_stats *stats)
d->next = now - NSEC_PER_SEC * 2;
}
-int bch2_ratelimit_wait_freezable_stoppable(struct bch_ratelimit *d)
-{
- bool kthread = (current->flags & PF_KTHREAD) != 0;
-
- while (1) {
- u64 delay = bch2_ratelimit_delay(d);
-
- if (delay)
- set_current_state(TASK_INTERRUPTIBLE);
-
- if (kthread && kthread_should_stop())
- return 1;
-
- if (!delay)
- return 0;
-
- schedule_timeout(delay);
- try_to_freeze();
- }
-}
-
/* pd controller: */
/*
pd->backpressure = 1;
}
-size_t bch2_pd_controller_print_debug(struct bch_pd_controller *pd, char *buf)
+void bch2_pd_controller_debug_to_text(struct printbuf *out, struct bch_pd_controller *pd)
{
- /* 2^64 - 1 is 20 digits, plus null byte */
- char rate[21];
- char actual[21];
- char target[21];
- char proportional[21];
- char derivative[21];
- char change[21];
- s64 next_io;
+ out->tabstops[0] = 20;
- bch2_hprint(rate, pd->rate.rate);
- bch2_hprint(actual, pd->last_actual);
- bch2_hprint(target, pd->last_target);
- bch2_hprint(proportional, pd->last_proportional);
- bch2_hprint(derivative, pd->last_derivative);
- bch2_hprint(change, pd->last_change);
+ prt_printf(out, "rate:");
+ prt_tab(out);
+ prt_human_readable_s64(out, pd->rate.rate);
+ prt_newline(out);
- next_io = div64_s64(pd->rate.next - local_clock(), NSEC_PER_MSEC);
+ prt_printf(out, "target:");
+ prt_tab(out);
+ prt_human_readable_u64(out, pd->last_target);
+ prt_newline(out);
- return sprintf(buf,
- "rate:\t\t%s/sec\n"
- "target:\t\t%s\n"
- "actual:\t\t%s\n"
- "proportional:\t%s\n"
- "derivative:\t%s\n"
- "change:\t\t%s/sec\n"
- "next io:\t%llims\n",
- rate, target, actual, proportional,
- derivative, change, next_io);
-}
+ prt_printf(out, "actual:");
+ prt_tab(out);
+ prt_human_readable_u64(out, pd->last_actual);
+ prt_newline(out);
-/* misc: */
+ prt_printf(out, "proportional:");
+ prt_tab(out);
+ prt_human_readable_s64(out, pd->last_proportional);
+ prt_newline(out);
-void bch2_bio_map(struct bio *bio, void *base)
-{
- size_t size = bio->bi_iter.bi_size;
- struct bio_vec *bv = bio->bi_io_vec;
+ prt_printf(out, "derivative:");
+ prt_tab(out);
+ prt_human_readable_s64(out, pd->last_derivative);
+ prt_newline(out);
- BUG_ON(!bio->bi_iter.bi_size);
- BUG_ON(bio->bi_vcnt);
+ prt_printf(out, "change:");
+ prt_tab(out);
+ prt_human_readable_s64(out, pd->last_change);
+ prt_newline(out);
- bv->bv_offset = base ? offset_in_page(base) : 0;
- goto start;
+ prt_printf(out, "next io:");
+ prt_tab(out);
+ prt_printf(out, "%llims", div64_s64(pd->rate.next - local_clock(), NSEC_PER_MSEC));
+ prt_newline(out);
+}
- for (; size; bio->bi_vcnt++, bv++) {
- bv->bv_offset = 0;
-start: bv->bv_len = min_t(size_t, PAGE_SIZE - bv->bv_offset,
- size);
- BUG_ON(bio->bi_vcnt >= bio->bi_max_vecs);
- if (base) {
- bv->bv_page = is_vmalloc_addr(base)
+/* misc: */
+
+void bch2_bio_map(struct bio *bio, void *base, size_t size)
+{
+ while (size) {
+ struct page *page = is_vmalloc_addr(base)
? vmalloc_to_page(base)
: virt_to_page(base);
+ unsigned offset = offset_in_page(base);
+ unsigned len = min_t(size_t, PAGE_SIZE - offset, size);
- base += bv->bv_len;
- }
-
- size -= bv->bv_len;
+ BUG_ON(!bio_add_page(bio, page, len, offset));
+ size -= len;
+ base += len;
}
}
-int bch2_bio_alloc_pages(struct bio *bio, gfp_t gfp_mask)
+int bch2_bio_alloc_pages(struct bio *bio, size_t size, gfp_t gfp_mask)
{
- int i;
- struct bio_vec *bv;
+ while (size) {
+ struct page *page = alloc_page(gfp_mask);
+ unsigned len = min_t(size_t, PAGE_SIZE, size);
- bio_for_each_segment_all(bv, bio, i) {
- bv->bv_page = alloc_page(gfp_mask);
- if (!bv->bv_page) {
- while (--bv >= bio->bi_io_vec)
- __free_page(bv->bv_page);
+ if (!page)
return -ENOMEM;
+
+ if (unlikely(!bio_add_page(bio, page, len, 0))) {
+ __free_page(page);
+ break;
}
+
+ size -= len;
}
return 0;
return rand;
}
-void memcpy_to_bio(struct bio *dst, struct bvec_iter dst_iter, void *src)
+void memcpy_to_bio(struct bio *dst, struct bvec_iter dst_iter, const void *src)
{
struct bio_vec bv;
struct bvec_iter iter;
}
}
-size_t bch_scnmemcpy(char *buf, size_t size, const char *src, size_t len)
-{
- size_t n;
-
- if (!size)
- return 0;
-
- n = min(size - 1, len);
- memcpy(buf, src, n);
- buf[n] = '\0';
-
- return n;
-}
-
#include "eytzinger.h"
static int alignment_ok(const void *base, size_t align)
kfree(test_array);
}
#endif
+
+/*
+ * Accumulate percpu counters onto one cpu's copy - only valid when access
+ * against any percpu counter is guarded against
+ */
+u64 *bch2_acc_percpu_u64s(u64 __percpu *p, unsigned nr)
+{
+ u64 *ret;
+ int cpu;
+
+ /* access to pcpu vars has to be blocked by other locking */
+ preempt_disable();
+ ret = this_cpu_ptr(p);
+ preempt_enable();
+
+ for_each_possible_cpu(cpu) {
+ u64 *i = per_cpu_ptr(p, cpu);
+
+ if (i != ret) {
+ acc_u64s(ret, i, nr);
+ memset(i, 0, nr * sizeof(u64));
+ }
+ }
+
+ return ret;
+}