From 2c1962aba6e73dd0c430636082145f92d636ff13 Mon Sep 17 00:00:00 2001 From: Conrad Meyer Date: Sat, 28 Dec 2019 01:35:32 +0000 Subject: [PATCH] epoch.9: Add missing functions, clean up documentation Various rototilling. --- share/man/man9/epoch.9 | 191 ++++++++++++++++++++++++++++------------- 1 file changed, 133 insertions(+), 58 deletions(-) diff --git a/share/man/man9/epoch.9 b/share/man/man9/epoch.9 index 48ad09d2e249..ca3886c9860c 100644 --- a/share/man/man9/epoch.9 +++ b/share/man/man9/epoch.9 @@ -26,7 +26,7 @@ .\" .\" $FreeBSD$ .\" -.Dd June 28, 2019 +.Dd December 27, 2019 .Dt EPOCH 9 .Os .Sh NAME @@ -37,34 +37,58 @@ .Nm epoch_enter , .Nm epoch_exit , .Nm epoch_wait , +.Nm epoch_enter_preempt , +.Nm epoch_exit_preempt , +.Nm epoch_wait_preempt , .Nm epoch_call , .Nm epoch_drain_callbacks , .Nm in_epoch , +.Nm in_epoch_verbose , .Nd kernel epoch based reclamation .Sh SYNOPSIS .In sys/param.h .In sys/proc.h .In sys/epoch.h +.\" Types +.Bd -literal +struct epoch; /* Opaque */ +.Ed +.Vt typedef "struct epoch *epoch_t" ; +.Bd -literal +struct epoch_context { + void *data[2]; +}; +.Ed +.Vt typedef "struct epoch_context *epoch_context_t" ; +.Bd -literal +struct epoch_tracker; /* Opaque */ +.Ed +.Vt typedef "struct epoch_tracker *epoch_tracker_t" ; +.\" Declarations .Ft epoch_t -.Fn epoch_alloc "int flags" +.Fn epoch_alloc "const char *name" "int flags" +.Ft void +.Fn epoch_free "epoch_t epoch" .Ft void .Fn epoch_enter "epoch_t epoch" .Ft void -.Fn epoch_enter_preempt "epoch_t epoch" "epoch_tracker_t et" -.Ft void .Fn epoch_exit "epoch_t epoch" .Ft void -.Fn epoch_exit_preempt "epoch_t epoch" "epoch_tracker_t et" -.Ft void .Fn epoch_wait "epoch_t epoch" .Ft void +.Fn epoch_enter_preempt "epoch_t epoch" "epoch_tracker_t et" +.Ft void +.Fn epoch_exit_preempt "epoch_t epoch" "epoch_tracker_t et" +.Ft void .Fn epoch_wait_preempt "epoch_t epoch" .Ft void -.Fn epoch_call "epoch_t epoch" "epoch_context_t ctx" "void (*callback) (epoch_context_t)" +.Fn epoch_call "epoch_t epoch" "epoch_context_t ctx" "void (*callback)(epoch_context_t)" .Ft void .Fn epoch_drain_callbacks "epoch_t epoch" .Ft int .Fn in_epoch "epoch_t epoch" +.Ft int +.Fn in_epoch_verbose "epoch_t epoch" "int dump_onfail" .Sh DESCRIPTION Epochs are used to guarantee liveness and immutability of data by deferring reclamation and mutation until a grace period has elapsed. @@ -72,58 +96,105 @@ Epochs do not have any lock ordering issues. Entering and leaving an epoch section will never block. .Pp Epochs are allocated with -.Fn epoch_alloc -and freed with +.Fn epoch_alloc . +The +.Fa name +argument is used for debugging convenience when the +.Cd EPOCH_TRACE +kernel option is configured. +By default, epochs do not allow preemption during sections. +By default mutexes cannot be held across +.Fn epoch_wait_preempt . +The +.Fa flags +specified are formed by +.Em OR Ns 'ing +the following values: +.Bl -tag -offset indent -width Ds +.It Dv EPOCH_LOCKED +Permit holding mutexes across +.Fn epoch_wait_preempt +(requires +.Dv EPOCH_PREEMPT ) . +When doing this one must be cautious of creating a situation where a deadlock +is possible. +.It Dv EPOCH_PREEMPT +The +.Vt epoch +will allow preemption during sections. +Only non-sleepable locks may be acquired during a preemptible epoch. +The functions +.Fn epoch_enter_preempt , +.Fn epoch_exit_preempt , +and +.Fn epoch_wait_preempt +must be used in place of +.Fn epoch_enter , +.Fn epoch_exit , +and +.Fn epoch_wait , +respectively. +.El +.Pp +.Vt epoch Ns s +are freed with .Fn epoch_free . -The flags passed to epoch_alloc determine whether preemption is -allowed during a section or not (the default), as specified by -EPOCH_PREEMPT. +.Pp Threads indicate the start of an epoch critical section by calling -.Fn epoch_enter . -The end of a critical section is indicated by calling -.Fn epoch_exit . -The _preempt variants can be used around code which requires preemption. -A thread can wait until a grace period has elapsed -since any threads have entered -the epoch by calling -.Fn epoch_wait -or -.Fn epoch_wait_preempt , -depending on the epoch_type. -The use of a default epoch type allows one to use -.Fn epoch_wait -which is guaranteed to have much shorter completion times since -we know that none of the threads in an epoch section will be preempted -before completing its section. -If the thread can't sleep or is otherwise in a performance sensitive -path it can ensure that a grace period has elapsed by calling -.Fn epoch_call -with a callback with any work that needs to wait for an epoch to elapse. -Only non-sleepable locks can be acquired during a section protected by +.Fn epoch_enter +(or +.Fn epoch_enter_preempt +for preemptible epochs). +Threads call +.Fn epoch_exit +(or +.Fn epoch_exit_preempt +for preemptible epochs) +to indicate the end of a critical section. +.Vt struct epoch_tracker Ns s +are stack objects whose pointers are passed to .Fn epoch_enter_preempt and -.Fn epoch_exit_preempt . +.Fn epoch_exit_preempt +(much like +.Vt struct rm_priotracker ) . +.Pp +Threads can defer work until a grace period has expired since any thread has +entered the epoch either synchronously or asynchronously. +.Fn epoch_call +defers work asynchronously by invoking the provided +.Fa callback +at a later time. +.Fn epoch_wait +(or +.Fn epoch_wait_preempt ) +blocks the current thread until the grace period has expired and the work can be +done safely. +.Pp +Default, non-preemptible epoch wait +.Fn ( epoch_wait ) +is guaranteed to have much shorter completion times relative to +preemptible epoch wait +.Fn ( epoch_wait_preempt ) . +(In the default type, none of the threads in an epoch section will be preempted +before completing its section.) +.Pp INVARIANTS can assert that a thread is in an epoch by using .Fn in_epoch . +.Fn in_epoch "epoch" +is equivalent to invoking +.Fn in_epoch_verbose "epoch" "0" . +If +.Cd EPOCH_TRACE +is enabled, +.Fn in_epoch_verbose "epoch" "1" +provides additional verbose debugging information. .Pp The epoch API currently does not support sleeping in epoch_preempt sections. A caller should never call .Fn epoch_wait in the middle of an epoch section for the same epoch as this will lead to a deadlock. .Pp -By default mutexes cannot be held across -.Fn epoch_wait_preempt . -To permit this the epoch must be allocated with -EPOCH_LOCKED. -When doing this one must be cautious of creating a situation where a deadlock is -possible. Note that epochs are not a straight replacement for read locks. -Callers must use safe list and tailq traversal routines in an epoch (see ck_queue). -When modifying a list referenced from an epoch section safe removal -routines must be used and the caller can no longer modify a list entry -in place. -An item to be modified must be handled with copy on write -and frees must be deferred until after a grace period has elapsed. -.Pp The .Fn epoch_drain_callbacks function is used to drain all pending callbacks which have been invoked by prior @@ -140,12 +211,18 @@ This function can sleep and is not optimized for performance. will return 1 if curthread is in curepoch, 0 otherwise. .Sh CAVEATS One must be cautious when using -.Fn epoch_wait_preempt -threads are pinned during epoch sections so if a thread in a section is then -preempted by a higher priority compute bound thread on that CPU it can be -prevented from leaving the section. -Thus the wait time for the waiter is -potentially unbounded. +.Fn epoch_wait_preempt . +Threads are pinned during epoch sections, so if a thread in a section is then +preempted by a higher priority compute bound thread on that CPU, it can be +prevented from leaving the section indefinitely. +.Pp +Epochs are not a straight replacement for read locks. +Callers must use safe list and tailq traversal routines in an epoch (see ck_queue). +When modifying a list referenced from an epoch section safe removal +routines must be used and the caller can no longer modify a list entry +in place. +An item to be modified must be handled with copy on write +and frees must be deferred until after a grace period has elapsed. .Sh EXAMPLES Async free example: Thread 1: @@ -154,8 +231,8 @@ int in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_laddr *laddr, struct ucred *cred) { - /* ... */ - epoch_enter(net_epoch); + /* ... */ + epoch_enter(net_epoch); CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { sa = ifa->ifa_addr; if (sa->sa_family != AF_INET) @@ -167,7 +244,7 @@ in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_laddr *laddr, } } epoch_exit(net_epoch); - /* ... */ + /* ... */ } .Ed Thread 2: @@ -198,8 +275,6 @@ and then defers deletion. More general mutation or a synchronous free would have to follow a call to .Fn epoch_wait . -.Sh ERRORS -None. .Sh NOTES The .Nm