Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cfebd7e2 authored by Vlad Buslov's avatar Vlad Buslov Committed by David S. Miller
Browse files

net: sched: change tcf block reference counter type to refcount_t



As a preparation for removing rtnl lock dependency from rules update path,
change tcf block reference counter type to refcount_t to allow modification
by concurrent users.

In block put function perform decrement and check reference counter once to
accommodate concurrent modification by unlocked users. After this change
tcf_chain_put at the end of block put function is called with
block->refcnt==0 and will deallocate block after the last chain is
released, so there is no need to manually deallocate block in this case.
However, if block reference counter reached 0 and there are no chains to
release, block must still be deallocated manually.

Signed-off-by: default avatarVlad Buslov <vladbu@mellanox.com>
Acked-by: default avatarJiri Pirko <jiri@mellanox.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent e368fdb6
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -345,7 +345,7 @@ struct tcf_chain {
struct tcf_block {
	struct list_head chain_list;
	u32 index; /* block index for shared blocks */
	unsigned int refcnt;
	refcount_t refcnt;
	struct net *net;
	struct Qdisc *q;
	struct list_head cb_list;
+35 −24
Original line number Diff line number Diff line
@@ -240,7 +240,7 @@ static void tcf_chain_destroy(struct tcf_chain *chain)
	if (!chain->index)
		block->chain0.chain = NULL;
	kfree(chain);
	if (list_empty(&block->chain_list) && block->refcnt == 0)
	if (list_empty(&block->chain_list) && !refcount_read(&block->refcnt))
		kfree(block);
}

@@ -510,7 +510,7 @@ static struct tcf_block *tcf_block_create(struct net *net, struct Qdisc *q,
	INIT_LIST_HEAD(&block->owner_list);
	INIT_LIST_HEAD(&block->chain0.filter_chain_list);

	block->refcnt = 1;
	refcount_set(&block->refcnt, 1);
	block->net = net;
	block->index = block_index;

@@ -710,7 +710,7 @@ int tcf_block_get_ext(struct tcf_block **p_block, struct Qdisc *q,
		/* block_index not 0 means the shared block is requested */
		block = tcf_block_lookup(net, ei->block_index);
		if (block)
			block->refcnt++;
			refcount_inc(&block->refcnt);
	}

	if (!block) {
@@ -753,7 +753,7 @@ int tcf_block_get_ext(struct tcf_block **p_block, struct Qdisc *q,
err_block_insert:
		kfree(block);
	} else {
		block->refcnt--;
		refcount_dec(&block->refcnt);
	}
	return err;
}
@@ -793,12 +793,21 @@ void tcf_block_put_ext(struct tcf_block *block, struct Qdisc *q,
	tcf_chain0_head_change_cb_del(block, ei);
	tcf_block_owner_del(block, q, ei->binder_type);

	if (block->refcnt == 1) {
	if (refcount_dec_and_test(&block->refcnt)) {
		/* Flushing/putting all chains will cause the block to be
		 * deallocated when last chain is freed. However, if chain_list
		 * is empty, block has to be manually deallocated. After block
		 * reference counter reached 0, it is no longer possible to
		 * increment it or add new chains to block.
		 */
		bool free_block = list_empty(&block->chain_list);

		if (tcf_block_shared(block))
			tcf_block_remove(block, block->net);

		/* Hold a refcnt for all chains, so that they don't disappear
		 * while we are iterating.
		if (!free_block) {
			/* Hold a refcnt for all chains, so that they don't
			 * disappear while we are iterating.
			 */
			list_for_each_entry(chain, &block->chain_list, list)
				tcf_chain_hold(chain);
@@ -809,18 +818,20 @@ void tcf_block_put_ext(struct tcf_block *block, struct Qdisc *q,

		tcf_block_offload_unbind(block, q, ei);

	if (block->refcnt == 1) {
		/* At this point, all the chains should have refcnt >= 1. */
		list_for_each_entry_safe(chain, tmp, &block->chain_list, list) {
		if (free_block) {
			kfree(block);
		} else {
			/* At this point, all the chains should have
			 * refcnt >= 1.
			 */
			list_for_each_entry_safe(chain, tmp, &block->chain_list,
						 list) {
				tcf_chain_put_explicitly_created(chain);
				tcf_chain_put(chain);
			}

		block->refcnt--;
		if (list_empty(&block->chain_list))
			kfree(block);
		}
	} else {
		block->refcnt--;
		tcf_block_offload_unbind(block, q, ei);
	}
}
EXPORT_SYMBOL(tcf_block_put_ext);