+// 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";
static int __bch2_strtoh(const char *cp, u64 *res,
STRTO_H(strtouint, unsigned int)
STRTO_H(strtoll, long long)
STRTO_H(strtoull, unsigned long long)
+STRTO_H(strtou64, u64)
-ssize_t bch2_hprint(char *buf, s64 v)
+void bch2_hprint(struct printbuf *buf, s64 v)
{
- char dec[4] = "";
int u, t = 0;
for (u = 0; v >= 1024 || v <= -1024; u++) {
v >>= 10;
}
- if (!u)
- return sprintf(buf, "%lli", v);
+ 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 (v < 100 && v > -100)
- scnprintf(dec, sizeof(dec), ".%i", t / 103);
-
- return sprintf(buf, "%lli%s%c", v, dec, si_units[u]);
+ if (u && v < 100 && v > -100)
+ pr_buf(buf, ".%i", t / 103);
+ if (u)
+ pr_buf(buf, "%c", si_units[u]);
}
-ssize_t bch2_scnprint_string_list(char *buf, size_t size,
- const char * const list[],
- size_t selected)
+void bch2_string_opt_to_text(struct printbuf *out,
+ const char * const list[],
+ size_t selected)
{
- char *out = buf;
size_t i;
- if (size)
- *out = '\0';
-
for (i = 0; list[i]; i++)
- out += scnprintf(out, buf + size - out,
- i == selected ? "[%s] " : "%s ", list[i]);
-
- if (out != buf)
- *--out = '\0';
-
- return out - buf;
+ pr_buf(out, i == selected ? "[%s] " : "%s ", list[i]);
}
-ssize_t bch2_scnprint_flag_list(char *buf, size_t size,
- const char * const list[], u64 flags)
+void bch2_flags_to_text(struct printbuf *out,
+ const char * const list[], u64 flags)
{
- char *out = buf, *end = buf + size;
unsigned bit, nr = 0;
+ bool first = true;
+
+ if (out->pos != out->end)
+ *out->pos = '\0';
while (list[nr])
nr++;
- if (size)
- *out = '\0';
-
while (flags && (bit = __ffs(flags)) < nr) {
- out += scnprintf(out, end - out, "%s,", list[bit]);
+ if (!first)
+ pr_buf(out, ",");
+ first = false;
+ pr_buf(out, "%s", list[bit]);
flags ^= 1 << bit;
}
-
- if (out != buf)
- *--out = '\0';
-
- return out - buf;
}
u64 bch2_read_flag_list(char *opt, const char * const list[])
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);
+ pr_buf(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",
+ pr_buf(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);
+ pr_buf(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);
+ pr_buf(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);
+ pr_buf(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);
+ pr_buf(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);
+ pr_buf(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" : " ");
+ pr_buf(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)
char change[21];
s64 next_io;
- 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);
+ bch2_hprint(&PBUF(rate), pd->rate.rate);
+ bch2_hprint(&PBUF(actual), pd->last_actual);
+ bch2_hprint(&PBUF(target), pd->last_target);
+ bch2_hprint(&PBUF(proportional), pd->last_proportional);
+ bch2_hprint(&PBUF(derivative), pd->last_derivative);
+ bch2_hprint(&PBUF(change), pd->last_change);
next_io = div64_s64(pd->rate.next - local_clock(), NSEC_PER_MSEC);
/* misc: */
-void bch2_bio_map(struct bio *bio, void *base)
+void bch2_bio_map(struct bio *bio, void *base, size_t size)
{
- size_t size = bio->bi_iter.bi_size;
- struct bio_vec *bv = bio->bi_io_vec;
-
- BUG_ON(!bio->bi_iter.bi_size);
- BUG_ON(bio->bi_vcnt);
-
- bv->bv_offset = base ? offset_in_page(base) : 0;
- goto start;
-
- 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)
+ 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;
- }
+
+ BUG_ON(!bio_add_page(bio, page, len, 0));
+ 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)
+void bch_scnmemcpy(struct printbuf *out,
+ const char *src, size_t len)
{
- size_t n;
-
- if (!size)
- return 0;
+ size_t n = printbuf_remaining(out);
- n = min(size - 1, len);
- memcpy(buf, src, n);
- buf[n] = '\0';
-
- return n;
+ if (n) {
+ n = min(n - 1, len);
+ memcpy(out->pos, src, n);
+ out->pos += n;
+ *out->pos = '\0';
+ }
}
#include "eytzinger.h"
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;
+
+ 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;
+}