net/netfilter/nf_conntrack_core.c

Source file repositories/reference/linux-study-clean/net/netfilter/nf_conntrack_core.c

File Facts

System
Linux kernel
Corpus path
net/netfilter/nf_conntrack_core.c
Extension
.c
Size
74082 bytes
Lines
2791
Domain
Networking Core
Bucket
Sockets, Protocols, Packet Path, And Network Policy
Inferred role
Networking Core: exported/initcall integration point
Status
integration implementation candidate

Why This File Exists

Networking stack implementation surface: socket APIs, protocol dispatch, packet flow, routing, filtering, and network namespaces.

Dependency Surface

Detected Declarations

Annotated Snippet

struct conntrack_gc_work {
	struct delayed_work	dwork;
	u32			next_bucket;
	u32			avg_timeout;
	u32			count;
	u32			start_time;
	bool			exiting;
	bool			early_drop;
};

static __read_mostly struct kmem_cache *nf_conntrack_cachep;
static DEFINE_SPINLOCK(nf_conntrack_locks_all_lock);
static __read_mostly bool nf_conntrack_locks_all;

/* serialize hash resizes and nf_ct_iterate_cleanup */
static DEFINE_MUTEX(nf_conntrack_mutex);

#define GC_SCAN_INTERVAL_MAX	(60ul * HZ)
#define GC_SCAN_INTERVAL_MIN	(1ul * HZ)

/* clamp timeouts to this value (TCP unacked) */
#define GC_SCAN_INTERVAL_CLAMP	(300ul * HZ)

/* Initial bias pretending we have 100 entries at the upper bound so we don't
 * wakeup often just because we have three entries with a 1s timeout while still
 * allowing non-idle machines to wakeup more often when needed.
 */
#define GC_SCAN_INITIAL_COUNT	100
#define GC_SCAN_INTERVAL_INIT	GC_SCAN_INTERVAL_MAX

#define GC_SCAN_MAX_DURATION	msecs_to_jiffies(10)
#define GC_SCAN_EXPIRED_MAX	(64000u / HZ)

#define MIN_CHAINLEN	50u
#define MAX_CHAINLEN	(80u - MIN_CHAINLEN)

static struct conntrack_gc_work conntrack_gc_work;

void nf_conntrack_lock(spinlock_t *lock) __acquires(lock)
{
	/* 1) Acquire the lock */
	spin_lock(lock);

	/* 2) read nf_conntrack_locks_all, with ACQUIRE semantics
	 * It pairs with the smp_store_release() in nf_conntrack_all_unlock()
	 */
	if (likely(smp_load_acquire(&nf_conntrack_locks_all) == false))
		return;

	/* fast path failed, unlock */
	spin_unlock(lock);

	/* Slow path 1) get global lock */
	spin_lock(&nf_conntrack_locks_all_lock);

	/* Slow path 2) get the lock we want */
	spin_lock(lock);

	/* Slow path 3) release the global lock */
	spin_unlock(&nf_conntrack_locks_all_lock);
}
EXPORT_SYMBOL_GPL(nf_conntrack_lock);

static void nf_conntrack_double_unlock(unsigned int h1, unsigned int h2)
{
	h1 %= CONNTRACK_LOCKS;
	h2 %= CONNTRACK_LOCKS;
	spin_unlock(&nf_conntrack_locks[h1]);
	if (h1 != h2)
		spin_unlock(&nf_conntrack_locks[h2]);
}

/* return true if we need to recompute hashes (in case hash table was resized) */
static bool nf_conntrack_double_lock(unsigned int h1, unsigned int h2,
				     unsigned int sequence)
{
	h1 %= CONNTRACK_LOCKS;
	h2 %= CONNTRACK_LOCKS;
	if (h1 <= h2) {
		nf_conntrack_lock(&nf_conntrack_locks[h1]);
		if (h1 != h2)
			spin_lock_nested(&nf_conntrack_locks[h2],
					 SINGLE_DEPTH_NESTING);
	} else {
		nf_conntrack_lock(&nf_conntrack_locks[h2]);
		spin_lock_nested(&nf_conntrack_locks[h1],
				 SINGLE_DEPTH_NESTING);
	}
	if (read_seqcount_retry(&nf_conntrack_generation, sequence)) {
		nf_conntrack_double_unlock(h1, h2);

Annotation

Implementation Notes