summaryrefslogtreecommitdiff
path: root/linux
diff options
context:
space:
mode:
Diffstat (limited to 'linux')
-rw-r--r--linux/blkdev.c4
-rw-r--r--linux/mean_and_variance.c2
-rw-r--r--linux/pretty-printers.c60
-rw-r--r--linux/printbuf.c368
-rw-r--r--linux/printbuf_userspace.c34
-rw-r--r--linux/seq_buf.c152
-rw-r--r--linux/six.c230
-rw-r--r--linux/string_helpers.c1
8 files changed, 316 insertions, 535 deletions
diff --git a/linux/blkdev.c b/linux/blkdev.c
index 54cd6e9c..0a5cedfe 100644
--- a/linux/blkdev.c
+++ b/linux/blkdev.c
@@ -184,7 +184,7 @@ struct block_device *blkdev_get_by_path(const char *path, fmode_t mode,
if (buffered_fd < 0)
return ERR_PTR(-errno);
- fd = open(path, flags|O_DIRECT);
+ fd = open(path, flags);
if (fd < 0)
fd = dup(buffered_fd);
if (fd < 0) {
@@ -192,7 +192,7 @@ struct block_device *blkdev_get_by_path(const char *path, fmode_t mode,
return ERR_PTR(-errno);
}
- sync_fd = open(path, flags|O_DIRECT|O_SYNC);
+ sync_fd = open(path, flags|O_SYNC);
if (sync_fd < 0)
sync_fd = open(path, flags|O_SYNC);
if (sync_fd < 0) {
diff --git a/linux/mean_and_variance.c b/linux/mean_and_variance.c
index 55d46c9d..bd08da5f 100644
--- a/linux/mean_and_variance.c
+++ b/linux/mean_and_variance.c
@@ -42,8 +42,6 @@
#include <linux/math64.h>
#include <linux/mean_and_variance.h>
#include <linux/module.h>
-#include <linux/printbuf.h>
-
/**
* fast_divpow2() - fast approximation for n / (1 << d)
diff --git a/linux/pretty-printers.c b/linux/pretty-printers.c
deleted file mode 100644
index addbac95..00000000
--- a/linux/pretty-printers.c
+++ /dev/null
@@ -1,60 +0,0 @@
-// SPDX-License-Identifier: LGPL-2.1+
-/* Copyright (C) 2022 Kent Overstreet */
-
-#include <linux/bitops.h>
-#include <linux/kernel.h>
-#include <linux/printbuf.h>
-#include <linux/pretty-printers.h>
-
-/**
- * prt_string_option - Given a list of strings, print out the list and indicate
- * which option is selected, with square brackets (sysfs style)
- *
- * @out: The printbuf to output to
- * @list: List of strings to choose from
- * @selected: The option to highlight, with square brackets
- */
-void prt_string_option(struct printbuf *out,
- const char * const list[],
- size_t selected)
-{
- size_t i;
-
- for (i = 0; list[i]; i++) {
- if (i)
- prt_char(out, ' ');
- if (i == selected)
- prt_char(out, '[');
- prt_str(out, list[i]);
- if (i == selected)
- prt_char(out, ']');
- }
-}
-EXPORT_SYMBOL(prt_string_option);
-
-/**
- * prt_bitflags: Given a bitmap and a list of names for each bit, print out which
- * bits are on, comma separated
- *
- * @out: The printbuf to output to
- * @list: List of names for each bit
- * @flags: Bits to print
- */
-void prt_bitflags(struct printbuf *out,
- const char * const list[], u64 flags)
-{
- unsigned bit, nr = 0;
- bool first = true;
-
- while (list[nr])
- nr++;
-
- while (flags && (bit = __ffs(flags)) < nr) {
- if (!first)
- prt_char(out, ',');
- first = false;
- prt_str(out, list[bit]);
- flags ^= 1 << bit;
- }
-}
-EXPORT_SYMBOL(prt_bitflags);
diff --git a/linux/printbuf.c b/linux/printbuf.c
deleted file mode 100644
index 5cf79d43..00000000
--- a/linux/printbuf.c
+++ /dev/null
@@ -1,368 +0,0 @@
-// SPDX-License-Identifier: LGPL-2.1+
-/* Copyright (C) 2022 Kent Overstreet */
-
-#include <linux/err.h>
-#include <linux/export.h>
-#include <linux/kernel.h>
-#include <linux/printbuf.h>
-#include <linux/slab.h>
-#include <linux/string_helpers.h>
-
-static inline unsigned printbuf_linelen(struct printbuf *buf)
-{
- return buf->pos - buf->last_newline;
-}
-
-int printbuf_make_room(struct printbuf *out, unsigned extra)
-{
- unsigned new_size;
- char *buf;
-
- if (!out->heap_allocated)
- return 0;
-
- /* Reserved space for terminating nul: */
- extra += 1;
-
- if (out->pos + extra < out->size)
- return 0;
-
- new_size = roundup_pow_of_two(out->size + extra);
-
- /*
- * Note: output buffer must be freeable with kfree(), it's not required
- * that the user use printbuf_exit().
- */
- buf = krealloc(out->buf, new_size, !out->atomic ? GFP_KERNEL : GFP_NOWAIT);
-
- if (!buf) {
- out->allocation_failure = true;
- return -ENOMEM;
- }
-
- out->buf = buf;
- out->size = new_size;
- return 0;
-}
-EXPORT_SYMBOL(printbuf_make_room);
-
-/**
- * printbuf_str - returns printbuf's buf as a C string, guaranteed to be null
- * terminated
- */
-const char *printbuf_str(const struct printbuf *buf)
-{
- /*
- * If we've written to a printbuf then it's guaranteed to be a null
- * terminated string - but if we haven't, then we might not have
- * allocated a buffer at all:
- */
- return buf->pos
- ? buf->buf
- : "";
-}
-EXPORT_SYMBOL(printbuf_str);
-
-/**
- * printbuf_exit - exit a printbuf, freeing memory it owns and poisoning it
- * against accidental use.
- */
-void printbuf_exit(struct printbuf *buf)
-{
- if (buf->heap_allocated) {
- kfree(buf->buf);
- buf->buf = ERR_PTR(-EINTR); /* poison value */
- }
-}
-EXPORT_SYMBOL(printbuf_exit);
-
-void printbuf_tabstops_reset(struct printbuf *buf)
-{
- buf->nr_tabstops = 0;
-}
-EXPORT_SYMBOL(printbuf_tabstops_reset);
-
-void printbuf_tabstop_pop(struct printbuf *buf)
-{
- if (buf->nr_tabstops)
- --buf->nr_tabstops;
-}
-EXPORT_SYMBOL(printbuf_tabstop_pop);
-
-/*
- * printbuf_tabstop_set - add a tabstop, n spaces from the previous tabstop
- *
- * @buf: printbuf to control
- * @spaces: number of spaces from previous tabpstop
- *
- * In the future this function may allocate memory if setting more than
- * PRINTBUF_INLINE_TABSTOPS or setting tabstops more than 255 spaces from start
- * of line.
- */
-int printbuf_tabstop_push(struct printbuf *buf, unsigned spaces)
-{
- unsigned prev_tabstop = buf->nr_tabstops
- ? buf->_tabstops[buf->nr_tabstops - 1]
- : 0;
-
- if (WARN_ON(buf->nr_tabstops >= ARRAY_SIZE(buf->_tabstops)))
- return -EINVAL;
-
- buf->_tabstops[buf->nr_tabstops++] = prev_tabstop + spaces;
- buf->has_indent_or_tabstops = true;
- return 0;
-}
-EXPORT_SYMBOL(printbuf_tabstop_push);
-
-/**
- * printbuf_indent_add - add to the current indent level
- *
- * @buf: printbuf to control
- * @spaces: number of spaces to add to the current indent level
- *
- * Subsequent lines, and the current line if the output position is at the start
- * of the current line, will be indented by @spaces more spaces.
- */
-void printbuf_indent_add(struct printbuf *buf, unsigned spaces)
-{
- if (WARN_ON_ONCE(buf->indent + spaces < buf->indent))
- spaces = 0;
-
- buf->indent += spaces;
- prt_chars(buf, ' ', spaces);
-
- buf->has_indent_or_tabstops = true;
-}
-EXPORT_SYMBOL(printbuf_indent_add);
-
-/**
- * printbuf_indent_sub - subtract from the current indent level
- *
- * @buf: printbuf to control
- * @spaces: number of spaces to subtract from the current indent level
- *
- * Subsequent lines, and the current line if the output position is at the start
- * of the current line, will be indented by @spaces less spaces.
- */
-void printbuf_indent_sub(struct printbuf *buf, unsigned spaces)
-{
- if (WARN_ON_ONCE(spaces > buf->indent))
- spaces = buf->indent;
-
- if (buf->last_newline + buf->indent == buf->pos) {
- buf->pos -= spaces;
- printbuf_nul_terminate(buf);
- }
- buf->indent -= spaces;
-
- if (!buf->indent && !buf->nr_tabstops)
- buf->has_indent_or_tabstops = false;
-}
-EXPORT_SYMBOL(printbuf_indent_sub);
-
-void prt_newline(struct printbuf *buf)
-{
- unsigned i;
-
- printbuf_make_room(buf, 1 + buf->indent);
-
- __prt_char(buf, '\n');
-
- buf->last_newline = buf->pos;
-
- for (i = 0; i < buf->indent; i++)
- __prt_char(buf, ' ');
-
- printbuf_nul_terminate(buf);
-
- buf->last_field = buf->pos;
- buf->cur_tabstop = 0;
-}
-EXPORT_SYMBOL(prt_newline);
-
-/*
- * Returns spaces from start of line, if set, or 0 if unset:
- */
-static inline unsigned cur_tabstop(struct printbuf *buf)
-{
- return buf->cur_tabstop < buf->nr_tabstops
- ? buf->_tabstops[buf->cur_tabstop]
- : 0;
-}
-
-static void __prt_tab(struct printbuf *out)
-{
- int spaces = max_t(int, 0, cur_tabstop(out) - printbuf_linelen(out));
-
- prt_chars(out, ' ', spaces);
-
- out->last_field = out->pos;
- out->cur_tabstop++;
-}
-
-/**
- * prt_tab - Advance printbuf to the next tabstop
- *
- * @buf: printbuf to control
- *
- * Advance output to the next tabstop by printing spaces.
- */
-void prt_tab(struct printbuf *out)
-{
- if (WARN_ON(!cur_tabstop(out)))
- return;
-
- __prt_tab(out);
-}
-EXPORT_SYMBOL(prt_tab);
-
-static void __prt_tab_rjust(struct printbuf *buf)
-{
- unsigned move = buf->pos - buf->last_field;
- int pad = (int) cur_tabstop(buf) - (int) printbuf_linelen(buf);
-
- if (pad > 0) {
- printbuf_make_room(buf, pad);
-
- if (buf->last_field + pad < buf->size)
- memmove(buf->buf + buf->last_field + pad,
- buf->buf + buf->last_field,
- min(move, buf->size - 1 - buf->last_field - pad));
-
- if (buf->last_field < buf->size)
- memset(buf->buf + buf->last_field, ' ',
- min((unsigned) pad, buf->size - buf->last_field));
-
- buf->pos += pad;
- printbuf_nul_terminate(buf);
- }
-
- buf->last_field = buf->pos;
- buf->cur_tabstop++;
-}
-
-/**
- * prt_tab_rjust - Advance printbuf to the next tabstop, right justifying
- * previous output
- *
- * @buf: printbuf to control
- *
- * Advance output to the next tabstop by inserting spaces immediately after the
- * previous tabstop, right justifying previously outputted text.
- */
-void prt_tab_rjust(struct printbuf *buf)
-{
- if (WARN_ON(!cur_tabstop(buf)))
- return;
-
- __prt_tab_rjust(buf);
-}
-EXPORT_SYMBOL(prt_tab_rjust);
-
-/**
- * prt_bytes_indented - Print an array of chars, handling embedded control characters
- *
- * @out: printbuf to output to
- * @str: string to print
- * @count: number of bytes to print
- *
- * The following contol characters are handled as so:
- * \n: prt_newline newline that obeys current indent level
- * \t: prt_tab advance to next tabstop
- * \r: prt_tab_rjust advance to next tabstop, with right justification
- */
-void prt_bytes_indented(struct printbuf *out, const char *str, unsigned count)
-{
- const char *unprinted_start = str;
- const char *end = str + count;
-
- if (!out->has_indent_or_tabstops || out->suppress_indent_tabstop_handling) {
- prt_bytes(out, str, count);
- return;
- }
-
- while (str != end) {
- switch (*str) {
- case '\n':
- prt_bytes(out, unprinted_start, str - unprinted_start);
- unprinted_start = str + 1;
- prt_newline(out);
- break;
- case '\t':
- if (likely(cur_tabstop(out))) {
- prt_bytes(out, unprinted_start, str - unprinted_start);
- unprinted_start = str + 1;
- __prt_tab(out);
- }
- break;
- case '\r':
- if (likely(cur_tabstop(out))) {
- prt_bytes(out, unprinted_start, str - unprinted_start);
- unprinted_start = str + 1;
- __prt_tab_rjust(out);
- }
- break;
- }
-
- str++;
- }
-
- prt_bytes(out, unprinted_start, str - unprinted_start);
-}
-EXPORT_SYMBOL(prt_bytes_indented);
-
-/**
- * prt_human_readable_u64 - Print out a u64 in human readable units
- *
- * Units of 2^10 (default) or 10^3 are controlled via @buf->si_units
- */
-void prt_human_readable_u64(struct printbuf *buf, u64 v)
-{
- printbuf_make_room(buf, 10);
- buf->pos += string_get_size(v, 1, !buf->si_units,
- buf->buf + buf->pos,
- printbuf_remaining_size(buf));
-}
-EXPORT_SYMBOL(prt_human_readable_u64);
-
-/**
- * prt_human_readable_s64 - Print out a s64 in human readable units
- *
- * Units of 2^10 (default) or 10^3 are controlled via @buf->si_units
- */
-void prt_human_readable_s64(struct printbuf *buf, s64 v)
-{
- if (v < 0)
- prt_char(buf, '-');
- prt_human_readable_u64(buf, abs(v));
-}
-EXPORT_SYMBOL(prt_human_readable_s64);
-
-/**
- * prt_units_u64 - Print out a u64 according to printbuf unit options
- *
- * Units are either raw (default), or human reabable units (controlled via
- * @buf->human_readable_units)
- */
-void prt_units_u64(struct printbuf *out, u64 v)
-{
- if (out->human_readable_units)
- prt_human_readable_u64(out, v);
- else
- prt_printf(out, "%llu", v);
-}
-EXPORT_SYMBOL(prt_units_u64);
-
-/**
- * prt_units_s64 - Print out a s64 according to printbuf unit options
- *
- * Units are either raw (default), or human reabable units (controlled via
- * @buf->human_readable_units)
- */
-void prt_units_s64(struct printbuf *out, s64 v)
-{
- if (v < 0)
- prt_char(out, '-');
- prt_units_u64(out, abs(v));
-}
-EXPORT_SYMBOL(prt_units_s64);
diff --git a/linux/printbuf_userspace.c b/linux/printbuf_userspace.c
deleted file mode 100644
index 0ae56ee1..00000000
--- a/linux/printbuf_userspace.c
+++ /dev/null
@@ -1,34 +0,0 @@
-
-#include <stdio.h>
-#include <linux/printbuf.h>
-
-void prt_vprintf(struct printbuf *out, const char *fmt, va_list args)
-{
- int len;
-
- do {
- va_list args2;
-
- va_copy(args2, args);
- len = vsnprintf(out->buf + out->pos, printbuf_remaining(out), fmt, args2);
- } while (len + 1 >= printbuf_remaining(out) &&
- !printbuf_make_room(out, len + 1));
-
- len = min_t(size_t, len,
- printbuf_remaining(out) ? printbuf_remaining(out) - 1 : 0);
- out->pos += len;
-}
-
-void prt_printf(struct printbuf *out, const char *fmt, ...)
-{
- va_list args;
-
- va_start(args, fmt);
- prt_vprintf(out, fmt, args);
- va_end(args);
-}
-
-void prt_u64(struct printbuf *out, u64 v)
-{
- prt_printf(out, "%llu", v);
-}
diff --git a/linux/seq_buf.c b/linux/seq_buf.c
new file mode 100644
index 00000000..cf8709ad
--- /dev/null
+++ b/linux/seq_buf.c
@@ -0,0 +1,152 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * seq_buf.c
+ *
+ * Copyright (C) 2014 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
+ *
+ * The seq_buf is a handy tool that allows you to pass a descriptor around
+ * to a buffer that other functions can write to. It is similar to the
+ * seq_file functionality but has some differences.
+ *
+ * To use it, the seq_buf must be initialized with seq_buf_init().
+ * This will set up the counters within the descriptor. You can call
+ * seq_buf_init() more than once to reset the seq_buf to start
+ * from scratch.
+ */
+#include <linux/seq_buf.h>
+#include <stdio.h>
+
+/**
+ * seq_buf_can_fit - can the new data fit in the current buffer?
+ * @s: the seq_buf descriptor
+ * @len: The length to see if it can fit in the current buffer
+ *
+ * Returns true if there's enough unused space in the seq_buf buffer
+ * to fit the amount of new data according to @len.
+ */
+static bool seq_buf_can_fit(struct seq_buf *s, size_t len)
+{
+ return s->len + len <= s->size;
+}
+
+/**
+ * seq_buf_vprintf - sequence printing of information.
+ * @s: seq_buf descriptor
+ * @fmt: printf format string
+ * @args: va_list of arguments from a printf() type function
+ *
+ * Writes a vnprintf() format into the sequencce buffer.
+ *
+ * Returns zero on success, -1 on overflow.
+ */
+int seq_buf_vprintf(struct seq_buf *s, const char *fmt, va_list args)
+{
+ int len;
+
+ WARN_ON(s->size == 0);
+
+ if (s->len < s->size) {
+ len = vsnprintf(s->buffer + s->len, s->size - s->len, fmt, args);
+ if (s->len + len < s->size) {
+ s->len += len;
+ return 0;
+ }
+ }
+ seq_buf_set_overflow(s);
+ return -1;
+}
+
+/**
+ * seq_buf_printf - sequence printing of information
+ * @s: seq_buf descriptor
+ * @fmt: printf format string
+ *
+ * Writes a printf() format into the sequence buffer.
+ *
+ * Returns zero on success, -1 on overflow.
+ */
+int seq_buf_printf(struct seq_buf *s, const char *fmt, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, fmt);
+ ret = seq_buf_vprintf(s, fmt, ap);
+ va_end(ap);
+
+ return ret;
+}
+
+/**
+ * seq_buf_puts - sequence printing of simple string
+ * @s: seq_buf descriptor
+ * @str: simple string to record
+ *
+ * Copy a simple string into the sequence buffer.
+ *
+ * Returns zero on success, -1 on overflow
+ */
+int seq_buf_puts(struct seq_buf *s, const char *str)
+{
+ size_t len = strlen(str);
+
+ WARN_ON(s->size == 0);
+
+ /* Add 1 to len for the trailing null byte which must be there */
+ len += 1;
+
+ if (seq_buf_can_fit(s, len)) {
+ memcpy(s->buffer + s->len, str, len);
+ /* Don't count the trailing null byte against the capacity */
+ s->len += len - 1;
+ return 0;
+ }
+ seq_buf_set_overflow(s);
+ return -1;
+}
+
+/**
+ * seq_buf_putc - sequence printing of simple character
+ * @s: seq_buf descriptor
+ * @c: simple character to record
+ *
+ * Copy a single character into the sequence buffer.
+ *
+ * Returns zero on success, -1 on overflow
+ */
+int seq_buf_putc(struct seq_buf *s, unsigned char c)
+{
+ WARN_ON(s->size == 0);
+
+ if (seq_buf_can_fit(s, 1)) {
+ s->buffer[s->len++] = c;
+ return 0;
+ }
+ seq_buf_set_overflow(s);
+ return -1;
+}
+
+/**
+ * seq_buf_putmem - write raw data into the sequenc buffer
+ * @s: seq_buf descriptor
+ * @mem: The raw memory to copy into the buffer
+ * @len: The length of the raw memory to copy (in bytes)
+ *
+ * There may be cases where raw memory needs to be written into the
+ * buffer and a strcpy() would not work. Using this function allows
+ * for such cases.
+ *
+ * Returns zero on success, -1 on overflow
+ */
+int seq_buf_putmem(struct seq_buf *s, const void *mem, unsigned int len)
+{
+ WARN_ON(s->size == 0);
+
+ if (seq_buf_can_fit(s, len)) {
+ memcpy(s->buffer + s->len, mem, len);
+ s->len += len;
+ return 0;
+ }
+ seq_buf_set_overflow(s);
+ return -1;
+}
diff --git a/linux/six.c b/linux/six.c
index 39a9bd6e..41337a7f 100644
--- a/linux/six.c
+++ b/linux/six.c
@@ -11,14 +11,16 @@
#include <linux/six.h>
#include <linux/slab.h>
+#include <trace/events/lock.h>
+
#ifdef DEBUG
#define EBUG_ON(cond) BUG_ON(cond)
#else
#define EBUG_ON(cond) do {} while (0)
#endif
-#define six_acquire(l, t, r) lock_acquire(l, 0, t, r, 1, NULL, _RET_IP_)
-#define six_release(l) lock_release(l, _RET_IP_)
+#define six_acquire(l, t, r, ip) lock_acquire(l, 0, t, r, 1, NULL, ip)
+#define six_release(l, ip) lock_release(l, ip)
static void do_six_unlock_type(struct six_lock *lock, enum six_lock_type type);
@@ -278,19 +280,20 @@ static bool do_six_trylock_type(struct six_lock *lock,
}
__always_inline __flatten
-static bool __six_trylock_type(struct six_lock *lock, enum six_lock_type type)
+static bool __six_trylock_type(struct six_lock *lock, enum six_lock_type type,
+ unsigned long ip)
{
if (!do_six_trylock_type(lock, type, true))
return false;
if (type != SIX_LOCK_write)
- six_acquire(&lock->dep_map, 1, type == SIX_LOCK_read);
+ six_acquire(&lock->dep_map, 1, type == SIX_LOCK_read, ip);
return true;
}
__always_inline __flatten
static bool __six_relock_type(struct six_lock *lock, enum six_lock_type type,
- unsigned seq)
+ unsigned seq, unsigned long ip)
{
const struct six_lock_vals l[] = LOCK_VALS;
union six_lock_state old;
@@ -321,7 +324,7 @@ static bool __six_relock_type(struct six_lock *lock, enum six_lock_type type,
six_lock_wakeup(lock, old, SIX_LOCK_write);
if (ret)
- six_acquire(&lock->dep_map, 1, type == SIX_LOCK_read);
+ six_acquire(&lock->dep_map, 1, type == SIX_LOCK_read, ip);
return ret;
}
@@ -338,36 +341,48 @@ static bool __six_relock_type(struct six_lock *lock, enum six_lock_type type,
six_set_owner(lock, type, old, current);
if (type != SIX_LOCK_write)
- six_acquire(&lock->dep_map, 1, type == SIX_LOCK_read);
+ six_acquire(&lock->dep_map, 1, type == SIX_LOCK_read, ip);
return true;
}
-/*
- * We don't see stable performance with SIX_LOCK_SPIN_ON_OWNER enabled, so it's
- * off for now:
- */
-#ifdef SIX_LOCK_SPIN_ON_OWNER
+#ifdef CONFIG_LOCK_SPIN_ON_OWNER
-static inline bool six_optimistic_spin(struct six_lock *lock,
- struct six_lock_waiter *wait)
+static inline bool six_can_spin_on_owner(struct six_lock *lock)
{
- struct task_struct *owner, *task = current;
+ struct task_struct *owner;
+ bool ret;
- switch (wait->lock_want) {
- case SIX_LOCK_read:
- break;
- case SIX_LOCK_intent:
- if (lock->wait_list.next != &wait->list)
- return false;
- break;
- case SIX_LOCK_write:
+ if (need_resched())
return false;
- }
rcu_read_lock();
owner = READ_ONCE(lock->owner);
+ ret = !owner || owner_on_cpu(owner);
+ rcu_read_unlock();
+
+ return ret;
+}
+
+static inline void six_set_nospin(struct six_lock *lock)
+{
+ union six_lock_state old, new;
+ u64 v = READ_ONCE(lock->state.v);
+
+ do {
+ new.v = old.v = v;
+ new.nospin = true;
+ } while ((v = atomic64_cmpxchg(&lock->state.counter, old.v, new.v)) != old.v);
+}
- while (owner && lock->owner == owner) {
+static inline bool six_spin_on_owner(struct six_lock *lock,
+ struct task_struct *owner,
+ u64 end_time)
+{
+ bool ret = true;
+ unsigned loop = 0;
+
+ rcu_read_lock();
+ while (lock->owner == owner) {
/*
* Ensure we emit the owner->on_cpu, dereference _after_
* checking lock->owner still matches owner. If that fails,
@@ -376,27 +391,94 @@ static inline bool six_optimistic_spin(struct six_lock *lock,
*/
barrier();
+ if (!owner_on_cpu(owner) || need_resched()) {
+ ret = false;
+ break;
+ }
+
+ if (!(++loop & 0xf) && (time_after64(sched_clock(), end_time))) {
+ six_set_nospin(lock);
+ ret = false;
+ break;
+ }
+
+ cpu_relax();
+ }
+ rcu_read_unlock();
+
+ return ret;
+}
+
+static inline bool six_optimistic_spin(struct six_lock *lock, enum six_lock_type type)
+{
+ struct task_struct *task = current;
+ u64 end_time;
+
+ if (type == SIX_LOCK_write)
+ return false;
+
+ preempt_disable();
+ if (!six_can_spin_on_owner(lock))
+ goto fail;
+
+ if (!osq_lock(&lock->osq))
+ goto fail;
+
+ end_time = sched_clock() + 10 * NSEC_PER_USEC;
+
+ while (1) {
+ struct task_struct *owner;
+
+ /*
+ * If there's an owner, wait for it to either
+ * release the lock or go to sleep.
+ */
+ owner = READ_ONCE(lock->owner);
+ if (owner && !six_spin_on_owner(lock, owner, end_time))
+ break;
+
+ if (do_six_trylock_type(lock, type, false)) {
+ osq_unlock(&lock->osq);
+ preempt_enable();
+ return true;
+ }
+
/*
- * If we're an RT task that will live-lock because we won't let
+ * When there's no owner, we might have preempted between the
+ * owner acquiring the lock and setting the owner field. If
+ * we're an RT task that will live-lock because we won't let
* the owner complete.
*/
- if (wait->lock_acquired ||
- !owner->on_cpu ||
- rt_task(task) ||
- need_resched())
+ if (!owner && (need_resched() || rt_task(task)))
break;
+ /*
+ * The cpu_relax() call is a compiler barrier which forces
+ * everything in this loop to be re-loaded. We don't need
+ * memory barriers as we'll eventually observe the right
+ * values at the cost of a few extra spins.
+ */
cpu_relax();
}
- rcu_read_unlock();
- return wait->lock_acquired;
+ osq_unlock(&lock->osq);
+fail:
+ preempt_enable();
+
+ /*
+ * If we fell out of the spin path because of need_resched(),
+ * reschedule now, before we try-lock again. This avoids getting
+ * scheduled out right after we obtained the lock.
+ */
+ if (need_resched())
+ schedule();
+
+ return false;
}
#else /* CONFIG_LOCK_SPIN_ON_OWNER */
-static inline bool six_optimistic_spin(struct six_lock *lock,
- struct six_lock_waiter *wait)
+static inline bool six_optimistic_spin(struct six_lock *lock, enum six_lock_type type)
{
return false;
}
@@ -406,7 +488,8 @@ static inline bool six_optimistic_spin(struct six_lock *lock,
noinline
static int __six_lock_type_slowpath(struct six_lock *lock, enum six_lock_type type,
struct six_lock_waiter *wait,
- six_lock_should_sleep_fn should_sleep_fn, void *p)
+ six_lock_should_sleep_fn should_sleep_fn, void *p,
+ unsigned long ip)
{
union six_lock_state old;
int ret = 0;
@@ -417,7 +500,11 @@ static int __six_lock_type_slowpath(struct six_lock *lock, enum six_lock_type ty
smp_mb__after_atomic();
}
- lock_contended(&lock->dep_map, _RET_IP_);
+ trace_contention_begin(lock, 0);
+ lock_contended(&lock->dep_map, ip);
+
+ if (six_optimistic_spin(lock, type))
+ goto out;
wait->task = current;
wait->lock_want = type;
@@ -457,9 +544,6 @@ static int __six_lock_type_slowpath(struct six_lock *lock, enum six_lock_type ty
ret = 0;
}
- if (six_optimistic_spin(lock, wait))
- goto out;
-
while (1) {
set_current_state(TASK_UNINTERRUPTIBLE);
@@ -488,6 +572,7 @@ out:
&lock->state.counter);
six_lock_wakeup(lock, old, SIX_LOCK_read);
}
+ trace_contention_end(lock, 0);
return ret;
}
@@ -495,33 +580,35 @@ out:
__always_inline __flatten
static int __six_lock_type_waiter(struct six_lock *lock, enum six_lock_type type,
struct six_lock_waiter *wait,
- six_lock_should_sleep_fn should_sleep_fn, void *p)
+ six_lock_should_sleep_fn should_sleep_fn, void *p,
+ unsigned long ip)
{
int ret;
wait->start_time = 0;
if (type != SIX_LOCK_write)
- six_acquire(&lock->dep_map, 0, type == SIX_LOCK_read);
+ six_acquire(&lock->dep_map, 0, type == SIX_LOCK_read, ip);
ret = do_six_trylock_type(lock, type, true) ? 0
- : __six_lock_type_slowpath(lock, type, wait, should_sleep_fn, p);
+ : __six_lock_type_slowpath(lock, type, wait, should_sleep_fn, p, ip);
if (ret && type != SIX_LOCK_write)
- six_release(&lock->dep_map);
+ six_release(&lock->dep_map, ip);
if (!ret)
- lock_acquired(&lock->dep_map, _RET_IP_);
+ lock_acquired(&lock->dep_map, ip);
return ret;
}
__always_inline
static int __six_lock_type(struct six_lock *lock, enum six_lock_type type,
- six_lock_should_sleep_fn should_sleep_fn, void *p)
+ six_lock_should_sleep_fn should_sleep_fn, void *p,
+ unsigned long ip)
{
struct six_lock_waiter wait;
- return __six_lock_type_waiter(lock, type, &wait, should_sleep_fn, p);
+ return __six_lock_type_waiter(lock, type, &wait, should_sleep_fn, p, ip);
}
__always_inline __flatten
@@ -540,16 +627,21 @@ static void do_six_unlock_type(struct six_lock *lock, enum six_lock_type type)
smp_mb(); /* between unlocking and checking for waiters */
state.v = READ_ONCE(lock->state.v);
} else {
+ u64 v = l[type].unlock_val;
+
+ if (type != SIX_LOCK_read)
+ v -= lock->state.v & __SIX_VAL(nospin, 1);
+
EBUG_ON(!(lock->state.v & l[type].held_mask));
- state.v = atomic64_add_return_release(l[type].unlock_val,
- &lock->state.counter);
+ state.v = atomic64_add_return_release(v, &lock->state.counter);
}
six_lock_wakeup(lock, state, l[type].unlock_wakeup);
}
__always_inline __flatten
-static void __six_unlock_type(struct six_lock *lock, enum six_lock_type type)
+static void __six_unlock_type(struct six_lock *lock, enum six_lock_type type,
+ unsigned long ip)
{
EBUG_ON(type == SIX_LOCK_write &&
!(lock->state.v & __SIX_LOCK_HELD_intent));
@@ -558,7 +650,7 @@ static void __six_unlock_type(struct six_lock *lock, enum six_lock_type type)
lock->owner != current);
if (type != SIX_LOCK_write)
- six_release(&lock->dep_map);
+ six_release(&lock->dep_map, ip);
if (type == SIX_LOCK_intent &&
lock->intent_lock_recurse) {
@@ -570,38 +662,40 @@ static void __six_unlock_type(struct six_lock *lock, enum six_lock_type type)
}
#define __SIX_LOCK(type) \
-bool six_trylock_##type(struct six_lock *lock) \
+bool six_trylock_ip_##type(struct six_lock *lock, unsigned long ip) \
{ \
- return __six_trylock_type(lock, SIX_LOCK_##type); \
+ return __six_trylock_type(lock, SIX_LOCK_##type, ip); \
} \
-EXPORT_SYMBOL_GPL(six_trylock_##type); \
+EXPORT_SYMBOL_GPL(six_trylock_ip_##type); \
\
-bool six_relock_##type(struct six_lock *lock, u32 seq) \
+bool six_relock_ip_##type(struct six_lock *lock, u32 seq, unsigned long ip)\
{ \
- return __six_relock_type(lock, SIX_LOCK_##type, seq); \
+ return __six_relock_type(lock, SIX_LOCK_##type, seq, ip); \
} \
-EXPORT_SYMBOL_GPL(six_relock_##type); \
+EXPORT_SYMBOL_GPL(six_relock_ip_##type); \
\
-int six_lock_##type(struct six_lock *lock, \
- six_lock_should_sleep_fn should_sleep_fn, void *p) \
+int six_lock_ip_##type(struct six_lock *lock, \
+ six_lock_should_sleep_fn should_sleep_fn, void *p, \
+ unsigned long ip) \
{ \
- return __six_lock_type(lock, SIX_LOCK_##type, should_sleep_fn, p);\
+ return __six_lock_type(lock, SIX_LOCK_##type, should_sleep_fn, p, ip);\
} \
-EXPORT_SYMBOL_GPL(six_lock_##type); \
+EXPORT_SYMBOL_GPL(six_lock_ip_##type); \
\
-int six_lock_waiter_##type(struct six_lock *lock, \
+int six_lock_ip_waiter_##type(struct six_lock *lock, \
struct six_lock_waiter *wait, \
- six_lock_should_sleep_fn should_sleep_fn, void *p)\
+ six_lock_should_sleep_fn should_sleep_fn, void *p,\
+ unsigned long ip) \
{ \
- return __six_lock_type_waiter(lock, SIX_LOCK_##type, wait, should_sleep_fn, p);\
+ return __six_lock_type_waiter(lock, SIX_LOCK_##type, wait, should_sleep_fn, p, ip);\
} \
-EXPORT_SYMBOL_GPL(six_lock_waiter_##type); \
+EXPORT_SYMBOL_GPL(six_lock_ip_waiter_##type); \
\
-void six_unlock_##type(struct six_lock *lock) \
+void six_unlock_ip_##type(struct six_lock *lock, unsigned long ip) \
{ \
- __six_unlock_type(lock, SIX_LOCK_##type); \
+ __six_unlock_type(lock, SIX_LOCK_##type, ip); \
} \
-EXPORT_SYMBOL_GPL(six_unlock_##type);
+EXPORT_SYMBOL_GPL(six_unlock_ip_##type);
__SIX_LOCK(read)
__SIX_LOCK(intent)
@@ -672,7 +766,7 @@ void six_lock_increment(struct six_lock *lock, enum six_lock_type type)
{
const struct six_lock_vals l[] = LOCK_VALS;
- six_acquire(&lock->dep_map, 0, type == SIX_LOCK_read);
+ six_acquire(&lock->dep_map, 0, type == SIX_LOCK_read, _RET_IP_);
/* XXX: assert already locked, and that we don't overflow: */
diff --git a/linux/string_helpers.c b/linux/string_helpers.c
index 29c498ad..0810ca13 100644
--- a/linux/string_helpers.c
+++ b/linux/string_helpers.c
@@ -14,7 +14,6 @@
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/limits.h>
-#include <linux/printbuf.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/string_helpers.h>