diff mbox series

[net-next,v6,06/11] net: sched: use block index as a handle instead of qdisc when block is shared

Message ID 20180105230929.5645-7-jiri@resnulli.us
State Changes Requested, archived
Delegated to: David Miller
Headers show
Series net: sched: allow qdiscs to share filter block instances | expand

Commit Message

Jiri Pirko Jan. 5, 2018, 11:09 p.m. UTC
From: Jiri Pirko <jiri@mellanox.com>

As the tcm_ifindex 0 is invalid ifindex, reuse it to indicate that we
work with block, instead of qdisc. So if tcm_ifindex is 0, tcm_parent is
used to carry block_index.

If the block is set to be shared between at least 2 qdiscs, it is
forbidden to use the qdisc handle to add/delete filters. In that case,
userspace has to pass block_index.

Also, for dump of the filters, in case the block is shared in between at
least 2 qdiscs, the each filter is dumped with tcm_ifindex 0 and
tcm_parent set to block_index. That gives the user clear indication,
that the filter belongs to a shared block and not only to one qdisc
under which it is dumped.

Suggested-by: David Ahern <dsahern@gmail.com>
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
---
v5->v6:
  - new patch
---
 include/net/pkt_cls.h |   7 +-
 net/sched/cls_api.c   | 208 ++++++++++++++++++++++++++++++--------------------
 2 files changed, 132 insertions(+), 83 deletions(-)

Comments

Jiri Pirko Jan. 6, 2018, 8:43 p.m. UTC | #1
Sat, Jan 06, 2018 at 12:09:24AM CET, jiri@resnulli.us wrote:
>From: Jiri Pirko <jiri@mellanox.com>
>
>As the tcm_ifindex 0 is invalid ifindex, reuse it to indicate that we
>work with block, instead of qdisc. So if tcm_ifindex is 0, tcm_parent is
>used to carry block_index.
>
>If the block is set to be shared between at least 2 qdiscs, it is
>forbidden to use the qdisc handle to add/delete filters. In that case,
>userspace has to pass block_index.
>
>Also, for dump of the filters, in case the block is shared in between at
>least 2 qdiscs, the each filter is dumped with tcm_ifindex 0 and
>tcm_parent set to block_index. That gives the user clear indication,
>that the filter belongs to a shared block and not only to one qdisc
>under which it is dumped.
>
>Suggested-by: David Ahern <dsahern@gmail.com>
>Signed-off-by: Jiri Pirko <jiri@mellanox.com>
>---

[...]

>@@ -886,8 +887,13 @@ static int tcf_fill_node(struct net *net, struct sk_buff *skb,
> 	tcm->tcm_family = AF_UNSPEC;
> 	tcm->tcm__pad1 = 0;
> 	tcm->tcm__pad2 = 0;
>-	tcm->tcm_ifindex = qdisc_dev(q)->ifindex;
>-	tcm->tcm_parent = parent;
>+	if (q) {
>+		tcm->tcm_ifindex = qdisc_dev(q)->ifindex;
>+		tcm->tcm_parent = parent;
>+	} else {
>+		tcm->tcm_ifindex = 0; /* block index is stored in parent */
>+		tcm->tcm_parent = block->index;
>+	}

Please guys, please look at this reuse (also on clt side). I would like
you to double-check this reuse of existing API for balock_index carrying
purpose. I believe it's UAPI safe. But please, check it out carefully.
Jamal Hadi Salim Jan. 7, 2018, 1:11 p.m. UTC | #2
On 18-01-06 03:43 PM, Jiri Pirko wrote:


> 
>> @@ -886,8 +887,13 @@ static int tcf_fill_node(struct net *net, struct sk_buff *skb,
>> 	tcm->tcm_family = AF_UNSPEC;
>> 	tcm->tcm__pad1 = 0;
>> 	tcm->tcm__pad2 = 0;
>> -	tcm->tcm_ifindex = qdisc_dev(q)->ifindex;
>> -	tcm->tcm_parent = parent;
>> +	if (q) {
>> +		tcm->tcm_ifindex = qdisc_dev(q)->ifindex;
>> +		tcm->tcm_parent = parent;
>> +	} else {
>> +		tcm->tcm_ifindex = 0; /* block index is stored in parent */
>> +		tcm->tcm_parent = block->index;
>> +	}
> 
> Please guys, please look at this reuse (also on clt side). I would like
> you to double-check this reuse of existing API for balock_index carrying
> purpose. I believe it's UAPI safe. But please, check it out carefully.
> 


Should not break any ABI/UAPI AFAIK. Maybe go for a negative ifindex
(not sure if zero means something speacial to someone).

cheers,
jamal
Jiri Pirko Jan. 7, 2018, 1:46 p.m. UTC | #3
Sun, Jan 07, 2018 at 02:11:19PM CET, jhs@mojatatu.com wrote:
>On 18-01-06 03:43 PM, Jiri Pirko wrote:
>
>
>> 
>> > @@ -886,8 +887,13 @@ static int tcf_fill_node(struct net *net, struct sk_buff *skb,
>> > 	tcm->tcm_family = AF_UNSPEC;
>> > 	tcm->tcm__pad1 = 0;
>> > 	tcm->tcm__pad2 = 0;
>> > -	tcm->tcm_ifindex = qdisc_dev(q)->ifindex;
>> > -	tcm->tcm_parent = parent;
>> > +	if (q) {
>> > +		tcm->tcm_ifindex = qdisc_dev(q)->ifindex;
>> > +		tcm->tcm_parent = parent;
>> > +	} else {
>> > +		tcm->tcm_ifindex = 0; /* block index is stored in parent */
>> > +		tcm->tcm_parent = block->index;
>> > +	}
>> 
>> Please guys, please look at this reuse (also on clt side). I would like
>> you to double-check this reuse of existing API for balock_index carrying
>> purpose. I believe it's UAPI safe. But please, check it out carefully.
>> 
>
>
>Should not break any ABI/UAPI AFAIK. Maybe go for a negative ifindex
>(not sure if zero means something speacial to someone).

Like -1 means parent is block_index?

Why would 0 mean something special? Could you point to a code that
suggests it?

>
>cheers,
>jamal
>
Jamal Hadi Salim Jan. 7, 2018, 2:28 p.m. UTC | #4
On 18-01-07 08:46 AM, Jiri Pirko wrote:
> Sun, Jan 07, 2018 at 02:11:19PM CET, jhs@mojatatu.com wrote:
>> On 18-01-06 03:43 PM, Jiri Pirko wrote:
>>
>>
>>>
>>>> @@ -886,8 +887,13 @@ static int tcf_fill_node(struct net *net, struct sk_buff *skb,
>>>> 	tcm->tcm_family = AF_UNSPEC;
>>>> 	tcm->tcm__pad1 = 0;
>>>> 	tcm->tcm__pad2 = 0;
>>>> -	tcm->tcm_ifindex = qdisc_dev(q)->ifindex;
>>>> -	tcm->tcm_parent = parent;
>>>> +	if (q) {
>>>> +		tcm->tcm_ifindex = qdisc_dev(q)->ifindex;
>>>> +		tcm->tcm_parent = parent;
>>>> +	} else {
>>>> +		tcm->tcm_ifindex = 0; /* block index is stored in parent */
>>>> +		tcm->tcm_parent = block->index;
>>>> +	}
>>>
>>> Please guys, please look at this reuse (also on clt side). I would like
>>> you to double-check this reuse of existing API for balock_index carrying
>>> purpose. I believe it's UAPI safe. But please, check it out carefully.
>>>
>>
>>
>> Should not break any ABI/UAPI AFAIK. Maybe go for a negative ifindex
>> (not sure if zero means something speacial to someone).
> 
> Like -1 means parent is block_index?
> 

Yes.

> Why would 0 mean something special? Could you point to a code that
> suggests it?
> 

I cant point to any such code, it is just the ifindex is an int.
And the negative space looks like less likely someone would think
of using for signalling (0xFFFFFFFF as an example).
tcpdump -i any probably assumes soem weird ifindex (havent looked
at the code).

In any case, 0 is fine too.

cheers,
jamal
Jamal Hadi Salim Jan. 7, 2018, 2:51 p.m. UTC | #5
On 18-01-07 09:28 AM, Jamal Hadi Salim wrote:
> On 18-01-07 08:46 AM, Jiri Pirko wrote:
>> Sun, Jan 07, 2018 at 02:11:19PM CET, jhs@mojatatu.com wrote:
>>> On 18-01-06 03:43 PM, Jiri Pirko wrote:
>>>
>>>
>>>>
>>>>> @@ -886,8 +887,13 @@ static int tcf_fill_node(struct net *net, 
>>>>> struct sk_buff *skb,
>>>>>     tcm->tcm_family = AF_UNSPEC;
>>>>>     tcm->tcm__pad1 = 0;
>>>>>     tcm->tcm__pad2 = 0;
>>>>> -    tcm->tcm_ifindex = qdisc_dev(q)->ifindex;
>>>>> -    tcm->tcm_parent = parent;
>>>>> +    if (q) {
>>>>> +        tcm->tcm_ifindex = qdisc_dev(q)->ifindex;
>>>>> +        tcm->tcm_parent = parent;
>>>>> +    } else {
>>>>> +        tcm->tcm_ifindex = 0; /* block index is stored in parent */
>>>>> +        tcm->tcm_parent = block->index;
>>>>> +    }
>>>>
>>>> Please guys, please look at this reuse (also on clt side). I would like
>>>> you to double-check this reuse of existing API for balock_index 
>>>> carrying
>>>> purpose. I believe it's UAPI safe. But please, check it out carefully.
>>>>
>>>
>>>
>>> Should not break any ABI/UAPI AFAIK. Maybe go for a negative ifindex
>>> (not sure if zero means something speacial to someone).
>>
>> Like -1 means parent is block_index?
>>
> 
> Yes.
> 
>> Why would 0 mean something special? Could you point to a code that
>> suggests it?
>>
> 
> I cant point to any such code, it is just the ifindex is an int.
> And the negative space looks like less likely someone would think
> of using for signalling (0xFFFFFFFF as an example).
> tcpdump -i any probably assumes soem weird ifindex (havent looked
> at the code).
> 
> In any case, 0 is fine too.

Also a #define for whatever value you use would make it more readable,
ex:
#define IFINDEX_ANY ..

cheers,
jamal
diff mbox series

Patch

diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h
index 5b53fda..6f9999c 100644
--- a/include/net/pkt_cls.h
+++ b/include/net/pkt_cls.h
@@ -51,9 +51,14 @@  void tcf_block_put(struct tcf_block *block);
 void tcf_block_put_ext(struct tcf_block *block, struct Qdisc *q,
 		       struct tcf_block_ext_info *ei);
 
+static inline bool tcf_block_shared(struct tcf_block *block)
+{
+	return block->refcnt != 1;
+}
+
 static inline struct Qdisc *tcf_block_q(struct tcf_block *block)
 {
-	WARN_ON(block->refcnt != 1);
+	WARN_ON(tcf_block_shared(block));
 	return block->q;
 }
 
diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
index ae60fce..cf4fc7a 100644
--- a/net/sched/cls_api.c
+++ b/net/sched/cls_api.c
@@ -872,8 +872,9 @@  static struct tcf_proto *tcf_chain_tp_find(struct tcf_chain *chain,
 }
 
 static int tcf_fill_node(struct net *net, struct sk_buff *skb,
-			 struct tcf_proto *tp, struct Qdisc *q, u32 parent,
-			 void *fh, u32 portid, u32 seq, u16 flags, int event)
+			 struct tcf_proto *tp, struct tcf_block *block,
+			 struct Qdisc *q, u32 parent, void *fh,
+			 u32 portid, u32 seq, u16 flags, int event)
 {
 	struct tcmsg *tcm;
 	struct nlmsghdr  *nlh;
@@ -886,8 +887,13 @@  static int tcf_fill_node(struct net *net, struct sk_buff *skb,
 	tcm->tcm_family = AF_UNSPEC;
 	tcm->tcm__pad1 = 0;
 	tcm->tcm__pad2 = 0;
-	tcm->tcm_ifindex = qdisc_dev(q)->ifindex;
-	tcm->tcm_parent = parent;
+	if (q) {
+		tcm->tcm_ifindex = qdisc_dev(q)->ifindex;
+		tcm->tcm_parent = parent;
+	} else {
+		tcm->tcm_ifindex = 0; /* block index is stored in parent */
+		tcm->tcm_parent = block->index;
+	}
 	tcm->tcm_info = TC_H_MAKE(tp->prio, tp->protocol);
 	if (nla_put_string(skb, TCA_KIND, tp->ops->kind))
 		goto nla_put_failure;
@@ -910,8 +916,8 @@  static int tcf_fill_node(struct net *net, struct sk_buff *skb,
 
 static int tfilter_notify(struct net *net, struct sk_buff *oskb,
 			  struct nlmsghdr *n, struct tcf_proto *tp,
-			  struct Qdisc *q, u32 parent,
-			  void *fh, int event, bool unicast)
+			  struct tcf_block *block, struct Qdisc *q,
+			  u32 parent, void *fh, int event, bool unicast)
 {
 	struct sk_buff *skb;
 	u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
@@ -920,8 +926,8 @@  static int tfilter_notify(struct net *net, struct sk_buff *oskb,
 	if (!skb)
 		return -ENOBUFS;
 
-	if (tcf_fill_node(net, skb, tp, q, parent, fh, portid, n->nlmsg_seq,
-			  n->nlmsg_flags, event) <= 0) {
+	if (tcf_fill_node(net, skb, tp, block, q, parent, fh, portid,
+			  n->nlmsg_seq, n->nlmsg_flags, event) <= 0) {
 		kfree_skb(skb);
 		return -EINVAL;
 	}
@@ -935,8 +941,8 @@  static int tfilter_notify(struct net *net, struct sk_buff *oskb,
 
 static int tfilter_del_notify(struct net *net, struct sk_buff *oskb,
 			      struct nlmsghdr *n, struct tcf_proto *tp,
-			      struct Qdisc *q, u32 parent,
-			      void *fh, bool unicast, bool *last)
+			      struct tcf_block *block, struct Qdisc *q,
+			      u32 parent, void *fh, bool unicast, bool *last)
 {
 	struct sk_buff *skb;
 	u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
@@ -946,8 +952,8 @@  static int tfilter_del_notify(struct net *net, struct sk_buff *oskb,
 	if (!skb)
 		return -ENOBUFS;
 
-	if (tcf_fill_node(net, skb, tp, q, parent, fh, portid, n->nlmsg_seq,
-			  n->nlmsg_flags, RTM_DELTFILTER) <= 0) {
+	if (tcf_fill_node(net, skb, tp, block, q, parent, fh, portid,
+			  n->nlmsg_seq, n->nlmsg_flags, RTM_DELTFILTER) <= 0) {
 		kfree_skb(skb);
 		return -EINVAL;
 	}
@@ -966,15 +972,16 @@  static int tfilter_del_notify(struct net *net, struct sk_buff *oskb,
 }
 
 static void tfilter_notify_chain(struct net *net, struct sk_buff *oskb,
-				 struct Qdisc *q, u32 parent,
-				 struct nlmsghdr *n,
+				 struct tcf_block *block, struct Qdisc *q,
+				 u32 parent, struct nlmsghdr *n,
 				 struct tcf_chain *chain, int event)
 {
 	struct tcf_proto *tp;
 
 	for (tp = rtnl_dereference(chain->filter_chain);
 	     tp; tp = rtnl_dereference(tp->next))
-		tfilter_notify(net, oskb, n, tp, q, parent, 0, event, false);
+		tfilter_notify(net, oskb, n, tp, block,
+			       q, parent, 0, event, false);
 }
 
 /* Add/change/delete/get a filter node */
@@ -990,13 +997,11 @@  static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
 	bool prio_allocate;
 	u32 parent;
 	u32 chain_index;
-	struct net_device *dev;
-	struct Qdisc  *q;
+	struct Qdisc *q = NULL;
 	struct tcf_chain_info chain_info;
 	struct tcf_chain *chain = NULL;
 	struct tcf_block *block;
 	struct tcf_proto *tp;
-	const struct Qdisc_class_ops *cops;
 	unsigned long cl;
 	void *fh;
 	int err;
@@ -1043,41 +1048,63 @@  static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
 
 	/* Find head of filter chain. */
 
-	/* Find link */
-	dev = __dev_get_by_index(net, t->tcm_ifindex);
-	if (dev == NULL)
-		return -ENODEV;
 
-	/* Find qdisc */
-	if (!parent) {
-		q = dev->qdisc;
-		parent = q->handle;
-	} else {
-		q = qdisc_lookup(dev, TC_H_MAJ(t->tcm_parent));
-		if (q == NULL)
+	if (t->tcm_ifindex) {
+		const struct Qdisc_class_ops *cops;
+		struct net_device *dev;
+
+		/* Find link */
+		dev = __dev_get_by_index(net, t->tcm_ifindex);
+		if (!dev)
+			return -ENODEV;
+
+		/* Find qdisc */
+		if (!parent) {
+			q = dev->qdisc;
+			parent = q->handle;
+		} else {
+			q = qdisc_lookup(dev, TC_H_MAJ(t->tcm_parent));
+			if (!q)
+				return -EINVAL;
+		}
+
+		/* Is it classful? */
+		cops = q->ops->cl_ops;
+		if (!cops)
 			return -EINVAL;
-	}
 
-	/* Is it classful? */
-	cops = q->ops->cl_ops;
-	if (!cops)
-		return -EINVAL;
+		if (!cops->tcf_block)
+			return -EOPNOTSUPP;
 
-	if (!cops->tcf_block)
-		return -EOPNOTSUPP;
+		/* Do we search for filter, attached to class? */
+		if (TC_H_MIN(parent)) {
+			cl = cops->find(q, parent);
+			if (cl == 0)
+				return -ENOENT;
+		}
 
-	/* Do we search for filter, attached to class? */
-	if (TC_H_MIN(parent)) {
-		cl = cops->find(q, parent);
-		if (cl == 0)
-			return -ENOENT;
-	}
+		/* And the last stroke */
+		block = cops->tcf_block(q, cl, extack);
+		if (!block) {
+			err = -EINVAL;
+			goto errout;
+		}
+		if (tcf_block_shared(block)) {
+			NL_SET_ERR_MSG(extack, "Cannot work with shared block, please use block index");
+			err = -EOPNOTSUPP;
+			goto errout;
+		}
+	} else {
+		/* tcm_ifindex == means that the user wants to work with block.
+		 * The block index is stored in tcm_parent.
+		 */
+		u32 block_index = t->tcm_parent;
 
-	/* And the last stroke */
-	block = cops->tcf_block(q, cl, extack);
-	if (!block) {
-		err = -EINVAL;
-		goto errout;
+		block = tcf_block_lookup(net, block_index);
+		if (!block) {
+			err = -EINVAL;
+			goto errout;
+		}
 	}
 
 	chain_index = tca[TCA_CHAIN] ? nla_get_u32(tca[TCA_CHAIN]) : 0;
@@ -1093,7 +1120,7 @@  static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
 	}
 
 	if (n->nlmsg_type == RTM_DELTFILTER && prio == 0) {
-		tfilter_notify_chain(net, skb, q, parent, n,
+		tfilter_notify_chain(net, skb, block, q, parent, n,
 				     chain, RTM_DELTFILTER);
 		tcf_chain_flush(chain);
 		err = 0;
@@ -1141,7 +1168,7 @@  static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
 	if (!fh) {
 		if (n->nlmsg_type == RTM_DELTFILTER && t->tcm_handle == 0) {
 			tcf_chain_tp_remove(chain, &chain_info, tp);
-			tfilter_notify(net, skb, n, tp, q, parent, fh,
+			tfilter_notify(net, skb, n, tp, block, q, parent, fh,
 				       RTM_DELTFILTER, false);
 			tcf_proto_destroy(tp);
 			err = 0;
@@ -1166,8 +1193,8 @@  static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
 			}
 			break;
 		case RTM_DELTFILTER:
-			err = tfilter_del_notify(net, skb, n, tp, q, parent,
-						 fh, false, &last);
+			err = tfilter_del_notify(net, skb, n, tp, block,
+						 q, parent, fh, false, &last);
 			if (err)
 				goto errout;
 			if (last) {
@@ -1176,8 +1203,8 @@  static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
 			}
 			goto errout;
 		case RTM_GETTFILTER:
-			err = tfilter_notify(net, skb, n, tp, q, parent, fh,
-					     RTM_NEWTFILTER, true);
+			err = tfilter_notify(net, skb, n, tp, block, q, parent,
+					     fh, RTM_NEWTFILTER, true);
 			goto errout;
 		default:
 			err = -EINVAL;
@@ -1190,7 +1217,7 @@  static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
 	if (err == 0) {
 		if (tp_created)
 			tcf_chain_tp_insert(chain, &chain_info, tp);
-		tfilter_notify(net, skb, n, tp, q, parent, fh,
+		tfilter_notify(net, skb, n, tp, block, q, parent, fh,
 			       RTM_NEWTFILTER, false);
 	} else {
 		if (tp_created)
@@ -1210,6 +1237,7 @@  struct tcf_dump_args {
 	struct tcf_walker w;
 	struct sk_buff *skb;
 	struct netlink_callback *cb;
+	struct tcf_block *block;
 	struct Qdisc *q;
 	u32 parent;
 };
@@ -1219,7 +1247,7 @@  static int tcf_node_dump(struct tcf_proto *tp, void *n, struct tcf_walker *arg)
 	struct tcf_dump_args *a = (void *)arg;
 	struct net *net = sock_net(a->skb->sk);
 
-	return tcf_fill_node(net, a->skb, tp, a->q, a->parent,
+	return tcf_fill_node(net, a->skb, tp, a->block, a->q, a->parent,
 			     n, NETLINK_CB(a->cb->skb).portid,
 			     a->cb->nlh->nlmsg_seq, NLM_F_MULTI,
 			     RTM_NEWTFILTER);
@@ -1230,6 +1258,7 @@  static bool tcf_chain_dump(struct tcf_chain *chain, struct Qdisc *q, u32 parent,
 			   long index_start, long *p_index)
 {
 	struct net *net = sock_net(skb->sk);
+	struct tcf_block *block = chain->block;
 	struct tcmsg *tcm = nlmsg_data(cb->nlh);
 	struct tcf_dump_args arg;
 	struct tcf_proto *tp;
@@ -1248,7 +1277,7 @@  static bool tcf_chain_dump(struct tcf_chain *chain, struct Qdisc *q, u32 parent,
 			memset(&cb->args[1], 0,
 			       sizeof(cb->args) - sizeof(cb->args[0]));
 		if (cb->args[1] == 0) {
-			if (tcf_fill_node(net, skb, tp, q, parent, 0,
+			if (tcf_fill_node(net, skb, tp, block, q, parent, 0,
 					  NETLINK_CB(cb->skb).portid,
 					  cb->nlh->nlmsg_seq, NLM_F_MULTI,
 					  RTM_NEWTFILTER) <= 0)
@@ -1261,6 +1290,7 @@  static bool tcf_chain_dump(struct tcf_chain *chain, struct Qdisc *q, u32 parent,
 		arg.w.fn = tcf_node_dump;
 		arg.skb = skb;
 		arg.cb = cb;
+		arg.block = block;
 		arg.q = q;
 		arg.parent = parent;
 		arg.w.stop = 0;
@@ -1279,13 +1309,10 @@  static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
 {
 	struct net *net = sock_net(skb->sk);
 	struct nlattr *tca[TCA_MAX + 1];
-	struct net_device *dev;
-	struct Qdisc *q;
+	struct Qdisc *q = NULL;
 	struct tcf_block *block;
 	struct tcf_chain *chain;
 	struct tcmsg *tcm = nlmsg_data(cb->nlh);
-	unsigned long cl = 0;
-	const struct Qdisc_class_ops *cops;
 	long index_start;
 	long index;
 	u32 parent;
@@ -1298,32 +1325,49 @@  static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
 	if (err)
 		return err;
 
-	dev = __dev_get_by_index(net, tcm->tcm_ifindex);
-	if (!dev)
-		return skb->len;
+	if (tcm->tcm_ifindex) {
+		const struct Qdisc_class_ops *cops;
+		struct net_device *dev;
+		unsigned long cl = 0;
+
+		dev = __dev_get_by_index(net, tcm->tcm_ifindex);
+		if (!dev)
+			return skb->len;
 
-	parent = tcm->tcm_parent;
-	if (!parent) {
-		q = dev->qdisc;
-		parent = q->handle;
+		parent = tcm->tcm_parent;
+		if (!parent) {
+			q = dev->qdisc;
+			parent = q->handle;
+		} else {
+			q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent));
+		}
+		if (!q)
+			goto out;
+		cops = q->ops->cl_ops;
+		if (!cops)
+			goto out;
+		if (!cops->tcf_block)
+			goto out;
+		if (TC_H_MIN(tcm->tcm_parent)) {
+			cl = cops->find(q, tcm->tcm_parent);
+			if (cl == 0)
+				goto out;
+		}
+		block = cops->tcf_block(q, cl, NULL);
+		if (!block)
+			goto out;
+		if (tcf_block_shared(block))
+			q = NULL;
 	} else {
-		q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent));
-	}
-	if (!q)
-		goto out;
-	cops = q->ops->cl_ops;
-	if (!cops)
-		goto out;
-	if (!cops->tcf_block)
-		goto out;
-	if (TC_H_MIN(tcm->tcm_parent)) {
-		cl = cops->find(q, tcm->tcm_parent);
-		if (cl == 0)
+		/* tcm_ifindex == means that the user wants to work with block.
+		 * The block index is stored in tcm_parent.
+		 */
+		u32 block_index = tcm->tcm_parent;
+
+		block = tcf_block_lookup(net, block_index);
+		if (!block)
 			goto out;
 	}
-	block = cops->tcf_block(q, cl, NULL);
-	if (!block)
-		goto out;
 
 	index_start = cb->args[0];
 	index = 0;