netfilter: nf_tables: defer all object release via rcu
Now that all objects are released in the reverse order via the transaction infrastructure, we can enqueue the release via call_rcu to save one synchronize_rcu. For small rule-sets loaded via nft -f, it now takes around 50ms less here. Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
This commit is contained in:
		
					parent
					
						
							
								128ad3322b
							
						
					
				
			
			
				commit
				
					
						c7c32e72cb
					
				
			
		
					 2 changed files with 56 additions and 39 deletions
				
			
		|  | @ -393,12 +393,14 @@ struct nft_rule { | |||
| /**
 | ||||
|  *	struct nft_trans - nf_tables object update in transaction | ||||
|  * | ||||
|  *	@rcu_head: rcu head to defer release of transaction data | ||||
|  *	@list: used internally | ||||
|  *	@msg_type: message type | ||||
|  *	@ctx: transaction context | ||||
|  *	@data: internal information related to the transaction | ||||
|  */ | ||||
| struct nft_trans { | ||||
| 	struct rcu_head			rcu_head; | ||||
| 	struct list_head		list; | ||||
| 	int				msg_type; | ||||
| 	struct nft_ctx			ctx; | ||||
|  |  | |||
|  | @ -3298,6 +3298,30 @@ static void nft_chain_commit_update(struct nft_trans *trans) | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* Schedule objects for release via rcu to make sure no packets are accesing
 | ||||
|  * removed rules. | ||||
|  */ | ||||
| static void nf_tables_commit_release_rcu(struct rcu_head *rt) | ||||
| { | ||||
| 	struct nft_trans *trans = container_of(rt, struct nft_trans, rcu_head); | ||||
| 
 | ||||
| 	switch (trans->msg_type) { | ||||
| 	case NFT_MSG_DELTABLE: | ||||
| 		nf_tables_table_destroy(&trans->ctx); | ||||
| 		break; | ||||
| 	case NFT_MSG_DELCHAIN: | ||||
| 		nf_tables_chain_destroy(trans->ctx.chain); | ||||
| 		break; | ||||
| 	case NFT_MSG_DELRULE: | ||||
| 		nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans)); | ||||
| 		break; | ||||
| 	case NFT_MSG_DELSET: | ||||
| 		nft_set_destroy(nft_trans_set(trans)); | ||||
| 		break; | ||||
| 	} | ||||
| 	kfree(trans); | ||||
| } | ||||
| 
 | ||||
| static int nf_tables_commit(struct sk_buff *skb) | ||||
| { | ||||
| 	struct net *net = sock_net(skb->sk); | ||||
|  | @ -3397,32 +3421,39 @@ static int nf_tables_commit(struct sk_buff *skb) | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* Make sure we don't see any packet traversing old rules */ | ||||
| 	synchronize_rcu(); | ||||
| 
 | ||||
| 	/* Now we can safely release unused old rules */ | ||||
| 	list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { | ||||
| 		switch (trans->msg_type) { | ||||
| 		case NFT_MSG_DELTABLE: | ||||
| 			nf_tables_table_destroy(&trans->ctx); | ||||
| 			break; | ||||
| 		case NFT_MSG_DELCHAIN: | ||||
| 			nf_tables_chain_destroy(trans->ctx.chain); | ||||
| 			break; | ||||
| 		case NFT_MSG_DELRULE: | ||||
| 			nf_tables_rule_destroy(&trans->ctx, | ||||
| 					       nft_trans_rule(trans)); | ||||
| 			break; | ||||
| 		case NFT_MSG_DELSET: | ||||
| 			nft_set_destroy(nft_trans_set(trans)); | ||||
| 			break; | ||||
| 		} | ||||
| 		nft_trans_destroy(trans); | ||||
| 		list_del(&trans->list); | ||||
| 		trans->ctx.nla = NULL; | ||||
| 		call_rcu(&trans->rcu_head, nf_tables_commit_release_rcu); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* Schedule objects for release via rcu to make sure no packets are accesing
 | ||||
|  * aborted rules. | ||||
|  */ | ||||
| static void nf_tables_abort_release_rcu(struct rcu_head *rt) | ||||
| { | ||||
| 	struct nft_trans *trans = container_of(rt, struct nft_trans, rcu_head); | ||||
| 
 | ||||
| 	switch (trans->msg_type) { | ||||
| 	case NFT_MSG_NEWTABLE: | ||||
| 		nf_tables_table_destroy(&trans->ctx); | ||||
| 		break; | ||||
| 	case NFT_MSG_NEWCHAIN: | ||||
| 		nf_tables_chain_destroy(trans->ctx.chain); | ||||
| 		break; | ||||
| 	case NFT_MSG_NEWRULE: | ||||
| 		nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans)); | ||||
| 		break; | ||||
| 	case NFT_MSG_NEWSET: | ||||
| 		nft_set_destroy(nft_trans_set(trans)); | ||||
| 		break; | ||||
| 	} | ||||
| 	kfree(trans); | ||||
| } | ||||
| 
 | ||||
| static int nf_tables_abort(struct sk_buff *skb) | ||||
| { | ||||
| 	struct net *net = sock_net(skb->sk); | ||||
|  | @ -3495,26 +3526,10 @@ static int nf_tables_abort(struct sk_buff *skb) | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* Make sure we don't see any packet accessing aborted rules */ | ||||
| 	synchronize_rcu(); | ||||
| 
 | ||||
| 	list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { | ||||
| 		switch (trans->msg_type) { | ||||
| 		case NFT_MSG_NEWTABLE: | ||||
| 			nf_tables_table_destroy(&trans->ctx); | ||||
| 			break; | ||||
| 		case NFT_MSG_NEWCHAIN: | ||||
| 			nf_tables_chain_destroy(trans->ctx.chain); | ||||
| 			break; | ||||
| 		case NFT_MSG_NEWRULE: | ||||
| 			nf_tables_rule_destroy(&trans->ctx, | ||||
| 					       nft_trans_rule(trans)); | ||||
| 			break; | ||||
| 		case NFT_MSG_NEWSET: | ||||
| 			nft_set_destroy(nft_trans_set(trans)); | ||||
| 			break; | ||||
| 		} | ||||
| 		nft_trans_destroy(trans); | ||||
| 		list_del(&trans->list); | ||||
| 		trans->ctx.nla = NULL; | ||||
| 		call_rcu(&trans->rcu_head, nf_tables_abort_release_rcu); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Pablo Neira Ayuso
				Pablo Neira Ayuso