|
@@ -375,6 +375,7 @@ static struct tcf_block *tcf_block_create(struct net *net, struct Qdisc *q,
|
|
|
}
|
|
|
INIT_LIST_HEAD(&block->chain_list);
|
|
|
INIT_LIST_HEAD(&block->cb_list);
|
|
|
+ INIT_LIST_HEAD(&block->owner_list);
|
|
|
|
|
|
/* Create chain 0 by default, it has to be always present. */
|
|
|
chain = tcf_chain_create(block, 0);
|
|
@@ -406,6 +407,65 @@ static struct tcf_chain *tcf_block_chain_zero(struct tcf_block *block)
|
|
|
return list_first_entry(&block->chain_list, struct tcf_chain, list);
|
|
|
}
|
|
|
|
|
|
+struct tcf_block_owner_item {
|
|
|
+ struct list_head list;
|
|
|
+ struct Qdisc *q;
|
|
|
+ enum tcf_block_binder_type binder_type;
|
|
|
+};
|
|
|
+
|
|
|
+static void
|
|
|
+tcf_block_owner_netif_keep_dst(struct tcf_block *block,
|
|
|
+ struct Qdisc *q,
|
|
|
+ enum tcf_block_binder_type binder_type)
|
|
|
+{
|
|
|
+ if (block->keep_dst &&
|
|
|
+ binder_type != TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS &&
|
|
|
+ binder_type != TCF_BLOCK_BINDER_TYPE_CLSACT_EGRESS)
|
|
|
+ netif_keep_dst(qdisc_dev(q));
|
|
|
+}
|
|
|
+
|
|
|
+void tcf_block_netif_keep_dst(struct tcf_block *block)
|
|
|
+{
|
|
|
+ struct tcf_block_owner_item *item;
|
|
|
+
|
|
|
+ block->keep_dst = true;
|
|
|
+ list_for_each_entry(item, &block->owner_list, list)
|
|
|
+ tcf_block_owner_netif_keep_dst(block, item->q,
|
|
|
+ item->binder_type);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(tcf_block_netif_keep_dst);
|
|
|
+
|
|
|
+static int tcf_block_owner_add(struct tcf_block *block,
|
|
|
+ struct Qdisc *q,
|
|
|
+ enum tcf_block_binder_type binder_type)
|
|
|
+{
|
|
|
+ struct tcf_block_owner_item *item;
|
|
|
+
|
|
|
+ item = kmalloc(sizeof(*item), GFP_KERNEL);
|
|
|
+ if (!item)
|
|
|
+ return -ENOMEM;
|
|
|
+ item->q = q;
|
|
|
+ item->binder_type = binder_type;
|
|
|
+ list_add(&item->list, &block->owner_list);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void tcf_block_owner_del(struct tcf_block *block,
|
|
|
+ struct Qdisc *q,
|
|
|
+ enum tcf_block_binder_type binder_type)
|
|
|
+{
|
|
|
+ struct tcf_block_owner_item *item;
|
|
|
+
|
|
|
+ list_for_each_entry(item, &block->owner_list, list) {
|
|
|
+ if (item->q == q && item->binder_type == binder_type) {
|
|
|
+ list_del(&item->list);
|
|
|
+ kfree(item);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ WARN_ON(1);
|
|
|
+}
|
|
|
+
|
|
|
int tcf_block_get_ext(struct tcf_block **p_block, struct Qdisc *q,
|
|
|
struct tcf_block_ext_info *ei,
|
|
|
struct netlink_ext_ack *extack)
|
|
@@ -435,6 +495,12 @@ int tcf_block_get_ext(struct tcf_block **p_block, struct Qdisc *q,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ err = tcf_block_owner_add(block, q, ei->binder_type);
|
|
|
+ if (err)
|
|
|
+ goto err_block_owner_add;
|
|
|
+
|
|
|
+ tcf_block_owner_netif_keep_dst(block, q, ei->binder_type);
|
|
|
+
|
|
|
err = tcf_chain_head_change_cb_add(tcf_block_chain_zero(block),
|
|
|
ei, extack);
|
|
|
if (err)
|
|
@@ -444,6 +510,8 @@ int tcf_block_get_ext(struct tcf_block **p_block, struct Qdisc *q,
|
|
|
return 0;
|
|
|
|
|
|
err_chain_head_change_cb_add:
|
|
|
+ tcf_block_owner_del(block, q, ei->binder_type);
|
|
|
+err_block_owner_add:
|
|
|
if (created) {
|
|
|
if (tcf_block_shared(block))
|
|
|
tcf_block_remove(block, net);
|
|
@@ -489,6 +557,7 @@ void tcf_block_put_ext(struct tcf_block *block, struct Qdisc *q,
|
|
|
if (!block)
|
|
|
return;
|
|
|
tcf_chain_head_change_cb_del(tcf_block_chain_zero(block), ei);
|
|
|
+ tcf_block_owner_del(block, q, ei->binder_type);
|
|
|
|
|
|
if (--block->refcnt == 0) {
|
|
|
if (tcf_block_shared(block))
|