Skip to content

Commit

Permalink
locking/lockdep: Rework FS_RECLAIM annotation
Browse files Browse the repository at this point in the history
A while ago someone, and I cannot find the email just now, asked if we
could not implement the RECLAIM_FS inversion stuff with a 'fake' lock
like we use for other things like workqueues etc. I think this should
be possible which allows reducing the 'irq' states and will reduce the
amount of __bfs() lookups we do.

Removing the 1 IRQ state results in 4 less __bfs() walks per
dependency, improving lockdep performance. And by moving this
annotation out of the lockdep code it becomes easier for the mm people
to extend.

Signed-off-by: Peter Zijlstra (Intel) <[email protected]>
Cc: Byungchul Park <[email protected]>
Cc: Linus Torvalds <[email protected]>
Cc: Mel Gorman <[email protected]>
Cc: Michal Hocko <[email protected]>
Cc: Nikolay Borisov <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Signed-off-by: Ingo Molnar <[email protected]>
  • Loading branch information
Peter Zijlstra authored and Ingo Molnar committed Aug 10, 2017
1 parent a9668cd commit d92a8cf
Show file tree
Hide file tree
Showing 10 changed files with 75 additions and 120 deletions.
5 changes: 3 additions & 2 deletions drivers/gpu/drm/i915/i915_debugfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

#include <linux/debugfs.h>
#include <linux/sort.h>
#include <linux/sched/mm.h>
#include "intel_drv.h"

static inline struct drm_i915_private *node_to_i915(struct drm_info_node *node)
Expand Down Expand Up @@ -4331,7 +4332,7 @@ i915_drop_caches_set(void *data, u64 val)
mutex_unlock(&dev->struct_mutex);
}

lockdep_set_current_reclaim_state(GFP_KERNEL);
fs_reclaim_acquire(GFP_KERNEL);
if (val & DROP_BOUND)
i915_gem_shrink(dev_priv, LONG_MAX, I915_SHRINK_BOUND);

Expand All @@ -4340,7 +4341,7 @@ i915_drop_caches_set(void *data, u64 val)

if (val & DROP_SHRINK_ALL)
i915_gem_shrink_all(dev_priv);
lockdep_clear_current_reclaim_state();
fs_reclaim_release(GFP_KERNEL);

if (val & DROP_FREED) {
synchronize_rcu();
Expand Down
11 changes: 2 additions & 9 deletions include/linux/lockdep.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ extern int lock_stat;
* We'd rather not expose kernel/lockdep_states.h this wide, but we do need
* the total number of states... :-(
*/
#define XXX_LOCK_USAGE_STATES (1+3*4)
#define XXX_LOCK_USAGE_STATES (1+2*4)

/*
* NR_LOCKDEP_CACHING_CLASSES ... Number of classes
Expand Down Expand Up @@ -363,10 +363,6 @@ static inline void lock_set_subclass(struct lockdep_map *lock,

extern void lock_downgrade(struct lockdep_map *lock, unsigned long ip);

extern void lockdep_set_current_reclaim_state(gfp_t gfp_mask);
extern void lockdep_clear_current_reclaim_state(void);
extern void lockdep_trace_alloc(gfp_t mask);

struct pin_cookie { unsigned int val; };

#define NIL_COOKIE (struct pin_cookie){ .val = 0U, }
Expand All @@ -375,7 +371,7 @@ extern struct pin_cookie lock_pin_lock(struct lockdep_map *lock);
extern void lock_repin_lock(struct lockdep_map *lock, struct pin_cookie);
extern void lock_unpin_lock(struct lockdep_map *lock, struct pin_cookie);

# define INIT_LOCKDEP .lockdep_recursion = 0, .lockdep_reclaim_gfp = 0,
# define INIT_LOCKDEP .lockdep_recursion = 0,

#define lockdep_depth(tsk) (debug_locks ? (tsk)->lockdep_depth : 0)

Expand Down Expand Up @@ -416,9 +412,6 @@ static inline void lockdep_on(void)
# define lock_downgrade(l, i) do { } while (0)
# define lock_set_class(l, n, k, s, i) do { } while (0)
# define lock_set_subclass(l, s, i) do { } while (0)
# define lockdep_set_current_reclaim_state(g) do { } while (0)
# define lockdep_clear_current_reclaim_state() do { } while (0)
# define lockdep_trace_alloc(g) do { } while (0)
# define lockdep_info() do { } while (0)
# define lockdep_init_map(lock, name, key, sub) \
do { (void)(name); (void)(key); } while (0)
Expand Down
1 change: 0 additions & 1 deletion include/linux/sched.h
Original file line number Diff line number Diff line change
Expand Up @@ -846,7 +846,6 @@ struct task_struct {
int lockdep_depth;
unsigned int lockdep_recursion;
struct held_lock held_locks[MAX_LOCK_DEPTH];
gfp_t lockdep_reclaim_gfp;
#endif

#ifdef CONFIG_UBSAN
Expand Down
8 changes: 8 additions & 0 deletions include/linux/sched/mm.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,14 @@ static inline gfp_t current_gfp_context(gfp_t flags)
return flags;
}

#ifdef CONFIG_LOCKDEP
extern void fs_reclaim_acquire(gfp_t gfp_mask);
extern void fs_reclaim_release(gfp_t gfp_mask);
#else
static inline void fs_reclaim_acquire(gfp_t gfp_mask) { }
static inline void fs_reclaim_release(gfp_t gfp_mask) { }
#endif

static inline unsigned int memalloc_noio_save(void)
{
unsigned int flags = current->flags & PF_MEMALLOC_NOIO;
Expand Down
95 changes: 1 addition & 94 deletions kernel/locking/lockdep.c
Original file line number Diff line number Diff line change
Expand Up @@ -344,14 +344,12 @@ EXPORT_SYMBOL(lockdep_on);
#if VERBOSE
# define HARDIRQ_VERBOSE 1
# define SOFTIRQ_VERBOSE 1
# define RECLAIM_VERBOSE 1
#else
# define HARDIRQ_VERBOSE 0
# define SOFTIRQ_VERBOSE 0
# define RECLAIM_VERBOSE 0
#endif

#if VERBOSE || HARDIRQ_VERBOSE || SOFTIRQ_VERBOSE || RECLAIM_VERBOSE
#if VERBOSE || HARDIRQ_VERBOSE || SOFTIRQ_VERBOSE
/*
* Quick filtering for interesting events:
*/
Expand Down Expand Up @@ -2567,14 +2565,6 @@ static int SOFTIRQ_verbose(struct lock_class *class)
return 0;
}

static int RECLAIM_FS_verbose(struct lock_class *class)
{
#if RECLAIM_VERBOSE
return class_filter(class);
#endif
return 0;
}

#define STRICT_READ_CHECKS 1

static int (*state_verbose_f[])(struct lock_class *class) = {
Expand Down Expand Up @@ -2870,57 +2860,6 @@ void trace_softirqs_off(unsigned long ip)
debug_atomic_inc(redundant_softirqs_off);
}

static void __lockdep_trace_alloc(gfp_t gfp_mask, unsigned long flags)
{
struct task_struct *curr = current;

if (unlikely(!debug_locks))
return;

gfp_mask = current_gfp_context(gfp_mask);

/* no reclaim without waiting on it */
if (!(gfp_mask & __GFP_DIRECT_RECLAIM))
return;

/* this guy won't enter reclaim */
if ((curr->flags & PF_MEMALLOC) && !(gfp_mask & __GFP_NOMEMALLOC))
return;

/* We're only interested __GFP_FS allocations for now */
if (!(gfp_mask & __GFP_FS) || (curr->flags & PF_MEMALLOC_NOFS))
return;

/*
* Oi! Can't be having __GFP_FS allocations with IRQs disabled.
*/
if (DEBUG_LOCKS_WARN_ON(irqs_disabled_flags(flags)))
return;

/* Disable lockdep if explicitly requested */
if (gfp_mask & __GFP_NOLOCKDEP)
return;

mark_held_locks(curr, RECLAIM_FS);
}

static void check_flags(unsigned long flags);

void lockdep_trace_alloc(gfp_t gfp_mask)
{
unsigned long flags;

if (unlikely(current->lockdep_recursion))
return;

raw_local_irq_save(flags);
check_flags(flags);
current->lockdep_recursion = 1;
__lockdep_trace_alloc(gfp_mask, flags);
current->lockdep_recursion = 0;
raw_local_irq_restore(flags);
}

static int mark_irqflags(struct task_struct *curr, struct held_lock *hlock)
{
/*
Expand Down Expand Up @@ -2966,22 +2905,6 @@ static int mark_irqflags(struct task_struct *curr, struct held_lock *hlock)
}
}

/*
* We reuse the irq context infrastructure more broadly as a general
* context checking code. This tests GFP_FS recursion (a lock taken
* during reclaim for a GFP_FS allocation is held over a GFP_FS
* allocation).
*/
if (!hlock->trylock && (curr->lockdep_reclaim_gfp & __GFP_FS)) {
if (hlock->read) {
if (!mark_lock(curr, hlock, LOCK_USED_IN_RECLAIM_FS_READ))
return 0;
} else {
if (!mark_lock(curr, hlock, LOCK_USED_IN_RECLAIM_FS))
return 0;
}
}

return 1;
}

Expand Down Expand Up @@ -3040,10 +2963,6 @@ static inline int separate_irq_context(struct task_struct *curr,
return 0;
}

void lockdep_trace_alloc(gfp_t gfp_mask)
{
}

#endif /* defined(CONFIG_TRACE_IRQFLAGS) && defined(CONFIG_PROVE_LOCKING) */

/*
Expand Down Expand Up @@ -3952,18 +3871,6 @@ void lock_unpin_lock(struct lockdep_map *lock, struct pin_cookie cookie)
}
EXPORT_SYMBOL_GPL(lock_unpin_lock);

void lockdep_set_current_reclaim_state(gfp_t gfp_mask)
{
current->lockdep_reclaim_gfp = current_gfp_context(gfp_mask);
}
EXPORT_SYMBOL_GPL(lockdep_set_current_reclaim_state);

void lockdep_clear_current_reclaim_state(void)
{
current->lockdep_reclaim_gfp = 0;
}
EXPORT_SYMBOL_GPL(lockdep_clear_current_reclaim_state);

#ifdef CONFIG_LOCK_STAT
static int
print_lock_contention_bug(struct task_struct *curr, struct lockdep_map *lock,
Expand Down
1 change: 0 additions & 1 deletion kernel/locking/lockdep_states.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,3 @@
*/
LOCKDEP_STATE(HARDIRQ)
LOCKDEP_STATE(SOFTIRQ)
LOCKDEP_STATE(RECLAIM_FS)
49 changes: 46 additions & 3 deletions mm/page_alloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
#include <linux/kthread.h>
#include <linux/memcontrol.h>
#include <linux/ftrace.h>
#include <linux/lockdep.h>

#include <asm/sections.h>
#include <asm/tlbflush.h>
Expand Down Expand Up @@ -3490,6 +3491,47 @@ should_compact_retry(struct alloc_context *ac, unsigned int order, int alloc_fla
}
#endif /* CONFIG_COMPACTION */

#ifdef CONFIG_LOCKDEP
struct lockdep_map __fs_reclaim_map =
STATIC_LOCKDEP_MAP_INIT("fs_reclaim", &__fs_reclaim_map);

static bool __need_fs_reclaim(gfp_t gfp_mask)
{
gfp_mask = current_gfp_context(gfp_mask);

/* no reclaim without waiting on it */
if (!(gfp_mask & __GFP_DIRECT_RECLAIM))
return false;

/* this guy won't enter reclaim */
if ((current->flags & PF_MEMALLOC) && !(gfp_mask & __GFP_NOMEMALLOC))
return false;

/* We're only interested __GFP_FS allocations for now */
if (!(gfp_mask & __GFP_FS))
return false;

if (gfp_mask & __GFP_NOLOCKDEP)
return false;

return true;
}

void fs_reclaim_acquire(gfp_t gfp_mask)
{
if (__need_fs_reclaim(gfp_mask))
lock_map_acquire(&__fs_reclaim_map);
}
EXPORT_SYMBOL_GPL(fs_reclaim_acquire);

void fs_reclaim_release(gfp_t gfp_mask)
{
if (__need_fs_reclaim(gfp_mask))
lock_map_release(&__fs_reclaim_map);
}
EXPORT_SYMBOL_GPL(fs_reclaim_release);
#endif

/* Perform direct synchronous page reclaim */
static int
__perform_reclaim(gfp_t gfp_mask, unsigned int order,
Expand All @@ -3504,15 +3546,15 @@ __perform_reclaim(gfp_t gfp_mask, unsigned int order,
/* We now go into synchronous reclaim */
cpuset_memory_pressure_bump();
noreclaim_flag = memalloc_noreclaim_save();
lockdep_set_current_reclaim_state(gfp_mask);
fs_reclaim_acquire(gfp_mask);
reclaim_state.reclaimed_slab = 0;
current->reclaim_state = &reclaim_state;

progress = try_to_free_pages(ac->zonelist, order, gfp_mask,
ac->nodemask);

current->reclaim_state = NULL;
lockdep_clear_current_reclaim_state();
fs_reclaim_release(gfp_mask);
memalloc_noreclaim_restore(noreclaim_flag);

cond_resched();
Expand Down Expand Up @@ -4041,7 +4083,8 @@ static inline bool prepare_alloc_pages(gfp_t gfp_mask, unsigned int order,
*alloc_flags |= ALLOC_CPUSET;
}

lockdep_trace_alloc(gfp_mask);
fs_reclaim_acquire(gfp_mask);
fs_reclaim_release(gfp_mask);

might_sleep_if(gfp_mask & __GFP_DIRECT_RECLAIM);

Expand Down
6 changes: 5 additions & 1 deletion mm/slab.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ struct kmem_cache {
#include <linux/kasan.h>
#include <linux/kmemleak.h>
#include <linux/random.h>
#include <linux/sched/mm.h>

/*
* State of the slab allocator.
Expand Down Expand Up @@ -412,7 +413,10 @@ static inline struct kmem_cache *slab_pre_alloc_hook(struct kmem_cache *s,
gfp_t flags)
{
flags &= gfp_allowed_mask;
lockdep_trace_alloc(flags);

fs_reclaim_acquire(flags);
fs_reclaim_release(flags);

might_sleep_if(gfpflags_allow_blocking(flags));

if (should_failslab(s, flags))
Expand Down
6 changes: 4 additions & 2 deletions mm/slob.c
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,8 @@ __do_kmalloc_node(size_t size, gfp_t gfp, int node, unsigned long caller)

gfp &= gfp_allowed_mask;

lockdep_trace_alloc(gfp);
fs_reclaim_acquire(gfp);
fs_reclaim_release(gfp);

if (size < PAGE_SIZE - align) {
if (!size)
Expand Down Expand Up @@ -538,7 +539,8 @@ static void *slob_alloc_node(struct kmem_cache *c, gfp_t flags, int node)

flags &= gfp_allowed_mask;

lockdep_trace_alloc(flags);
fs_reclaim_acquire(flags);
fs_reclaim_release(flags);

if (c->size < PAGE_SIZE) {
b = slob_alloc(c->size, flags, c->align, node);
Expand Down
Loading

0 comments on commit d92a8cf

Please sign in to comment.