netfilter: ecache: document extension area access rules
Once ct->ext gets free'd via kfree() rather than kfree_rcu we can't access the extension area anymore without owning the conntrack. This is a special case: The worker is walking the pcpu dying list while holding dying list lock: Neither ct nor ct->ext can be free'd until after the walk has completed. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
This commit is contained in:
Родитель
f8615bf8a3
Коммит
63f55acf7b
|
@ -30,6 +30,7 @@
|
||||||
static DEFINE_MUTEX(nf_ct_ecache_mutex);
|
static DEFINE_MUTEX(nf_ct_ecache_mutex);
|
||||||
|
|
||||||
#define ECACHE_RETRY_WAIT (HZ/10)
|
#define ECACHE_RETRY_WAIT (HZ/10)
|
||||||
|
#define ECACHE_STACK_ALLOC (256 / sizeof(void *))
|
||||||
|
|
||||||
enum retry_state {
|
enum retry_state {
|
||||||
STATE_CONGESTED,
|
STATE_CONGESTED,
|
||||||
|
@ -39,11 +40,11 @@ enum retry_state {
|
||||||
|
|
||||||
static enum retry_state ecache_work_evict_list(struct ct_pcpu *pcpu)
|
static enum retry_state ecache_work_evict_list(struct ct_pcpu *pcpu)
|
||||||
{
|
{
|
||||||
struct nf_conn *refs[16];
|
struct nf_conn *refs[ECACHE_STACK_ALLOC];
|
||||||
|
enum retry_state ret = STATE_DONE;
|
||||||
struct nf_conntrack_tuple_hash *h;
|
struct nf_conntrack_tuple_hash *h;
|
||||||
struct hlist_nulls_node *n;
|
struct hlist_nulls_node *n;
|
||||||
unsigned int evicted = 0;
|
unsigned int evicted = 0;
|
||||||
enum retry_state ret = STATE_DONE;
|
|
||||||
|
|
||||||
spin_lock(&pcpu->lock);
|
spin_lock(&pcpu->lock);
|
||||||
|
|
||||||
|
@ -54,10 +55,22 @@ static enum retry_state ecache_work_evict_list(struct ct_pcpu *pcpu)
|
||||||
if (!nf_ct_is_confirmed(ct))
|
if (!nf_ct_is_confirmed(ct))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
/* This ecache access is safe because the ct is on the
|
||||||
|
* pcpu dying list and we hold the spinlock -- the entry
|
||||||
|
* cannot be free'd until after the lock is released.
|
||||||
|
*
|
||||||
|
* This is true even if ct has a refcount of 0: the
|
||||||
|
* cpu that is about to free the entry must remove it
|
||||||
|
* from the dying list and needs the lock to do so.
|
||||||
|
*/
|
||||||
e = nf_ct_ecache_find(ct);
|
e = nf_ct_ecache_find(ct);
|
||||||
if (!e || e->state != NFCT_ECACHE_DESTROY_FAIL)
|
if (!e || e->state != NFCT_ECACHE_DESTROY_FAIL)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
/* ct is in NFCT_ECACHE_DESTROY_FAIL state, this means
|
||||||
|
* the worker owns this entry: the ct will remain valid
|
||||||
|
* until the worker puts its ct reference.
|
||||||
|
*/
|
||||||
if (nf_conntrack_event(IPCT_DESTROY, ct)) {
|
if (nf_conntrack_event(IPCT_DESTROY, ct)) {
|
||||||
ret = STATE_CONGESTED;
|
ret = STATE_CONGESTED;
|
||||||
break;
|
break;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче