* Re: [PATCH 0/4] mwifiex PCI/wake-up interrupt fixes
From: Marc Zyngier @ 2019-02-27 10:02 UTC (permalink / raw)
To: Brian Norris
Cc: Amitkumar Karwar, Enric Balletbo i Serra, Ganapathi Bhat,
Heiko Stuebner, Kalle Valo, Nishant Sarmukadam, Rob Herring,
Xinming Hu, David S. Miller, devicetree, linux-arm-kernel,
linux-kernel, linux-rockchip, linux-wireless, netdev, linux-pm,
Jeffy Chen, Rafael J. Wysocki, Tony Lindgren, Lorenzo Pieralisi
In-Reply-To: <20190226232822.GA174696@google.com>
+ Lorenzo
Hi Brian,
On 26/02/2019 23:28, Brian Norris wrote:
> + others
>
> Hi Marc,
>
> Thanks for the series. I have a few bits of history to add to this, and
> some comments.
>
> On Sun, Feb 24, 2019 at 02:04:22PM +0000, Marc Zyngier wrote:
>> For quite some time, I wondered why the PCI mwifiex device built in my
>> Chromebook was unable to use the good old legacy interrupts. But as MSIs
>> were working fine, I never really bothered investigating. I finally had a
>> look, and the result isn't very pretty.
>>
>> On this machine (rk3399-based kevin), the wake-up interrupt is described as
>> such:
>>
>> &pci_rootport {
>> mvl_wifi: wifi@0,0 {
>> compatible = "pci1b4b,2b42";
>> reg = <0x83010000 0x0 0x00000000 0x0 0x00100000
>> 0x83010000 0x0 0x00100000 0x0 0x00100000>;
>> interrupt-parent = <&gpio0>;
>> interrupts = <8 IRQ_TYPE_LEVEL_LOW>;
>> pinctrl-names = "default";
>> pinctrl-0 = <&wlan_host_wake_l>;
>> wakeup-source;
>> };
>> };
>>
>> Note how the interrupt is part of the properties directly attached to the
>> PCI node. And yet, this interrupt has nothing to do with a PCI legacy
>> interrupt, as it is attached to the wake-up widget that bypasses the PCIe RC
>> altogether (Yay for the broken design!). This is in total violation of the
>> IEEE Std 1275-1994 spec[1], which clearly documents that such interrupt
>> specifiers describe the PCI device interrupts, and must obey the
>> INT-{A,B,C,D} mapping. Oops!
>
> You're not the first person to notice this. All the motivations are not
> necessarily painted clearly in their cover letter, but here are some
> previous attempts at solving this problem:
>
> [RFC PATCH v11 0/5] PCI: rockchip: Move PCIe WAKE# handling into pci core
> https://lkml.kernel.org/lkml/20171225114742.18920-1-jeffy.chen@rock-chips.com/
> http://lkml.kernel.org/lkml/20171226023646.17722-1-jeffy.chen@rock-chips.com/
>
> As you can see by the 12th iteration, it wasn't left unsolved for lack
> of trying...
I wasn't aware of this. That's definitely a better approach than my
hack, and I would really like this to be revived.
>
> Frankly, if a proper DT replacement to the admittedly bad binding isn't
> agreed upon quickly, I'd be very happy to just have WAKE# support
> removed from the DTS for now, and the existing mwifiex binding should
> just be removed. (Wake-on-WiFi was never properly vetted on these
> platforms anyway.) It mostly serves to just cause problems like you've
> noticed.
Agreed. If there is no actual use for this, and that we can build a case
for a better solution, let's remove the wakeup support from the Gru DT
(it is invalid anyway), and bring it back if and when we get the right
level of support.
[...]
> One problem Rockchip authors were also trying to resolve here is that
> PCIe WAKE# handling should not really be something the PCI device driver
> has to handle directly. Despite your complaints about not using in-band
> TLP wakeup, a separate WAKE# pin is in fact a documented part of the
> PCIe standard, and it so happens that the Rockchip RC does not support
> handling TLPs in S3, if you want to have decent power consumption. (Your
> "bad hardware" complaints could justifiably fall here, I suppose.)
>
> Additionally, I've had pushback from PCI driver authors/maintainers on
> adding more special handling for this sort of interrupt property (not
> the binding specifically, but just the concept of handling WAKE# in the
> driver), as they claim this should be handled by the system firmware,
> when they set the appropriate wakeup flags, which filter down to
> __pci_enable_wake() -> platform_pci_set_wakeup(). That's how x86 systems
> do it (note: I know for a fact that many have a very similar
> architecture -- WAKE# is not routed to the RC, because, why does it need
> to? and they *don't* use TLP wakeup either -- they just hide it in
> firmware better), and it Just Works.
Even on an arm64 platform, there is no reason why a wakeup interrupt
couldn't be handled by FW rather than the OS. It just need to be wired
to the right spot (so that it generates a secure interrupt that can be
handled by FW).
> So, we basically concluded that we should standardize on a way to
> describe WAKE# interrupts such that PCI drivers don't have to deal with
> it at all, and the PCI core can do it for us. 12 revisions later
> and...we still never agreed on a good device tree binding for this.
Is the DT binding the only problem? Do we have an agreement for the core
code?
> IOW, maybe your wake-up sub-node is the best way to side-step the
> problems of conflicting with the OF PCI spec. But I'd still really like
> to avoid parsing it in mwifiex, if at all possible.
Honestly, my solution is just a terrible hack. I wasn't aware that this
was a more general problem, and I'd love it to be addressed in the core
PCI code.
> (We'd still be left with the marvell,wakeup-pin propery to parse
> specifically in mwifiex, which sadly has to exist because....well,
> Samsung decided to do chip-on-board, and then they failed to use the
> correct pin on Marvell's side when wiring up WAKE#. Sigh.)
Oh well...
Thanks,
M.
--
Jazz is not dead. It just smells funny...
^ permalink raw reply
* RE: [PATCH net-next] net: sched: pie: fix 64-bit division
From: David Laight @ 2019-02-27 10:11 UTC (permalink / raw)
To: 'Leslie Monis', davem@davemloft.net; +Cc: netdev@vger.kernel.org
In-Reply-To: <20190227010006.22219-1-lesliemonis@gmail.com>
From: Leslie Monis
> Sent: 27 February 2019 01:00
> Use div_u64() to resolve build failures on 32-bit platforms.
>
> Fixes: 3f7ae5f3dc52 ("net: sched: pie: add more cases to auto-tune alpha and beta")
> Signed-off-by: Leslie Monis <lesliemonis@gmail.com>
> ---
> net/sched/sch_pie.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/net/sched/sch_pie.c b/net/sched/sch_pie.c
> index 4c0670b6aec1..f93cfe034c72 100644
> --- a/net/sched/sch_pie.c
> +++ b/net/sched/sch_pie.c
> @@ -429,7 +429,7 @@ static void calculate_probability(struct Qdisc *sch)
> */
>
> if (qdelay == 0 && qdelay_old == 0 && update_prob)
> - q->vars.prob = (q->vars.prob * 98) / 100;
> + q->vars.prob = 98 * div_u64(q->vars.prob, 100);
This has significantly different rounding after the change.
The result for small values is very different.
The alterative:
q->vars.prob -= div_u64(q->vars.prob, 50);
is much nearer to the original - but never goes to zero.
If the 98% decay factor isn't critical then you could remove
1/64th or 1/32nd + 1/16th to avoid the slow division.
David
-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)
^ permalink raw reply
* [PATCH net-next v2 00/12] Refactor flower classifier to remove dependency on rtnl lock
From: Vlad Buslov @ 2019-02-27 10:12 UTC (permalink / raw)
To: netdev; +Cc: jhs, xiyou.wangcong, jiri, davem, sbrivio, Vlad Buslov
Currently, all netlink protocol handlers for updating rules, actions and
qdiscs are protected with single global rtnl lock which removes any
possibility for parallelism. This patch set is a third step to remove
rtnl lock dependency from TC rules update path.
Recently, new rtnl registration flag RTNL_FLAG_DOIT_UNLOCKED was added.
TC rule update handlers (RTM_NEWTFILTER, RTM_DELTFILTER, etc.) are
already registered with this flag and only take rtnl lock when qdisc or
classifier requires it. Classifiers can indicate that their ops
callbacks don't require caller to hold rtnl lock by setting the
TCF_PROTO_OPS_DOIT_UNLOCKED flag. The goal of this change is to refactor
flower classifier to support unlocked execution and register it with
unlocked flag.
This patch set implements following changes to make flower classifier
concurrency-safe:
- Implement reference counting for individual filters. Change fl_get to
take reference to filter. Implement tp->ops->put callback that was
introduced in cls API patch set to release reference to flower filter.
- Use tp->lock spinlock to protect internal classifier data structures
from concurrent modification.
- Handle concurrent tcf proto deletion by returning EAGAIN, which will
cause cls API to retry and create new proto instance or return error
to the user (depending on message type).
- Handle concurrent insertion of filter with same priority and handle by
returning EAGAIN, which will cause cls API to lookup filter again and
process it accordingly to netlink message flags.
- Extend flower mask with reference counting and protect masks list with
masks_lock spinlock.
- Prevent concurrent mask insertion by inserting temporary value to
masks hash table. This is necessary because mask initialization is a
sleeping operation and cannot be done while holding tp->lock.
Both chain level and classifier level conflicts are resolved by
returning -EAGAIN to cls API that results restart of whole operation.
This retry mechanism is a result of fine-grained locking approach used
in this and previous changes in series and is necessary to allow
concurrent updates on same chain instance. Alternative approach would be
to lock the whole chain while updating filters on any of child tp's,
adding and removing classifier instances from the chain. However, since
most CPU-intensive parts of filter update code are specifically in
classifier code and its dependencies (extensions and hw offloads), such
approach would negate most of the gains introduced by this change and
previous changes in the series when updating same chain instance.
Tcf hw offloads API is not changed by this patch set and still requires
caller to hold rtnl lock. Refactored flower classifier tracks rtnl lock
state by means of 'rtnl_held' flag provided by cls API and obtains the
lock before calling hw offloads. Following patch set will lift this
restriction and refactor cls hw offloads API to support unlocked
execution.
With these changes flower classifier is safely registered with
TCF_PROTO_OPS_DOIT_UNLOCKED flag in last patch.
Changes from V1 to V2:
- Extend cover letter with explanation about retry mechanism.
- Rebase on current net-next.
- Patch 1:
- Use rcu_dereference_raw() for tp->root dereference.
- Update comment in fl_head_dereference().
- Patch 2:
- Remove redundant check in fl_change error handling code.
- Add empty line between error check and new handle assignment.
- Patch 3:
- Refactor loop in fl_get_next_filter() to improve readability.
- Patch 4:
- Refactor __fl_delete() to improve readability.
- Patch 6:
- Fix comment in fl_check_assign_mask().
- Patch 9:
- Extend commit message.
- Fix error code in comment.
- Patch 11:
- Fix fl_hw_replace_filter() to always release rtnl lock in error
handlers.
- Patch 12:
- Don't take rtnl lock before calling __fl_destroy_filter() in
workqueue context.
- Extend commit message with explanation why flower still takes rtnl
lock before calling hardware offloads API.
Github: [https://github.com/vbuslov/linux/tree/unlocked_flower_cong2]
Vlad Buslov (12):
net: sched: flower: don't check for rtnl on head dereference
net: sched: flower: refactor fl_change
net: sched: flower: introduce reference counting for filters
net: sched: flower: track filter deletion with flag
net: sched: flower: add reference counter to flower mask
net: sched: flower: handle concurrent mask insertion
net: sched: flower: protect masks list with spinlock
net: sched: flower: handle concurrent filter insertion in fl_change
net: sched: flower: handle concurrent tcf proto deletion
net: sched: flower: protect flower classifier state with spinlock
net: sched: flower: track rtnl lock state
net: sched: flower: set unlocked flag for flower proto ops
net/sched/cls_flower.c | 440 ++++++++++++++++++++++++++++++++++++-------------
1 file changed, 329 insertions(+), 111 deletions(-)
--
2.13.6
^ permalink raw reply
* [PATCH net-next v2 04/12] net: sched: flower: track filter deletion with flag
From: Vlad Buslov @ 2019-02-27 10:12 UTC (permalink / raw)
To: netdev; +Cc: jhs, xiyou.wangcong, jiri, davem, sbrivio, Vlad Buslov
In-Reply-To: <20190227101226.26196-1-vladbu@mellanox.com>
In order to prevent double deletion of filter by concurrent tasks when rtnl
lock is not used for synchronization, add 'deleted' filter field. Check
value of this field when modifying filters and return error if concurrent
deletion is detected.
Refactor __fl_delete() to accept pointer to 'last' boolean as argument,
and return error code as function return value instead. This is necessary
to signal concurrent filter delete to caller.
Signed-off-by: Vlad Buslov <vladbu@mellanox.com>
---
Changes from V1 to V2:
- Refactor __fl_delete() to improve readability.
net/sched/cls_flower.c | 39 +++++++++++++++++++++++++++++----------
1 file changed, 29 insertions(+), 10 deletions(-)
diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c
index 9ed7c9b804a7..dd8a65cef6e1 100644
--- a/net/sched/cls_flower.c
+++ b/net/sched/cls_flower.c
@@ -110,6 +110,7 @@ struct cls_fl_filter {
* synchronization. Use atomic reference counter to be concurrency-safe.
*/
refcount_t refcnt;
+ bool deleted;
};
static const struct rhashtable_params mask_ht_params = {
@@ -458,6 +459,8 @@ static void __fl_put(struct cls_fl_filter *f)
if (!refcount_dec_and_test(&f->refcnt))
return;
+ WARN_ON(!f->deleted);
+
if (tcf_exts_get_net(&f->exts))
tcf_queue_work(&f->rwork, fl_destroy_filter_work);
else
@@ -495,22 +498,29 @@ static struct cls_fl_filter *fl_get_next_filter(struct tcf_proto *tp,
return f;
}
-static bool __fl_delete(struct tcf_proto *tp, struct cls_fl_filter *f,
- struct netlink_ext_ack *extack)
+static int __fl_delete(struct tcf_proto *tp, struct cls_fl_filter *f,
+ bool *last, struct netlink_ext_ack *extack)
{
struct cls_fl_head *head = fl_head_dereference(tp);
bool async = tcf_exts_get_net(&f->exts);
- bool last;
+ *last = false;
+
+ if (f->deleted)
+ return -ENOENT;
+
+ f->deleted = true;
+ rhashtable_remove_fast(&f->mask->ht, &f->ht_node,
+ f->mask->filter_ht_params);
idr_remove(&head->handle_idr, f->handle);
list_del_rcu(&f->list);
- last = fl_mask_put(head, f->mask, async);
+ *last = fl_mask_put(head, f->mask, async);
if (!tc_skip_hw(f->flags))
fl_hw_destroy_filter(tp, f, extack);
tcf_unbind_filter(tp, &f->res);
__fl_put(f);
- return last;
+ return 0;
}
static void fl_destroy_sleepable(struct work_struct *work)
@@ -530,10 +540,12 @@ static void fl_destroy(struct tcf_proto *tp, bool rtnl_held,
struct cls_fl_head *head = fl_head_dereference(tp);
struct fl_flow_mask *mask, *next_mask;
struct cls_fl_filter *f, *next;
+ bool last;
list_for_each_entry_safe(mask, next_mask, &head->masks, list) {
list_for_each_entry_safe(f, next, &mask->filters, list) {
- if (__fl_delete(tp, f, extack))
+ __fl_delete(tp, f, &last, extack);
+ if (last)
break;
}
}
@@ -1444,6 +1456,12 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
refcount_inc(&fnew->refcnt);
if (fold) {
+ /* Fold filter was deleted concurrently. Retry lookup. */
+ if (fold->deleted) {
+ err = -EAGAIN;
+ goto errout_hw;
+ }
+
fnew->handle = handle;
err = rhashtable_insert_fast(&fnew->mask->ht, &fnew->ht_node,
@@ -1456,6 +1474,7 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
fold->mask->filter_ht_params);
idr_replace(&head->handle_idr, fnew, fnew->handle);
list_replace_rcu(&fold->list, &fnew->list);
+ fold->deleted = true;
if (!tc_skip_hw(fold->flags))
fl_hw_destroy_filter(tp, fold, NULL);
@@ -1525,14 +1544,14 @@ static int fl_delete(struct tcf_proto *tp, void *arg, bool *last,
{
struct cls_fl_head *head = fl_head_dereference(tp);
struct cls_fl_filter *f = arg;
+ bool last_on_mask;
+ int err = 0;
- rhashtable_remove_fast(&f->mask->ht, &f->ht_node,
- f->mask->filter_ht_params);
- __fl_delete(tp, f, extack);
+ err = __fl_delete(tp, f, &last_on_mask, extack);
*last = list_empty(&head->masks);
__fl_put(f);
- return 0;
+ return err;
}
static void fl_walk(struct tcf_proto *tp, struct tcf_walker *arg,
--
2.13.6
^ permalink raw reply related
* [PATCH net-next v2 01/12] net: sched: flower: don't check for rtnl on head dereference
From: Vlad Buslov @ 2019-02-27 10:12 UTC (permalink / raw)
To: netdev; +Cc: jhs, xiyou.wangcong, jiri, davem, sbrivio, Vlad Buslov
In-Reply-To: <20190227101226.26196-1-vladbu@mellanox.com>
Flower classifier only changes root pointer during init and destroy. Cls
API implements reference counting for tcf_proto, so there is no danger of
concurrent access to tp when it is being destroyed, even without protection
provided by rtnl lock.
Implement new function fl_head_dereference() to dereference tp->root
without checking for rtnl lock. Use it in all flower function that obtain
head pointer instead of rtnl_dereference().
Signed-off-by: Vlad Buslov <vladbu@mellanox.com>
---
Changes from V1 to V2:
- Use rcu_dereference_raw() for tp->root dereference.
- Update comment in fl_head_dereference().
net/sched/cls_flower.c | 24 +++++++++++++++++-------
1 file changed, 17 insertions(+), 7 deletions(-)
diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c
index 27300a3e76c7..37bced9e2672 100644
--- a/net/sched/cls_flower.c
+++ b/net/sched/cls_flower.c
@@ -437,10 +437,20 @@ static void fl_hw_update_stats(struct tcf_proto *tp, struct cls_fl_filter *f)
cls_flower.stats.lastused);
}
+static struct cls_fl_head *fl_head_dereference(struct tcf_proto *tp)
+{
+ /* Flower classifier only changes root pointer during init and destroy.
+ * Users must obtain reference to tcf_proto instance before calling its
+ * API, so tp->root pointer is protected from concurrent call to
+ * fl_destroy() by reference counting.
+ */
+ return rcu_dereference_raw(tp->root);
+}
+
static bool __fl_delete(struct tcf_proto *tp, struct cls_fl_filter *f,
struct netlink_ext_ack *extack)
{
- struct cls_fl_head *head = rtnl_dereference(tp->root);
+ struct cls_fl_head *head = fl_head_dereference(tp);
bool async = tcf_exts_get_net(&f->exts);
bool last;
@@ -472,7 +482,7 @@ static void fl_destroy_sleepable(struct work_struct *work)
static void fl_destroy(struct tcf_proto *tp, bool rtnl_held,
struct netlink_ext_ack *extack)
{
- struct cls_fl_head *head = rtnl_dereference(tp->root);
+ struct cls_fl_head *head = fl_head_dereference(tp);
struct fl_flow_mask *mask, *next_mask;
struct cls_fl_filter *f, *next;
@@ -490,7 +500,7 @@ static void fl_destroy(struct tcf_proto *tp, bool rtnl_held,
static void *fl_get(struct tcf_proto *tp, u32 handle)
{
- struct cls_fl_head *head = rtnl_dereference(tp->root);
+ struct cls_fl_head *head = fl_head_dereference(tp);
return idr_find(&head->handle_idr, handle);
}
@@ -1308,7 +1318,7 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
void **arg, bool ovr, bool rtnl_held,
struct netlink_ext_ack *extack)
{
- struct cls_fl_head *head = rtnl_dereference(tp->root);
+ struct cls_fl_head *head = fl_head_dereference(tp);
struct cls_fl_filter *fold = *arg;
struct cls_fl_filter *fnew;
struct fl_flow_mask *mask;
@@ -1445,7 +1455,7 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
static int fl_delete(struct tcf_proto *tp, void *arg, bool *last,
bool rtnl_held, struct netlink_ext_ack *extack)
{
- struct cls_fl_head *head = rtnl_dereference(tp->root);
+ struct cls_fl_head *head = fl_head_dereference(tp);
struct cls_fl_filter *f = arg;
rhashtable_remove_fast(&f->mask->ht, &f->ht_node,
@@ -1458,7 +1468,7 @@ static int fl_delete(struct tcf_proto *tp, void *arg, bool *last,
static void fl_walk(struct tcf_proto *tp, struct tcf_walker *arg,
bool rtnl_held)
{
- struct cls_fl_head *head = rtnl_dereference(tp->root);
+ struct cls_fl_head *head = fl_head_dereference(tp);
struct cls_fl_filter *f;
arg->count = arg->skip;
@@ -1477,7 +1487,7 @@ static void fl_walk(struct tcf_proto *tp, struct tcf_walker *arg,
static int fl_reoffload(struct tcf_proto *tp, bool add, tc_setup_cb_t *cb,
void *cb_priv, struct netlink_ext_ack *extack)
{
- struct cls_fl_head *head = rtnl_dereference(tp->root);
+ struct cls_fl_head *head = fl_head_dereference(tp);
struct tc_cls_flower_offload cls_flower = {};
struct tcf_block *block = tp->chain->block;
struct fl_flow_mask *mask;
--
2.13.6
^ permalink raw reply related
* [PATCH net-next v2 02/12] net: sched: flower: refactor fl_change
From: Vlad Buslov @ 2019-02-27 10:12 UTC (permalink / raw)
To: netdev; +Cc: jhs, xiyou.wangcong, jiri, davem, sbrivio, Vlad Buslov
In-Reply-To: <20190227101226.26196-1-vladbu@mellanox.com>
As a preparation for using classifier spinlock instead of relying on
external rtnl lock, rearrange code in fl_change. The goal is to group the
code which changes classifier state in single block in order to allow
following commits in this set to protect it from parallel modification with
tp->lock. Data structures that require tp->lock protection are mask
hashtable and filters list, and classifier handle_idr.
fl_hw_replace_filter() is a sleeping function and cannot be called while
holding a spinlock. In order to execute all sequence of changes to shared
classifier data structures atomically, call fl_hw_replace_filter() before
modifying them.
Signed-off-by: Vlad Buslov <vladbu@mellanox.com>
---
Changes from V1 to V2:
- Remove redundant check in fl_change error handling code.
- Add empty line between error check and new handle assignment.
net/sched/cls_flower.c | 87 ++++++++++++++++++++++++++------------------------
1 file changed, 45 insertions(+), 42 deletions(-)
diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c
index 37bced9e2672..d36ceb5001f9 100644
--- a/net/sched/cls_flower.c
+++ b/net/sched/cls_flower.c
@@ -1358,90 +1358,93 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
if (err < 0)
goto errout;
- if (!handle) {
- handle = 1;
- err = idr_alloc_u32(&head->handle_idr, fnew, &handle,
- INT_MAX, GFP_KERNEL);
- } else if (!fold) {
- /* user specifies a handle and it doesn't exist */
- err = idr_alloc_u32(&head->handle_idr, fnew, &handle,
- handle, GFP_KERNEL);
- }
- if (err)
- goto errout;
- fnew->handle = handle;
-
if (tb[TCA_FLOWER_FLAGS]) {
fnew->flags = nla_get_u32(tb[TCA_FLOWER_FLAGS]);
if (!tc_flags_valid(fnew->flags)) {
err = -EINVAL;
- goto errout_idr;
+ goto errout;
}
}
err = fl_set_parms(net, tp, fnew, mask, base, tb, tca[TCA_RATE], ovr,
tp->chain->tmplt_priv, extack);
if (err)
- goto errout_idr;
+ goto errout;
err = fl_check_assign_mask(head, fnew, fold, mask);
if (err)
- goto errout_idr;
-
- if (!fold && __fl_lookup(fnew->mask, &fnew->mkey)) {
- err = -EEXIST;
- goto errout_mask;
- }
-
- err = rhashtable_insert_fast(&fnew->mask->ht, &fnew->ht_node,
- fnew->mask->filter_ht_params);
- if (err)
- goto errout_mask;
+ goto errout;
if (!tc_skip_hw(fnew->flags)) {
err = fl_hw_replace_filter(tp, fnew, extack);
if (err)
- goto errout_mask_ht;
+ goto errout_mask;
}
if (!tc_in_hw(fnew->flags))
fnew->flags |= TCA_CLS_FLAGS_NOT_IN_HW;
if (fold) {
+ fnew->handle = handle;
+
+ err = rhashtable_insert_fast(&fnew->mask->ht, &fnew->ht_node,
+ fnew->mask->filter_ht_params);
+ if (err)
+ goto errout_hw;
+
rhashtable_remove_fast(&fold->mask->ht,
&fold->ht_node,
fold->mask->filter_ht_params);
- if (!tc_skip_hw(fold->flags))
- fl_hw_destroy_filter(tp, fold, NULL);
- }
-
- *arg = fnew;
-
- if (fold) {
idr_replace(&head->handle_idr, fnew, fnew->handle);
list_replace_rcu(&fold->list, &fnew->list);
+
+ if (!tc_skip_hw(fold->flags))
+ fl_hw_destroy_filter(tp, fold, NULL);
tcf_unbind_filter(tp, &fold->res);
tcf_exts_get_net(&fold->exts);
tcf_queue_work(&fold->rwork, fl_destroy_filter_work);
} else {
+ if (__fl_lookup(fnew->mask, &fnew->mkey)) {
+ err = -EEXIST;
+ goto errout_hw;
+ }
+
+ if (handle) {
+ /* user specifies a handle and it doesn't exist */
+ err = idr_alloc_u32(&head->handle_idr, fnew, &handle,
+ handle, GFP_ATOMIC);
+ } else {
+ handle = 1;
+ err = idr_alloc_u32(&head->handle_idr, fnew, &handle,
+ INT_MAX, GFP_ATOMIC);
+ }
+ if (err)
+ goto errout_hw;
+
+ fnew->handle = handle;
+
+ err = rhashtable_insert_fast(&fnew->mask->ht, &fnew->ht_node,
+ fnew->mask->filter_ht_params);
+ if (err)
+ goto errout_idr;
+
list_add_tail_rcu(&fnew->list, &fnew->mask->filters);
}
+ *arg = fnew;
+
kfree(tb);
kfree(mask);
return 0;
-errout_mask_ht:
- rhashtable_remove_fast(&fnew->mask->ht, &fnew->ht_node,
- fnew->mask->filter_ht_params);
-
+errout_idr:
+ idr_remove(&head->handle_idr, fnew->handle);
+errout_hw:
+ if (!tc_skip_hw(fnew->flags))
+ fl_hw_destroy_filter(tp, fnew, NULL);
errout_mask:
fl_mask_put(head, fnew->mask, false);
-
-errout_idr:
- if (!fold)
- idr_remove(&head->handle_idr, fnew->handle);
errout:
tcf_exts_destroy(&fnew->exts);
kfree(fnew);
--
2.13.6
^ permalink raw reply related
* [PATCH net-next v2 12/12] net: sched: flower: set unlocked flag for flower proto ops
From: Vlad Buslov @ 2019-02-27 10:12 UTC (permalink / raw)
To: netdev; +Cc: jhs, xiyou.wangcong, jiri, davem, sbrivio, Vlad Buslov
In-Reply-To: <20190227101226.26196-1-vladbu@mellanox.com>
Set TCF_PROTO_OPS_DOIT_UNLOCKED for flower classifier to indicate that its
ops callbacks don't require caller to hold rtnl lock. Don't take rtnl lock
in fl_destroy_filter_work() that is executed on workqueue instead of being
called by cls API and is not affected by setting
TCF_PROTO_OPS_DOIT_UNLOCKED. Rtnl mutex is still manually taken by flower
classifier before calling hardware offloads API that has not been updated
for unlocked execution.
Signed-off-by: Vlad Buslov <vladbu@mellanox.com>
---
Changes from V1 to V2:
- Don't take rtnl lock before calling __fl_destroy_filter() in
workqueue context.
- Extend commit message with explanation why flower still takes rtnl
lock before calling hardware offloads API.
net/sched/cls_flower.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c
index 68bac808cf35..0638f17ac5ab 100644
--- a/net/sched/cls_flower.c
+++ b/net/sched/cls_flower.c
@@ -368,9 +368,7 @@ static void fl_destroy_filter_work(struct work_struct *work)
struct cls_fl_filter *f = container_of(to_rcu_work(work),
struct cls_fl_filter, rwork);
- rtnl_lock();
__fl_destroy_filter(f);
- rtnl_unlock();
}
static void fl_hw_destroy_filter(struct tcf_proto *tp, struct cls_fl_filter *f,
@@ -2372,6 +2370,7 @@ static struct tcf_proto_ops cls_fl_ops __read_mostly = {
.tmplt_destroy = fl_tmplt_destroy,
.tmplt_dump = fl_tmplt_dump,
.owner = THIS_MODULE,
+ .flags = TCF_PROTO_OPS_DOIT_UNLOCKED,
};
static int __init cls_fl_init(void)
--
2.13.6
^ permalink raw reply related
* [PATCH net-next v2 11/12] net: sched: flower: track rtnl lock state
From: Vlad Buslov @ 2019-02-27 10:12 UTC (permalink / raw)
To: netdev; +Cc: jhs, xiyou.wangcong, jiri, davem, sbrivio, Vlad Buslov
In-Reply-To: <20190227101226.26196-1-vladbu@mellanox.com>
Use 'rtnl_held' flag to track if caller holds rtnl lock. Propagate the flag
to internal functions that need to know rtnl lock state. Take rtnl lock
before calling tcf APIs that require it (hw offload, bind filter, etc.).
Signed-off-by: Vlad Buslov <vladbu@mellanox.com>
---
Changes from V1 to V2:
- Fix fl_hw_replace_filter() to always release rtnl lock in error
handlers.
net/sched/cls_flower.c | 82 ++++++++++++++++++++++++++++++++++----------------
1 file changed, 56 insertions(+), 26 deletions(-)
diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c
index 04210d645c78..68bac808cf35 100644
--- a/net/sched/cls_flower.c
+++ b/net/sched/cls_flower.c
@@ -374,11 +374,14 @@ static void fl_destroy_filter_work(struct work_struct *work)
}
static void fl_hw_destroy_filter(struct tcf_proto *tp, struct cls_fl_filter *f,
- struct netlink_ext_ack *extack)
+ bool rtnl_held, struct netlink_ext_ack *extack)
{
struct tc_cls_flower_offload cls_flower = {};
struct tcf_block *block = tp->chain->block;
+ if (!rtnl_held)
+ rtnl_lock();
+
tc_cls_common_offload_init(&cls_flower.common, tp, f->flags, extack);
cls_flower.command = TC_CLSFLOWER_DESTROY;
cls_flower.cookie = (unsigned long) f;
@@ -387,20 +390,28 @@ static void fl_hw_destroy_filter(struct tcf_proto *tp, struct cls_fl_filter *f,
spin_lock(&tp->lock);
tcf_block_offload_dec(block, &f->flags);
spin_unlock(&tp->lock);
+
+ if (!rtnl_held)
+ rtnl_unlock();
}
static int fl_hw_replace_filter(struct tcf_proto *tp,
- struct cls_fl_filter *f,
+ struct cls_fl_filter *f, bool rtnl_held,
struct netlink_ext_ack *extack)
{
struct tc_cls_flower_offload cls_flower = {};
struct tcf_block *block = tp->chain->block;
bool skip_sw = tc_skip_sw(f->flags);
- int err;
+ int err = 0;
+
+ if (!rtnl_held)
+ rtnl_lock();
cls_flower.rule = flow_rule_alloc(tcf_exts_num_actions(&f->exts));
- if (!cls_flower.rule)
- return -ENOMEM;
+ if (!cls_flower.rule) {
+ err = -ENOMEM;
+ goto errout;
+ }
tc_cls_common_offload_init(&cls_flower.common, tp, f->flags, extack);
cls_flower.command = TC_CLSFLOWER_REPLACE;
@@ -413,37 +424,48 @@ static int fl_hw_replace_filter(struct tcf_proto *tp,
err = tc_setup_flow_action(&cls_flower.rule->action, &f->exts);
if (err) {
kfree(cls_flower.rule);
- if (skip_sw) {
+ if (skip_sw)
NL_SET_ERR_MSG_MOD(extack, "Failed to setup flow action");
- return err;
- }
- return 0;
+ else
+ err = 0;
+ goto errout;
}
err = tc_setup_cb_call(block, TC_SETUP_CLSFLOWER, &cls_flower, skip_sw);
kfree(cls_flower.rule);
if (err < 0) {
- fl_hw_destroy_filter(tp, f, NULL);
- return err;
+ fl_hw_destroy_filter(tp, f, true, NULL);
+ goto errout;
} else if (err > 0) {
f->in_hw_count = err;
+ err = 0;
spin_lock(&tp->lock);
tcf_block_offload_inc(block, &f->flags);
spin_unlock(&tp->lock);
}
- if (skip_sw && !(f->flags & TCA_CLS_FLAGS_IN_HW))
- return -EINVAL;
+ if (skip_sw && !(f->flags & TCA_CLS_FLAGS_IN_HW)) {
+ err = -EINVAL;
+ goto errout;
+ }
- return 0;
+errout:
+ if (!rtnl_held)
+ rtnl_unlock();
+
+ return err;
}
-static void fl_hw_update_stats(struct tcf_proto *tp, struct cls_fl_filter *f)
+static void fl_hw_update_stats(struct tcf_proto *tp, struct cls_fl_filter *f,
+ bool rtnl_held)
{
struct tc_cls_flower_offload cls_flower = {};
struct tcf_block *block = tp->chain->block;
+ if (!rtnl_held)
+ rtnl_lock();
+
tc_cls_common_offload_init(&cls_flower.common, tp, f->flags, NULL);
cls_flower.command = TC_CLSFLOWER_STATS;
cls_flower.cookie = (unsigned long) f;
@@ -454,6 +476,9 @@ static void fl_hw_update_stats(struct tcf_proto *tp, struct cls_fl_filter *f)
tcf_exts_stats_update(&f->exts, cls_flower.stats.bytes,
cls_flower.stats.pkts,
cls_flower.stats.lastused);
+
+ if (!rtnl_held)
+ rtnl_unlock();
}
static struct cls_fl_head *fl_head_dereference(struct tcf_proto *tp)
@@ -511,7 +536,8 @@ static struct cls_fl_filter *fl_get_next_filter(struct tcf_proto *tp,
}
static int __fl_delete(struct tcf_proto *tp, struct cls_fl_filter *f,
- bool *last, struct netlink_ext_ack *extack)
+ bool *last, bool rtnl_held,
+ struct netlink_ext_ack *extack)
{
struct cls_fl_head *head = fl_head_dereference(tp);
bool async = tcf_exts_get_net(&f->exts);
@@ -533,7 +559,7 @@ static int __fl_delete(struct tcf_proto *tp, struct cls_fl_filter *f,
*last = fl_mask_put(head, f->mask, async);
if (!tc_skip_hw(f->flags))
- fl_hw_destroy_filter(tp, f, extack);
+ fl_hw_destroy_filter(tp, f, rtnl_held, extack);
tcf_unbind_filter(tp, &f->res);
__fl_put(f);
@@ -561,7 +587,7 @@ static void fl_destroy(struct tcf_proto *tp, bool rtnl_held,
list_for_each_entry_safe(mask, next_mask, &head->masks, list) {
list_for_each_entry_safe(f, next, &mask->filters, list) {
- __fl_delete(tp, f, &last, extack);
+ __fl_delete(tp, f, &last, rtnl_held, extack);
if (last)
break;
}
@@ -1401,19 +1427,23 @@ static int fl_set_parms(struct net *net, struct tcf_proto *tp,
struct cls_fl_filter *f, struct fl_flow_mask *mask,
unsigned long base, struct nlattr **tb,
struct nlattr *est, bool ovr,
- struct fl_flow_tmplt *tmplt,
+ struct fl_flow_tmplt *tmplt, bool rtnl_held,
struct netlink_ext_ack *extack)
{
int err;
- err = tcf_exts_validate(net, tp, tb, est, &f->exts, ovr, true,
+ err = tcf_exts_validate(net, tp, tb, est, &f->exts, ovr, rtnl_held,
extack);
if (err < 0)
return err;
if (tb[TCA_FLOWER_CLASSID]) {
f->res.classid = nla_get_u32(tb[TCA_FLOWER_CLASSID]);
+ if (!rtnl_held)
+ rtnl_lock();
tcf_bind_filter(tp, &f->res, base);
+ if (!rtnl_held)
+ rtnl_unlock();
}
err = fl_set_key(net, tb, &f->key, &mask->key, extack);
@@ -1492,7 +1522,7 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
}
err = fl_set_parms(net, tp, fnew, mask, base, tb, tca[TCA_RATE], ovr,
- tp->chain->tmplt_priv, extack);
+ tp->chain->tmplt_priv, rtnl_held, extack);
if (err)
goto errout;
@@ -1501,7 +1531,7 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
goto errout;
if (!tc_skip_hw(fnew->flags)) {
- err = fl_hw_replace_filter(tp, fnew, extack);
+ err = fl_hw_replace_filter(tp, fnew, rtnl_held, extack);
if (err)
goto errout_mask;
}
@@ -1545,7 +1575,7 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
fl_mask_put(head, fold->mask, true);
if (!tc_skip_hw(fold->flags))
- fl_hw_destroy_filter(tp, fold, NULL);
+ fl_hw_destroy_filter(tp, fold, rtnl_held, NULL);
tcf_unbind_filter(tp, &fold->res);
tcf_exts_get_net(&fold->exts);
/* Caller holds reference to fold, so refcnt is always > 0
@@ -1602,7 +1632,7 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
errout_hw:
spin_unlock(&tp->lock);
if (!tc_skip_hw(fnew->flags))
- fl_hw_destroy_filter(tp, fnew, NULL);
+ fl_hw_destroy_filter(tp, fnew, rtnl_held, NULL);
errout_mask:
fl_mask_put(head, fnew->mask, true);
errout:
@@ -1626,7 +1656,7 @@ static int fl_delete(struct tcf_proto *tp, void *arg, bool *last,
bool last_on_mask;
int err = 0;
- err = __fl_delete(tp, f, &last_on_mask, extack);
+ err = __fl_delete(tp, f, &last_on_mask, rtnl_held, extack);
*last = list_empty(&head->masks);
__fl_put(f);
@@ -2270,7 +2300,7 @@ static int fl_dump(struct net *net, struct tcf_proto *tp, void *fh,
spin_unlock(&tp->lock);
if (!skip_hw)
- fl_hw_update_stats(tp, f);
+ fl_hw_update_stats(tp, f, rtnl_held);
if (nla_put_u32(skb, TCA_FLOWER_IN_HW_COUNT, f->in_hw_count))
goto nla_put_failure;
--
2.13.6
^ permalink raw reply related
* [PATCH net-next v2 08/12] net: sched: flower: handle concurrent filter insertion in fl_change
From: Vlad Buslov @ 2019-02-27 10:12 UTC (permalink / raw)
To: netdev; +Cc: jhs, xiyou.wangcong, jiri, davem, sbrivio, Vlad Buslov
In-Reply-To: <20190227101226.26196-1-vladbu@mellanox.com>
Check if user specified a handle and another filter with the same handle
was inserted concurrently. Return EAGAIN to retry filter processing (in
case it is an overwrite request).
Signed-off-by: Vlad Buslov <vladbu@mellanox.com>
Acked-by: Jiri Pirko <jiri@mellanox.com>
---
net/sched/cls_flower.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c
index db47828ea5e2..70b357f23391 100644
--- a/net/sched/cls_flower.c
+++ b/net/sched/cls_flower.c
@@ -1542,6 +1542,15 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
/* user specifies a handle and it doesn't exist */
err = idr_alloc_u32(&head->handle_idr, fnew, &handle,
handle, GFP_ATOMIC);
+
+ /* Filter with specified handle was concurrently
+ * inserted after initial check in cls_api. This is not
+ * necessarily an error if NLM_F_EXCL is not set in
+ * message flags. Returning EAGAIN will cause cls_api to
+ * try to update concurrently inserted rule.
+ */
+ if (err == -ENOSPC)
+ err = -EAGAIN;
} else {
handle = 1;
err = idr_alloc_u32(&head->handle_idr, fnew, &handle,
--
2.13.6
^ permalink raw reply related
* [PATCH net-next v2 06/12] net: sched: flower: handle concurrent mask insertion
From: Vlad Buslov @ 2019-02-27 10:12 UTC (permalink / raw)
To: netdev; +Cc: jhs, xiyou.wangcong, jiri, davem, sbrivio, Vlad Buslov
In-Reply-To: <20190227101226.26196-1-vladbu@mellanox.com>
Without rtnl lock protection masks with same key can be inserted
concurrently. Insert temporary mask with reference count zero to masks
hashtable. This will cause any concurrent modifications to retry.
Wait for rcu grace period to complete after removing temporary mask from
masks hashtable to accommodate concurrent readers.
Signed-off-by: Vlad Buslov <vladbu@mellanox.com>
Acked-by: Jiri Pirko <jiri@mellanox.com>
Suggested-by: Jiri Pirko <jiri@mellanox.com>
---
Changes from V1 to V2:
- Fix comment in fl_check_assign_mask().
net/sched/cls_flower.c | 41 ++++++++++++++++++++++++++++++++++-------
1 file changed, 34 insertions(+), 7 deletions(-)
diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c
index e98313cd710a..92478bb122d3 100644
--- a/net/sched/cls_flower.c
+++ b/net/sched/cls_flower.c
@@ -1304,11 +1304,14 @@ static struct fl_flow_mask *fl_create_new_mask(struct cls_fl_head *head,
INIT_LIST_HEAD_RCU(&newmask->filters);
refcount_set(&newmask->refcnt, 1);
- err = rhashtable_insert_fast(&head->ht, &newmask->ht_node,
- mask_ht_params);
+ err = rhashtable_replace_fast(&head->ht, &mask->ht_node,
+ &newmask->ht_node, mask_ht_params);
if (err)
goto errout_destroy;
+ /* Wait until any potential concurrent users of mask are finished */
+ synchronize_rcu();
+
list_add_tail_rcu(&newmask->list, &head->masks);
return newmask;
@@ -1330,19 +1333,36 @@ static int fl_check_assign_mask(struct cls_fl_head *head,
int ret = 0;
rcu_read_lock();
- fnew->mask = rhashtable_lookup_fast(&head->ht, mask, mask_ht_params);
+
+ /* Insert mask as temporary node to prevent concurrent creation of mask
+ * with same key. Any concurrent lookups with same key will return
+ * -EAGAIN because mask's refcnt is zero. It is safe to insert
+ * stack-allocated 'mask' to masks hash table because we call
+ * synchronize_rcu() before returning from this function (either in case
+ * of error or after replacing it with heap-allocated mask in
+ * fl_create_new_mask()).
+ */
+ fnew->mask = rhashtable_lookup_get_insert_fast(&head->ht,
+ &mask->ht_node,
+ mask_ht_params);
if (!fnew->mask) {
rcu_read_unlock();
- if (fold)
- return -EINVAL;
+ if (fold) {
+ ret = -EINVAL;
+ goto errout_cleanup;
+ }
newmask = fl_create_new_mask(head, mask);
- if (IS_ERR(newmask))
- return PTR_ERR(newmask);
+ if (IS_ERR(newmask)) {
+ ret = PTR_ERR(newmask);
+ goto errout_cleanup;
+ }
fnew->mask = newmask;
return 0;
+ } else if (IS_ERR(fnew->mask)) {
+ ret = PTR_ERR(fnew->mask);
} else if (fold && fold->mask != fnew->mask) {
ret = -EINVAL;
} else if (!refcount_inc_not_zero(&fnew->mask->refcnt)) {
@@ -1351,6 +1371,13 @@ static int fl_check_assign_mask(struct cls_fl_head *head,
}
rcu_read_unlock();
return ret;
+
+errout_cleanup:
+ rhashtable_remove_fast(&head->ht, &mask->ht_node,
+ mask_ht_params);
+ /* Wait until any potential concurrent users of mask are finished */
+ synchronize_rcu();
+ return ret;
}
static int fl_set_parms(struct net *net, struct tcf_proto *tp,
--
2.13.6
^ permalink raw reply related
* [PATCH net-next v2 10/12] net: sched: flower: protect flower classifier state with spinlock
From: Vlad Buslov @ 2019-02-27 10:12 UTC (permalink / raw)
To: netdev; +Cc: jhs, xiyou.wangcong, jiri, davem, sbrivio, Vlad Buslov
In-Reply-To: <20190227101226.26196-1-vladbu@mellanox.com>
struct tcf_proto was extended with spinlock to be used by classifiers
instead of global rtnl lock. Use it to protect shared flower classifier
data structures (handle_idr, mask hashtable and list) and fields of
individual filters that can be accessed concurrently. This patch set uses
tcf_proto->lock as per instance lock that protects all filters on
tcf_proto.
Signed-off-by: Vlad Buslov <vladbu@mellanox.com>
---
net/sched/cls_flower.c | 39 ++++++++++++++++++++++++++++++++-------
1 file changed, 32 insertions(+), 7 deletions(-)
diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c
index 25a4d64b82db..04210d645c78 100644
--- a/net/sched/cls_flower.c
+++ b/net/sched/cls_flower.c
@@ -384,7 +384,9 @@ static void fl_hw_destroy_filter(struct tcf_proto *tp, struct cls_fl_filter *f,
cls_flower.cookie = (unsigned long) f;
tc_setup_cb_call(block, TC_SETUP_CLSFLOWER, &cls_flower, false);
+ spin_lock(&tp->lock);
tcf_block_offload_dec(block, &f->flags);
+ spin_unlock(&tp->lock);
}
static int fl_hw_replace_filter(struct tcf_proto *tp,
@@ -426,7 +428,9 @@ static int fl_hw_replace_filter(struct tcf_proto *tp,
return err;
} else if (err > 0) {
f->in_hw_count = err;
+ spin_lock(&tp->lock);
tcf_block_offload_inc(block, &f->flags);
+ spin_unlock(&tp->lock);
}
if (skip_sw && !(f->flags & TCA_CLS_FLAGS_IN_HW))
@@ -514,14 +518,19 @@ static int __fl_delete(struct tcf_proto *tp, struct cls_fl_filter *f,
*last = false;
- if (f->deleted)
+ spin_lock(&tp->lock);
+ if (f->deleted) {
+ spin_unlock(&tp->lock);
return -ENOENT;
+ }
f->deleted = true;
rhashtable_remove_fast(&f->mask->ht, &f->ht_node,
f->mask->filter_ht_params);
idr_remove(&head->handle_idr, f->handle);
list_del_rcu(&f->list);
+ spin_unlock(&tp->lock);
+
*last = fl_mask_put(head, f->mask, async);
if (!tc_skip_hw(f->flags))
fl_hw_destroy_filter(tp, f, extack);
@@ -1500,6 +1509,8 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
if (!tc_in_hw(fnew->flags))
fnew->flags |= TCA_CLS_FLAGS_NOT_IN_HW;
+ spin_lock(&tp->lock);
+
/* tp was deleted concurrently. -EAGAIN will cause caller to lookup
* proto again or create new one, if necessary.
*/
@@ -1530,6 +1541,8 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
list_replace_rcu(&fold->list, &fnew->list);
fold->deleted = true;
+ spin_unlock(&tp->lock);
+
fl_mask_put(head, fold->mask, true);
if (!tc_skip_hw(fold->flags))
fl_hw_destroy_filter(tp, fold, NULL);
@@ -1575,6 +1588,7 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
goto errout_idr;
list_add_tail_rcu(&fnew->list, &fnew->mask->filters);
+ spin_unlock(&tp->lock);
}
*arg = fnew;
@@ -1586,6 +1600,7 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
errout_idr:
idr_remove(&head->handle_idr, fnew->handle);
errout_hw:
+ spin_unlock(&tp->lock);
if (!tc_skip_hw(fnew->flags))
fl_hw_destroy_filter(tp, fnew, NULL);
errout_mask:
@@ -1688,8 +1703,10 @@ static int fl_reoffload(struct tcf_proto *tp, bool add, tc_setup_cb_t *cb,
continue;
}
+ spin_lock(&tp->lock);
tc_cls_offload_cnt_update(block, &f->in_hw_count,
&f->flags, add);
+ spin_unlock(&tp->lock);
}
}
@@ -2223,6 +2240,7 @@ static int fl_dump(struct net *net, struct tcf_proto *tp, void *fh,
struct cls_fl_filter *f = fh;
struct nlattr *nest;
struct fl_flow_key *key, *mask;
+ bool skip_hw;
if (!f)
return skb->len;
@@ -2233,21 +2251,26 @@ static int fl_dump(struct net *net, struct tcf_proto *tp, void *fh,
if (!nest)
goto nla_put_failure;
+ spin_lock(&tp->lock);
+
if (f->res.classid &&
nla_put_u32(skb, TCA_FLOWER_CLASSID, f->res.classid))
- goto nla_put_failure;
+ goto nla_put_failure_locked;
key = &f->key;
mask = &f->mask->key;
+ skip_hw = tc_skip_hw(f->flags);
if (fl_dump_key(skb, net, key, mask))
- goto nla_put_failure;
-
- if (!tc_skip_hw(f->flags))
- fl_hw_update_stats(tp, f);
+ goto nla_put_failure_locked;
if (f->flags && nla_put_u32(skb, TCA_FLOWER_FLAGS, f->flags))
- goto nla_put_failure;
+ goto nla_put_failure_locked;
+
+ spin_unlock(&tp->lock);
+
+ if (!skip_hw)
+ fl_hw_update_stats(tp, f);
if (nla_put_u32(skb, TCA_FLOWER_IN_HW_COUNT, f->in_hw_count))
goto nla_put_failure;
@@ -2262,6 +2285,8 @@ static int fl_dump(struct net *net, struct tcf_proto *tp, void *fh,
return skb->len;
+nla_put_failure_locked:
+ spin_unlock(&tp->lock);
nla_put_failure:
nla_nest_cancel(skb, nest);
return -1;
--
2.13.6
^ permalink raw reply related
* [PATCH net-next v2 03/12] net: sched: flower: introduce reference counting for filters
From: Vlad Buslov @ 2019-02-27 10:12 UTC (permalink / raw)
To: netdev; +Cc: jhs, xiyou.wangcong, jiri, davem, sbrivio, Vlad Buslov
In-Reply-To: <20190227101226.26196-1-vladbu@mellanox.com>
Extend flower filters with reference counting in order to remove dependency
on rtnl lock in flower ops and allow to modify filters concurrently.
Reference to flower filter can be taken/released concurrently as soon as it
is marked as 'unlocked' by last patch in this series. Use atomic reference
counter type to make concurrent modifications safe.
Always take reference to flower filter while working with it:
- Modify fl_get() to take reference to filter.
- Implement tp->put() callback as fl_put() function to allow cls API to
release reference taken by fl_get().
- Modify fl_change() to assume that caller holds reference to fold and take
reference to fnew.
- Take reference to filter while using it in fl_walk().
Implement helper functions to get/put filter reference counter.
Signed-off-by: Vlad Buslov <vladbu@mellanox.com>
---
Changes from V1 to V2:
- Refactor loop in fl_get_next_filter() to improve readability.
net/sched/cls_flower.c | 96 ++++++++++++++++++++++++++++++++++++++++++--------
1 file changed, 82 insertions(+), 14 deletions(-)
diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c
index d36ceb5001f9..9ed7c9b804a7 100644
--- a/net/sched/cls_flower.c
+++ b/net/sched/cls_flower.c
@@ -14,6 +14,7 @@
#include <linux/module.h>
#include <linux/rhashtable.h>
#include <linux/workqueue.h>
+#include <linux/refcount.h>
#include <linux/if_ether.h>
#include <linux/in6.h>
@@ -104,6 +105,11 @@ struct cls_fl_filter {
u32 in_hw_count;
struct rcu_work rwork;
struct net_device *hw_dev;
+ /* Flower classifier is unlocked, which means that its reference counter
+ * can be changed concurrently without any kind of external
+ * synchronization. Use atomic reference counter to be concurrency-safe.
+ */
+ refcount_t refcnt;
};
static const struct rhashtable_params mask_ht_params = {
@@ -447,6 +453,48 @@ static struct cls_fl_head *fl_head_dereference(struct tcf_proto *tp)
return rcu_dereference_raw(tp->root);
}
+static void __fl_put(struct cls_fl_filter *f)
+{
+ if (!refcount_dec_and_test(&f->refcnt))
+ return;
+
+ if (tcf_exts_get_net(&f->exts))
+ tcf_queue_work(&f->rwork, fl_destroy_filter_work);
+ else
+ __fl_destroy_filter(f);
+}
+
+static struct cls_fl_filter *__fl_get(struct cls_fl_head *head, u32 handle)
+{
+ struct cls_fl_filter *f;
+
+ rcu_read_lock();
+ f = idr_find(&head->handle_idr, handle);
+ if (f && !refcount_inc_not_zero(&f->refcnt))
+ f = NULL;
+ rcu_read_unlock();
+
+ return f;
+}
+
+static struct cls_fl_filter *fl_get_next_filter(struct tcf_proto *tp,
+ unsigned long *handle)
+{
+ struct cls_fl_head *head = fl_head_dereference(tp);
+ struct cls_fl_filter *f;
+
+ rcu_read_lock();
+ while ((f = idr_get_next_ul(&head->handle_idr, handle))) {
+ /* don't return filters that are being deleted */
+ if (refcount_inc_not_zero(&f->refcnt))
+ break;
+ ++(*handle);
+ }
+ rcu_read_unlock();
+
+ return f;
+}
+
static bool __fl_delete(struct tcf_proto *tp, struct cls_fl_filter *f,
struct netlink_ext_ack *extack)
{
@@ -460,10 +508,7 @@ static bool __fl_delete(struct tcf_proto *tp, struct cls_fl_filter *f,
if (!tc_skip_hw(f->flags))
fl_hw_destroy_filter(tp, f, extack);
tcf_unbind_filter(tp, &f->res);
- if (async)
- tcf_queue_work(&f->rwork, fl_destroy_filter_work);
- else
- __fl_destroy_filter(f);
+ __fl_put(f);
return last;
}
@@ -498,11 +543,18 @@ static void fl_destroy(struct tcf_proto *tp, bool rtnl_held,
tcf_queue_work(&head->rwork, fl_destroy_sleepable);
}
+static void fl_put(struct tcf_proto *tp, void *arg)
+{
+ struct cls_fl_filter *f = arg;
+
+ __fl_put(f);
+}
+
static void *fl_get(struct tcf_proto *tp, u32 handle)
{
struct cls_fl_head *head = fl_head_dereference(tp);
- return idr_find(&head->handle_idr, handle);
+ return __fl_get(head, handle);
}
static const struct nla_policy fl_policy[TCA_FLOWER_MAX + 1] = {
@@ -1325,12 +1377,16 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
struct nlattr **tb;
int err;
- if (!tca[TCA_OPTIONS])
- return -EINVAL;
+ if (!tca[TCA_OPTIONS]) {
+ err = -EINVAL;
+ goto errout_fold;
+ }
mask = kzalloc(sizeof(struct fl_flow_mask), GFP_KERNEL);
- if (!mask)
- return -ENOBUFS;
+ if (!mask) {
+ err = -ENOBUFS;
+ goto errout_fold;
+ }
tb = kcalloc(TCA_FLOWER_MAX + 1, sizeof(struct nlattr *), GFP_KERNEL);
if (!tb) {
@@ -1353,6 +1409,7 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
err = -ENOBUFS;
goto errout_tb;
}
+ refcount_set(&fnew->refcnt, 1);
err = tcf_exts_init(&fnew->exts, net, TCA_FLOWER_ACT, 0);
if (err < 0)
@@ -1385,6 +1442,7 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
if (!tc_in_hw(fnew->flags))
fnew->flags |= TCA_CLS_FLAGS_NOT_IN_HW;
+ refcount_inc(&fnew->refcnt);
if (fold) {
fnew->handle = handle;
@@ -1403,7 +1461,11 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
fl_hw_destroy_filter(tp, fold, NULL);
tcf_unbind_filter(tp, &fold->res);
tcf_exts_get_net(&fold->exts);
- tcf_queue_work(&fold->rwork, fl_destroy_filter_work);
+ /* Caller holds reference to fold, so refcnt is always > 0
+ * after this.
+ */
+ refcount_dec(&fold->refcnt);
+ __fl_put(fold);
} else {
if (__fl_lookup(fnew->mask, &fnew->mkey)) {
err = -EEXIST;
@@ -1452,6 +1514,9 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
kfree(tb);
errout_mask_alloc:
kfree(mask);
+errout_fold:
+ if (fold)
+ __fl_put(fold);
return err;
}
@@ -1465,24 +1530,26 @@ static int fl_delete(struct tcf_proto *tp, void *arg, bool *last,
f->mask->filter_ht_params);
__fl_delete(tp, f, extack);
*last = list_empty(&head->masks);
+ __fl_put(f);
+
return 0;
}
static void fl_walk(struct tcf_proto *tp, struct tcf_walker *arg,
bool rtnl_held)
{
- struct cls_fl_head *head = fl_head_dereference(tp);
struct cls_fl_filter *f;
arg->count = arg->skip;
- while ((f = idr_get_next_ul(&head->handle_idr,
- &arg->cookie)) != NULL) {
+ while ((f = fl_get_next_filter(tp, &arg->cookie)) != NULL) {
if (arg->fn(tp, f, arg) < 0) {
+ __fl_put(f);
arg->stop = 1;
break;
}
- arg->cookie = f->handle + 1;
+ __fl_put(f);
+ arg->cookie++;
arg->count++;
}
}
@@ -2156,6 +2223,7 @@ static struct tcf_proto_ops cls_fl_ops __read_mostly = {
.init = fl_init,
.destroy = fl_destroy,
.get = fl_get,
+ .put = fl_put,
.change = fl_change,
.delete = fl_delete,
.walk = fl_walk,
--
2.13.6
^ permalink raw reply related
* [PATCH net-next v2 07/12] net: sched: flower: protect masks list with spinlock
From: Vlad Buslov @ 2019-02-27 10:12 UTC (permalink / raw)
To: netdev; +Cc: jhs, xiyou.wangcong, jiri, davem, sbrivio, Vlad Buslov
In-Reply-To: <20190227101226.26196-1-vladbu@mellanox.com>
Protect modifications of flower masks list with spinlock to remove
dependency on rtnl lock and allow concurrent access.
Signed-off-by: Vlad Buslov <vladbu@mellanox.com>
Acked-by: Jiri Pirko <jiri@mellanox.com>
---
net/sched/cls_flower.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c
index 92478bb122d3..db47828ea5e2 100644
--- a/net/sched/cls_flower.c
+++ b/net/sched/cls_flower.c
@@ -88,6 +88,7 @@ struct fl_flow_tmplt {
struct cls_fl_head {
struct rhashtable ht;
+ spinlock_t masks_lock; /* Protect masks list */
struct list_head masks;
struct rcu_work rwork;
struct idr handle_idr;
@@ -312,6 +313,7 @@ static int fl_init(struct tcf_proto *tp)
if (!head)
return -ENOBUFS;
+ spin_lock_init(&head->masks_lock);
INIT_LIST_HEAD_RCU(&head->masks);
rcu_assign_pointer(tp->root, head);
idr_init(&head->handle_idr);
@@ -341,7 +343,11 @@ static bool fl_mask_put(struct cls_fl_head *head, struct fl_flow_mask *mask,
return false;
rhashtable_remove_fast(&head->ht, &mask->ht_node, mask_ht_params);
+
+ spin_lock(&head->masks_lock);
list_del_rcu(&mask->list);
+ spin_unlock(&head->masks_lock);
+
if (async)
tcf_queue_work(&mask->rwork, fl_mask_free_work);
else
@@ -1312,7 +1318,9 @@ static struct fl_flow_mask *fl_create_new_mask(struct cls_fl_head *head,
/* Wait until any potential concurrent users of mask are finished */
synchronize_rcu();
+ spin_lock(&head->masks_lock);
list_add_tail_rcu(&newmask->list, &head->masks);
+ spin_unlock(&head->masks_lock);
return newmask;
--
2.13.6
^ permalink raw reply related
* [PATCH net-next v2 09/12] net: sched: flower: handle concurrent tcf proto deletion
From: Vlad Buslov @ 2019-02-27 10:12 UTC (permalink / raw)
To: netdev; +Cc: jhs, xiyou.wangcong, jiri, davem, sbrivio, Vlad Buslov
In-Reply-To: <20190227101226.26196-1-vladbu@mellanox.com>
Without rtnl lock protection tcf proto can be deleted concurrently. Check
tcf proto 'deleting' flag after taking tcf spinlock to verify that no
concurrent deletion is in progress. Return EAGAIN error if concurrent
deletion detected, which will cause caller to retry and possibly create new
instance of tcf proto.
Retry mechanism is a result of fine-grained locking approach used in this
and previous changes in series and is necessary to allow concurrent updates
on same chain instance. Alternative approach would be to lock the whole
chain while updating filters on any of child tp's, adding and removing
classifier instances from the chain. However, since most CPU-intensive
parts of filter update code are specifically in classifier code and its
dependencies (extensions and hw offloads), such approach would negate most
of the gains introduced by this change and previous changes in the series
when updating same chain instance.
Signed-off-by: Vlad Buslov <vladbu@mellanox.com>
---
Changes from V1 to V2:
- Extend commit message.
- Fix error code in comment.
net/sched/cls_flower.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c
index 70b357f23391..25a4d64b82db 100644
--- a/net/sched/cls_flower.c
+++ b/net/sched/cls_flower.c
@@ -1500,6 +1500,14 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
if (!tc_in_hw(fnew->flags))
fnew->flags |= TCA_CLS_FLAGS_NOT_IN_HW;
+ /* tp was deleted concurrently. -EAGAIN will cause caller to lookup
+ * proto again or create new one, if necessary.
+ */
+ if (tp->deleting) {
+ err = -EAGAIN;
+ goto errout_hw;
+ }
+
refcount_inc(&fnew->refcnt);
if (fold) {
/* Fold filter was deleted concurrently. Retry lookup. */
--
2.13.6
^ permalink raw reply related
* [PATCH net-next v2 05/12] net: sched: flower: add reference counter to flower mask
From: Vlad Buslov @ 2019-02-27 10:12 UTC (permalink / raw)
To: netdev; +Cc: jhs, xiyou.wangcong, jiri, davem, sbrivio, Vlad Buslov
In-Reply-To: <20190227101226.26196-1-vladbu@mellanox.com>
Extend fl_flow_mask structure with reference counter to allow parallel
modification without relying on rtnl lock. Use rcu read lock to safely
lookup mask and increment reference counter in order to accommodate
concurrent deletes.
Signed-off-by: Vlad Buslov <vladbu@mellanox.com>
Acked-by: Jiri Pirko <jiri@mellanox.com>
---
net/sched/cls_flower.c | 22 +++++++++++++++++-----
1 file changed, 17 insertions(+), 5 deletions(-)
diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c
index dd8a65cef6e1..e98313cd710a 100644
--- a/net/sched/cls_flower.c
+++ b/net/sched/cls_flower.c
@@ -76,6 +76,7 @@ struct fl_flow_mask {
struct list_head filters;
struct rcu_work rwork;
struct list_head list;
+ refcount_t refcnt;
};
struct fl_flow_tmplt {
@@ -320,6 +321,7 @@ static int fl_init(struct tcf_proto *tp)
static void fl_mask_free(struct fl_flow_mask *mask)
{
+ WARN_ON(!list_empty(&mask->filters));
rhashtable_destroy(&mask->ht);
kfree(mask);
}
@@ -335,7 +337,7 @@ static void fl_mask_free_work(struct work_struct *work)
static bool fl_mask_put(struct cls_fl_head *head, struct fl_flow_mask *mask,
bool async)
{
- if (!list_empty(&mask->filters))
+ if (!refcount_dec_and_test(&mask->refcnt))
return false;
rhashtable_remove_fast(&head->ht, &mask->ht_node, mask_ht_params);
@@ -1301,6 +1303,7 @@ static struct fl_flow_mask *fl_create_new_mask(struct cls_fl_head *head,
INIT_LIST_HEAD_RCU(&newmask->filters);
+ refcount_set(&newmask->refcnt, 1);
err = rhashtable_insert_fast(&head->ht, &newmask->ht_node,
mask_ht_params);
if (err)
@@ -1324,9 +1327,13 @@ static int fl_check_assign_mask(struct cls_fl_head *head,
struct fl_flow_mask *mask)
{
struct fl_flow_mask *newmask;
+ int ret = 0;
+ rcu_read_lock();
fnew->mask = rhashtable_lookup_fast(&head->ht, mask, mask_ht_params);
if (!fnew->mask) {
+ rcu_read_unlock();
+
if (fold)
return -EINVAL;
@@ -1335,11 +1342,15 @@ static int fl_check_assign_mask(struct cls_fl_head *head,
return PTR_ERR(newmask);
fnew->mask = newmask;
+ return 0;
} else if (fold && fold->mask != fnew->mask) {
- return -EINVAL;
+ ret = -EINVAL;
+ } else if (!refcount_inc_not_zero(&fnew->mask->refcnt)) {
+ /* Mask was deleted concurrently, try again */
+ ret = -EAGAIN;
}
-
- return 0;
+ rcu_read_unlock();
+ return ret;
}
static int fl_set_parms(struct net *net, struct tcf_proto *tp,
@@ -1476,6 +1487,7 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
list_replace_rcu(&fold->list, &fnew->list);
fold->deleted = true;
+ fl_mask_put(head, fold->mask, true);
if (!tc_skip_hw(fold->flags))
fl_hw_destroy_filter(tp, fold, NULL);
tcf_unbind_filter(tp, &fold->res);
@@ -1525,7 +1537,7 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
if (!tc_skip_hw(fnew->flags))
fl_hw_destroy_filter(tp, fnew, NULL);
errout_mask:
- fl_mask_put(head, fnew->mask, false);
+ fl_mask_put(head, fnew->mask, true);
errout:
tcf_exts_destroy(&fnew->exts);
kfree(fnew);
--
2.13.6
^ permalink raw reply related
* Re: [PATCH 0/4] mwifiex PCI/wake-up interrupt fixes
From: Ard Biesheuvel @ 2019-02-27 10:16 UTC (permalink / raw)
To: Marc Zyngier
Cc: Brian Norris, Ganapathi Bhat, Jeffy Chen, Heiko Stuebner,
Devicetree List, Xinming Hu, <netdev@vger.kernel.org>,
linux-pm, <linux-wireless@vger.kernel.org>,
Linux Kernel Mailing List, Amitkumar Karwar, linux-rockchip,
Nishant Sarmukadam, Rob Herring, Rafael J. Wysocki,
linux-arm-kernel, Enric Balletbo i Serra, Lorenzo Pieralisi,
David S. Miller, Kalle Valo, Tony Lindgren
In-Reply-To: <d67512fe-42b4-513f-d27a-fed85c19e9c2@arm.com>
On Wed, 27 Feb 2019 at 11:02, Marc Zyngier <marc.zyngier@arm.com> wrote:
>
> + Lorenzo
>
> Hi Brian,
>
> On 26/02/2019 23:28, Brian Norris wrote:
> > + others
> >
> > Hi Marc,
> >
> > Thanks for the series. I have a few bits of history to add to this, and
> > some comments.
> >
> > On Sun, Feb 24, 2019 at 02:04:22PM +0000, Marc Zyngier wrote:
> >> For quite some time, I wondered why the PCI mwifiex device built in my
> >> Chromebook was unable to use the good old legacy interrupts. But as MSIs
> >> were working fine, I never really bothered investigating. I finally had a
> >> look, and the result isn't very pretty.
> >>
> >> On this machine (rk3399-based kevin), the wake-up interrupt is described as
> >> such:
> >>
> >> &pci_rootport {
> >> mvl_wifi: wifi@0,0 {
> >> compatible = "pci1b4b,2b42";
> >> reg = <0x83010000 0x0 0x00000000 0x0 0x00100000
> >> 0x83010000 0x0 0x00100000 0x0 0x00100000>;
> >> interrupt-parent = <&gpio0>;
> >> interrupts = <8 IRQ_TYPE_LEVEL_LOW>;
> >> pinctrl-names = "default";
> >> pinctrl-0 = <&wlan_host_wake_l>;
> >> wakeup-source;
> >> };
> >> };
> >>
> >> Note how the interrupt is part of the properties directly attached to the
> >> PCI node. And yet, this interrupt has nothing to do with a PCI legacy
> >> interrupt, as it is attached to the wake-up widget that bypasses the PCIe RC
> >> altogether (Yay for the broken design!). This is in total violation of the
> >> IEEE Std 1275-1994 spec[1], which clearly documents that such interrupt
> >> specifiers describe the PCI device interrupts, and must obey the
> >> INT-{A,B,C,D} mapping. Oops!
> >
> > You're not the first person to notice this. All the motivations are not
> > necessarily painted clearly in their cover letter, but here are some
> > previous attempts at solving this problem:
> >
> > [RFC PATCH v11 0/5] PCI: rockchip: Move PCIe WAKE# handling into pci core
> > https://lkml.kernel.org/lkml/20171225114742.18920-1-jeffy.chen@rock-chips.com/
> > http://lkml.kernel.org/lkml/20171226023646.17722-1-jeffy.chen@rock-chips.com/
> >
> > As you can see by the 12th iteration, it wasn't left unsolved for lack
> > of trying...
>
> I wasn't aware of this. That's definitely a better approach than my
> hack, and I would really like this to be revived.
>
I don't think this approach is entirely sound either.
From the side of the PCI device, WAKE# is just a GPIO line, and how it
is wired into the system is an entirely separate matter. So I don't
think it is justified to overload the notion of legacy interrupts with
some other pin that may behave in a way that is vaguely similar to how
a true wake-up capable interrupt works.
So I'd argue that we should add an optional 'wake-gpio' DT property
instead to the generic PCI device binding, and leave the interrupt
binding and discovery alone.
^ permalink raw reply
* RE: Realtek r8822be kernel module does not negotiate 802.11ac connection
From: Tony Chuang @ 2019-02-27 10:23 UTC (permalink / raw)
To: David R. Bergstein
Cc: Pkshih, linux-wireless@vger.kernel.org, netdev@vger.kernel.org,
linux-kernel@vger.kernel.org
In-Reply-To: <51d62a31-7cde-e815-bdee-4ef5433007aa@gmail.com>
> This message is in regard to a bug I have open on bugs.launchpad.net, 1813372,
> linked below. This issue, originally identified in an Ubuntu kernel, has been
> duplicated in the most current mainline kernel, 5.0-rc8, and is in regard to
> problems attaining a wireless connection at 802.11ac speeds.
>
> https://bugs.launchpad.net/bugs/1813372
>
> At your earliest convenience, please see the bug report above, and advise if a fix
> will be available for the r8822be kernel module.
>
> Sincerely,
>
> David R. Bergstein
>
Hi,
If it's possible I will suggest that you can use the new driver "rtw88" being
reviewed now. rtw88 is better supported by Realtek and has significant
improvement in contrast to r8822be. And also it is aim to better support
802.11ac series ICs, so it may help resolve your problem about 802.11ac
connection speeds.
Thanks!
Yan-Hsuan
^ permalink raw reply
* [PATCH v1] net: dev: Use unsigned integer as an argument to left-shift
From: Andy Shevchenko @ 2019-02-27 10:37 UTC (permalink / raw)
To: David S. Miller, netdev; +Cc: Andy Shevchenko
1 << 31 is Undefined Behaviour according to the C standard.
Use U type modifier to avoid theoretical overflow.
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
---
include/linux/netdevice.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 86dbb3e29139..848b54b7ec91 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -3861,7 +3861,7 @@ static inline u32 netif_msg_init(int debug_value, int default_msg_enable_bits)
if (debug_value == 0) /* no output */
return 0;
/* set low N bits */
- return (1 << debug_value) - 1;
+ return (1U << debug_value) - 1;
}
static inline void __netif_tx_lock(struct netdev_queue *txq, int cpu)
--
2.20.1
^ permalink raw reply related
* [PATCH v1] enc28j60: Correct description of debug module parameter
From: Andy Shevchenko @ 2019-02-27 10:45 UTC (permalink / raw)
To: David S. Miller, netdev; +Cc: Andy Shevchenko
The netif_msg_init() API takes on input the amount of bits to be set. The
description of debug parameter in the enc28j60 module is misleading in this
sense and passing 0xffff does not give an expected behaviour.
Fix the description of debug module parameter to show what exactly is expected.
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
---
drivers/net/ethernet/microchip/enc28j60.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/microchip/enc28j60.c b/drivers/net/ethernet/microchip/enc28j60.c
index f6ecfa778660..8f72587b5a2c 100644
--- a/drivers/net/ethernet/microchip/enc28j60.c
+++ b/drivers/net/ethernet/microchip/enc28j60.c
@@ -1681,5 +1681,5 @@ MODULE_DESCRIPTION(DRV_NAME " ethernet driver");
MODULE_AUTHOR("Claudio Lanconelli <lanconelli.claudio@eptar.com>");
MODULE_LICENSE("GPL");
module_param_named(debug, debug.msg_enable, int, 0);
-MODULE_PARM_DESC(debug, "Debug verbosity level (0=none, ..., ffff=all)");
+MODULE_PARM_DESC(debug, "Debug verbosity level in amount of bits set (0=none, ..., 31=all)");
MODULE_ALIAS("spi:" DRV_NAME);
--
2.20.1
^ permalink raw reply related
* Re: [PATCH V4] can: flexcan: implement can Runtime PM
From: Marc Kleine-Budde @ 2019-02-27 10:35 UTC (permalink / raw)
To: Joakim Zhang, linux-can@vger.kernel.org
Cc: wg@grandegger.com, netdev@vger.kernel.org,
linux-kernel@vger.kernel.org, dl-linux-imx, Aisheng DONG
In-Reply-To: <20181130085119.7948-1-qiangqing.zhang@nxp.com>
[-- Attachment #1.1: Type: text/plain, Size: 10064 bytes --]
On 11/30/18 9:53 AM, Joakim Zhang wrote:
> From: Aisheng Dong <aisheng.dong@nxp.com>
>
> Flexcan will be disabled during suspend if no wakeup function required and
> enabled after resume accordingly. During this period, we could explicitly
> disable clocks.
> Since PM is optional, the clock is enabled at probe to guarante the
> clock is running when PM is not enabled in the kernel.
>
> Implement Runtime PM which will:
> 1) Without CONFIG_PM, clock is running whether Flexcan up or down.
> 2) With CONFIG_PM, clock enabled while Flexcan up and disabled when
> Flexcan down.
> 3) Disable clock when do system suspend and enable clock while system
> resume.
> 4) Make Power Domain framework be able to shutdown the corresponding
> power domain of this device.
>
> Signed-off-by: Aisheng Dong <aisheng.dong@nxp.com>
> Signed-off-by: Joakim Zhang <qiangqing.zhang@nxp.com>
> ---
> ChangeLog:
> V1->V2:
> *rebased on patch "can: flexcan: add self wakeup support".
> V2->V3:
> *fix device fails to probe without CONFIG_PM.
> V3->V4:
> *runtime pm enable should ahead of registering device.
> *disable device even if keeping the clocks on.
> ---
> drivers/net/can/flexcan.c | 111 +++++++++++++++++++++++++-------------
> 1 file changed, 73 insertions(+), 38 deletions(-)
>
> diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c
> index 0f36eafe3ac1..cad42f20cfe5 100644
> --- a/drivers/net/can/flexcan.c
> +++ b/drivers/net/can/flexcan.c
> @@ -24,6 +24,7 @@
> #include <linux/of.h>
> #include <linux/of_device.h>
> #include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> #include <linux/regulator/consumer.h>
> #include <linux/regmap.h>
>
> @@ -277,6 +278,7 @@ struct flexcan_priv {
> u32 reg_imask1_default;
> u32 reg_imask2_default;
>
> + struct device *dev;
> struct clk *clk_ipg;
> struct clk *clk_per;
> const struct flexcan_devtype_data *devtype_data;
> @@ -444,6 +446,27 @@ static inline void flexcan_error_irq_disable(const struct flexcan_priv *priv)
> priv->write(reg_ctrl, ®s->ctrl);
> }
>
> +static int flexcan_clks_enable(const struct flexcan_priv *priv)
> +{
> + int err;
> +
> + err = clk_prepare_enable(priv->clk_ipg);
> + if (err)
> + return err;
> +
> + err = clk_prepare_enable(priv->clk_per);
> + if (err)
> + clk_disable_unprepare(priv->clk_ipg);
> +
> + return err;
> +}
> +
> +static void flexcan_clks_disable(const struct flexcan_priv *priv)
> +{
> + clk_disable_unprepare(priv->clk_ipg);
> + clk_disable_unprepare(priv->clk_per);
The original code first disabled the per then the ipg.
> +}
> +
> static inline int flexcan_transceiver_enable(const struct flexcan_priv *priv)
> {
> if (!priv->reg_xceiver)
> @@ -570,19 +593,13 @@ static int flexcan_get_berr_counter(const struct net_device *dev,
> const struct flexcan_priv *priv = netdev_priv(dev);
> int err;
>
> - err = clk_prepare_enable(priv->clk_ipg);
> - if (err)
> + err = pm_runtime_get_sync(priv->dev);
> + if (err < 0)
> return err;
>
> - err = clk_prepare_enable(priv->clk_per);
> - if (err)
> - goto out_disable_ipg;
> -
> err = __flexcan_get_berr_counter(dev, bec);
>
> - clk_disable_unprepare(priv->clk_per);
> - out_disable_ipg:
> - clk_disable_unprepare(priv->clk_ipg);
> + pm_runtime_put(priv->dev);
>
> return err;
> }
> @@ -1215,17 +1232,13 @@ static int flexcan_open(struct net_device *dev)
> struct flexcan_priv *priv = netdev_priv(dev);
> int err;
>
> - err = clk_prepare_enable(priv->clk_ipg);
> - if (err)
> + err = pm_runtime_get_sync(priv->dev);
> + if (err < 0)
> return err;
>
> - err = clk_prepare_enable(priv->clk_per);
> - if (err)
> - goto out_disable_ipg;
> -
> err = open_candev(dev);
> if (err)
> - goto out_disable_per;
> + goto out_disable_clks;
nitpick: you do a pm_runtime_put, so rename the label...
>
> err = request_irq(dev->irq, flexcan_irq, IRQF_SHARED, dev->name, dev);
> if (err)
> @@ -1288,10 +1301,8 @@ static int flexcan_open(struct net_device *dev)
> free_irq(dev->irq, dev);
> out_close:
> close_candev(dev);
> - out_disable_per:
> - clk_disable_unprepare(priv->clk_per);
> - out_disable_ipg:
> - clk_disable_unprepare(priv->clk_ipg);
> + out_disable_clks:
> + pm_runtime_put(priv->dev);
...to out_runtime_put
>
> return err;
> }
> @@ -1306,10 +1317,9 @@ static int flexcan_close(struct net_device *dev)
>
> can_rx_offload_del(&priv->offload);
> free_irq(dev->irq, dev);
> - clk_disable_unprepare(priv->clk_per);
> - clk_disable_unprepare(priv->clk_ipg);
>
> close_candev(dev);
> + pm_runtime_put(priv->dev);
>
> can_led_event(dev, CAN_LED_EVENT_STOP);
>
> @@ -1349,18 +1359,14 @@ static int register_flexcandev(struct net_device *dev)
> struct flexcan_regs __iomem *regs = priv->regs;
> u32 reg, err;
>
> - err = clk_prepare_enable(priv->clk_ipg);
> + err = flexcan_clks_enable(priv);
> if (err)
> return err;
>
> - err = clk_prepare_enable(priv->clk_per);
> - if (err)
> - goto out_disable_ipg;
> -
> /* select "bus clock", chip must be disabled */
> err = flexcan_chip_disable(priv);
> if (err)
> - goto out_disable_per;
> + goto out_disable_clks;
nitpick: The function is called lexcan_clks_disable(), so let's rename
the label accordingly.
> reg = priv->read(®s->ctrl);
> reg |= FLEXCAN_CTRL_CLK_SRC;
> priv->write(reg, ®s->ctrl);
> @@ -1389,14 +1395,13 @@ static int register_flexcandev(struct net_device *dev)
>
> err = register_candev(dev);
No error handling?
>
> - /* disable core and turn off clocks */
Better move the pm_runtime_put() here and adjust the comment.
> - out_chip_disable:
> flexcan_chip_disable(priv);
> - out_disable_per:
> - clk_disable_unprepare(priv->clk_per);
> - out_disable_ipg:
> - clk_disable_unprepare(priv->clk_ipg);
> + return 0;
>
> + out_chip_disable:
> + flexcan_chip_disable(priv);
> + out_disable_clks:
> + flexcan_clks_disable(priv);
> return err;
> }
>
> @@ -1556,6 +1561,7 @@ static int flexcan_probe(struct platform_device *pdev)
> priv->write = flexcan_write_le;
> }
>
> + priv->dev = &pdev->dev;
> priv->can.clock.freq = clock_freq;
> priv->can.bittiming_const = &flexcan_bittiming_const;
> priv->can.do_set_mode = flexcan_set_mode;
> @@ -1569,6 +1575,10 @@ static int flexcan_probe(struct platform_device *pdev)
> priv->devtype_data = devtype_data;
> priv->reg_xceiver = reg_xceiver;
>
> + pm_runtime_get_noresume(&pdev->dev);
> + pm_runtime_set_active(&pdev->dev);
> + pm_runtime_enable(&pdev->dev);
> +
> err = register_flexcandev(dev);
> if (err) {
> dev_err(&pdev->dev, "registering netdev failed\n");
> @@ -1586,6 +1596,7 @@ static int flexcan_probe(struct platform_device *pdev)
> dev_info(&pdev->dev, "device registered (reg_base=%p, irq=%d)\n",
> priv->regs, dev->irq);
>
> + pm_runtime_put(&pdev->dev);
> return 0;
>
> failed_register:
> @@ -1598,6 +1609,7 @@ static int flexcan_remove(struct platform_device *pdev)
> struct net_device *dev = platform_get_drvdata(pdev);
>
> unregister_flexcandev(dev);
> + pm_runtime_disable(&pdev->dev);
> free_candev(dev);
>
> return 0;
> @@ -1607,7 +1619,7 @@ static int __maybe_unused flexcan_suspend(struct device *device)
> {
> struct net_device *dev = dev_get_drvdata(device);
> struct flexcan_priv *priv = netdev_priv(dev);
> - int err;
> + int err = 0;
>
> if (netif_running(dev)) {
> /* if wakeup is enabled, enter stop mode
> @@ -1620,20 +1632,22 @@ static int __maybe_unused flexcan_suspend(struct device *device)
> err = flexcan_chip_disable(priv);
> if (err)
> return err;
> +
> + err = pm_runtime_force_suspend(device);
> }
> netif_stop_queue(dev);
> netif_device_detach(dev);
> }
> priv->can.state = CAN_STATE_SLEEPING;
>
> - return 0;
> + return err;
> }
>
> static int __maybe_unused flexcan_resume(struct device *device)
> {
> struct net_device *dev = dev_get_drvdata(device);
> struct flexcan_priv *priv = netdev_priv(dev);
> - int err;
> + int err = 0;
>
> priv->can.state = CAN_STATE_ERROR_ACTIVE;
> if (netif_running(dev)) {
> @@ -1642,14 +1656,34 @@ static int __maybe_unused flexcan_resume(struct device *device)
> if (device_may_wakeup(device)) {
> disable_irq_wake(dev->irq);
> } else {
> - err = flexcan_chip_enable(priv);
> + err = pm_runtime_force_resume(device);
> if (err)
> return err;
> +
> + err = flexcan_chip_enable(priv);
> }
> }
> + return err;
> +}
> +
> +static int __maybe_unused flexcan_runtime_suspend(struct device *device)
> +{
> + struct net_device *dev = dev_get_drvdata(device);
> + struct flexcan_priv *priv = netdev_priv(dev);
> +
> + flexcan_clks_disable(priv);
> +
> return 0;
> }
>
> +static int __maybe_unused flexcan_runtime_resume(struct device *device)
> +{
> + struct net_device *dev = dev_get_drvdata(device);
> + struct flexcan_priv *priv = netdev_priv(dev);
> +
> + return flexcan_clks_enable(priv);
> +}
> +
> static int __maybe_unused flexcan_noirq_suspend(struct device *device)
> {
> struct net_device *dev = dev_get_drvdata(device);
> @@ -1676,6 +1710,7 @@ static int __maybe_unused flexcan_noirq_resume(struct device *device)
>
> static const struct dev_pm_ops flexcan_pm_ops = {
> SET_SYSTEM_SLEEP_PM_OPS(flexcan_suspend, flexcan_resume)
> + SET_RUNTIME_PM_OPS(flexcan_runtime_suspend, flexcan_runtime_resume, NULL)
> SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(flexcan_noirq_suspend, flexcan_noirq_resume)
> };
>
>
Marc
--
Pengutronix e.K. | Marc Kleine-Budde |
Industrial Linux Solutions | Phone: +49-231-2826-924 |
Vertretung West/Dortmund | Fax: +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de |
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* Re: [PATCH] can: flexcan: fix timeout when set small bitrate
From: Marc Kleine-Budde @ 2019-02-27 11:06 UTC (permalink / raw)
To: Joakim Zhang, linux-can@vger.kernel.org
Cc: wg@grandegger.com, netdev@vger.kernel.org,
linux-kernel@vger.kernel.org, dl-linux-imx
In-Reply-To: <20190131093509.12613-1-qiangqing.zhang@nxp.com>
[-- Attachment #1.1: Type: text/plain, Size: 1250 bytes --]
On 1/31/19 10:37 AM, Joakim Zhang wrote:
> Current we can meet timeout issue when setting a small bitrate like 10000
> as follows on i.MX6UL EVK board (ipg clock = 66MHZ, per clock = 30MHZ):
> root@imx6ul7d:~# ip link set can0 up type can bitrate 10000
> A link change request failed with some changes committed already.
> Interface can0 may have been left with an inconsistent configuration, please check.
> RTNETLINK answers: Connection timed out
>
> It is caused by calling of flexcan_chip_unfreeze() timeout.
>
> Originally the code is using usleep_range(10, 20) for unfreeze operation,
> but the patch (8badd65 can: flexcan: avoid calling usleep_range from interrupt
> context) changed it into udelay(10) which is only a half delay of before,
> there're also some other delay changes.
>
> After double to FLEXCAN_TIMEOUT_US to 100 can fix the issue.
>
> Signed-off-by: Joakim Zhang <qiangqing.zhang@nxp.com>
Applied to linux-can
Tnx,
Marc
--
Pengutronix e.K. | Marc Kleine-Budde |
Industrial Linux Solutions | Phone: +49-231-2826-924 |
Vertretung West/Dortmund | Fax: +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de |
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* Re: stmmac / meson8b-dwmac
From: Jose Abreu @ 2019-02-27 11:09 UTC (permalink / raw)
To: Simon Huelck, Sebastian Gottschall, Jerome Brunet, Jose Abreu,
Martin Blumenstingl
Cc: linux-amlogic, Gpeppe.cavallaro, alexandre.torgue,
Emiliano Ingrassia, netdev
In-Reply-To: <3a040370-e7e5-990e-81dc-8e9bb0ab7761@gmx.de>
Hi Simon,
On 2/24/2019 8:34 PM, Simon Huelck wrote:
> the topic is about ODROID C2 / Amlogic S905X since the start. we have a
> performance regression since 4.14.
As we are not advancing in this topic I suggest you try bisecting
the offending commit.
Thanks,
Jose Miguel Abreu
^ permalink raw reply
* Re: [PATCH] net: netem: fix skb length BUG_ON in __skb_to_sgvec
From: Sheng Lan @ 2019-02-27 11:26 UTC (permalink / raw)
To: Eric Dumazet, Stephen Hemminger
Cc: davem, netdev, netem, xuhanbing, zhengshaoyu, jiqin.ji,
liuzhiqiang26, yuehaibing
In-Reply-To: <375ee6d3-ef86-c653-5d8d-df48cea8b3ba@gmail.com>
在 2019/2/26 23:52, Eric Dumazet 写道:
>
>
> On 02/26/2019 05:02 AM, Sheng Lan wrote:
>>
>>
>>
>>> On Mon, 25 Feb 2019 22:49:39 +0800
>>> Sheng Lan <lansheng@huawei.com> wrote:
>>>
>>>> From: Sheng Lan <lansheng@huawei.com>
>>>> Subject: [PATCH] net: netem: fix skb length BUG_ON in __skb_to_sgvec
>>>>
>>>> It can be reproduced by following steps:
>>>> 1. virtio_net NIC is configured with gso/tso on
>>>> 2. configure nginx as http server with an index file bigger than 1M bytes
>>>> 3. use tc netem to produce duplicate packets and delay:
>>>> tc qdisc add dev eth0 root netem delay 100ms 10ms 30% duplicate 90%
>>>> 4. continually curl the nginx http server to get index file on client
>>>> 5. BUG_ON is seen quickly
>>>>
>>>> [10258690.371129] kernel BUG at net/core/skbuff.c:4028!
>>>> [10258690.371748] invalid opcode: 0000 [#1] SMP PTI
>>>> [10258690.372094] CPU: 5 PID: 0 Comm: swapper/5 Tainted: G W 5.0.0-rc6 #2
>>>> [10258690.372094] RSP: 0018:ffffa05797b43da0 EFLAGS: 00010202
>>>> [10258690.372094] RBP: 00000000000005ea R08: 0000000000000000 R09: 00000000000005ea
>>>> [10258690.372094] R10: ffffa0579334d800 R11: 00000000000002c0 R12: 0000000000000002
>>>> [10258690.372094] R13: 0000000000000000 R14: ffffa05793122900 R15: ffffa0578f7cb028
>>>> [10258690.372094] FS: 0000000000000000(0000) GS:ffffa05797b40000(0000) knlGS:0000000000000000
>>>> [10258690.372094] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
>>>> [10258690.372094] CR2: 00007f1a6dc00868 CR3: 000000001000e000 CR4: 00000000000006e0
>>>> [10258690.372094] Call Trace:
>>>> [10258690.372094] <IRQ>
>>>> [10258690.372094] skb_to_sgvec+0x11/0x40
>>>> [10258690.372094] start_xmit+0x38c/0x520 [virtio_net]
>>>> [10258690.372094] dev_hard_start_xmit+0x9b/0x200
>>>> [10258690.372094] sch_direct_xmit+0xff/0x260
>>>> [10258690.372094] __qdisc_run+0x15e/0x4e0
>>>> [10258690.372094] net_tx_action+0x137/0x210
>>>> [10258690.372094] __do_softirq+0xd6/0x2a9
>>>> [10258690.372094] irq_exit+0xde/0xf0
>>>> [10258690.372094] smp_apic_timer_interrupt+0x74/0x140
>>>> [10258690.372094] apic_timer_interrupt+0xf/0x20
>>>> [10258690.372094] </IRQ>
>>>>
>>>> In __skb_to_sgvec, the skb->len is not equal to the sum of the skb's
>>>> linear data size and nonlinear data size, thus BUG_ON triggered. The
>>>> bad skb's nonlinear data size is less than skb->data_len, because the
>>>> skb is cloned and a part of related cloned skb's nonlinear data is
>>>> split off.
>>>>
>>>> Duplicate packet is cloned by skb_clone in netem_enqueue and may be delayed
>>>> some time in qdisc. Due to the delay time, the original skb will be pushed
>>>> again later in __tcp_push_pending_frames when tcp receives new packets.
>>>> In tcp_write_xmit, when the tcp_mss_split_point returns a smaller limit,
>>>> the original skb will be fragmented and the skb's nonlinear data will be
>>>> split off. The length of the skb cloned by netem will not be updated.
>>>> When we use virtio_net NIC, the duplicated cloned skb will be filled into
>>>> a scatter-gather list in __skb_to_sgvec and trigger the BUG_ON.
>>>>
>>>> Here I replace the skb_clone with skb_copy in netem_enqueue to ensure
>>>> the duplicated skb's nonlinear data is independent.
>>>>
>>>> Signed-off-by: Sheng Lan <lansheng@huawei.com>
>>>> Reported-by: Qin Ji <jiqin.ji@huawei.com>
>>>>
>>>> Fixes: 0afb51e7 ("netem: reinsert for duplication")
>>>
>>> This sounds like a bug in the other layers (either TCP or Virtio net)
>>> not handling a cloned skb properly.
>>>
>>
>> I have traced the route of skb by printk, let me take an example to describe the problem to make it clearly:
>> Mss value equals to 1448. Limit value is the split size when tcp do tso_fragment, is depending on the size of the sending congestion window and mss value.
>>
>> TCP layer transmit the index file to client, the original skb1 size is large:
>> ...
>> tcp_write_xmit (skb1->data_len == 62264, limit == 2*mss == 2896)
>> tso_fragment (it needs to be fragmented by limit value)
>> skb_split (after split, skb1->data_len == 2896, skb_shinfo(skb1)->frags[0] == 2896, skb_shinfo(skb1)->nr_frags == 1)
>> ...
>> netem_enqueue (netem construct a duplicate packet of skb1 by skb_clone)
>> skb2 = skb_clone(skb1) (skb1->data_len == skb2->data_len == 2896, skb1 and skb2 share the nonlinear data frags[0] == 2896)
>> waiting 30ms (skb1 and skb2 will be delayed in qdisc queue due to the netem delay configuration)
>>
>>
>> TCP layer receives new packets and trys to retransmit the skb1:
>> tcp_rcv_established
>> __tcp_push_pending_frames
>> tcp_write_xmit (skb1->data_len == 2896, cwnd size decreased or packets in flight increased, cause the limit decreased to 1*mss == 1448)
>
> tcp_write_xmit() only deals with packet in the write queue,
> they never were sent. They can not be any clone of them by definition, since
> skbs in the TCP write queue are private to TCP stack,
>
> Once a packet is sent, the master skb is moved to the rtx rb-tree,
> while the clone is going through lower stacks.
>
> When/if a retransmit is due, we always make sure there is no clone on it,
> look at the various calls to skb_unclone()
I traced again and found that the skb was not sent, master skb was still in write queue,
because the function tcp_transmit_skb() returns 1 (NET_XMIT_DROP), thus it can be retransmit.
I found the error value NET_XMIT_DROP returns from netem_enqueue(), when the length of qdisc queue
is greater than queue limit value.
In netem_enqueue() the skb is cloned before returning the NET_XMIT_DROP error value,
thus the master skb is still in write queue and be cloned in netem_enqueue(). This may cause the master
skb be retransmit and fragmented again while it is cloned.
I think there are potential risks that tso_fragment() will get a cloned skb if skb is cloned by lower layer.
I try to fix it by moving returning error value statment to the front of the skb_clone() in netem_enqueue(), and it works.
And netem_enqueue() constructs corrupt packets statment returns NET_XMIT_DROP too. To fix this completely should I move the
constructing corrupt statment to the front of the skb_clone() ?
Please correct me if I am wrong, and I need your advice.
Thanks
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index 75046ec..615a341 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -474,6 +474,9 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch,
if (q->latency || q->jitter || q->rate)
skb_orphan_partial(skb);
+ if (unlikely(sch->q.qlen >= sch->limit))
+ return qdisc_drop_all(skb, sch, to_free);
+
/*
* If we need to duplicate packet, then re-insert at top of the
* qdisc tree, since parent queuer expects that only one
@@ -521,9 +524,6 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch,
1<<(prandom_u32() % 8);
}
- if (unlikely(sch->q.qlen >= sch->limit))
- return qdisc_drop_all(skb, sch, to_free);
-
qdisc_qstats_backlog_inc(sch, skb);
cb = netem_skb_cb(skb);
--
^ permalink raw reply related
* Re: [PATCH iproute2-next] rdma: Add the prefix for driver attributes
From: Leon Romanovsky @ 2019-02-27 11:38 UTC (permalink / raw)
To: oulijun
Cc: David Ahern, Steve Wise, netdev, RDMA mailing list,
Stephen Hemminger
In-Reply-To: <d6d1bd56-5def-aa25-4845-e41d2ea5d667@huawei.com>
[-- Attachment #1: Type: text/plain, Size: 2899 bytes --]
On Wed, Feb 27, 2019 at 07:30:34PM +0800, oulijun wrote:
> 在 2019/2/27 14:41, Leon Romanovsky 写道:
> > From: Leon Romanovsky <leonro@mellanox.com>
> >
> > There is a need to distinguish between driver vs. general exposed
> > attributes. The most common use case is to expose some internal
> > garbage under extremely common and sexy name, e.g. pi, ci e.t.c
> >
> > In order to achieve that, we will add "drv_" prefix to all strings
> > which were received through RDMA_NLDEV_ATTR_DRIVER_* attributes.
> >
> > Signed-off-by: Leon Romanovsky <leonro@mellanox.com>a
> > ---
> > rdma/utils.c | 34 ++++++++++++++++++++++------------
> > 1 file changed, 22 insertions(+), 12 deletions(-)
> >
> > diff --git a/rdma/utils.c b/rdma/utils.c
> > index 6bc14cd5..1f6bf330 100644
> > --- a/rdma/utils.c
> > +++ b/rdma/utils.c
> > @@ -829,27 +829,37 @@ static int print_driver_entry(struct rd *rd, struct nlattr *key_attr,
> > struct nlattr *val_attr,
> > enum rdma_nldev_print_type print_type)
> > {
> > - const char *key_str = mnl_attr_get_str(key_attr);
> > int attr_type = nla_type(val_attr);
> > + int ret = -EINVAL;
> > + char *key_str;
> > +
> > + if (asprintf(&key_str, "drv_%s", mnl_attr_get_str(key_attr)) == -1)
> > + return -ENOMEM;
> >
> > switch (attr_type) {
> > case RDMA_NLDEV_ATTR_DRIVER_STRING:
> > - return print_driver_string(rd, key_str,
> > - mnl_attr_get_str(val_attr));
> > + ret = print_driver_string(rd, key_str,
> > + mnl_attr_get_str(val_attr));
> > + break;
> > case RDMA_NLDEV_ATTR_DRIVER_S32:
> > - return print_driver_s32(rd, key_str,
> > - mnl_attr_get_u32(val_attr), print_type);
> > + ret = print_driver_s32(rd, key_str, mnl_attr_get_u32(val_attr),
> > + print_type);
> > + break;
> > case RDMA_NLDEV_ATTR_DRIVER_U32:
> > - return print_driver_u32(rd, key_str,
> > - mnl_attr_get_u32(val_attr), print_type);
> > + ret = print_driver_u32(rd, key_str, mnl_attr_get_u32(val_attr),
> > + print_type);
> > + break;
> > case RDMA_NLDEV_ATTR_DRIVER_S64:
> > - return print_driver_s64(rd, key_str,
> > - mnl_attr_get_u64(val_attr), print_type);
> > + ret = print_driver_s64(rd, key_str, mnl_attr_get_u64(val_attr),
> > + print_type);
> > + break;
> > case RDMA_NLDEV_ATTR_DRIVER_U64:
> > - return print_driver_u64(rd, key_str,
> > - mnl_attr_get_u64(val_attr), print_type);
> > + ret = print_driver_u64(rd, key_str, mnl_attr_get_u64(val_attr),
> > + print_type);
> > + break;
> > }
> > - return -EINVAL;
> > + free(key_str);
> > + return ret;
> > }
> >
> > void print_driver_table(struct rd *rd, struct nlattr *tb)
> > --
> > 2.19.1
> >
> >
> > .
> >
> This is fine to me and the implementation is more simple.
>
> Tested-by: Lijun Ou <oulijun@huawei.com>
Thanks a lot.
>
> thanks.
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 801 bytes --]
^ permalink raw reply
* Re: [PATCH net-next v2 1/5] net/mlx5e: Return -EOPNOTSUPP when modify header action zero
From: Tonghao Zhang @ 2019-02-27 11:46 UTC (permalink / raw)
To: Roi Dayan; +Cc: Saeed Mahameed, gerlitz.or@gmail.com, netdev@vger.kernel.org
In-Reply-To: <bbaaf93a-b9a3-289a-afc3-fcaa5068f322@mellanox.com>
On Tue, Feb 26, 2019 at 9:54 PM Roi Dayan <roid@mellanox.com> wrote:
>
>
>
> On 25/02/2019 12:40, xiangxia.m.yue@gmail.com wrote:
> > From: Tonghao Zhang <xiangxia.m.yue@gmail.com>
> >
> > When max modify header action is zero, we return -EOPNOTSUPP
> > directly. In this way, we can ignore wrong message info (e.g.
> > "mlx5: parsed 0 pedit actions, can't do more").
> >
> > This happens when offloading pedit actions on mlx VFs.
> >
> > For example:
> > $ tc filter add dev mlx5_vf parent ffff: protocol ip prio 1 \
> > flower skip_sw dst_mac 00:10:56:fb:64:e8 \
> > dst_ip 1.1.1.100 src_ip 1.1.1.200 \
> > action pedit ex munge eth src set 00:10:56:b4:5d:20
> >
> > Signed-off-by: Tonghao Zhang <xiangxia.m.yue@gmail.com>
> > ---
> > drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 12 ++++++++++--
> > 1 file changed, 10 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
> > index b38986e..708f819 100644
> > --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
> > +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
> > @@ -2002,7 +2002,8 @@ static int offload_pedit_fields(struct pedit_headers_action *hdrs,
> > static int alloc_mod_hdr_actions(struct mlx5e_priv *priv,
> > struct pedit_headers_action *hdrs,
> > int namespace,
> > - struct mlx5e_tc_flow_parse_attr *parse_attr)
> > + struct mlx5e_tc_flow_parse_attr *parse_attr,
> > + struct netlink_ext_ack *extack)
> > {
> > int nkeys, action_size, max_actions;
> >
> > @@ -2015,6 +2016,12 @@ static int alloc_mod_hdr_actions(struct mlx5e_priv *priv,
> > else /* namespace is MLX5_FLOW_NAMESPACE_KERNEL - NIC offloading */
> > max_actions = MLX5_CAP_FLOWTABLE_NIC_RX(priv->mdev, max_modify_header_actions);
> >
> > + if (!max_actions) {
> > + NL_SET_ERR_MSG_MOD(extack,
> > + "don't support pedit actions, can't offload");
>
> can we rephrase that to match the msg style you did in patch 5 ?
Yes, good idea
> i.e. The pedit offload action is not supported
>
>
> > + return -EOPNOTSUPP;
> > + }
> > +
> > /* can get up to crazingly 16 HW actions in 32 bits pedit SW key */
> > max_actions = min(max_actions, nkeys * 16);
> >
> > @@ -2072,7 +2079,8 @@ static int alloc_tc_pedit_action(struct mlx5e_priv *priv, int namespace,
> > u8 cmd;
> >
> > if (!parse_attr->mod_hdr_actions) {
> > - err = alloc_mod_hdr_actions(priv, hdrs, namespace, parse_attr);
> > + err = alloc_mod_hdr_actions(priv, hdrs,
> > + namespace, parse_attr, extack);
> > if (err)
> > goto out_err;
> > }
> >
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox