* Re: [PATCH v2 net-next] net: filter: export pkt_type_offset() helper
From: Alexei Starovoitov @ 2014-09-03 23:14 UTC (permalink / raw)
To: Daniel Borkmann, Eric Dumazet
Cc: Denis Kirjanov, netdev@vger.kernel.org, Markos Chandras,
Martin Schwidefsky
In-Reply-To: <54078FBC.5050402@redhat.com>
On Wed, Sep 3, 2014 at 3:01 PM, Daniel Borkmann <dborkman@redhat.com> wrote:
> On 09/03/2014 11:08 PM, Denis Kirjanov wrote:
>>
>
>> +static int __init init_pkt_type_offset(void)
>> {
>> struct sk_buff skb_probe = { .pkt_type = ~0, };
>> u8 *ct = (u8 *) &skb_probe;
>> unsigned int off;
>>
>> + pkt_type_offset = -1;
>> for (off = 0; off < sizeof(struct sk_buff); off++) {
>> - if (ct[off] == PKT_TYPE_MAX)
>> + if (ct[off] == PKT_TYPE_MAX) {
>> + pkt_type_offset = off;
>> return off;
>> + }
>> }
>
>
> Why not BUG_ON() when pkt_type_offset could not be found?
>
> That way we would know that something is broken and you can
> spare the checks for negative offsets everywhere ...
also such BUG_ON will be right in the face, since the kernel
won't even boot :)
I guess we can only hit it if compiler changes and starts
doing crazing things with bitfields.
Eric, you're the original author of this function, thoughts?
>> case SKF_AD_OFF + SKF_AD_PKTTYPE:
>> + if (pkt_type_offset < 0)
>> + return false;
with BUG_ON we wouldn't need this check.
>> *insn = BPF_LDX_MEM(BPF_B, BPF_REG_A, BPF_REG_CTX,
>> - pkt_type_offset());
>> + pkt_type_offset);
>> if (insn->off < 0)
>> return false;
and this one too.
and other checks in arch/*/jit
^ permalink raw reply
* Re: [PATCH 2/2] net-timestamp: Make the clone operation stand-alone from phy timestamping
From: Alexander Duyck @ 2014-09-03 23:02 UTC (permalink / raw)
To: Eric Dumazet; +Cc: netdev, richardcochran, davem, willemb
In-Reply-To: <1409781908.26422.55.camel@edumazet-glaptop2.roam.corp.google.com>
On 09/03/2014 03:05 PM, Eric Dumazet wrote:
> On Wed, 2014-09-03 at 14:07 -0700, Eric Dumazet wrote:
>
>>
>> Normally, if one skb holds a reference to a socket, it should have
>> skb->destructor set to a cleanup function.
>>
>> Otherwise, we rely on callers following a convention, to release sk
>> reference.
>>
>> If you believe its needed, it should be dully documented.
>
>
> BTW, skb_orphan() would BUG() on this case (skb->destrutor == NULL and
> skb->sk != NULL)
That was why we were setting skb->sk to null before passing it to
sock_queue_err_skb. I think I found the reason why things were done the
way they were. It looks like skb_orphan is called in
sock_queue_err_skb. So any destructor added would be fired there before
before being replaced.
The only part I am not sure about is if that would actually be any sort
of issue. Do we really need to hold the extra reference to the socket
all the way through the sock_queue_err_skb call or can we just let it
get dropped with the call to skb_orphan and let the fact that the
message is being queued be good enough?
I'm not sure due to the comment about "before exiting rcu section, make
sure dst is refcounted". It kind of implies I should be able to hand
off the reference counts without worrying about the socket being freed
out from under me.
Thanks,
Alex
^ permalink raw reply
* Re: [PATCH net-next 2/2] bonding: Simplify the xmit function for modes that use xmit_hash
From: Jay Vosburgh @ 2014-09-03 22:51 UTC (permalink / raw)
To: Mahesh Bandewar
Cc: Veaceslav Falico, Andy Gospodarek, David Miller, netdev,
Eric Dumazet, Maciej Zenczykowski
In-Reply-To: <1409780851-32081-1-git-send-email-maheshb@google.com>
Mahesh Bandewar <maheshb@google.com> wrote:
>Earlier change to use usable slave array for TLB mode had an additional
>performance advantage. So extending the same logic to all other modes
>that use xmit-hash for slave selection (viz 802.3AD, and XOR modes).
>Also consolidating this with the earlier TLB change.
>
>The main idea is to build the usable slaves array in the control path
>and use that array for slave selection during xmit operation.
>
>Measured performance in a setup with a bond of 4x1G NICs with 200
>instances of netperf for the modes involved (3ad, xor, tlb)
>cmd: netperf -t TCP_RR -H <TargetHost> -l 60 -s 5
>
>Mode TPS-Before TPS-After
>
>802.3ad : 468,694 493,101
>TLB (lb=0): 392,583 392,965
>XOR : 475,696 484,517
In general I think this is an improvement over traversing linked
lists; however, I have a couple of comments.
First, for the 802.3ad mode, is the case within
bond_3ad_state_machine_handler that runs the ad_agg_selection_logic (if
the agg_select_timer runs out) handled? This may change the active
aggregator, and thus the list of slaves that ought to be in the array,
and looking at the patch I don't see where this would be taken care of.
As a practical matter, the usage of the agg_select_timer is such
that it's unlikely to cause a change of active aggregator (it will
usually set an active aggregator when there is none, e.g., during open
processing), but this case should probably either be handled or
commented to explain why it is safe.
It may be that the bond_3ad_initialize call to set the
agg_select_timer to 8 seconds is no longer needed, but for now it's
there and may change the active aggregator.
One more comment below.
>Signed-off-by: Mahesh Bandewar <maheshb@google.com>
>---
> drivers/net/bonding/bond_3ad.c | 77 ++++---------------------------
> drivers/net/bonding/bond_alb.c | 43 ++---------------
> drivers/net/bonding/bond_alb.h | 8 ----
> drivers/net/bonding/bond_main.c | 100 ++++++++++++++++++++++++++++++++++++----
> drivers/net/bonding/bonding.h | 8 ++++
> 5 files changed, 113 insertions(+), 123 deletions(-)
>
>diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c
>index ee2c73a9de39..d42fd65fdfa9 100644
>--- a/drivers/net/bonding/bond_3ad.c
>+++ b/drivers/net/bonding/bond_3ad.c
>@@ -1717,6 +1717,8 @@ static void ad_enable_collecting_distributing(struct port *port)
> port->actor_port_number,
> port->aggregator->aggregator_identifier);
> __enable_port(port);
>+ if (bond_update_slave_arr(port->slave->bond, NULL))
>+ pr_debug("Failed to build slave-array for 3ad mode.\n");
> }
> }
>
>@@ -1733,6 +1735,8 @@ static void ad_disable_collecting_distributing(struct port *port)
> port->actor_port_number,
> port->aggregator->aggregator_identifier);
> __disable_port(port);
>+ if (bond_update_slave_arr(port->slave->bond, NULL))
>+ pr_debug("Failed to build slave-array for 3ad mode.\n");
> }
> }
>
>@@ -1917,6 +1921,9 @@ void bond_3ad_unbind_slave(struct slave *slave)
> __update_lacpdu_from_port(port);
> ad_lacpdu_send(port);
>
>+ if (bond_update_slave_arr(bond, slave))
>+ pr_debug("Failed to build slave-array for 3AD mode.\n");
>+
> /* check if this aggregator is occupied */
> if (aggregator->lag_ports) {
> /* check if there are other ports related to this aggregator
>@@ -2311,6 +2318,9 @@ void bond_3ad_handle_link_change(struct slave *slave, char link)
> */
> port->sm_vars |= AD_PORT_BEGIN;
>
>+ if (bond_update_slave_arr(slave->bond, NULL))
>+ pr_debug("Failed to build slave-array for 3ad mode.\n");
>+
> __release_state_machine_lock(port);
> }
>
>@@ -2407,73 +2417,6 @@ int bond_3ad_get_active_agg_info(struct bonding *bond, struct ad_info *ad_info)
> return ret;
> }
>
>-int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev)
>-{
>- struct bonding *bond = netdev_priv(dev);
>- struct slave *slave, *first_ok_slave;
>- struct aggregator *agg;
>- struct ad_info ad_info;
>- struct list_head *iter;
>- int slaves_in_agg;
>- int slave_agg_no;
>- int agg_id;
>-
>- if (__bond_3ad_get_active_agg_info(bond, &ad_info)) {
>- netdev_dbg(dev, "__bond_3ad_get_active_agg_info failed\n");
>- goto err_free;
>- }
>-
>- slaves_in_agg = ad_info.ports;
>- agg_id = ad_info.aggregator_id;
>-
>- if (slaves_in_agg == 0) {
>- netdev_dbg(dev, "active aggregator is empty\n");
>- goto err_free;
>- }
>-
>- slave_agg_no = bond_xmit_hash(bond, skb) % slaves_in_agg;
>- first_ok_slave = NULL;
>-
>- bond_for_each_slave_rcu(bond, slave, iter) {
>- agg = SLAVE_AD_INFO(slave)->port.aggregator;
>- if (!agg || agg->aggregator_identifier != agg_id)
>- continue;
>-
>- if (slave_agg_no >= 0) {
>- if (!first_ok_slave && bond_slave_can_tx(slave))
>- first_ok_slave = slave;
>- slave_agg_no--;
>- continue;
>- }
>-
>- if (bond_slave_can_tx(slave)) {
>- bond_dev_queue_xmit(bond, skb, slave->dev);
>- goto out;
>- }
>- }
>-
>- if (slave_agg_no >= 0) {
>- netdev_err(dev, "Couldn't find a slave to tx on for aggregator ID %d\n",
>- agg_id);
>- goto err_free;
>- }
>-
>- /* we couldn't find any suitable slave after the agg_no, so use the
>- * first suitable found, if found.
>- */
>- if (first_ok_slave)
>- bond_dev_queue_xmit(bond, skb, first_ok_slave->dev);
>- else
>- goto err_free;
>-
>-out:
>- return NETDEV_TX_OK;
>-err_free:
>- /* no suitable interface, frame not sent */
>- dev_kfree_skb_any(skb);
>- goto out;
>-}
>-
> int bond_3ad_lacpdu_recv(const struct sk_buff *skb, struct bonding *bond,
> struct slave *slave)
> {
>diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c
>index 73c21e233131..b2be7b3216d6 100644
>--- a/drivers/net/bonding/bond_alb.c
>+++ b/drivers/net/bonding/bond_alb.c
>@@ -200,7 +200,6 @@ static int tlb_initialize(struct bonding *bond)
> static void tlb_deinitialize(struct bonding *bond)
> {
> struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
>- struct tlb_up_slave *arr;
>
> _lock_tx_hashtbl_bh(bond);
>
>@@ -208,10 +207,6 @@ static void tlb_deinitialize(struct bonding *bond)
> bond_info->tx_hashtbl = NULL;
>
> _unlock_tx_hashtbl_bh(bond);
>-
>- arr = rtnl_dereference(bond_info->slave_arr);
>- if (arr)
>- kfree_rcu(arr, rcu);
> }
>
> static long long compute_gap(struct slave *slave)
>@@ -1409,39 +1404,9 @@ out:
> return NETDEV_TX_OK;
> }
>
>-static int bond_tlb_update_slave_arr(struct bonding *bond,
>- struct slave *skipslave)
>-{
>- struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
>- struct slave *tx_slave;
>- struct list_head *iter;
>- struct tlb_up_slave *new_arr, *old_arr;
>-
>- new_arr = kzalloc(offsetof(struct tlb_up_slave, arr[bond->slave_cnt]),
>- GFP_ATOMIC);
>- if (!new_arr)
>- return -ENOMEM;
>-
>- bond_for_each_slave(bond, tx_slave, iter) {
>- if (!bond_slave_can_tx(tx_slave))
>- continue;
>- if (skipslave == tx_slave)
>- continue;
>- new_arr->arr[new_arr->count++] = tx_slave;
>- }
>-
>- old_arr = rtnl_dereference(bond_info->slave_arr);
>- rcu_assign_pointer(bond_info->slave_arr, new_arr);
>- if (old_arr)
>- kfree_rcu(old_arr, rcu);
>-
>- return 0;
>-}
>-
> int bond_tlb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
> {
> struct bonding *bond = netdev_priv(bond_dev);
>- struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
> struct ethhdr *eth_data;
> struct slave *tx_slave = NULL;
> u32 hash_index;
>@@ -1462,9 +1427,9 @@ int bond_tlb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
> hash_index & 0xFF,
> skb->len);
> } else {
>- struct tlb_up_slave *slaves;
>+ struct bond_up_slave *slaves;
>
>- slaves = rcu_dereference(bond_info->slave_arr);
>+ slaves = rcu_dereference(bond->slave_arr);
> if (slaves && slaves->count)
> tx_slave = slaves->arr[hash_index %
> slaves->count];
>@@ -1734,7 +1699,7 @@ void bond_alb_deinit_slave(struct bonding *bond, struct slave *slave)
> }
>
> if (bond_is_nondyn_tlb(bond))
>- if (bond_tlb_update_slave_arr(bond, slave))
>+ if (bond_update_slave_arr(bond, slave))
> pr_err("Failed to build slave-array for TLB mode.\n");
>
> }
>@@ -1762,7 +1727,7 @@ void bond_alb_handle_link_change(struct bonding *bond, struct slave *slave, char
> }
>
> if (bond_is_nondyn_tlb(bond)) {
>- if (bond_tlb_update_slave_arr(bond, NULL))
>+ if (bond_update_slave_arr(bond, NULL))
> pr_err("Failed to build slave-array for TLB mode.\n");
> }
> }
>diff --git a/drivers/net/bonding/bond_alb.h b/drivers/net/bonding/bond_alb.h
>index aaeac61d03cf..5fc76c01636c 100644
>--- a/drivers/net/bonding/bond_alb.h
>+++ b/drivers/net/bonding/bond_alb.h
>@@ -139,20 +139,12 @@ struct tlb_slave_info {
> */
> };
>
>-struct tlb_up_slave {
>- unsigned int count;
>- struct rcu_head rcu;
>- struct slave *arr[0];
>-};
>-
> struct alb_bond_info {
> struct tlb_client_info *tx_hashtbl; /* Dynamically allocated */
> spinlock_t tx_hashtbl_lock;
> u32 unbalanced_load;
> int tx_rebalance_counter;
> int lp_counter;
>- /* -------- non-dynamic tlb mode only ---------*/
>- struct tlb_up_slave __rcu *slave_arr; /* Up slaves */
> /* -------- rlb parameters -------- */
> int rlb_enabled;
> struct rlb_client_info *rx_hashtbl; /* Receive hash table */
>diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
>index f0f5eab0fab1..b0e8e7cfa10f 100644
>--- a/drivers/net/bonding/bond_main.c
>+++ b/drivers/net/bonding/bond_main.c
>@@ -1693,6 +1693,9 @@ static int __bond_release_one(struct net_device *bond_dev,
> if (BOND_MODE(bond) == BOND_MODE_8023AD)
> bond_3ad_unbind_slave(slave);
>
>+ else if (BOND_MODE(bond) == BOND_MODE_XOR)
>+ bond_update_slave_arr(bond, slave);
>+
> write_unlock_bh(&bond->lock);
>
> netdev_info(bond_dev, "Releasing %s interface %s\n",
>@@ -2009,6 +2012,9 @@ static void bond_miimon_commit(struct bonding *bond)
> bond_alb_handle_link_change(bond, slave,
> BOND_LINK_UP);
>
>+ if (BOND_MODE(bond) == BOND_MODE_XOR)
>+ bond_update_slave_arr(bond, NULL);
>+
> if (!bond->curr_active_slave ||
> (slave == bond->primary_slave))
> goto do_failover;
>@@ -2037,6 +2043,9 @@ static void bond_miimon_commit(struct bonding *bond)
> bond_alb_handle_link_change(bond, slave,
> BOND_LINK_DOWN);
>
>+ if (BOND_MODE(bond) == BOND_MODE_XOR)
>+ bond_update_slave_arr(bond, slave);
>+
> if (slave == rcu_access_pointer(bond->curr_active_slave))
> goto do_failover;
>
>@@ -3149,6 +3158,7 @@ static int bond_open(struct net_device *bond_dev)
> static int bond_close(struct net_device *bond_dev)
> {
> struct bonding *bond = netdev_priv(bond_dev);
>+ struct bond_up_slave *arr;
>
> bond_work_cancel_all(bond);
> bond->send_peer_notif = 0;
>@@ -3156,6 +3166,10 @@ static int bond_close(struct net_device *bond_dev)
> bond_alb_deinitialize(bond);
> bond->recv_probe = NULL;
>
>+ arr = rtnl_dereference(bond->slave_arr);
>+ if (arr)
>+ kfree_rcu(arr, rcu);
>+
> return 0;
> }
>
>@@ -3684,15 +3698,84 @@ static int bond_xmit_activebackup(struct sk_buff *skb, struct net_device *bond_d
> return NETDEV_TX_OK;
> }
>
>-/* In bond_xmit_xor() , we determine the output device by using a pre-
>- * determined xmit_hash_policy(), If the selected device is not enabled,
>- * find the next active slave.
>+/* Build the usable slaves array in control path for modes that use xmit-hash
>+ * to determine the slave interface -
>+ * (a) BOND_MODE_8023AD
>+ * (b) BOND_MODE_XOR
>+ * (c) BOND_MODE_TLB && tlb_dynamic_lb == 0
> */
>-static int bond_xmit_xor(struct sk_buff *skb, struct net_device *bond_dev)
>+int bond_update_slave_arr(struct bonding *bond, struct slave *skipslave)
> {
>- struct bonding *bond = netdev_priv(bond_dev);
>+ struct slave *slave;
>+ struct list_head *iter;
>+ struct bond_up_slave *new_arr, *old_arr;
>+ int slaves_in_agg;
>+ int agg_id = 0;
>+ int ret = 0;
>+
>+ new_arr = kzalloc(offsetof(struct bond_up_slave, arr[bond->slave_cnt]),
>+ GFP_ATOMIC);
>+ if (!new_arr) {
>+ ret = -ENOMEM;
>+ goto out;
>+ }
If this allocation fails, won't the in-place array be left with
a reference to a slave that has potentially been freed, if, e.g., the
call came in via the __bond_release_one path?
I haven't tested it, but it seems plausible that we could
resolve this by writing over the "disappearing" slave's entry in the
array, e.g., move the last element of the array to the "disappearing"
element.
If this is a problem, the same logic exists in the current
(tlb-only) code, so a fix for stable may be needed as well.
-J
>+ if (BOND_MODE(bond) == BOND_MODE_8023AD) {
>+ struct ad_info ad_info;
>+
>+ if (__bond_3ad_get_active_agg_info(bond, &ad_info)) {
>+ pr_debug("__bond_3ad_get_active_agg_info failed\n");
>+ kfree_rcu(new_arr, rcu);
>+ ret = -EINVAL;
>+ goto out;
>+ }
>+ slaves_in_agg = ad_info.ports;
>+ agg_id = ad_info.aggregator_id;
>+ }
>+ bond_for_each_slave(bond, slave, iter) {
>+ if (BOND_MODE(bond) == BOND_MODE_8023AD) {
>+ struct aggregator *agg;
>+
>+ agg = SLAVE_AD_INFO(slave)->port.aggregator;
>+ if (!agg || agg->aggregator_identifier != agg_id)
>+ continue;
>+ }
>+ if (!bond_slave_can_tx(slave))
>+ continue;
>+ if (skipslave == slave)
>+ continue;
>+ new_arr->arr[new_arr->count++] = slave;
>+ }
>
>- bond_xmit_slave_id(bond, skb, bond_xmit_hash(bond, skb) % bond->slave_cnt);
>+ old_arr = rcu_dereference_protected(bond->slave_arr,
>+ lockdep_rtnl_is_held() ||
>+ lockdep_is_held(&bond->lock) ||
>+ lockdep_is_held(&bond->curr_slave_lock));
>+ rcu_assign_pointer(bond->slave_arr, new_arr);
>+ if (old_arr)
>+ kfree_rcu(old_arr, rcu);
>+
>+out:
>+ return ret;
>+}
>+
>+/* Use this Xmit function for 3AD as well as XOR modes. The current
>+ * usable slave array is formed in the control path. The xmit function
>+ * just calculates hash and sends the packet out.
>+ */
>+int bond_3ad_xor_xmit(struct sk_buff *skb, struct net_device *dev)
>+{
>+ struct bonding *bond = netdev_priv(dev);
>+ struct slave *slave;
>+ struct bond_up_slave *slaves;
>+
>+ slaves = rcu_dereference(bond->slave_arr);
>+ if (slaves && slaves->count) {
>+ slave = slaves->arr[bond_xmit_hash(bond, skb) % slaves->count];
>+ bond_dev_queue_xmit(bond, skb, slave->dev);
>+ } else {
>+ dev_kfree_skb_any(skb);
>+ atomic_long_inc(&dev->tx_dropped);
>+ }
>
> return NETDEV_TX_OK;
> }
>@@ -3794,12 +3877,11 @@ static netdev_tx_t __bond_start_xmit(struct sk_buff *skb, struct net_device *dev
> return bond_xmit_roundrobin(skb, dev);
> case BOND_MODE_ACTIVEBACKUP:
> return bond_xmit_activebackup(skb, dev);
>+ case BOND_MODE_8023AD:
> case BOND_MODE_XOR:
>- return bond_xmit_xor(skb, dev);
>+ return bond_3ad_xor_xmit(skb, dev);
> case BOND_MODE_BROADCAST:
> return bond_xmit_broadcast(skb, dev);
>- case BOND_MODE_8023AD:
>- return bond_3ad_xmit_xor(skb, dev);
> case BOND_MODE_ALB:
> return bond_alb_xmit(skb, dev);
> case BOND_MODE_TLB:
>diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h
>index aace510d08d1..4a6195c0de60 100644
>--- a/drivers/net/bonding/bonding.h
>+++ b/drivers/net/bonding/bonding.h
>@@ -177,6 +177,12 @@ struct slave {
> struct kobject kobj;
> };
>
>+struct bond_up_slave {
>+ unsigned int count;
>+ struct rcu_head rcu;
>+ struct slave *arr[0];
>+};
>+
> /*
> * Link pseudo-state only used internally by monitors
> */
>@@ -196,6 +202,7 @@ struct bonding {
> struct slave __rcu *curr_active_slave;
> struct slave __rcu *current_arp_slave;
> struct slave *primary_slave;
>+ struct bond_up_slave __rcu *slave_arr; /* Array of usable slaves */
> bool force_primary;
> s32 slave_cnt; /* never change this value outside the attach/detach wrappers */
> int (*recv_probe)(const struct sk_buff *, struct bonding *,
>@@ -527,6 +534,7 @@ const char *bond_slave_link_status(s8 link);
> struct bond_vlan_tag *bond_verify_device_path(struct net_device *start_dev,
> struct net_device *end_dev,
> int level);
>+int bond_update_slave_arr(struct bonding *bond, struct slave *skipslave);
>
> #ifdef CONFIG_PROC_FS
> void bond_create_proc_entry(struct bonding *bond);
>--
>2.1.0.rc2.206.gedb03e5
>
---
-Jay Vosburgh, jay.vosburgh@canonical.com
^ permalink raw reply
* Re: [PATCH 2/2] net-timestamp: Make the clone operation stand-alone from phy timestamping
From: Eric Dumazet @ 2014-09-03 22:05 UTC (permalink / raw)
To: Alexander Duyck; +Cc: netdev, richardcochran, davem, willemb
In-Reply-To: <1409778438.26422.54.camel@edumazet-glaptop2.roam.corp.google.com>
On Wed, 2014-09-03 at 14:07 -0700, Eric Dumazet wrote:
>
> Normally, if one skb holds a reference to a socket, it should have
> skb->destructor set to a cleanup function.
>
> Otherwise, we rely on callers following a convention, to release sk
> reference.
>
> If you believe its needed, it should be dully documented.
BTW, skb_orphan() would BUG() on this case (skb->destrutor == NULL and
skb->sk != NULL)
^ permalink raw reply
* Re: [PATCH v2 net-next] net: filter: export pkt_type_offset() helper
From: Daniel Borkmann @ 2014-09-03 22:01 UTC (permalink / raw)
To: Denis Kirjanov
Cc: netdev, Markos Chandras, Martin Schwidefsky, Alexei Starovoitov
In-Reply-To: <1409778511-21273-1-git-send-email-kda@linux-powerpc.org>
On 09/03/2014 11:08 PM, Denis Kirjanov wrote:
> Currently we have 2 pkt_type_offset functions doing
> the same thing and spread across the architecture files.
> Let's use the generic helper routine.
>
> Signed-off-by: Denis Kirjanov <kda@linux-powerpc.org>
> Cc: Markos Chandras <markos.chandras@imgtec.com>
> Cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
> Cc: Daniel Borkmann <dborkman@redhat.com>
> Cc: Alexei Starovoitov <alexei.starovoitov@gmail.com>
>
> V2: Initialize the pkt_type_offset through the pure_initcall()
> ---
> arch/mips/net/bpf_jit.c | 27 ++-------------------------
> arch/s390/net/bpf_jit_comp.c | 31 -------------------------------
> include/linux/filter.h | 2 ++
> net/core/filter.c | 16 +++++++++++++---
> 4 files changed, 17 insertions(+), 59 deletions(-)
>
> diff --git a/arch/mips/net/bpf_jit.c b/arch/mips/net/bpf_jit.c
> index 05a5661..bb9b53f 100644
> --- a/arch/mips/net/bpf_jit.c
> +++ b/arch/mips/net/bpf_jit.c
> @@ -765,27 +765,6 @@ static u64 jit_get_skb_w(struct sk_buff *skb, unsigned offset)
> return (u64)err << 32 | ntohl(ret);
> }
>
> -#ifdef __BIG_ENDIAN_BITFIELD
> -#define PKT_TYPE_MAX (7 << 5)
> -#else
> -#define PKT_TYPE_MAX 7
> -#endif
> -static int pkt_type_offset(void)
> -{
> - struct sk_buff skb_probe = {
> - .pkt_type = ~0,
> - };
> - u8 *ct = (u8 *)&skb_probe;
> - unsigned int off;
> -
> - for (off = 0; off < sizeof(struct sk_buff); off++) {
> - if (ct[off] == PKT_TYPE_MAX)
> - return off;
> - }
> - pr_err_once("Please fix pkt_type_offset(), as pkt_type couldn't be found\n");
> - return -1;
> -}
> -
> static int build_body(struct jit_ctx *ctx)
> {
> void *load_func[] = {jit_get_skb_b, jit_get_skb_h, jit_get_skb_w};
> @@ -1332,11 +1311,9 @@ jmp_cmp:
> case BPF_ANC | SKF_AD_PKTTYPE:
> ctx->flags |= SEEN_SKB;
>
> - off = pkt_type_offset();
> -
> - if (off < 0)
> + if (pkt_type_offset < 0)
> return -1;
> - emit_load_byte(r_tmp, r_skb, off, ctx);
> + emit_load_byte(r_tmp, r_skb, pkt_type_offset, ctx);
> /* Keep only the last 3 bits */
> emit_andi(r_A, r_tmp, PKT_TYPE_MAX, ctx);
> #ifdef __BIG_ENDIAN_BITFIELD
> diff --git a/arch/s390/net/bpf_jit_comp.c b/arch/s390/net/bpf_jit_comp.c
> index 61e45b7..caa0cab 100644
> --- a/arch/s390/net/bpf_jit_comp.c
> +++ b/arch/s390/net/bpf_jit_comp.c
> @@ -223,37 +223,6 @@ static void bpf_jit_epilogue(struct bpf_jit *jit)
> EMIT2(0x07fe);
> }
>
> -/* Helper to find the offset of pkt_type in sk_buff
> - * Make sure its still a 3bit field starting at the MSBs within a byte.
> - */
> -#define PKT_TYPE_MAX 0xe0
> -static int pkt_type_offset;
> -
> -static int __init bpf_pkt_type_offset_init(void)
> -{
> - struct sk_buff skb_probe = {
> - .pkt_type = ~0,
> - };
> - char *ct = (char *)&skb_probe;
> - int off;
> -
> - pkt_type_offset = -1;
> - for (off = 0; off < sizeof(struct sk_buff); off++) {
> - if (!ct[off])
> - continue;
> - if (ct[off] == PKT_TYPE_MAX)
> - pkt_type_offset = off;
> - else {
> - /* Found non matching bit pattern, fix needed. */
> - WARN_ON_ONCE(1);
> - pkt_type_offset = -1;
> - return -1;
> - }
> - }
> - return 0;
> -}
> -device_initcall(bpf_pkt_type_offset_init);
> -
> /*
> * make sure we dont leak kernel information to user
> */
> diff --git a/include/linux/filter.h b/include/linux/filter.h
> index a5227ab..6814b54 100644
> --- a/include/linux/filter.h
> +++ b/include/linux/filter.h
> @@ -424,6 +424,8 @@ static inline void *bpf_load_pointer(const struct sk_buff *skb, int k,
> return bpf_internal_load_pointer_neg_helper(skb, k, size);
> }
>
> +extern __read_mostly int pkt_type_offset;
> +
Nit:
extern unsigned int pkt_type_offset;
(Perhaps this should also rather be named bpf_pkt_type_offset to not
cause any current/future name collisions when it's in the header?)
> #ifdef CONFIG_BPF_JIT
> #include <stdarg.h>
> #include <linux/linkage.h>
> diff --git a/net/core/filter.c b/net/core/filter.c
> index d814b8a..edd17f1 100644
> --- a/net/core/filter.c
> +++ b/net/core/filter.c
> @@ -96,21 +96,29 @@ EXPORT_SYMBOL(sk_filter);
> #else
> #define PKT_TYPE_MAX 7
> #endif
> -static unsigned int pkt_type_offset(void)
> +
> +__read_mostly int pkt_type_offset;
Nit:
unsigned int pkt_type_offset __read_mostly;
> +static int __init init_pkt_type_offset(void)
> {
> struct sk_buff skb_probe = { .pkt_type = ~0, };
> u8 *ct = (u8 *) &skb_probe;
> unsigned int off;
>
> + pkt_type_offset = -1;
> for (off = 0; off < sizeof(struct sk_buff); off++) {
> - if (ct[off] == PKT_TYPE_MAX)
> + if (ct[off] == PKT_TYPE_MAX) {
> + pkt_type_offset = off;
> return off;
> + }
> }
Why not BUG_ON() when pkt_type_offset could not be found?
That way we would know that something is broken and you can
spare the checks for negative offsets everywhere ...
> pr_err_once("Please fix %s, as pkt_type couldn't be found!\n", __func__);
> return -1;
> }
>
> +pure_initcall(init_pkt_type_offset);
> +
> static u64 __skb_get_pay_offset(u64 ctx, u64 a, u64 x, u64 r4, u64 r5)
> {
> return __skb_get_poff((struct sk_buff *)(unsigned long) ctx);
> @@ -190,8 +198,10 @@ static bool convert_bpf_extensions(struct sock_filter *fp,
> break;
>
> case SKF_AD_OFF + SKF_AD_PKTTYPE:
> + if (pkt_type_offset < 0)
> + return false;
> *insn = BPF_LDX_MEM(BPF_B, BPF_REG_A, BPF_REG_CTX,
> - pkt_type_offset());
> + pkt_type_offset);
> if (insn->off < 0)
> return false;
> insn++;
>
^ permalink raw reply
* [PATCH net 2/2] ipv6: use addrconf_get_prefix_route() to remove peer addr
From: Nicolas Dichtel @ 2014-09-03 21:59 UTC (permalink / raw)
To: yoshfuji, davem; +Cc: netdev, Nicolas Dichtel
In-Reply-To: <1409781562-4191-1-git-send-email-nicolas.dichtel@6wind.com>
addrconf_get_prefix_route() ensures to get the right route in the right table.
Signed-off-by: Nicolas Dichtel <nicolas.dichtel@6wind.com>
---
net/ipv6/addrconf.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index e75ecb1e848b..bb9ca1655cc5 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -4773,10 +4773,9 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
addrconf_leave_solict(ifp->idev, &ifp->addr);
if (!ipv6_addr_any(&ifp->peer_addr)) {
struct rt6_info *rt;
- struct net_device *dev = ifp->idev->dev;
- rt = rt6_lookup(dev_net(dev), &ifp->peer_addr, NULL,
- dev->ifindex, 1);
+ rt = addrconf_get_prefix_route(&ifp->peer_addr, 128,
+ ifp->idev->dev, 0, 0);
if (rt && ip6_del_rt(rt))
dst_free(&rt->dst);
}
--
1.9.0
^ permalink raw reply related
* [PATCH net 1/2] ipv6: fix a refcnt leak with peer addr
From: Nicolas Dichtel @ 2014-09-03 21:59 UTC (permalink / raw)
To: yoshfuji, davem; +Cc: netdev, Nicolas Dichtel
There is no reason to take a refcnt before deleting the peer address route.
It's done some lines below for the local prefix route because
inet6_ifa_finish_destroy() will release it at the end.
For the peer address route, we want to free it right now.
This bug has been introduced by commit
caeaba79009c ("ipv6: add support of peer address").
Signed-off-by: Nicolas Dichtel <nicolas.dichtel@6wind.com>
---
net/ipv6/addrconf.c | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 0b239fc1816e..e75ecb1e848b 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -4777,11 +4777,8 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
rt = rt6_lookup(dev_net(dev), &ifp->peer_addr, NULL,
dev->ifindex, 1);
- if (rt) {
- dst_hold(&rt->dst);
- if (ip6_del_rt(rt))
- dst_free(&rt->dst);
- }
+ if (rt && ip6_del_rt(rt))
+ dst_free(&rt->dst);
}
dst_hold(&ifp->rt->dst);
--
1.9.0
^ permalink raw reply related
* Re: [patch net-next 01/13] openvswitch: split flow structures into ovs specific and generic ones
From: Pravin Shelar @ 2014-09-03 21:59 UTC (permalink / raw)
To: Jamal Hadi Salim
Cc: ryazanov.s.a, jasowang, john.r.fastabend, neil.jerram,
Eric Dumazet, andy, dev-yBygre7rU0TnMu66kgdUjQ@public.gmane.org,
nbd, f.fainelli, Rony Efraim, jeffrey.t.kirsher, Or Gerlitz,
Ben Hutchings, buytenh, Jiri Pirko, roopa, aviadr,
Nicolas Dichtel, vyasevic, nhorman, netdev, Stephen Hemminger,
Daniel Borkmann, ebiederm
In-Reply-To: <54078694.5040104-jkUAjuhPggJWk0Htik3J/w@public.gmane.org>
On Wed, Sep 3, 2014 at 2:22 PM, Jamal Hadi Salim <jhs-jkUAjuhPggJWk0Htik3J/w@public.gmane.org> wrote:
> On 09/03/14 14:41, Pravin Shelar wrote:
>>
>> On Wed, Sep 3, 2014 at 2:24 AM, Jiri Pirko <jiri-rHqAuBHg3fBzbRFIqnYvSA@public.gmane.org> wrote:
>
>
>> HW offload API should be separate from OVS module.
>
>
> The above part i agree with. Infact it is very odd that it seems
> hard to get this point across ;->
>
>
>> This has following
>> advantages.
>> 1. It can be managed by OVS userspace vswitchd process which has much
>> better context to setup hardware flow table. Once we add capabilities
>> for swdev, it is much more easier for vswitchd process to choose
>> correct (hw or sw) flow table for given flow.
>
>
> This i disagree with.
> The desire is to have existing user tools to work with offloads.
> When necessary, we then create new tools.
> Existing tools may need to be taught to do selectively do
> hardware vs software offload. We have a precedence with
> bridging code which selectively offloads to hardware using iproute2.
>
Both of us are saying same thing.
What I meant was for OVS use-case, where OVS wants to use offload for
switching flows, vswitchd userspace process can program HW offload
using kernel HW offload APIs directly from userspace, rather than
going through OVS kernel module. If user wants to use some other tool,
then the tool can use same kernel HW offload APIs.
>
>> 2. Other application that wants to use HW offload does not have
>> dependency on OVS kernel module.
>
>
> Or on OF for that matter.
>
>
>> 3. Hardware and software datapath remains separate, these two
>> components has no dependency on each other, both can be developed
>> independent of each other.
>>
>
> The basic definition of "offload" implies dependency;-> So,
> I strongly disagree. You may need to go backwards and look at
> views expressed on this (other than emails - theres slideware).
>
I was referring to code dependency in kernel. For example ovs flow-key
structure used. This complicates OVS internal structure which needs to
be shared plus OVS might need to extend interface for configuring HW
match or action that does not exist in OVS software datapath.
I agree these two components are related and that dependency can be
handled from userspace.
^ permalink raw reply
* [PATCH net-next 2/2] bonding: Simplify the xmit function for modes that use xmit_hash
From: Mahesh Bandewar @ 2014-09-03 21:47 UTC (permalink / raw)
To: Jay Vosburgh, Veaceslav Falico, Andy Gospodarek, David Miller
Cc: netdev, Mahesh Bandewar, Eric Dumazet, Maciej Zenczykowski
Earlier change to use usable slave array for TLB mode had an additional
performance advantage. So extending the same logic to all other modes
that use xmit-hash for slave selection (viz 802.3AD, and XOR modes).
Also consolidating this with the earlier TLB change.
The main idea is to build the usable slaves array in the control path
and use that array for slave selection during xmit operation.
Measured performance in a setup with a bond of 4x1G NICs with 200
instances of netperf for the modes involved (3ad, xor, tlb)
cmd: netperf -t TCP_RR -H <TargetHost> -l 60 -s 5
Mode TPS-Before TPS-After
802.3ad : 468,694 493,101
TLB (lb=0): 392,583 392,965
XOR : 475,696 484,517
Signed-off-by: Mahesh Bandewar <maheshb@google.com>
---
drivers/net/bonding/bond_3ad.c | 77 ++++---------------------------
drivers/net/bonding/bond_alb.c | 43 ++---------------
drivers/net/bonding/bond_alb.h | 8 ----
drivers/net/bonding/bond_main.c | 100 ++++++++++++++++++++++++++++++++++++----
drivers/net/bonding/bonding.h | 8 ++++
5 files changed, 113 insertions(+), 123 deletions(-)
diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c
index ee2c73a9de39..d42fd65fdfa9 100644
--- a/drivers/net/bonding/bond_3ad.c
+++ b/drivers/net/bonding/bond_3ad.c
@@ -1717,6 +1717,8 @@ static void ad_enable_collecting_distributing(struct port *port)
port->actor_port_number,
port->aggregator->aggregator_identifier);
__enable_port(port);
+ if (bond_update_slave_arr(port->slave->bond, NULL))
+ pr_debug("Failed to build slave-array for 3ad mode.\n");
}
}
@@ -1733,6 +1735,8 @@ static void ad_disable_collecting_distributing(struct port *port)
port->actor_port_number,
port->aggregator->aggregator_identifier);
__disable_port(port);
+ if (bond_update_slave_arr(port->slave->bond, NULL))
+ pr_debug("Failed to build slave-array for 3ad mode.\n");
}
}
@@ -1917,6 +1921,9 @@ void bond_3ad_unbind_slave(struct slave *slave)
__update_lacpdu_from_port(port);
ad_lacpdu_send(port);
+ if (bond_update_slave_arr(bond, slave))
+ pr_debug("Failed to build slave-array for 3AD mode.\n");
+
/* check if this aggregator is occupied */
if (aggregator->lag_ports) {
/* check if there are other ports related to this aggregator
@@ -2311,6 +2318,9 @@ void bond_3ad_handle_link_change(struct slave *slave, char link)
*/
port->sm_vars |= AD_PORT_BEGIN;
+ if (bond_update_slave_arr(slave->bond, NULL))
+ pr_debug("Failed to build slave-array for 3ad mode.\n");
+
__release_state_machine_lock(port);
}
@@ -2407,73 +2417,6 @@ int bond_3ad_get_active_agg_info(struct bonding *bond, struct ad_info *ad_info)
return ret;
}
-int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev)
-{
- struct bonding *bond = netdev_priv(dev);
- struct slave *slave, *first_ok_slave;
- struct aggregator *agg;
- struct ad_info ad_info;
- struct list_head *iter;
- int slaves_in_agg;
- int slave_agg_no;
- int agg_id;
-
- if (__bond_3ad_get_active_agg_info(bond, &ad_info)) {
- netdev_dbg(dev, "__bond_3ad_get_active_agg_info failed\n");
- goto err_free;
- }
-
- slaves_in_agg = ad_info.ports;
- agg_id = ad_info.aggregator_id;
-
- if (slaves_in_agg == 0) {
- netdev_dbg(dev, "active aggregator is empty\n");
- goto err_free;
- }
-
- slave_agg_no = bond_xmit_hash(bond, skb) % slaves_in_agg;
- first_ok_slave = NULL;
-
- bond_for_each_slave_rcu(bond, slave, iter) {
- agg = SLAVE_AD_INFO(slave)->port.aggregator;
- if (!agg || agg->aggregator_identifier != agg_id)
- continue;
-
- if (slave_agg_no >= 0) {
- if (!first_ok_slave && bond_slave_can_tx(slave))
- first_ok_slave = slave;
- slave_agg_no--;
- continue;
- }
-
- if (bond_slave_can_tx(slave)) {
- bond_dev_queue_xmit(bond, skb, slave->dev);
- goto out;
- }
- }
-
- if (slave_agg_no >= 0) {
- netdev_err(dev, "Couldn't find a slave to tx on for aggregator ID %d\n",
- agg_id);
- goto err_free;
- }
-
- /* we couldn't find any suitable slave after the agg_no, so use the
- * first suitable found, if found.
- */
- if (first_ok_slave)
- bond_dev_queue_xmit(bond, skb, first_ok_slave->dev);
- else
- goto err_free;
-
-out:
- return NETDEV_TX_OK;
-err_free:
- /* no suitable interface, frame not sent */
- dev_kfree_skb_any(skb);
- goto out;
-}
-
int bond_3ad_lacpdu_recv(const struct sk_buff *skb, struct bonding *bond,
struct slave *slave)
{
diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c
index 73c21e233131..b2be7b3216d6 100644
--- a/drivers/net/bonding/bond_alb.c
+++ b/drivers/net/bonding/bond_alb.c
@@ -200,7 +200,6 @@ static int tlb_initialize(struct bonding *bond)
static void tlb_deinitialize(struct bonding *bond)
{
struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
- struct tlb_up_slave *arr;
_lock_tx_hashtbl_bh(bond);
@@ -208,10 +207,6 @@ static void tlb_deinitialize(struct bonding *bond)
bond_info->tx_hashtbl = NULL;
_unlock_tx_hashtbl_bh(bond);
-
- arr = rtnl_dereference(bond_info->slave_arr);
- if (arr)
- kfree_rcu(arr, rcu);
}
static long long compute_gap(struct slave *slave)
@@ -1409,39 +1404,9 @@ out:
return NETDEV_TX_OK;
}
-static int bond_tlb_update_slave_arr(struct bonding *bond,
- struct slave *skipslave)
-{
- struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
- struct slave *tx_slave;
- struct list_head *iter;
- struct tlb_up_slave *new_arr, *old_arr;
-
- new_arr = kzalloc(offsetof(struct tlb_up_slave, arr[bond->slave_cnt]),
- GFP_ATOMIC);
- if (!new_arr)
- return -ENOMEM;
-
- bond_for_each_slave(bond, tx_slave, iter) {
- if (!bond_slave_can_tx(tx_slave))
- continue;
- if (skipslave == tx_slave)
- continue;
- new_arr->arr[new_arr->count++] = tx_slave;
- }
-
- old_arr = rtnl_dereference(bond_info->slave_arr);
- rcu_assign_pointer(bond_info->slave_arr, new_arr);
- if (old_arr)
- kfree_rcu(old_arr, rcu);
-
- return 0;
-}
-
int bond_tlb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
{
struct bonding *bond = netdev_priv(bond_dev);
- struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
struct ethhdr *eth_data;
struct slave *tx_slave = NULL;
u32 hash_index;
@@ -1462,9 +1427,9 @@ int bond_tlb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
hash_index & 0xFF,
skb->len);
} else {
- struct tlb_up_slave *slaves;
+ struct bond_up_slave *slaves;
- slaves = rcu_dereference(bond_info->slave_arr);
+ slaves = rcu_dereference(bond->slave_arr);
if (slaves && slaves->count)
tx_slave = slaves->arr[hash_index %
slaves->count];
@@ -1734,7 +1699,7 @@ void bond_alb_deinit_slave(struct bonding *bond, struct slave *slave)
}
if (bond_is_nondyn_tlb(bond))
- if (bond_tlb_update_slave_arr(bond, slave))
+ if (bond_update_slave_arr(bond, slave))
pr_err("Failed to build slave-array for TLB mode.\n");
}
@@ -1762,7 +1727,7 @@ void bond_alb_handle_link_change(struct bonding *bond, struct slave *slave, char
}
if (bond_is_nondyn_tlb(bond)) {
- if (bond_tlb_update_slave_arr(bond, NULL))
+ if (bond_update_slave_arr(bond, NULL))
pr_err("Failed to build slave-array for TLB mode.\n");
}
}
diff --git a/drivers/net/bonding/bond_alb.h b/drivers/net/bonding/bond_alb.h
index aaeac61d03cf..5fc76c01636c 100644
--- a/drivers/net/bonding/bond_alb.h
+++ b/drivers/net/bonding/bond_alb.h
@@ -139,20 +139,12 @@ struct tlb_slave_info {
*/
};
-struct tlb_up_slave {
- unsigned int count;
- struct rcu_head rcu;
- struct slave *arr[0];
-};
-
struct alb_bond_info {
struct tlb_client_info *tx_hashtbl; /* Dynamically allocated */
spinlock_t tx_hashtbl_lock;
u32 unbalanced_load;
int tx_rebalance_counter;
int lp_counter;
- /* -------- non-dynamic tlb mode only ---------*/
- struct tlb_up_slave __rcu *slave_arr; /* Up slaves */
/* -------- rlb parameters -------- */
int rlb_enabled;
struct rlb_client_info *rx_hashtbl; /* Receive hash table */
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index f0f5eab0fab1..b0e8e7cfa10f 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -1693,6 +1693,9 @@ static int __bond_release_one(struct net_device *bond_dev,
if (BOND_MODE(bond) == BOND_MODE_8023AD)
bond_3ad_unbind_slave(slave);
+ else if (BOND_MODE(bond) == BOND_MODE_XOR)
+ bond_update_slave_arr(bond, slave);
+
write_unlock_bh(&bond->lock);
netdev_info(bond_dev, "Releasing %s interface %s\n",
@@ -2009,6 +2012,9 @@ static void bond_miimon_commit(struct bonding *bond)
bond_alb_handle_link_change(bond, slave,
BOND_LINK_UP);
+ if (BOND_MODE(bond) == BOND_MODE_XOR)
+ bond_update_slave_arr(bond, NULL);
+
if (!bond->curr_active_slave ||
(slave == bond->primary_slave))
goto do_failover;
@@ -2037,6 +2043,9 @@ static void bond_miimon_commit(struct bonding *bond)
bond_alb_handle_link_change(bond, slave,
BOND_LINK_DOWN);
+ if (BOND_MODE(bond) == BOND_MODE_XOR)
+ bond_update_slave_arr(bond, slave);
+
if (slave == rcu_access_pointer(bond->curr_active_slave))
goto do_failover;
@@ -3149,6 +3158,7 @@ static int bond_open(struct net_device *bond_dev)
static int bond_close(struct net_device *bond_dev)
{
struct bonding *bond = netdev_priv(bond_dev);
+ struct bond_up_slave *arr;
bond_work_cancel_all(bond);
bond->send_peer_notif = 0;
@@ -3156,6 +3166,10 @@ static int bond_close(struct net_device *bond_dev)
bond_alb_deinitialize(bond);
bond->recv_probe = NULL;
+ arr = rtnl_dereference(bond->slave_arr);
+ if (arr)
+ kfree_rcu(arr, rcu);
+
return 0;
}
@@ -3684,15 +3698,84 @@ static int bond_xmit_activebackup(struct sk_buff *skb, struct net_device *bond_d
return NETDEV_TX_OK;
}
-/* In bond_xmit_xor() , we determine the output device by using a pre-
- * determined xmit_hash_policy(), If the selected device is not enabled,
- * find the next active slave.
+/* Build the usable slaves array in control path for modes that use xmit-hash
+ * to determine the slave interface -
+ * (a) BOND_MODE_8023AD
+ * (b) BOND_MODE_XOR
+ * (c) BOND_MODE_TLB && tlb_dynamic_lb == 0
*/
-static int bond_xmit_xor(struct sk_buff *skb, struct net_device *bond_dev)
+int bond_update_slave_arr(struct bonding *bond, struct slave *skipslave)
{
- struct bonding *bond = netdev_priv(bond_dev);
+ struct slave *slave;
+ struct list_head *iter;
+ struct bond_up_slave *new_arr, *old_arr;
+ int slaves_in_agg;
+ int agg_id = 0;
+ int ret = 0;
+
+ new_arr = kzalloc(offsetof(struct bond_up_slave, arr[bond->slave_cnt]),
+ GFP_ATOMIC);
+ if (!new_arr) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ if (BOND_MODE(bond) == BOND_MODE_8023AD) {
+ struct ad_info ad_info;
+
+ if (__bond_3ad_get_active_agg_info(bond, &ad_info)) {
+ pr_debug("__bond_3ad_get_active_agg_info failed\n");
+ kfree_rcu(new_arr, rcu);
+ ret = -EINVAL;
+ goto out;
+ }
+ slaves_in_agg = ad_info.ports;
+ agg_id = ad_info.aggregator_id;
+ }
+ bond_for_each_slave(bond, slave, iter) {
+ if (BOND_MODE(bond) == BOND_MODE_8023AD) {
+ struct aggregator *agg;
+
+ agg = SLAVE_AD_INFO(slave)->port.aggregator;
+ if (!agg || agg->aggregator_identifier != agg_id)
+ continue;
+ }
+ if (!bond_slave_can_tx(slave))
+ continue;
+ if (skipslave == slave)
+ continue;
+ new_arr->arr[new_arr->count++] = slave;
+ }
- bond_xmit_slave_id(bond, skb, bond_xmit_hash(bond, skb) % bond->slave_cnt);
+ old_arr = rcu_dereference_protected(bond->slave_arr,
+ lockdep_rtnl_is_held() ||
+ lockdep_is_held(&bond->lock) ||
+ lockdep_is_held(&bond->curr_slave_lock));
+ rcu_assign_pointer(bond->slave_arr, new_arr);
+ if (old_arr)
+ kfree_rcu(old_arr, rcu);
+
+out:
+ return ret;
+}
+
+/* Use this Xmit function for 3AD as well as XOR modes. The current
+ * usable slave array is formed in the control path. The xmit function
+ * just calculates hash and sends the packet out.
+ */
+int bond_3ad_xor_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct bonding *bond = netdev_priv(dev);
+ struct slave *slave;
+ struct bond_up_slave *slaves;
+
+ slaves = rcu_dereference(bond->slave_arr);
+ if (slaves && slaves->count) {
+ slave = slaves->arr[bond_xmit_hash(bond, skb) % slaves->count];
+ bond_dev_queue_xmit(bond, skb, slave->dev);
+ } else {
+ dev_kfree_skb_any(skb);
+ atomic_long_inc(&dev->tx_dropped);
+ }
return NETDEV_TX_OK;
}
@@ -3794,12 +3877,11 @@ static netdev_tx_t __bond_start_xmit(struct sk_buff *skb, struct net_device *dev
return bond_xmit_roundrobin(skb, dev);
case BOND_MODE_ACTIVEBACKUP:
return bond_xmit_activebackup(skb, dev);
+ case BOND_MODE_8023AD:
case BOND_MODE_XOR:
- return bond_xmit_xor(skb, dev);
+ return bond_3ad_xor_xmit(skb, dev);
case BOND_MODE_BROADCAST:
return bond_xmit_broadcast(skb, dev);
- case BOND_MODE_8023AD:
- return bond_3ad_xmit_xor(skb, dev);
case BOND_MODE_ALB:
return bond_alb_xmit(skb, dev);
case BOND_MODE_TLB:
diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h
index aace510d08d1..4a6195c0de60 100644
--- a/drivers/net/bonding/bonding.h
+++ b/drivers/net/bonding/bonding.h
@@ -177,6 +177,12 @@ struct slave {
struct kobject kobj;
};
+struct bond_up_slave {
+ unsigned int count;
+ struct rcu_head rcu;
+ struct slave *arr[0];
+};
+
/*
* Link pseudo-state only used internally by monitors
*/
@@ -196,6 +202,7 @@ struct bonding {
struct slave __rcu *curr_active_slave;
struct slave __rcu *current_arp_slave;
struct slave *primary_slave;
+ struct bond_up_slave __rcu *slave_arr; /* Array of usable slaves */
bool force_primary;
s32 slave_cnt; /* never change this value outside the attach/detach wrappers */
int (*recv_probe)(const struct sk_buff *, struct bonding *,
@@ -527,6 +534,7 @@ const char *bond_slave_link_status(s8 link);
struct bond_vlan_tag *bond_verify_device_path(struct net_device *start_dev,
struct net_device *end_dev,
int level);
+int bond_update_slave_arr(struct bonding *bond, struct slave *skipslave);
#ifdef CONFIG_PROC_FS
void bond_create_proc_entry(struct bonding *bond);
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related
* [PATCH net-next 1/2] bonding: display xmit_hash_policy for non-dynamic-tlb mode
From: Mahesh Bandewar @ 2014-09-03 21:38 UTC (permalink / raw)
To: Jay Vosburgh, Veaceslav Falico, Andy Gospodarek, David Miller
Cc: netdev, Mahesh Bandewar, Eric Dumazet, Maciej Zenczykowski
It's trivial fix to display xmit_hash_policy for this new TLB mode
since it uses transmit-hash-poilicy as part of bonding-master info
(/proc/net/bonding/<bonding-interface).
Signed-off-by: Mahesh Bandewar <maheshb@google.com>
---
drivers/net/bonding/bond_procfs.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/net/bonding/bond_procfs.c b/drivers/net/bonding/bond_procfs.c
index de62c0385dfb..ebcf63a6ddf0 100644
--- a/drivers/net/bonding/bond_procfs.c
+++ b/drivers/net/bonding/bond_procfs.c
@@ -84,7 +84,8 @@ static void bond_info_show_master(struct seq_file *seq)
seq_printf(seq, "\n");
if (BOND_MODE(bond) == BOND_MODE_XOR ||
- BOND_MODE(bond) == BOND_MODE_8023AD) {
+ BOND_MODE(bond) == BOND_MODE_8023AD ||
+ bond_is_nondyn_tlb(bond)) {
optval = bond_opt_get_val(BOND_OPT_XMIT_HASH,
bond->params.xmit_policy);
seq_printf(seq, "Transmit Hash Policy: %s (%d)\n",
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related
* [PATCH v2 net-next] net: filter: export pkt_type_offset() helper
From: Denis Kirjanov @ 2014-09-03 21:08 UTC (permalink / raw)
To: netdev
Cc: Denis Kirjanov, Markos Chandras, Martin Schwidefsky,
Daniel Borkmann, Alexei Starovoitov
Currently we have 2 pkt_type_offset functions doing
the same thing and spread across the architecture files.
Let's use the generic helper routine.
Signed-off-by: Denis Kirjanov <kda@linux-powerpc.org>
Cc: Markos Chandras <markos.chandras@imgtec.com>
Cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
Cc: Daniel Borkmann <dborkman@redhat.com>
Cc: Alexei Starovoitov <alexei.starovoitov@gmail.com>
V2: Initialize the pkt_type_offset through the pure_initcall()
---
arch/mips/net/bpf_jit.c | 27 ++-------------------------
arch/s390/net/bpf_jit_comp.c | 31 -------------------------------
include/linux/filter.h | 2 ++
net/core/filter.c | 16 +++++++++++++---
4 files changed, 17 insertions(+), 59 deletions(-)
diff --git a/arch/mips/net/bpf_jit.c b/arch/mips/net/bpf_jit.c
index 05a5661..bb9b53f 100644
--- a/arch/mips/net/bpf_jit.c
+++ b/arch/mips/net/bpf_jit.c
@@ -765,27 +765,6 @@ static u64 jit_get_skb_w(struct sk_buff *skb, unsigned offset)
return (u64)err << 32 | ntohl(ret);
}
-#ifdef __BIG_ENDIAN_BITFIELD
-#define PKT_TYPE_MAX (7 << 5)
-#else
-#define PKT_TYPE_MAX 7
-#endif
-static int pkt_type_offset(void)
-{
- struct sk_buff skb_probe = {
- .pkt_type = ~0,
- };
- u8 *ct = (u8 *)&skb_probe;
- unsigned int off;
-
- for (off = 0; off < sizeof(struct sk_buff); off++) {
- if (ct[off] == PKT_TYPE_MAX)
- return off;
- }
- pr_err_once("Please fix pkt_type_offset(), as pkt_type couldn't be found\n");
- return -1;
-}
-
static int build_body(struct jit_ctx *ctx)
{
void *load_func[] = {jit_get_skb_b, jit_get_skb_h, jit_get_skb_w};
@@ -1332,11 +1311,9 @@ jmp_cmp:
case BPF_ANC | SKF_AD_PKTTYPE:
ctx->flags |= SEEN_SKB;
- off = pkt_type_offset();
-
- if (off < 0)
+ if (pkt_type_offset < 0)
return -1;
- emit_load_byte(r_tmp, r_skb, off, ctx);
+ emit_load_byte(r_tmp, r_skb, pkt_type_offset, ctx);
/* Keep only the last 3 bits */
emit_andi(r_A, r_tmp, PKT_TYPE_MAX, ctx);
#ifdef __BIG_ENDIAN_BITFIELD
diff --git a/arch/s390/net/bpf_jit_comp.c b/arch/s390/net/bpf_jit_comp.c
index 61e45b7..caa0cab 100644
--- a/arch/s390/net/bpf_jit_comp.c
+++ b/arch/s390/net/bpf_jit_comp.c
@@ -223,37 +223,6 @@ static void bpf_jit_epilogue(struct bpf_jit *jit)
EMIT2(0x07fe);
}
-/* Helper to find the offset of pkt_type in sk_buff
- * Make sure its still a 3bit field starting at the MSBs within a byte.
- */
-#define PKT_TYPE_MAX 0xe0
-static int pkt_type_offset;
-
-static int __init bpf_pkt_type_offset_init(void)
-{
- struct sk_buff skb_probe = {
- .pkt_type = ~0,
- };
- char *ct = (char *)&skb_probe;
- int off;
-
- pkt_type_offset = -1;
- for (off = 0; off < sizeof(struct sk_buff); off++) {
- if (!ct[off])
- continue;
- if (ct[off] == PKT_TYPE_MAX)
- pkt_type_offset = off;
- else {
- /* Found non matching bit pattern, fix needed. */
- WARN_ON_ONCE(1);
- pkt_type_offset = -1;
- return -1;
- }
- }
- return 0;
-}
-device_initcall(bpf_pkt_type_offset_init);
-
/*
* make sure we dont leak kernel information to user
*/
diff --git a/include/linux/filter.h b/include/linux/filter.h
index a5227ab..6814b54 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -424,6 +424,8 @@ static inline void *bpf_load_pointer(const struct sk_buff *skb, int k,
return bpf_internal_load_pointer_neg_helper(skb, k, size);
}
+extern __read_mostly int pkt_type_offset;
+
#ifdef CONFIG_BPF_JIT
#include <stdarg.h>
#include <linux/linkage.h>
diff --git a/net/core/filter.c b/net/core/filter.c
index d814b8a..edd17f1 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -96,21 +96,29 @@ EXPORT_SYMBOL(sk_filter);
#else
#define PKT_TYPE_MAX 7
#endif
-static unsigned int pkt_type_offset(void)
+
+__read_mostly int pkt_type_offset;
+
+static int __init init_pkt_type_offset(void)
{
struct sk_buff skb_probe = { .pkt_type = ~0, };
u8 *ct = (u8 *) &skb_probe;
unsigned int off;
+ pkt_type_offset = -1;
for (off = 0; off < sizeof(struct sk_buff); off++) {
- if (ct[off] == PKT_TYPE_MAX)
+ if (ct[off] == PKT_TYPE_MAX) {
+ pkt_type_offset = off;
return off;
+ }
}
pr_err_once("Please fix %s, as pkt_type couldn't be found!\n", __func__);
return -1;
}
+pure_initcall(init_pkt_type_offset);
+
static u64 __skb_get_pay_offset(u64 ctx, u64 a, u64 x, u64 r4, u64 r5)
{
return __skb_get_poff((struct sk_buff *)(unsigned long) ctx);
@@ -190,8 +198,10 @@ static bool convert_bpf_extensions(struct sock_filter *fp,
break;
case SKF_AD_OFF + SKF_AD_PKTTYPE:
+ if (pkt_type_offset < 0)
+ return false;
*insn = BPF_LDX_MEM(BPF_B, BPF_REG_A, BPF_REG_CTX,
- pkt_type_offset());
+ pkt_type_offset);
if (insn->off < 0)
return false;
insn++;
--
2.0.0
^ permalink raw reply related
* Re: [Patch net-next 04/11] net:fec: add multiqueue support
From: Florian Fainelli @ 2014-09-03 21:27 UTC (permalink / raw)
To: Frank Li, b38611, davem, netdev, lznuaa
Cc: shawn.guo, linux-arm-kernel, devicetree
In-Reply-To: <1409776697-1536-5-git-send-email-Frank.Li@freescale.com>
On 09/03/2014 01:38 PM, Frank Li wrote:
> From: Fugang Duan <B38611@freescale.com>
>
> Add multiqueue support, which is compatible with previous single queue
> driver for FEC/ENET IPs.
>
> By default, the tx/rx queue number is 1, user can config the queue number
> at DTS file like this:
> fsl,num_tx_queues=<3>;
> fsl,num_rx_queues=<3>;
>
> Ethernet multiqueue mechanism can improve performance in SMP system.
> For single hw queue, multiqueue can balance cpu loading.
> For multi hw queues, multiple cores can process network packets in parallel,
> and refer the article for the detail advantage for multiqueue:
> http://vger.kernel.org/~davem/davem_nyc09.pdf
You should probably split that patch in a minimum of two, or more
different parts.
Two parts:
- add TX multiqueue support
- add RX multiqueue support
More parts:
- prepare for TX multiqueue (introduce queue structure, definitions)
- transition TX path to use queue 0
- add support for the two other TX queues
- prepare for RX multiqueue (introduce queue structure, definitions)
- prepare for multiqueue, hardcode RX path to use queue 0
- add support for the two other RX queues
The number of changes are extremely hard to review...
--
Florian
^ permalink raw reply
* Re: [PATCH 2/2] net-timestamp: Make the clone operation stand-alone from phy timestamping
From: Alexander Duyck @ 2014-09-03 21:24 UTC (permalink / raw)
To: Willem de Bruijn
Cc: Network Development, Richard Cochran, David Miller,
dumazet >> Eric Dumazet
In-Reply-To: <CA+FuTSdsrAAd0iti0oF3MDvXTtQWnBByuo_UMGCW2Dd+WnWeLg@mail.gmail.com>
On 09/03/2014 11:54 AM, Willem de Bruijn wrote:
> On Wed, Sep 3, 2014 at 11:53 AM, Alexander Duyck
> <alexander.h.duyck@intel.com> wrote:
>> The phy timestamping takes a different path than the regular timestamping
>> does in that it will create a clone first so that the packets needing to be
>> timestamped can be placed in a queue, or the context block could be used.
>>
>> In order to support these use cases I am pulling the core of the code out
>> so it can be used in other drivers beyond just phy devices.
>
> Do you already have additional such use cases?
I have a driver that I am working on that I will probably push in a
couple of weeks that will make use of this interface. I basically need
to maintain a list of SKBs as I can multiple timestamps out for
completion at the same time.
>> +struct sk_buff *__skb_clone_tx_timestamp(struct sk_buff *skb)
>> +{
>> + struct sock *sk = skb->sk;
>> + struct sk_buff *clone;
>> +
>> + if (!sk || !atomic_inc_not_zero(&sk->sk_refcnt))
>> + return NULL;
>> +
>> + clone = skb_clone(skb, GFP_ATOMIC);
>> + if (!clone) {
>> + sock_put(sk);
>> + return NULL;
>> + }
>> +
>> + clone->sk = sk;
>> +
>> + return clone;
>> +}
>> +EXPORT_SYMBOL(__skb_clone_tx_timestamp);
>> +
>
> Code looks great. Again, this can be verified to be a functional noop.
> One minor comment is that this really is not a timestamping function,
> but an skb_clone variant. skb_clone_sk?
Let me think about this one. Between the comment Eric had about
skb->destructor and the fact that this is essentially just forking the
skb so we can hold it for the reply I might be able to come up with a
better solution.
I'm thinking it might be worthwhile to create a simple destructor as
then I could probably tear out several spots in the phy timestamping
code where it is having to use skb_complete_tx_timestamp to free the
frames that are allocated using the approach in this function.
Thanks,
Alex
^ permalink raw reply
* Re: [patch net-next 01/13] openvswitch: split flow structures into ovs specific and generic ones
From: Jamal Hadi Salim @ 2014-09-03 21:22 UTC (permalink / raw)
To: Pravin Shelar, Jiri Pirko
Cc: netdev, David Miller, nhorman, andy, Thomas Graf, Daniel Borkmann,
Or Gerlitz, Jesse Gross, Andy Zhou, Ben Hutchings,
Stephen Hemminger, jeffrey.t.kirsher, vyasevic, Cong Wang,
john.r.fastabend, Eric Dumazet, sfeldma, f.fainelli, roopa,
John Linville, dev@openvswitch.org, jasowang, ebiederm,
Nicolas Dichtel, ryazanov.s.a, buytenh, aviadr, nbd,
alexei.starovoitov
In-Reply-To: <CALnjE+pscRmfhaWgkWCunJfjvG04RiNUAj6nefSFHrknQTC+xw@mail.gmail.com>
On 09/03/14 14:41, Pravin Shelar wrote:
> On Wed, Sep 3, 2014 at 2:24 AM, Jiri Pirko <jiri@resnulli.us> wrote:
> HW offload API should be separate from OVS module.
The above part i agree with. Infact it is very odd that it seems
hard to get this point across ;->
> This has following
> advantages.
> 1. It can be managed by OVS userspace vswitchd process which has much
> better context to setup hardware flow table. Once we add capabilities
> for swdev, it is much more easier for vswitchd process to choose
> correct (hw or sw) flow table for given flow.
This i disagree with.
The desire is to have existing user tools to work with offloads.
When necessary, we then create new tools.
Existing tools may need to be taught to do selectively do
hardware vs software offload. We have a precedence with
bridging code which selectively offloads to hardware using iproute2.
> 2. Other application that wants to use HW offload does not have
> dependency on OVS kernel module.
Or on OF for that matter.
> 3. Hardware and software datapath remains separate, these two
> components has no dependency on each other, both can be developed
> independent of each other.
>
The basic definition of "offload" implies dependency;-> So,
I strongly disagree. You may need to go backwards and look at
views expressed on this (other than emails - theres slideware).
cheers,
jamal
^ permalink raw reply
* Re: [Patch net-next 03/11] net:fec: use multiqueue interface to allocate Ethernet device
From: Florian Fainelli @ 2014-09-03 21:20 UTC (permalink / raw)
To: Frank Li, b38611, davem, netdev, lznuaa
Cc: shawn.guo, linux-arm-kernel, devicetree
In-Reply-To: <1409776697-1536-4-git-send-email-Frank.Li@freescale.com>
On 09/03/2014 01:38 PM, Frank Li wrote:
> From: Fugang Duan <B38611@freescale.com>
>
> Since i.MX6SX enet-AVB IP support multi queues, so use multi queues
> interface to allocate and set up an Ethernet device.
>
> Signed-off-by: Fugang Duan <B38611@freescale.com>
> Signed-off-by: Frank Li <Frank.Li@freescale.com>
> ---
> drivers/net/ethernet/freescale/fec.h | 9 ++++++
> drivers/net/ethernet/freescale/fec_main.c | 49 ++++++++++++++++++++++++++++++-
> 2 files changed, 57 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h
> index 635772b..f77ed6f 100644
> --- a/drivers/net/ethernet/freescale/fec.h
> +++ b/drivers/net/ethernet/freescale/fec.h
> @@ -233,6 +233,13 @@ struct bufdesc_ex {
> /* This device has up to three irqs on some platforms */
> #define FEC_IRQ_NUM 3
>
> +/* Maximum number of queues supported
> + * ENET with AVB IP can support up to 3 independent tx queues and rx queues.
> + * User can point the queue number that is less than or equal to 3.
> + */
> +#define FEC_ENET_MAX_TX_QS 3
> +#define FEC_ENET_MAX_RX_QS 3
> +
> /* The number of Tx and Rx buffers. These are allocated from the page
> * pool. The code may assume these are power of two, so it it best
> * to keep them that size.
> @@ -278,6 +285,8 @@ struct fec_enet_private {
>
> bool ptp_clk_on;
> struct mutex ptp_clk_mutex;
> + int num_tx_queues;
> + int num_rx_queues;
unsigned int.
>
> /* The saved address of a sent-in-place packet/buffer, for skfree(). */
> unsigned char *tx_bounce[TX_RING_SIZE];
> diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
> index ee9f04f..00fcadd 100644
> --- a/drivers/net/ethernet/freescale/fec_main.c
> +++ b/drivers/net/ethernet/freescale/fec_main.c
> @@ -2573,6 +2573,39 @@ static void fec_reset_phy(struct platform_device *pdev)
> #endif /* CONFIG_OF */
>
> static int
> +fec_enet_get_queue_num(struct platform_device *pdev, int *num_tx, int *num_rx)
> +{
> + struct device_node *np = pdev->dev.of_node;
> + int err;
> +
> + if (!np || !of_device_is_available(np))
> + return -ENODEV;
This effectively breaks non-DT platform probing, is that intentional?
How about assining *num_tx and *num_rx to 1, and if you find the
property, use it?
> +
> + /* parse the num of tx and rx queues */
> + err = of_property_read_u32(np, "fsl,num_tx_queues", num_tx);
> + err |= of_property_read_u32(np, "fsl,num_rx_queues", num_rx);
> + if (err) {
> + *num_tx = 1;
> + *num_rx = 1;
> + return 0;
> + }
> +
> + if (*num_tx < 1 || *num_tx > FEC_ENET_MAX_TX_QS) {
> + dev_err(&pdev->dev, "num_tx(=%d) greater than MAX_TX_QS(=%d)\n",
> + *num_tx, FEC_ENET_MAX_TX_QS);
> + return -EINVAL;
> + }
You are dealing with two error conditions here, *num_tx == 0 and *num_tx
> FEC_ENET_MAX_TX_QS, so the message should not be "greater than"
> +
> + if (*num_rx < 1 || *num_rx > FEC_ENET_MAX_RX_QS) {
> + dev_err(&pdev->dev, "num_rx(=%d) greater than MAX_RX_QS(=%d)\n",
> + *num_rx, FEC_ENET_MAX_RX_QS);
> + return -EINVAL;
> + }
Same here.
> +
> + return 0;
> +}
> +
> +static int
> fec_probe(struct platform_device *pdev)
> {
> struct fec_enet_private *fep;
> @@ -2583,13 +2616,23 @@ fec_probe(struct platform_device *pdev)
> const struct of_device_id *of_id;
> static int dev_id;
> struct device_node *np = pdev->dev.of_node, *phy_node;
> + int num_tx_qs = 1;
> + int num_rx_qs = 1;
>
> of_id = of_match_device(fec_dt_ids, &pdev->dev);
> if (of_id)
> pdev->id_entry = of_id->data;
>
> + if (pdev->id_entry &&
> + (pdev->id_entry->driver_data & FEC_QUIRK_HAS_AVB)) {
> + ret = fec_enet_get_queue_num(pdev, &num_tx_qs, &num_rx_qs);
> + if (ret)
> + return ret;
> + }
This should be made a void function in order no to break non-DT probing
as mentioned before.
> +
> /* Init network device */
> - ndev = alloc_etherdev(sizeof(struct fec_enet_private));
> + ndev = alloc_etherdev_mqs(sizeof(struct fec_enet_private),
> + num_tx_qs, num_rx_qs);
> if (!ndev)
> return -ENOMEM;
>
> @@ -2598,6 +2641,10 @@ fec_probe(struct platform_device *pdev)
> /* setup board info structure */
> fep = netdev_priv(ndev);
>
> + fep->num_rx_queues = num_rx_qs;
> + fep->num_tx_queues = num_tx_qs;
> + netif_set_real_num_rx_queues(ndev, num_rx_qs);
How about netif_set_real_num_tx_queues? This is probably useless anyway
because you already allocated this network device with the correct
(supposedly) number of TX and RX queues coming from the platform
configuration data.
> +
> #if !defined(CONFIG_M5272)
> /* default enable pause frame auto negotiation */
> if (pdev->id_entry &&
>
^ permalink raw reply
* Re: [PATCH v5 1/4] ethernet: arc: Add support for Rockchip SoC layer device tree bindings
From: Beniamino Galvani @ 2014-09-03 21:12 UTC (permalink / raw)
To: Romain Perier
Cc: heiko, linux-rockchip, linux-arm-kernel, netdev, devicetree, arnd
In-Reply-To: <1409763165-16461-1-git-send-email-romain.perier@gmail.com>
On Wed, Sep 03, 2014 at 04:52:42PM +0000, Romain Perier wrote:
> This patch defines a platform glue layer for Rockchip SoCs which
> support arc-emac driver. It ensures that regulator for the rmii is on
> before trying to connect to the ethernet controller. It applies right
> speed and mode changes to the grf when ethernet settings change.
Hi Romain,
on a Radxa Rock when I try to remove the emac_rockchip module the
board locks up when calling clk_disable_unprepare(priv->refclk). The
tree is a net-next + your series, I don't know if I need some other
patches.
There is also the following build warning due to the emac dependency
on REGULATOR which in principle seems correct, but looking at other
drivers I wonder why they use the regulator APIs but don't have the
same dependency.
drivers/regulator/Kconfig:1:error: recursive dependency detected!
drivers/regulator/Kconfig:1: symbol REGULATOR is selected by MDIO_SUN4I
drivers/net/phy/Kconfig:159: symbol MDIO_SUN4I depends on PHYLIB
drivers/net/phy/Kconfig:5: symbol PHYLIB is selected by ARC_EMAC_CORE
drivers/net/ethernet/arc/Kconfig:20: symbol ARC_EMAC_CORE is selected by EMAC_ROCKCHIP
drivers/net/ethernet/arc/Kconfig:35: symbol EMAC_ROCKCHIP depends on REGULATOR
Regards,
Beniamino
^ permalink raw reply
* [Patch net-next 04/11] net:fec: add multiqueue support
From: y @ 2014-09-03 20:34 UTC (permalink / raw)
To: b38611, davem, netdev, lznuaa
Cc: shawn.guo, linux-arm-kernel, devicetree, Fugang Duan, Frank Li
In-Reply-To: <1409776486-1403-1-git-send-email-y>
From: Fugang Duan <B38611@freescale.com>
Add multiqueue support, which is compatible with previous single queue
driver for FEC/ENET IPs.
By default, the tx/rx queue number is 1, user can config the queue number
at DTS file like this:
fsl,num_tx_queues=<3>;
fsl,num_rx_queues=<3>;
Ethernet multiqueue mechanism can improve performance in SMP system.
For single hw queue, multiqueue can balance cpu loading.
For multi hw queues, multiple cores can process network packets in parallel,
and refer the article for the detail advantage for multiqueue:
http://vger.kernel.org/~davem/davem_nyc09.pdf
Signed-off-by: Fugang Duan <B38611@freescale.com>
Signed-off-by: Frank Li <frank.li@freescale.com>
---
drivers/net/ethernet/freescale/fec.h | 108 ++++-
drivers/net/ethernet/freescale/fec_main.c | 730 ++++++++++++++++++++----------
2 files changed, 577 insertions(+), 261 deletions(-)
diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h
index f77ed6f..984eaf2 100644
--- a/drivers/net/ethernet/freescale/fec.h
+++ b/drivers/net/ethernet/freescale/fec.h
@@ -27,8 +27,8 @@
*/
#define FEC_IEVENT 0x004 /* Interrupt event reg */
#define FEC_IMASK 0x008 /* Interrupt mask reg */
-#define FEC_R_DES_ACTIVE 0x010 /* Receive descriptor reg */
-#define FEC_X_DES_ACTIVE 0x014 /* Transmit descriptor reg */
+#define FEC_R_DES_ACTIVE_0 0x010 /* Receive descriptor reg */
+#define FEC_X_DES_ACTIVE_0 0x014 /* Transmit descriptor reg */
#define FEC_ECNTRL 0x024 /* Ethernet control reg */
#define FEC_MII_DATA 0x040 /* MII manage frame reg */
#define FEC_MII_SPEED 0x044 /* MII speed control reg */
@@ -45,14 +45,26 @@
#define FEC_X_WMRK 0x144 /* FIFO transmit water mark */
#define FEC_R_BOUND 0x14c /* FIFO receive bound reg */
#define FEC_R_FSTART 0x150 /* FIFO receive start reg */
-#define FEC_R_DES_START 0x180 /* Receive descriptor ring */
-#define FEC_X_DES_START 0x184 /* Transmit descriptor ring */
+#define FEC_R_DES_START_1 0x160 /* Receive descriptor ring 1 */
+#define FEC_X_DES_START_1 0x164 /* Transmit descriptor ring 1 */
+#define FEC_R_DES_START_2 0x16c /* Receive descriptor ring 2 */
+#define FEC_X_DES_START_2 0x170 /* Transmit descriptor ring 2 */
+#define FEC_R_DES_START_0 0x180 /* Receive descriptor ring */
+#define FEC_X_DES_START_0 0x184 /* Transmit descriptor ring */
#define FEC_R_BUFF_SIZE 0x188 /* Maximum receive buff size */
#define FEC_R_FIFO_RSFL 0x190 /* Receive FIFO section full threshold */
#define FEC_R_FIFO_RSEM 0x194 /* Receive FIFO section empty threshold */
#define FEC_R_FIFO_RAEM 0x198 /* Receive FIFO almost empty threshold */
#define FEC_R_FIFO_RAFL 0x19c /* Receive FIFO almost full threshold */
#define FEC_RACC 0x1C4 /* Receive Accelerator function */
+#define FEC_RCMR_1 0x1c8 /* Receive classification match ring 1 */
+#define FEC_RCMR_2 0x1cc /* Receive classification match ring 2 */
+#define FEC_DMA_CFG_1 0x1d8 /* DMA class configuration for ring 1 */
+#define FEC_DMA_CFG_2 0x1dc /* DMA class Configuration for ring 2 */
+#define FEC_R_DES_ACTIVE_1 0x1e0 /* Rx descriptor active for ring 1 */
+#define FEC_X_DES_ACTIVE_1 0x1e4 /* Tx descriptor active for ring 1 */
+#define FEC_R_DES_ACTIVE_2 0x1e8 /* Rx descriptor active for ring 2 */
+#define FEC_X_DES_ACTIVE_2 0x1ec /* Tx descriptor active for ring 2 */
#define FEC_MIIGSK_CFGR 0x300 /* MIIGSK Configuration reg */
#define FEC_MIIGSK_ENR 0x308 /* MIIGSK Enable reg */
@@ -240,6 +252,36 @@ struct bufdesc_ex {
#define FEC_ENET_MAX_TX_QS 3
#define FEC_ENET_MAX_RX_QS 3
+#define FEC_R_DES_START(X) ((X == 1) ? FEC_R_DES_START_1 : \
+ ((X == 2) ? \
+ FEC_R_DES_START_2 : FEC_R_DES_START_0))
+#define FEC_X_DES_START(X) ((X == 1) ? FEC_X_DES_START_1 : \
+ ((X == 2) ? \
+ FEC_X_DES_START_2 : FEC_X_DES_START_0))
+#define FEC_R_DES_ACTIVE(X) ((X == 1) ? FEC_R_DES_ACTIVE_1 : \
+ ((X == 2) ? \
+ FEC_R_DES_ACTIVE_2 : FEC_R_DES_ACTIVE_0))
+#define FEC_X_DES_ACTIVE(X) ((X == 1) ? FEC_X_DES_ACTIVE_1 : \
+ ((X == 2) ? \
+ FEC_X_DES_ACTIVE_2 : FEC_X_DES_ACTIVE_0))
+
+#define FEC_DMA_CFG(X) ((X == 2) ? FEC_DMA_CFG_2 : FEC_DMA_CFG_1)
+
+#define DMA_CLASS_EN (1 << 16)
+#define FEC_RCMR(X) ((X == 2) ? FEC_RCMR_2 : FEC_RCMR_1)
+#define IDLE_SLOPE_MASK 0xFFFF
+#define IDLE_SLOPE_1 0x200 /* BW fraction: 0.5 */
+#define IDLE_SLOPE_2 0x200 /* BW fraction: 0.5 */
+#define IDLE_SLOPE(X) ((X == 1) ? (IDLE_SLOPE_1 & IDLE_SLOPE_MASK) : \
+ (IDLE_SLOPE_2 & IDLE_SLOPE_MASK))
+#define RCMR_MATCHEN (0x1 << 16)
+#define RCMR_CMP_CFG(v, n) ((v & 0x7) << (n << 2))
+#define RCMR_CMP_1 (RCMR_CMP_CFG(0, 0) | RCMR_CMP_CFG(1, 1) | \
+ RCMR_CMP_CFG(2, 2) | RCMR_CMP_CFG(3, 3))
+#define RCMR_CMP_2 (RCMR_CMP_CFG(4, 0) | RCMR_CMP_CFG(5, 1) | \
+ RCMR_CMP_CFG(6, 2) | RCMR_CMP_CFG(7, 3))
+#define RCMR_CMP(X) ((X == 1) ? RCMR_CMP_1 : RCMR_CMP_2)
+
/* The number of Tx and Rx buffers. These are allocated from the page
* pool. The code may assume these are power of two, so it it best
* to keep them that size.
@@ -263,6 +305,35 @@ struct bufdesc_ex {
#define FLAG_RX_CSUM_ENABLED (BD_ENET_RX_ICE | BD_ENET_RX_PCR)
#define FLAG_RX_CSUM_ERROR (BD_ENET_RX_ICE | BD_ENET_RX_PCR)
+struct fec_enet_priv_tx_q {
+ int index;
+ unsigned char *tx_bounce[TX_RING_SIZE];
+ struct sk_buff *tx_skbuff[TX_RING_SIZE];
+
+ dma_addr_t bd_dma;
+ struct bufdesc *tx_bd_base;
+ uint tx_ring_size;
+
+ unsigned short tx_stop_threshold;
+ unsigned short tx_wake_threshold;
+
+ struct bufdesc *cur_tx;
+ struct bufdesc *dirty_tx;
+ char *tso_hdrs;
+ dma_addr_t tso_hdrs_dma;
+};
+
+struct fec_enet_priv_rx_q {
+ int index;
+ struct sk_buff *rx_skbuff[RX_RING_SIZE];
+
+ dma_addr_t bd_dma;
+ struct bufdesc *rx_bd_base;
+ uint rx_ring_size;
+
+ struct bufdesc *cur_rx;
+};
+
/* The FEC buffer descriptors track the ring buffers. The rx_bd_base and
* tx_bd_base always point to the base of the buffer descriptors. The
* cur_rx and cur_tx point to the currently available buffer.
@@ -289,29 +360,18 @@ struct fec_enet_private {
int num_rx_queues;
/* The saved address of a sent-in-place packet/buffer, for skfree(). */
- unsigned char *tx_bounce[TX_RING_SIZE];
- struct sk_buff *tx_skbuff[TX_RING_SIZE];
- struct sk_buff *rx_skbuff[RX_RING_SIZE];
+ struct fec_enet_priv_tx_q *tx_queue[FEC_ENET_MAX_TX_QS];
+ struct fec_enet_priv_rx_q *rx_queue[FEC_ENET_MAX_RX_QS];
- /* CPM dual port RAM relative addresses */
- dma_addr_t bd_dma;
- /* Address of Rx and Tx buffers */
- struct bufdesc *rx_bd_base;
- struct bufdesc *tx_bd_base;
- /* The next free ring entry */
- struct bufdesc *cur_rx, *cur_tx;
- /* The ring entries to be free()ed */
- struct bufdesc *dirty_tx;
+ unsigned int total_tx_ring_size;
+ unsigned int total_rx_ring_size;
- unsigned short bufdesc_size;
- unsigned short tx_ring_size;
- unsigned short rx_ring_size;
- unsigned short tx_stop_threshold;
- unsigned short tx_wake_threshold;
+ unsigned long work_tx;
+ unsigned long work_rx;
+ unsigned long work_ts;
+ unsigned long work_mdio;
- /* Software TSO */
- char *tso_hdrs;
- dma_addr_t tso_hdrs_dma;
+ unsigned short bufdesc_size;
struct platform_device *pdev;
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index 00fcadd..f973a92 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -72,6 +72,8 @@ static void set_multicast_list(struct net_device *ndev);
#define DRIVER_NAME "fec"
+#define FEC_ENET_GET_QUQUE(_x) ((_x == 0) ? 1 : ((_x == 1) ? 2 : 0))
+
/* Pause frame feild and FIFO threshold */
#define FEC_ENET_FCE (1 << 5)
#define FEC_ENET_RSEM_V 0x84
@@ -258,22 +260,26 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address");
static int mii_cnt;
static inline
-struct bufdesc *fec_enet_get_nextdesc(struct bufdesc *bdp, struct fec_enet_private *fep)
+struct bufdesc *fec_enet_get_nextdesc(struct bufdesc *bdp,
+ struct fec_enet_private *fep,
+ int queue_id)
{
struct bufdesc *new_bd = bdp + 1;
struct bufdesc_ex *ex_new_bd = (struct bufdesc_ex *)bdp + 1;
+ struct fec_enet_priv_tx_q *tx_queue = fep->tx_queue[queue_id];
+ struct fec_enet_priv_rx_q *rx_queue = fep->rx_queue[queue_id];
struct bufdesc_ex *ex_base;
struct bufdesc *base;
int ring_size;
- if (bdp >= fep->tx_bd_base) {
- base = fep->tx_bd_base;
- ring_size = fep->tx_ring_size;
- ex_base = (struct bufdesc_ex *)fep->tx_bd_base;
+ if (bdp >= tx_queue->tx_bd_base) {
+ base = tx_queue->tx_bd_base;
+ ring_size = tx_queue->tx_ring_size;
+ ex_base = (struct bufdesc_ex *)tx_queue->tx_bd_base;
} else {
- base = fep->rx_bd_base;
- ring_size = fep->rx_ring_size;
- ex_base = (struct bufdesc_ex *)fep->rx_bd_base;
+ base = rx_queue->rx_bd_base;
+ ring_size = rx_queue->rx_ring_size;
+ ex_base = (struct bufdesc_ex *)rx_queue->rx_bd_base;
}
if (fep->bufdesc_ex)
@@ -285,22 +291,26 @@ struct bufdesc *fec_enet_get_nextdesc(struct bufdesc *bdp, struct fec_enet_priva
}
static inline
-struct bufdesc *fec_enet_get_prevdesc(struct bufdesc *bdp, struct fec_enet_private *fep)
+struct bufdesc *fec_enet_get_prevdesc(struct bufdesc *bdp,
+ struct fec_enet_private *fep,
+ int queue_id)
{
struct bufdesc *new_bd = bdp - 1;
struct bufdesc_ex *ex_new_bd = (struct bufdesc_ex *)bdp - 1;
+ struct fec_enet_priv_tx_q *tx_queue = fep->tx_queue[queue_id];
+ struct fec_enet_priv_rx_q *rx_queue = fep->rx_queue[queue_id];
struct bufdesc_ex *ex_base;
struct bufdesc *base;
int ring_size;
- if (bdp >= fep->tx_bd_base) {
- base = fep->tx_bd_base;
- ring_size = fep->tx_ring_size;
- ex_base = (struct bufdesc_ex *)fep->tx_bd_base;
+ if (bdp >= tx_queue->tx_bd_base) {
+ base = tx_queue->tx_bd_base;
+ ring_size = tx_queue->tx_ring_size;
+ ex_base = (struct bufdesc_ex *)tx_queue->tx_bd_base;
} else {
- base = fep->rx_bd_base;
- ring_size = fep->rx_ring_size;
- ex_base = (struct bufdesc_ex *)fep->rx_bd_base;
+ base = rx_queue->rx_bd_base;
+ ring_size = rx_queue->rx_ring_size;
+ ex_base = (struct bufdesc_ex *)rx_queue->rx_bd_base;
}
if (fep->bufdesc_ex)
@@ -316,14 +326,15 @@ static int fec_enet_get_bd_index(struct bufdesc *base, struct bufdesc *bdp,
return ((const char *)bdp - (const char *)base) / fep->bufdesc_size;
}
-static int fec_enet_get_free_txdesc_num(struct fec_enet_private *fep)
+static int fec_enet_get_free_txdesc_num(struct fec_enet_private *fep,
+ struct fec_enet_priv_tx_q *txq)
{
int entries;
- entries = ((const char *)fep->dirty_tx -
- (const char *)fep->cur_tx) / fep->bufdesc_size - 1;
+ entries = ((const char *)txq->dirty_tx -
+ (const char *)txq->cur_tx) / fep->bufdesc_size - 1;
- return entries > 0 ? entries : entries + fep->tx_ring_size;
+ return entries > 0 ? entries : entries + txq->tx_ring_size;
}
static void *swap_buffer(void *bufaddr, int len)
@@ -340,22 +351,30 @@ static void *swap_buffer(void *bufaddr, int len)
static void fec_dump(struct net_device *ndev)
{
struct fec_enet_private *fep = netdev_priv(ndev);
- struct bufdesc *bdp = fep->tx_bd_base;
- unsigned int index = 0;
+ struct bufdesc *bdp;
+ struct fec_enet_priv_tx_q *txq;
+ int index = 0;
+ int i;
netdev_info(ndev, "TX ring dump\n");
pr_info("Nr SC addr len SKB\n");
- do {
- pr_info("%3u %c%c 0x%04x 0x%08lx %4u %p\n",
- index,
- bdp == fep->cur_tx ? 'S' : ' ',
- bdp == fep->dirty_tx ? 'H' : ' ',
- bdp->cbd_sc, bdp->cbd_bufaddr, bdp->cbd_datlen,
- fep->tx_skbuff[index]);
- bdp = fec_enet_get_nextdesc(bdp, fep);
- index++;
- } while (bdp != fep->tx_bd_base);
+ for (i = 0; i < fep->num_tx_queues; i++) {
+ txq = fep->tx_queue[i];
+ bdp = txq->tx_bd_base;
+ pr_info("Tx Queue %d\n", i);
+
+ do {
+ pr_info("%3u %c%c 0x%04x 0x%08lx %4u %p\n",
+ index,
+ bdp == txq->cur_tx ? 'S' : ' ',
+ bdp == txq->dirty_tx ? 'H' : ' ',
+ bdp->cbd_sc, bdp->cbd_bufaddr, bdp->cbd_datlen,
+ txq->tx_skbuff[index]);
+ bdp = fec_enet_get_nextdesc(bdp, fep, i);
+ index++;
+ } while (bdp != txq->tx_bd_base);
+ }
}
static inline bool is_ipv4_pkt(struct sk_buff *skb)
@@ -381,14 +400,17 @@ fec_enet_clear_csum(struct sk_buff *skb, struct net_device *ndev)
}
static int
-fec_enet_txq_submit_frag_skb(struct sk_buff *skb, struct net_device *ndev)
+fec_enet_txq_submit_frag_skb(struct fec_enet_priv_tx_q *txq,
+ struct sk_buff *skb,
+ struct net_device *ndev)
{
struct fec_enet_private *fep = netdev_priv(ndev);
const struct platform_device_id *id_entry =
platform_get_device_id(fep->pdev);
- struct bufdesc *bdp = fep->cur_tx;
+ struct bufdesc *bdp = txq->cur_tx;
struct bufdesc_ex *ebdp;
int nr_frags = skb_shinfo(skb)->nr_frags;
+ unsigned short queue = skb_get_queue_mapping(skb);
int frag, frag_len;
unsigned short status;
unsigned int estatus = 0;
@@ -400,7 +422,7 @@ fec_enet_txq_submit_frag_skb(struct sk_buff *skb, struct net_device *ndev)
for (frag = 0; frag < nr_frags; frag++) {
this_frag = &skb_shinfo(skb)->frags[frag];
- bdp = fec_enet_get_nextdesc(bdp, fep);
+ bdp = fec_enet_get_nextdesc(bdp, fep, queue);
ebdp = (struct bufdesc_ex *)bdp;
status = bdp->cbd_sc;
@@ -428,11 +450,11 @@ fec_enet_txq_submit_frag_skb(struct sk_buff *skb, struct net_device *ndev)
bufaddr = page_address(this_frag->page.p) + this_frag->page_offset;
- index = fec_enet_get_bd_index(fep->tx_bd_base, bdp, fep);
+ index = fec_enet_get_bd_index(txq->tx_bd_base, bdp, fep);
if (((unsigned long) bufaddr) & FEC_ALIGNMENT ||
id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) {
- memcpy(fep->tx_bounce[index], bufaddr, frag_len);
- bufaddr = fep->tx_bounce[index];
+ memcpy(txq->tx_bounce[index], bufaddr, frag_len);
+ bufaddr = txq->tx_bounce[index];
if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
swap_buffer(bufaddr, frag_len);
@@ -452,21 +474,22 @@ fec_enet_txq_submit_frag_skb(struct sk_buff *skb, struct net_device *ndev)
bdp->cbd_sc = status;
}
- fep->cur_tx = bdp;
+ txq->cur_tx = bdp;
return 0;
dma_mapping_error:
- bdp = fep->cur_tx;
+ bdp = txq->cur_tx;
for (i = 0; i < frag; i++) {
- bdp = fec_enet_get_nextdesc(bdp, fep);
+ bdp = fec_enet_get_nextdesc(bdp, fep, queue);
dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
bdp->cbd_datlen, DMA_TO_DEVICE);
}
return NETDEV_TX_OK;
}
-static int fec_enet_txq_submit_skb(struct sk_buff *skb, struct net_device *ndev)
+static int fec_enet_txq_submit_skb(struct fec_enet_priv_tx_q *txq,
+ struct sk_buff *skb, struct net_device *ndev)
{
struct fec_enet_private *fep = netdev_priv(ndev);
const struct platform_device_id *id_entry =
@@ -477,12 +500,13 @@ static int fec_enet_txq_submit_skb(struct sk_buff *skb, struct net_device *ndev)
dma_addr_t addr;
unsigned short status;
unsigned short buflen;
+ unsigned short queue;
unsigned int estatus = 0;
unsigned int index;
int entries_free;
int ret;
- entries_free = fec_enet_get_free_txdesc_num(fep);
+ entries_free = fec_enet_get_free_txdesc_num(fep, txq);
if (entries_free < MAX_SKB_FRAGS + 1) {
dev_kfree_skb_any(skb);
if (net_ratelimit())
@@ -497,7 +521,7 @@ static int fec_enet_txq_submit_skb(struct sk_buff *skb, struct net_device *ndev)
}
/* Fill in a Tx ring entry */
- bdp = fep->cur_tx;
+ bdp = txq->cur_tx;
status = bdp->cbd_sc;
status &= ~BD_ENET_TX_STATS;
@@ -505,11 +529,12 @@ static int fec_enet_txq_submit_skb(struct sk_buff *skb, struct net_device *ndev)
bufaddr = skb->data;
buflen = skb_headlen(skb);
- index = fec_enet_get_bd_index(fep->tx_bd_base, bdp, fep);
+ queue = skb_get_queue_mapping(skb);
+ index = fec_enet_get_bd_index(txq->tx_bd_base, bdp, fep);
if (((unsigned long) bufaddr) & FEC_ALIGNMENT ||
id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) {
- memcpy(fep->tx_bounce[index], skb->data, buflen);
- bufaddr = fep->tx_bounce[index];
+ memcpy(txq->tx_bounce[index], skb->data, buflen);
+ bufaddr = txq->tx_bounce[index];
if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
swap_buffer(bufaddr, buflen);
@@ -525,7 +550,7 @@ static int fec_enet_txq_submit_skb(struct sk_buff *skb, struct net_device *ndev)
}
if (nr_frags) {
- ret = fec_enet_txq_submit_frag_skb(skb, ndev);
+ ret = fec_enet_txq_submit_frag_skb(txq, skb, ndev);
if (ret)
return ret;
} else {
@@ -553,10 +578,10 @@ static int fec_enet_txq_submit_skb(struct sk_buff *skb, struct net_device *ndev)
ebdp->cbd_esc = estatus;
}
- last_bdp = fep->cur_tx;
- index = fec_enet_get_bd_index(fep->tx_bd_base, last_bdp, fep);
+ last_bdp = txq->cur_tx;
+ index = fec_enet_get_bd_index(txq->tx_bd_base, last_bdp, fep);
/* Save skb pointer */
- fep->tx_skbuff[index] = skb;
+ txq->tx_skbuff[index] = skb;
bdp->cbd_datlen = buflen;
bdp->cbd_bufaddr = addr;
@@ -568,22 +593,23 @@ static int fec_enet_txq_submit_skb(struct sk_buff *skb, struct net_device *ndev)
bdp->cbd_sc = status;
/* If this was the last BD in the ring, start at the beginning again. */
- bdp = fec_enet_get_nextdesc(last_bdp, fep);
+ bdp = fec_enet_get_nextdesc(last_bdp, fep, queue);
skb_tx_timestamp(skb);
- fep->cur_tx = bdp;
+ txq->cur_tx = bdp;
/* Trigger transmission start */
- writel(0, fep->hwp + FEC_X_DES_ACTIVE);
+ writel(0, fep->hwp + FEC_X_DES_ACTIVE(queue));
return 0;
}
static int
-fec_enet_txq_put_data_tso(struct sk_buff *skb, struct net_device *ndev,
- struct bufdesc *bdp, int index, char *data,
- int size, bool last_tcp, bool is_last)
+fec_enet_txq_put_data_tso(struct fec_enet_priv_tx_q *txq, struct sk_buff *skb,
+ struct net_device *ndev,
+ struct bufdesc *bdp, int index, char *data,
+ int size, bool last_tcp, bool is_last)
{
struct fec_enet_private *fep = netdev_priv(ndev);
const struct platform_device_id *id_entry =
@@ -600,8 +626,8 @@ fec_enet_txq_put_data_tso(struct sk_buff *skb, struct net_device *ndev,
if (((unsigned long) data) & FEC_ALIGNMENT ||
id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) {
- memcpy(fep->tx_bounce[index], data, size);
- data = fep->tx_bounce[index];
+ memcpy(txq->tx_bounce[index], data, size);
+ data = txq->tx_bounce[index];
if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
swap_buffer(data, size);
@@ -640,8 +666,9 @@ fec_enet_txq_put_data_tso(struct sk_buff *skb, struct net_device *ndev,
}
static int
-fec_enet_txq_put_hdr_tso(struct sk_buff *skb, struct net_device *ndev,
- struct bufdesc *bdp, int index)
+fec_enet_txq_put_hdr_tso(struct fec_enet_priv_tx_q *txq,
+ struct sk_buff *skb, struct net_device *ndev,
+ struct bufdesc *bdp, int index)
{
struct fec_enet_private *fep = netdev_priv(ndev);
const struct platform_device_id *id_entry =
@@ -657,12 +684,12 @@ fec_enet_txq_put_hdr_tso(struct sk_buff *skb, struct net_device *ndev,
status &= ~BD_ENET_TX_STATS;
status |= (BD_ENET_TX_TC | BD_ENET_TX_READY);
- bufaddr = fep->tso_hdrs + index * TSO_HEADER_SIZE;
- dmabuf = fep->tso_hdrs_dma + index * TSO_HEADER_SIZE;
+ bufaddr = txq->tso_hdrs + index * TSO_HEADER_SIZE;
+ dmabuf = txq->tso_hdrs_dma + index * TSO_HEADER_SIZE;
if (((unsigned long) bufaddr) & FEC_ALIGNMENT ||
id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) {
- memcpy(fep->tx_bounce[index], skb->data, hdr_len);
- bufaddr = fep->tx_bounce[index];
+ memcpy(txq->tx_bounce[index], skb->data, hdr_len);
+ bufaddr = txq->tx_bounce[index];
if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
swap_buffer(bufaddr, hdr_len);
@@ -692,17 +719,20 @@ fec_enet_txq_put_hdr_tso(struct sk_buff *skb, struct net_device *ndev,
return 0;
}
-static int fec_enet_txq_submit_tso(struct sk_buff *skb, struct net_device *ndev)
+static int fec_enet_txq_submit_tso(struct fec_enet_priv_tx_q *txq,
+ struct sk_buff *skb,
+ struct net_device *ndev)
{
struct fec_enet_private *fep = netdev_priv(ndev);
int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
int total_len, data_left;
- struct bufdesc *bdp = fep->cur_tx;
+ struct bufdesc *bdp = txq->cur_tx;
+ unsigned short queue = skb_get_queue_mapping(skb);
struct tso_t tso;
unsigned int index = 0;
int ret;
- if (tso_count_descs(skb) >= fec_enet_get_free_txdesc_num(fep)) {
+ if (tso_count_descs(skb) >= fec_enet_get_free_txdesc_num(fep, txq)) {
dev_kfree_skb_any(skb);
if (net_ratelimit())
netdev_err(ndev, "NOT enough BD for TSO!\n");
@@ -722,14 +752,14 @@ static int fec_enet_txq_submit_tso(struct sk_buff *skb, struct net_device *ndev)
while (total_len > 0) {
char *hdr;
- index = fec_enet_get_bd_index(fep->tx_bd_base, bdp, fep);
+ index = fec_enet_get_bd_index(txq->tx_bd_base, bdp, fep);
data_left = min_t(int, skb_shinfo(skb)->gso_size, total_len);
total_len -= data_left;
/* prepare packet headers: MAC + IP + TCP */
- hdr = fep->tso_hdrs + index * TSO_HEADER_SIZE;
+ hdr = txq->tso_hdrs + index * TSO_HEADER_SIZE;
tso_build_hdr(skb, hdr, &tso, data_left, total_len == 0);
- ret = fec_enet_txq_put_hdr_tso(skb, ndev, bdp, index);
+ ret = fec_enet_txq_put_hdr_tso(txq, skb, ndev, bdp, index);
if (ret)
goto err_release;
@@ -737,10 +767,13 @@ static int fec_enet_txq_submit_tso(struct sk_buff *skb, struct net_device *ndev)
int size;
size = min_t(int, tso.size, data_left);
- bdp = fec_enet_get_nextdesc(bdp, fep);
- index = fec_enet_get_bd_index(fep->tx_bd_base, bdp, fep);
- ret = fec_enet_txq_put_data_tso(skb, ndev, bdp, index, tso.data,
- size, size == data_left,
+ bdp = fec_enet_get_nextdesc(bdp, fep, queue);
+ index = fec_enet_get_bd_index(txq->tx_bd_base,
+ bdp, fep);
+ ret = fec_enet_txq_put_data_tso(txq, skb, ndev,
+ bdp, index,
+ tso.data, size,
+ size == data_left,
total_len == 0);
if (ret)
goto err_release;
@@ -749,17 +782,17 @@ static int fec_enet_txq_submit_tso(struct sk_buff *skb, struct net_device *ndev)
tso_build_data(skb, &tso, size);
}
- bdp = fec_enet_get_nextdesc(bdp, fep);
+ bdp = fec_enet_get_nextdesc(bdp, fep, queue);
}
/* Save skb pointer */
- fep->tx_skbuff[index] = skb;
+ txq->tx_skbuff[index] = skb;
skb_tx_timestamp(skb);
- fep->cur_tx = bdp;
+ txq->cur_tx = bdp;
/* Trigger transmission start */
- writel(0, fep->hwp + FEC_X_DES_ACTIVE);
+ writel(0, fep->hwp + FEC_X_DES_ACTIVE(queue));
return 0;
@@ -773,18 +806,25 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{
struct fec_enet_private *fep = netdev_priv(ndev);
int entries_free;
+ unsigned short queue;
+ struct fec_enet_priv_tx_q *txq;
+ struct netdev_queue *nq;
int ret;
+ queue = skb_get_queue_mapping(skb);
+ txq = fep->tx_queue[queue];
+ nq = netdev_get_tx_queue(ndev, queue);
+
if (skb_is_gso(skb))
- ret = fec_enet_txq_submit_tso(skb, ndev);
+ ret = fec_enet_txq_submit_tso(txq, skb, ndev);
else
- ret = fec_enet_txq_submit_skb(skb, ndev);
+ ret = fec_enet_txq_submit_skb(txq, skb, ndev);
if (ret)
return ret;
- entries_free = fec_enet_get_free_txdesc_num(fep);
- if (entries_free <= fep->tx_stop_threshold)
- netif_stop_queue(ndev);
+ entries_free = fec_enet_get_free_txdesc_num(fep, txq);
+ if (entries_free <= txq->tx_stop_threshold)
+ netif_tx_stop_queue(nq);
return NETDEV_TX_OK;
}
@@ -794,46 +834,101 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
static void fec_enet_bd_init(struct net_device *dev)
{
struct fec_enet_private *fep = netdev_priv(dev);
+ struct fec_enet_priv_tx_q *tx_queue;
+ struct fec_enet_priv_rx_q *rx_queue;
struct bufdesc *bdp;
- unsigned int i;
+ unsigned int i, j;
/* Initialize the receive buffer descriptors. */
- bdp = fep->rx_bd_base;
- for (i = 0; i < fep->rx_ring_size; i++) {
+ for (i = 0; i < fep->num_rx_queues; i++) {
+ rx_queue = fep->rx_queue[i];
+ bdp = rx_queue->rx_bd_base;
- /* Initialize the BD for every fragment in the page. */
- if (bdp->cbd_bufaddr)
- bdp->cbd_sc = BD_ENET_RX_EMPTY;
- else
- bdp->cbd_sc = 0;
- bdp = fec_enet_get_nextdesc(bdp, fep);
- }
+ for (j = 0; j < rx_queue->rx_ring_size; j++) {
- /* Set the last buffer to wrap */
- bdp = fec_enet_get_prevdesc(bdp, fep);
- bdp->cbd_sc |= BD_SC_WRAP;
+ /* Initialize the BD for every fragment in the page. */
+ if (bdp->cbd_bufaddr)
+ bdp->cbd_sc = BD_ENET_RX_EMPTY;
+ else
+ bdp->cbd_sc = 0;
+ bdp = fec_enet_get_nextdesc(bdp, fep, i);
+ }
- fep->cur_rx = fep->rx_bd_base;
+ /* Set the last buffer to wrap */
+ bdp = fec_enet_get_prevdesc(bdp, fep, i);
+ bdp->cbd_sc |= BD_SC_WRAP;
+
+ rx_queue->cur_rx = rx_queue->rx_bd_base;
+ }
/* ...and the same for transmit */
- bdp = fep->tx_bd_base;
- fep->cur_tx = bdp;
- for (i = 0; i < fep->tx_ring_size; i++) {
-
- /* Initialize the BD for every fragment in the page. */
- bdp->cbd_sc = 0;
- if (fep->tx_skbuff[i]) {
- dev_kfree_skb_any(fep->tx_skbuff[i]);
- fep->tx_skbuff[i] = NULL;
+ for (i = 0; i < fep->num_tx_queues; i++) {
+ tx_queue = fep->tx_queue[i];
+ bdp = tx_queue->tx_bd_base;
+ tx_queue->cur_tx = bdp;
+
+ for (j = 0; j < tx_queue->tx_ring_size; j++) {
+ /* Initialize the BD for every fragment in the page. */
+ bdp->cbd_sc = 0;
+ if (tx_queue->tx_skbuff[j]) {
+ dev_kfree_skb_any(tx_queue->tx_skbuff[j]);
+ tx_queue->tx_skbuff[j] = NULL;
+ }
+ bdp->cbd_bufaddr = 0;
+ bdp = fec_enet_get_nextdesc(bdp, fep, i);
}
- bdp->cbd_bufaddr = 0;
- bdp = fec_enet_get_nextdesc(bdp, fep);
+
+ /* Set the last buffer to wrap */
+ bdp = fec_enet_get_prevdesc(bdp, fep, i);
+ bdp->cbd_sc |= BD_SC_WRAP;
+ tx_queue->dirty_tx = bdp;
+ }
+}
+
+static inline void fec_enet_enable_ring(struct net_device *ndev)
+{
+ struct fec_enet_private *fep = netdev_priv(ndev);
+ struct fec_enet_priv_tx_q *tx_queue;
+ struct fec_enet_priv_rx_q *rx_queue;
+ int i;
+
+ for (i = 0; i < fep->num_rx_queues; i++) {
+ rx_queue = fep->rx_queue[i];
+ writel(rx_queue->bd_dma, fep->hwp + FEC_R_DES_START(i));
+
+ /* enable DMA1/2 */
+ if (i)
+ writel(RCMR_MATCHEN | RCMR_CMP(i),
+ fep->hwp + FEC_RCMR(i));
}
- /* Set the last buffer to wrap */
- bdp = fec_enet_get_prevdesc(bdp, fep);
- bdp->cbd_sc |= BD_SC_WRAP;
- fep->dirty_tx = bdp;
+ for (i = 0; i < fep->num_tx_queues; i++) {
+ tx_queue = fep->tx_queue[i];
+ writel(tx_queue->bd_dma, fep->hwp + FEC_X_DES_START(i));
+
+ /* enable DMA1/2 */
+ if (i)
+ writel(DMA_CLASS_EN | IDLE_SLOPE(i),
+ fep->hwp + FEC_DMA_CFG(i));
+ }
+}
+
+static void fec_enet_reset_skb(struct net_device *ndev)
+{
+ struct fec_enet_private *fep = netdev_priv(ndev);
+ struct fec_enet_priv_tx_q *tx_queue;
+ int i, j;
+
+ for (i = 0; i < fep->num_tx_queues; i++) {
+ tx_queue = fep->tx_queue[i];
+
+ for (j = 0; j < tx_queue->tx_ring_size; j++) {
+ if (tx_queue->tx_skbuff[j]) {
+ dev_kfree_skb_any(tx_queue->tx_skbuff[j]);
+ tx_queue->tx_skbuff[j] = NULL;
+ }
+ }
+ }
}
/*
@@ -847,11 +942,11 @@ fec_restart(struct net_device *ndev)
struct fec_enet_private *fep = netdev_priv(ndev);
const struct platform_device_id *id_entry =
platform_get_device_id(fep->pdev);
- int i;
u32 val;
u32 temp_mac[2];
u32 rcntl = OPT_FRAME_SIZE | 0x04;
u32 ecntl = 0x2; /* ETHEREN */
+ int i;
/* Whack a reset. We should wait for this. */
writel(1, fep->hwp + FEC_ECNTRL);
@@ -876,21 +971,10 @@ fec_restart(struct net_device *ndev)
fec_enet_bd_init(ndev);
/* Set receive and transmit descriptor base. */
- writel(fep->bd_dma, fep->hwp + FEC_R_DES_START);
- if (fep->bufdesc_ex)
- writel((unsigned long)fep->bd_dma + sizeof(struct bufdesc_ex)
- * fep->rx_ring_size, fep->hwp + FEC_X_DES_START);
- else
- writel((unsigned long)fep->bd_dma + sizeof(struct bufdesc)
- * fep->rx_ring_size, fep->hwp + FEC_X_DES_START);
-
+ fec_enet_enable_ring(ndev);
- for (i = 0; i <= TX_RING_MOD_MASK; i++) {
- if (fep->tx_skbuff[i]) {
- dev_kfree_skb_any(fep->tx_skbuff[i]);
- fep->tx_skbuff[i] = NULL;
- }
- }
+ /* Reset tx SKB buffers. */
+ fec_enet_reset_skb(ndev);
/* Enable MII mode */
if (fep->full_duplex == DUPLEX_FULL) {
@@ -1012,7 +1096,8 @@ fec_restart(struct net_device *ndev)
/* And last, enable the transmit and receive processing */
writel(ecntl, fep->hwp + FEC_ECNTRL);
- writel(0, fep->hwp + FEC_R_DES_ACTIVE);
+ for (i = 0; i < fep->num_rx_queues; i++)
+ writel(0, fep->hwp + FEC_R_DES_ACTIVE(i));
if (fep->bufdesc_ex)
fec_ptp_start_cyclecounter(ndev);
@@ -1097,37 +1182,45 @@ fec_enet_hwtstamp(struct fec_enet_private *fep, unsigned ts,
}
static void
-fec_enet_tx(struct net_device *ndev)
+fec_enet_tx_queue(struct net_device *ndev, u16 queue_id)
{
struct fec_enet_private *fep;
struct bufdesc *bdp;
unsigned short status;
struct sk_buff *skb;
+ struct fec_enet_priv_tx_q *txq;
+ struct netdev_queue *nq;
int index = 0;
int entries_free;
fep = netdev_priv(ndev);
- bdp = fep->dirty_tx;
+ queue_id = FEC_ENET_GET_QUQUE(queue_id);
+
+ txq = fep->tx_queue[queue_id];
/* get next bdp of dirty_tx */
- bdp = fec_enet_get_nextdesc(bdp, fep);
+ nq = netdev_get_tx_queue(ndev, queue_id);
+ bdp = txq->dirty_tx;
+
+ /* get next bdp of dirty_tx */
+ bdp = fec_enet_get_nextdesc(bdp, fep, queue_id);
while (((status = bdp->cbd_sc) & BD_ENET_TX_READY) == 0) {
/* current queue is empty */
- if (bdp == fep->cur_tx)
+ if (bdp == txq->cur_tx)
break;
- index = fec_enet_get_bd_index(fep->tx_bd_base, bdp, fep);
+ index = fec_enet_get_bd_index(txq->tx_bd_base, bdp, fep);
- skb = fep->tx_skbuff[index];
- fep->tx_skbuff[index] = NULL;
- if (!IS_TSO_HEADER(fep, bdp->cbd_bufaddr))
+ skb = txq->tx_skbuff[index];
+ txq->tx_skbuff[index] = NULL;
+ if (!IS_TSO_HEADER(txq, bdp->cbd_bufaddr))
dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
bdp->cbd_datlen, DMA_TO_DEVICE);
bdp->cbd_bufaddr = 0;
if (!skb) {
- bdp = fec_enet_get_nextdesc(bdp, fep);
+ bdp = fec_enet_get_nextdesc(bdp, fep, queue_id);
continue;
}
@@ -1169,23 +1262,37 @@ fec_enet_tx(struct net_device *ndev)
/* Free the sk buffer associated with this last transmit */
dev_kfree_skb_any(skb);
- fep->dirty_tx = bdp;
+ txq->dirty_tx = bdp;
/* Update pointer to next buffer descriptor to be transmitted */
- bdp = fec_enet_get_nextdesc(bdp, fep);
+ bdp = fec_enet_get_nextdesc(bdp, fep, queue_id);
/* Since we have freed up a buffer, the ring is no longer full
*/
if (netif_queue_stopped(ndev)) {
- entries_free = fec_enet_get_free_txdesc_num(fep);
- if (entries_free >= fep->tx_wake_threshold)
- netif_wake_queue(ndev);
+ entries_free = fec_enet_get_free_txdesc_num(fep, txq);
+ if (entries_free >= txq->tx_wake_threshold)
+ netif_tx_wake_queue(nq);
}
}
/* ERR006538: Keep the transmitter going */
- if (bdp != fep->cur_tx && readl(fep->hwp + FEC_X_DES_ACTIVE) == 0)
- writel(0, fep->hwp + FEC_X_DES_ACTIVE);
+ if (bdp != txq->cur_tx &&
+ readl(fep->hwp + FEC_X_DES_ACTIVE(queue_id)) == 0)
+ writel(0, fep->hwp + FEC_X_DES_ACTIVE(queue_id));
+}
+
+static void
+fec_enet_tx(struct net_device *ndev)
+{
+ struct fec_enet_private *fep = netdev_priv(ndev);
+ u16 queue_id;
+ /* First process class A queue, then Class B and Best Effort queue */
+ for_each_set_bit(queue_id, &fep->work_tx, FEC_ENET_MAX_TX_QS) {
+ clear_bit(queue_id, &fep->work_tx);
+ fec_enet_tx_queue(ndev, queue_id);
+ }
+ return;
}
/* During a receive, the cur_rx points to the current incoming buffer.
@@ -1194,11 +1301,12 @@ fec_enet_tx(struct net_device *ndev)
* effectively tossing the packet.
*/
static int
-fec_enet_rx(struct net_device *ndev, int budget)
+fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id)
{
struct fec_enet_private *fep = netdev_priv(ndev);
const struct platform_device_id *id_entry =
platform_get_device_id(fep->pdev);
+ struct fec_enet_priv_rx_q *rxq;
struct bufdesc *bdp;
unsigned short status;
struct sk_buff *skb;
@@ -1213,11 +1321,13 @@ fec_enet_rx(struct net_device *ndev, int budget)
#ifdef CONFIG_M532x
flush_cache_all();
#endif
+ queue_id = FEC_ENET_GET_QUQUE(queue_id);
+ rxq = fep->rx_queue[queue_id];
/* First, grab all of the stats for the incoming packet.
* These get messed up if we get called due to a busy condition.
*/
- bdp = fep->cur_rx;
+ bdp = rxq->cur_rx;
while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) {
@@ -1231,7 +1341,6 @@ fec_enet_rx(struct net_device *ndev, int budget)
if ((status & BD_ENET_RX_LAST) == 0)
netdev_err(ndev, "rcv is not +last\n");
- writel(FEC_ENET_RXF, fep->hwp + FEC_IEVENT);
/* Check for errors. */
if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO |
@@ -1264,8 +1373,8 @@ fec_enet_rx(struct net_device *ndev, int budget)
pkt_len = bdp->cbd_datlen;
ndev->stats.rx_bytes += pkt_len;
- index = fec_enet_get_bd_index(fep->rx_bd_base, bdp, fep);
- data = fep->rx_skbuff[index]->data;
+ index = fec_enet_get_bd_index(rxq->rx_bd_base, bdp, fep);
+ data = rxq->rx_skbuff[index]->data;
dma_sync_single_for_cpu(&fep->pdev->dev, bdp->cbd_bufaddr,
FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
@@ -1280,7 +1389,7 @@ fec_enet_rx(struct net_device *ndev, int budget)
/* If this is a VLAN packet remove the VLAN Tag */
vlan_packet_rcvd = false;
if ((ndev->features & NETIF_F_HW_VLAN_CTAG_RX) &&
- fep->bufdesc_ex && (ebdp->cbd_esc & BD_ENET_RX_VLAN)) {
+ fep->bufdesc_ex && (ebdp->cbd_esc & BD_ENET_RX_VLAN)) {
/* Push and remove the vlan tag */
struct vlan_hdr *vlan_header =
(struct vlan_hdr *) (data + ETH_HLEN);
@@ -1308,7 +1417,7 @@ fec_enet_rx(struct net_device *ndev, int budget)
skb_copy_to_linear_data(skb, data, (2 * ETH_ALEN));
if (vlan_packet_rcvd)
payload_offset = (2 * ETH_ALEN) + VLAN_HLEN;
- skb_copy_to_linear_data_offset(skb, (2 * ETH_ALEN),
+ skb_copy_to_linear_data_offset(skb, (2 * ETH_ALEN),
data + payload_offset,
pkt_len - 4 - (2 * ETH_ALEN));
@@ -1357,19 +1466,48 @@ rx_processing_done:
}
/* Update BD pointer to next entry */
- bdp = fec_enet_get_nextdesc(bdp, fep);
+ bdp = fec_enet_get_nextdesc(bdp, fep, queue_id);
/* Doing this here will keep the FEC running while we process
* incoming frames. On a heavily loaded network, we should be
* able to keep up at the expense of system resources.
*/
- writel(0, fep->hwp + FEC_R_DES_ACTIVE);
+ writel(0, fep->hwp + FEC_R_DES_ACTIVE(queue_id));
}
- fep->cur_rx = bdp;
+ rxq->cur_rx = bdp;
+ return pkt_received;
+}
+static int
+fec_enet_rx(struct net_device *ndev, int budget)
+{
+ int pkt_received = 0;
+ u16 queue_id;
+ struct fec_enet_private *fep = netdev_priv(ndev);
+
+ for_each_set_bit(queue_id, &fep->work_rx, FEC_ENET_MAX_RX_QS) {
+ clear_bit(queue_id, &fep->work_rx);
+ pkt_received += fec_enet_rx_queue(ndev,
+ budget - pkt_received, queue_id);
+ }
return pkt_received;
}
+static bool
+fec_enet_collect_events(struct fec_enet_private *fep, uint int_events)
+{
+ if (int_events == 0)
+ return false;
+
+ if (int_events & FEC_ENET_RXF)
+ fep->work_rx |= (1 << 2);
+
+ if (int_events & FEC_ENET_TXF)
+ fep->work_tx |= (1 << 2);
+
+ return true;
+}
+
static irqreturn_t
fec_enet_interrupt(int irq, void *dev_id)
{
@@ -1381,6 +1519,7 @@ fec_enet_interrupt(int irq, void *dev_id)
int_events = readl(fep->hwp + FEC_IEVENT);
writel(int_events & ~napi_mask, fep->hwp + FEC_IEVENT);
+ fec_enet_collect_events(fep, int_events);
if (int_events & napi_mask) {
ret = IRQ_HANDLED;
@@ -2129,93 +2268,117 @@ static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
static void fec_enet_free_buffers(struct net_device *ndev)
{
struct fec_enet_private *fep = netdev_priv(ndev);
- unsigned int i;
+ struct fec_enet_priv_tx_q *tx_queue;
+ struct fec_enet_priv_rx_q *rx_queue;
+ unsigned int i, j;
struct sk_buff *skb;
struct bufdesc *bdp;
- bdp = fep->rx_bd_base;
- for (i = 0; i < fep->rx_ring_size; i++) {
- skb = fep->rx_skbuff[i];
- fep->rx_skbuff[i] = NULL;
- if (skb) {
- dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
- FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
- dev_kfree_skb(skb);
+ for (j = 0; j < fep->num_rx_queues; j++) {
+ rx_queue = fep->rx_queue[j];
+ bdp = rx_queue->rx_bd_base;
+ for (i = 0; i < rx_queue->rx_ring_size; i++) {
+ skb = rx_queue->rx_skbuff[i];
+ rx_queue->rx_skbuff[i] = NULL;
+ if (skb) {
+ dma_unmap_single(&fep->pdev->dev,
+ bdp->cbd_bufaddr,
+ FEC_ENET_RX_FRSIZE,
+ DMA_FROM_DEVICE);
+ dev_kfree_skb(skb);
+ }
+ bdp = fec_enet_get_nextdesc(bdp, fep, j);
}
- bdp = fec_enet_get_nextdesc(bdp, fep);
}
- bdp = fep->tx_bd_base;
- for (i = 0; i < fep->tx_ring_size; i++) {
- kfree(fep->tx_bounce[i]);
- fep->tx_bounce[i] = NULL;
- skb = fep->tx_skbuff[i];
- fep->tx_skbuff[i] = NULL;
- dev_kfree_skb(skb);
+ for (j = 0; j < fep->num_tx_queues; j++) {
+ tx_queue = fep->tx_queue[j];
+ bdp = tx_queue->tx_bd_base;
+ for (i = 0; i < tx_queue->tx_ring_size; i++) {
+ kfree(tx_queue->tx_bounce[i]);
+ tx_queue->tx_bounce[i] = NULL;
+ skb = tx_queue->tx_skbuff[i];
+ tx_queue->tx_skbuff[i] = NULL;
+ dev_kfree_skb(skb);
+ }
}
}
static int fec_enet_alloc_buffers(struct net_device *ndev)
{
struct fec_enet_private *fep = netdev_priv(ndev);
- unsigned int i;
+ struct fec_enet_priv_tx_q *tx_queue;
+ struct fec_enet_priv_rx_q *rx_queue;
+ unsigned int i, j;
struct sk_buff *skb;
struct bufdesc *bdp;
- bdp = fep->rx_bd_base;
- for (i = 0; i < fep->rx_ring_size; i++) {
- dma_addr_t addr;
+ for (j = 0; j < fep->num_rx_queues; j++) {
+ rx_queue = fep->rx_queue[j];
+ bdp = rx_queue->rx_bd_base;
+ for (i = 0; i < rx_queue->rx_ring_size; i++) {
+ dma_addr_t addr;
+
+ skb = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE);
+ if (!skb)
+ goto err_alloc;
+
+ addr = dma_map_single(&fep->pdev->dev, skb->data,
+ FEC_ENET_RX_FRSIZE,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(&fep->pdev->dev, addr)) {
+ dev_kfree_skb(skb);
+ if (net_ratelimit())
+ netdev_err(ndev, "Rx DMA memory map failed\n");
+ goto err_alloc;
+ }
- skb = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE);
- if (!skb)
- goto err_alloc;
+ rx_queue->rx_skbuff[i] = skb;
+ bdp->cbd_bufaddr = addr;
+ bdp->cbd_sc = BD_ENET_RX_EMPTY;
- addr = dma_map_single(&fep->pdev->dev, skb->data,
- FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
- if (dma_mapping_error(&fep->pdev->dev, addr)) {
- dev_kfree_skb(skb);
- if (net_ratelimit())
- netdev_err(ndev, "Rx DMA memory map failed\n");
- goto err_alloc;
- }
+ if (fep->bufdesc_ex) {
+ struct bufdesc_ex *ebdp;
- fep->rx_skbuff[i] = skb;
- bdp->cbd_bufaddr = addr;
- bdp->cbd_sc = BD_ENET_RX_EMPTY;
+ ebdp = (struct bufdesc_ex *)bdp;
+ ebdp->cbd_esc = BD_ENET_RX_INT;
+ }
- if (fep->bufdesc_ex) {
- struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
- ebdp->cbd_esc = BD_ENET_RX_INT;
+ bdp = fec_enet_get_nextdesc(bdp, fep, j);
}
- bdp = fec_enet_get_nextdesc(bdp, fep);
+ /* Set the last buffer to wrap. */
+ bdp = fec_enet_get_prevdesc(bdp, fep, j);
+ bdp->cbd_sc |= BD_SC_WRAP;
}
- /* Set the last buffer to wrap. */
- bdp = fec_enet_get_prevdesc(bdp, fep);
- bdp->cbd_sc |= BD_SC_WRAP;
+ for (j = 0; j < fep->num_tx_queues; j++) {
+ tx_queue = fep->tx_queue[j];
+ bdp = tx_queue->tx_bd_base;
- bdp = fep->tx_bd_base;
- for (i = 0; i < fep->tx_ring_size; i++) {
- fep->tx_bounce[i] = kmalloc(FEC_ENET_TX_FRSIZE, GFP_KERNEL);
- if (!fep->tx_bounce[i])
- goto err_alloc;
+ for (i = 0; i < tx_queue->tx_ring_size; i++) {
+ tx_queue->tx_bounce[i] = kmalloc(FEC_ENET_TX_FRSIZE,
+ GFP_KERNEL);
+ if (!tx_queue->tx_bounce[i])
+ goto err_alloc;
- bdp->cbd_sc = 0;
- bdp->cbd_bufaddr = 0;
+ bdp->cbd_sc = 0;
+ bdp->cbd_bufaddr = 0;
- if (fep->bufdesc_ex) {
- struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
- ebdp->cbd_esc = BD_ENET_TX_INT;
- }
+ if (fep->bufdesc_ex) {
+ struct bufdesc_ex *ebdp;
- bdp = fec_enet_get_nextdesc(bdp, fep);
- }
+ ebdp = (struct bufdesc_ex *)bdp;
+ ebdp->cbd_esc = BD_ENET_TX_INT;
+ }
- /* Set the last buffer to wrap. */
- bdp = fec_enet_get_prevdesc(bdp, fep);
- bdp->cbd_sc |= BD_SC_WRAP;
+ bdp = fec_enet_get_nextdesc(bdp, fep, j);
+ }
+ /* Set the last buffer to wrap. */
+ bdp = fec_enet_get_prevdesc(bdp, fep, j);
+ bdp->cbd_sc |= BD_SC_WRAP;
+ }
return 0;
err_alloc:
@@ -2252,7 +2415,8 @@ fec_enet_open(struct net_device *ndev)
fec_restart(ndev);
napi_enable(&fep->napi);
phy_start(fep->phy_dev);
- netif_start_queue(ndev);
+ netif_tx_start_all_queues(ndev);
+
return 0;
}
@@ -2426,7 +2590,7 @@ static int fec_set_features(struct net_device *netdev,
/* Resume the device after updates */
if (netif_running(netdev) && changed & FEATURES_NEED_QUIESCE) {
fec_restart(netdev);
- netif_wake_queue(netdev);
+ netif_tx_wake_all_queues(netdev);
netif_tx_unlock_bh(netdev);
napi_enable(&fep->napi);
}
@@ -2434,10 +2598,17 @@ static int fec_set_features(struct net_device *netdev,
return 0;
}
+u16 fec_enet_select_queue(struct net_device *ndev, struct sk_buff *skb,
+ void *accel_priv, select_queue_fallback_t fallback)
+{
+ return skb_tx_hash(ndev, skb);
+}
+
static const struct net_device_ops fec_netdev_ops = {
.ndo_open = fec_enet_open,
.ndo_stop = fec_enet_close,
.ndo_start_xmit = fec_enet_start_xmit,
+ .ndo_select_queue = fec_enet_select_queue,
.ndo_set_rx_mode = set_multicast_list,
.ndo_change_mtu = eth_change_mtu,
.ndo_validate_addr = eth_validate_addr,
@@ -2450,6 +2621,76 @@ static const struct net_device_ops fec_netdev_ops = {
.ndo_set_features = fec_set_features,
};
+static void fec_enet_free_queue(struct net_device *ndev)
+{
+ struct fec_enet_private *fep = netdev_priv(ndev);
+ int i;
+
+ for (i = 0; i < fep->num_tx_queues; i++)
+ if (fep->tx_queue[i] && fep->tx_queue[i]->tso_hdrs)
+ dma_free_coherent(NULL,
+ fep->tx_queue[i]->tx_ring_size * TSO_HEADER_SIZE,
+ fep->tx_queue[i]->tso_hdrs,
+ fep->tx_queue[i]->tso_hdrs_dma);
+
+ for (i = 0; i < fep->num_rx_queues; i++)
+ if (fep->rx_queue[i])
+ kfree(fep->rx_queue[i]);
+
+ for (i = 0; i < fep->num_tx_queues; i++)
+ if (fep->tx_queue[i])
+ kfree(fep->tx_queue[i]);
+}
+
+static int fec_enet_alloc_queue(struct net_device *ndev)
+{
+ struct fec_enet_private *fep = netdev_priv(ndev);
+ int i;
+ int ret = 0;
+ struct fec_enet_priv_tx_q *txq;
+
+ for (i = 0; i < fep->num_tx_queues; i++) {
+ txq = kzalloc(sizeof(*txq), GFP_KERNEL);
+ if (!txq) {
+ ret = -ENOMEM;
+ goto alloc_failed;
+ }
+
+ fep->tx_queue[i] = txq;
+ txq->tx_ring_size = TX_RING_SIZE;
+ fep->total_tx_ring_size += fep->tx_queue[i]->tx_ring_size;
+
+ txq->tx_stop_threshold = FEC_MAX_SKB_DESCS;
+ txq->tx_wake_threshold =
+ (txq->tx_ring_size - txq->tx_stop_threshold) / 2;
+
+ txq->tso_hdrs = dma_alloc_coherent(NULL,
+ txq->tx_ring_size * TSO_HEADER_SIZE,
+ &txq->tso_hdrs_dma,
+ GFP_KERNEL);
+ if (!txq->tso_hdrs) {
+ ret = -ENOMEM;
+ goto alloc_failed;
+ }
+ }
+
+ for (i = 0; i < fep->num_rx_queues; i++) {
+ fep->rx_queue[i] = kzalloc(sizeof(*fep->rx_queue[i]),
+ GFP_KERNEL);
+ if (!fep->rx_queue[i]) {
+ ret = -ENOMEM;
+ goto alloc_failed;
+ }
+
+ fep->rx_queue[i]->rx_ring_size = RX_RING_SIZE;
+ fep->total_rx_ring_size += fep->rx_queue[i]->rx_ring_size;
+ }
+ return ret;
+
+alloc_failed:
+ fec_enet_free_queue(ndev);
+ return ret;
+}
/*
* XXX: We need to clean up on failure exits here.
*
@@ -2459,37 +2700,60 @@ static int fec_enet_init(struct net_device *ndev)
struct fec_enet_private *fep = netdev_priv(ndev);
const struct platform_device_id *id_entry =
platform_get_device_id(fep->pdev);
+ struct fec_enet_priv_tx_q *tx_queue;
+ struct fec_enet_priv_rx_q *rx_queue;
struct bufdesc *cbd_base;
+ dma_addr_t bd_dma;
int bd_size;
+ int i;
- /* init the tx & rx ring size */
- fep->tx_ring_size = TX_RING_SIZE;
- fep->rx_ring_size = RX_RING_SIZE;
-
- fep->tx_stop_threshold = FEC_MAX_SKB_DESCS;
- fep->tx_wake_threshold = (fep->tx_ring_size - fep->tx_stop_threshold) / 2;
+ fec_enet_alloc_queue(ndev);
if (fep->bufdesc_ex)
fep->bufdesc_size = sizeof(struct bufdesc_ex);
else
fep->bufdesc_size = sizeof(struct bufdesc);
- bd_size = (fep->tx_ring_size + fep->rx_ring_size) *
+ bd_size = (fep->total_tx_ring_size + fep->total_rx_ring_size) *
fep->bufdesc_size;
/* Allocate memory for buffer descriptors. */
- cbd_base = dma_alloc_coherent(NULL, bd_size, &fep->bd_dma,
+ cbd_base = dma_alloc_coherent(NULL, bd_size, &bd_dma,
GFP_KERNEL);
if (!cbd_base)
return -ENOMEM;
- fep->tso_hdrs = dma_alloc_coherent(NULL, fep->tx_ring_size * TSO_HEADER_SIZE,
- &fep->tso_hdrs_dma, GFP_KERNEL);
- if (!fep->tso_hdrs) {
- dma_free_coherent(NULL, bd_size, cbd_base, fep->bd_dma);
- return -ENOMEM;
+ memset(cbd_base, 0, bd_size);
+
+ for (i = 0; i < fep->num_rx_queues; i++) {
+ rx_queue = fep->rx_queue[i];
+ rx_queue->index = i;
+ rx_queue->rx_bd_base = (struct bufdesc *)cbd_base;
+ rx_queue->bd_dma = bd_dma;
+ if (fep->bufdesc_ex) {
+ bd_dma += sizeof(struct bufdesc_ex) * rx_queue->rx_ring_size;
+ cbd_base = (struct bufdesc *)
+ (((struct bufdesc_ex *)cbd_base) + rx_queue->rx_ring_size);
+ } else {
+ bd_dma += sizeof(struct bufdesc) * rx_queue->rx_ring_size;
+ cbd_base += rx_queue->rx_ring_size;
+ }
+ }
+
+ for (i = 0; i < fep->num_tx_queues; i++) {
+ tx_queue = fep->tx_queue[i];
+ tx_queue->index = i;
+ tx_queue->tx_bd_base = (struct bufdesc *)cbd_base;
+ tx_queue->bd_dma = bd_dma;
+ if (fep->bufdesc_ex) {
+ bd_dma += sizeof(struct bufdesc_ex) * tx_queue->tx_ring_size;
+ cbd_base = (struct bufdesc *)
+ (((struct bufdesc_ex *)cbd_base) + tx_queue->tx_ring_size);
+ } else {
+ bd_dma += sizeof(struct bufdesc) * tx_queue->tx_ring_size;
+ cbd_base += tx_queue->tx_ring_size;
+ }
}
- memset(cbd_base, 0, PAGE_SIZE);
fep->netdev = ndev;
@@ -2498,14 +2762,6 @@ static int fec_enet_init(struct net_device *ndev)
/* make sure MAC we just acquired is programmed into the hw */
fec_set_mac_address(ndev, NULL);
- /* Set receive and transmit descriptor base. */
- fep->rx_bd_base = cbd_base;
- if (fep->bufdesc_ex)
- fep->tx_bd_base = (struct bufdesc *)
- (((struct bufdesc_ex *)cbd_base) + fep->rx_ring_size);
- else
- fep->tx_bd_base = cbd_base + fep->rx_ring_size;
-
/* The FEC Ethernet specific entries in the device structure */
ndev->watchdog_timeo = TX_TIMEOUT;
ndev->netdev_ops = &fec_netdev_ops;
--
1.9.1
^ permalink raw reply related
* [Patch net-next 03/11] net:fec: use multiqueue interface to allocate Ethernet device
From: y @ 2014-09-03 20:34 UTC (permalink / raw)
To: b38611, davem, netdev, lznuaa
Cc: shawn.guo, linux-arm-kernel, devicetree, Fugang Duan, Frank Li
In-Reply-To: <1409776486-1403-1-git-send-email-y>
From: Fugang Duan <B38611@freescale.com>
Since i.MX6SX enet-AVB IP support multi queues, so use multi queues
interface to allocate and set up an Ethernet device.
Signed-off-by: Fugang Duan <B38611@freescale.com>
Signed-off-by: Frank Li <Frank.Li@freescale.com>
---
drivers/net/ethernet/freescale/fec.h | 9 ++++++
drivers/net/ethernet/freescale/fec_main.c | 49 ++++++++++++++++++++++++++++++-
2 files changed, 57 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h
index 635772b..f77ed6f 100644
--- a/drivers/net/ethernet/freescale/fec.h
+++ b/drivers/net/ethernet/freescale/fec.h
@@ -233,6 +233,13 @@ struct bufdesc_ex {
/* This device has up to three irqs on some platforms */
#define FEC_IRQ_NUM 3
+/* Maximum number of queues supported
+ * ENET with AVB IP can support up to 3 independent tx queues and rx queues.
+ * User can point the queue number that is less than or equal to 3.
+ */
+#define FEC_ENET_MAX_TX_QS 3
+#define FEC_ENET_MAX_RX_QS 3
+
/* The number of Tx and Rx buffers. These are allocated from the page
* pool. The code may assume these are power of two, so it it best
* to keep them that size.
@@ -278,6 +285,8 @@ struct fec_enet_private {
bool ptp_clk_on;
struct mutex ptp_clk_mutex;
+ int num_tx_queues;
+ int num_rx_queues;
/* The saved address of a sent-in-place packet/buffer, for skfree(). */
unsigned char *tx_bounce[TX_RING_SIZE];
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index ee9f04f..00fcadd 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -2573,6 +2573,39 @@ static void fec_reset_phy(struct platform_device *pdev)
#endif /* CONFIG_OF */
static int
+fec_enet_get_queue_num(struct platform_device *pdev, int *num_tx, int *num_rx)
+{
+ struct device_node *np = pdev->dev.of_node;
+ int err;
+
+ if (!np || !of_device_is_available(np))
+ return -ENODEV;
+
+ /* parse the num of tx and rx queues */
+ err = of_property_read_u32(np, "fsl,num_tx_queues", num_tx);
+ err |= of_property_read_u32(np, "fsl,num_rx_queues", num_rx);
+ if (err) {
+ *num_tx = 1;
+ *num_rx = 1;
+ return 0;
+ }
+
+ if (*num_tx < 1 || *num_tx > FEC_ENET_MAX_TX_QS) {
+ dev_err(&pdev->dev, "num_tx(=%d) greater than MAX_TX_QS(=%d)\n",
+ *num_tx, FEC_ENET_MAX_TX_QS);
+ return -EINVAL;
+ }
+
+ if (*num_rx < 1 || *num_rx > FEC_ENET_MAX_RX_QS) {
+ dev_err(&pdev->dev, "num_rx(=%d) greater than MAX_RX_QS(=%d)\n",
+ *num_rx, FEC_ENET_MAX_RX_QS);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int
fec_probe(struct platform_device *pdev)
{
struct fec_enet_private *fep;
@@ -2583,13 +2616,23 @@ fec_probe(struct platform_device *pdev)
const struct of_device_id *of_id;
static int dev_id;
struct device_node *np = pdev->dev.of_node, *phy_node;
+ int num_tx_qs = 1;
+ int num_rx_qs = 1;
of_id = of_match_device(fec_dt_ids, &pdev->dev);
if (of_id)
pdev->id_entry = of_id->data;
+ if (pdev->id_entry &&
+ (pdev->id_entry->driver_data & FEC_QUIRK_HAS_AVB)) {
+ ret = fec_enet_get_queue_num(pdev, &num_tx_qs, &num_rx_qs);
+ if (ret)
+ return ret;
+ }
+
/* Init network device */
- ndev = alloc_etherdev(sizeof(struct fec_enet_private));
+ ndev = alloc_etherdev_mqs(sizeof(struct fec_enet_private),
+ num_tx_qs, num_rx_qs);
if (!ndev)
return -ENOMEM;
@@ -2598,6 +2641,10 @@ fec_probe(struct platform_device *pdev)
/* setup board info structure */
fep = netdev_priv(ndev);
+ fep->num_rx_queues = num_rx_qs;
+ fep->num_tx_queues = num_tx_qs;
+ netif_set_real_num_rx_queues(ndev, num_rx_qs);
+
#if !defined(CONFIG_M5272)
/* default enable pause frame auto negotiation */
if (pdev->id_entry &&
--
1.9.1
^ permalink raw reply related
* [Patch net-next 05/11] net:fec: Disable enet-avb MAC instead of reset MAC
From: y @ 2014-09-03 20:34 UTC (permalink / raw)
To: b38611, davem, netdev, lznuaa
Cc: shawn.guo, linux-arm-kernel, devicetree, Fugang Duan, Frank Li
In-Reply-To: <1409776486-1403-1-git-send-email-y>
From: Fugang Duan <B38611@freescale.com>
For i.MX6SX enet use AXI bus, reset MAC will make system bus dead
if ENET-AXI bus has pending access (AHB bus should not have such issue).
So, disable enet with AVB MAC instead of reset MAC itself.
Signed-off-by: Fugang Duan <B38611@freescale.com>
Signed-off-by: Frank Li <Frank.Li@freescale.com>
---
drivers/net/ethernet/freescale/fec_main.c | 26 ++++++++++++++++++++------
1 file changed, 20 insertions(+), 6 deletions(-)
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index f973a92..5dab6a6 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -948,9 +948,16 @@ fec_restart(struct net_device *ndev)
u32 ecntl = 0x2; /* ETHEREN */
int i;
- /* Whack a reset. We should wait for this. */
- writel(1, fep->hwp + FEC_ECNTRL);
- udelay(10);
+ /* Whack a reset. We should wait for this.
+ * For i.MX6SX SOC, enet use AXI bus, we use disable MAC
+ * instead of reset MAC itself.
+ */
+ if (id_entry && id_entry->driver_data & FEC_QUIRK_HAS_AVB) {
+ writel(0, fep->hwp + FEC_ECNTRL);
+ } else {
+ writel(1, fep->hwp + FEC_ECNTRL);
+ udelay(10);
+ }
/*
* enet-mac reset will reset mac address registers too,
@@ -1122,9 +1129,16 @@ fec_stop(struct net_device *ndev)
netdev_err(ndev, "Graceful transmit stop did not complete!\n");
}
- /* Whack a reset. We should wait for this. */
- writel(1, fep->hwp + FEC_ECNTRL);
- udelay(10);
+ /* Whack a reset. We should wait for this.
+ * For i.MX6SX SOC, enet use AXI bus, we use disable MAC
+ * instead of reset MAC itself.
+ */
+ if (id_entry && id_entry->driver_data & FEC_QUIRK_HAS_AVB) {
+ writel(0, fep->hwp + FEC_ECNTRL);
+ } else {
+ writel(1, fep->hwp + FEC_ECNTRL);
+ udelay(10);
+ }
writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
--
1.9.1
^ permalink raw reply related
* [Patch net-next 01/11] net:fec: add enet refrence clock for i.MX 6SX chip
From: y @ 2014-09-03 20:34 UTC (permalink / raw)
To: b38611, davem, netdev, lznuaa
Cc: shawn.guo, linux-arm-kernel, devicetree, Fugang Duan, Frank Li
In-Reply-To: <1409776486-1403-1-git-send-email-y>
From: Fugang Duan <B38611@freescale.com>
i.MX6sx enet has below clocks for user config:
clk_ipg: ipg_clk_s, ipg_clk_mac0_s, 66Mhz
clk_ahb: enet system clock, it is enet AXI clock for imx6sx.
For imx6sx, it alos is the clock source of interrupt coalescing.
The clock range: 200Mhz ~ 266Mhz.
clk_ref: refrence clock for tx and rx. For imx6sx enet RGMII mode,
the refrence clock is 125Mhz coming from internal PLL or external.
In i.MX6sx-arm2 board, the clock is from internal PLL.
clk_ref is optional, depends on board.
clk_enet_out: The clock can be output from internal PLL. It can supply 50Mhz
clock for phy. clk_enet_out is optional, depends on chip and board.
clk_ptp: 1588 ts clock. It is optional, depends on chip.
The patch add clk_ref to distiguish the different clocks.
Signed-off-by: Fugang Duan <B38611@freescale.com>
Signed-off-by: Frank Li <Frank.Li@freescale.com>
---
drivers/net/ethernet/freescale/fec.h | 1 +
drivers/net/ethernet/freescale/fec_main.c | 17 +++++++++++++++++
2 files changed, 18 insertions(+)
diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h
index ee41d98..635772b 100644
--- a/drivers/net/ethernet/freescale/fec.h
+++ b/drivers/net/ethernet/freescale/fec.h
@@ -272,6 +272,7 @@ struct fec_enet_private {
struct clk *clk_ipg;
struct clk *clk_ahb;
+ struct clk *clk_ref;
struct clk *clk_enet_out;
struct clk *clk_ptp;
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index 89355a7..c21ecff1 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -1621,6 +1621,11 @@ static int fec_enet_clk_enable(struct net_device *ndev, bool enable)
}
mutex_unlock(&fep->ptp_clk_mutex);
}
+ if (fep->clk_ref) {
+ ret = clk_prepare_enable(fep->clk_ref);
+ if (ret)
+ goto failed_clk_ref;
+ }
} else {
clk_disable_unprepare(fep->clk_ahb);
clk_disable_unprepare(fep->clk_ipg);
@@ -1632,9 +1637,15 @@ static int fec_enet_clk_enable(struct net_device *ndev, bool enable)
fep->ptp_clk_on = false;
mutex_unlock(&fep->ptp_clk_mutex);
}
+ if (fep->clk_ref)
+ clk_disable_unprepare(fep->clk_ref);
}
return 0;
+
+failed_clk_ref:
+ if (fep->clk_ref)
+ clk_disable_unprepare(fep->clk_ref);
failed_clk_ptp:
if (fep->clk_enet_out)
clk_disable_unprepare(fep->clk_enet_out);
@@ -2637,6 +2648,12 @@ fec_probe(struct platform_device *pdev)
fep->ptp_clk_on = false;
mutex_init(&fep->ptp_clk_mutex);
+
+ /* clk_ref is optional, depends on board */
+ fep->clk_ref = devm_clk_get(&pdev->dev, "enet_clk_ref");
+ if (IS_ERR(fep->clk_ref))
+ fep->clk_ref = NULL;
+
fep->clk_ptp = devm_clk_get(&pdev->dev, "ptp");
fep->bufdesc_ex =
pdev->id_entry->driver_data & FEC_QUIRK_HAS_BUFDESC_EX;
--
1.9.1
^ permalink raw reply related
* [Patch net-next 09/11] net:fec: remove unnessary memory copy for address alignment in .xmit()
From: y @ 2014-09-03 20:34 UTC (permalink / raw)
To: b38611, davem, netdev, lznuaa
Cc: shawn.guo, linux-arm-kernel, devicetree, Fugang Duan, Frank Li
In-Reply-To: <1409776486-1403-1-git-send-email-y>
From: Fugang Duan <B38611@freescale.com>
i.MX6SX enet-avb IP support buffer address byte alignment for tx DMA
transfer, so remove the unnessary memory copy for the enet avb IP.
Signed-off-by: Fugang Duan <B38611@freescale.com>
Signed-off-by: Frank Li <Frank.Li@freescale.com>
---
drivers/net/ethernet/freescale/fec_main.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index b388e29f..d09fe16 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -518,8 +518,9 @@ static int fec_enet_txq_submit_skb(struct fec_enet_priv_tx_q *txq,
queue = skb_get_queue_mapping(skb);
index = fec_enet_get_bd_index(txq->tx_bd_base, bdp, fep);
- if (((unsigned long) bufaddr) & FEC_ALIGNMENT ||
- id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) {
+ if (!(id_entry->driver_data & FEC_QUIRK_HAS_AVB) &&
+ ((((unsigned long)bufaddr) & FEC_ALIGNMENT ||
+ id_entry->driver_data & FEC_QUIRK_SWAP_FRAME))) {
memcpy(txq->tx_bounce[index], skb->data, buflen);
bufaddr = txq->tx_bounce[index];
--
1.9.1
^ permalink raw reply related
* [Patch net-next 00/11] net: fec: imx6sx multiqueue support
From: y @ 2014-09-03 20:34 UTC (permalink / raw)
To: b38611, davem, netdev, lznuaa
Cc: shawn.guo, linux-arm-kernel, devicetree, Frank Li
From: Frank Li <Frank.Li@freescale.com>
These patches enable i.MX6SX multi queue support.
i.MX6SX support 3 queue and AVB feature.
Frank Li (3):
net:fec: add enet-avb IP support
ARM: Documentation: Update fec dts binding doc
ARM: dts: imx6sx: add multi-queue support enet
Fugang Duan (8):
net:fec: add enet refrence clock for i.MX 6SX chip
net:fec: add enet AVB feature macro define for imx6sx
net:fec: use multiqueue interface to allocate Ethernet device
net:fec: add multiqueue support
net:fec: Disable enet-avb MAC instead of reset MAC
net:fec: Add fsl,imx6sx-fec compatible strings
net:fec: change FEC alignment to 64 bytes for ARM platform
net:fec: remove unnessary memory copy for address alignment in .xmit()
Documentation/devicetree/bindings/net/fsl-fec.txt | 6 +
arch/arm/boot/dts/imx6sx.dtsi | 2 +
drivers/net/ethernet/freescale/fec.h | 151 +++-
drivers/net/ethernet/freescale/fec_main.c | 877 +++++++++++++++-------
4 files changed, 750 insertions(+), 286 deletions(-)
--
1.9.1
^ permalink raw reply
* [Patch net-next 10/11] ARM: Documentation: Update fec dts binding doc
From: y @ 2014-09-03 20:34 UTC (permalink / raw)
To: b38611, davem, netdev, lznuaa
Cc: shawn.guo, linux-arm-kernel, devicetree, Frank Li, Fugang Duan
In-Reply-To: <1409776486-1403-1-git-send-email-y>
From: Frank Li <Frank.Li@freescale.com>
This patch update fec devicetree binding doc that add Optional
properties "fsl,num_tx_queues" and "fsl,num_rx_queues".
Signed-off-by: Fugang Duan <B38611@freescale.com>
Signed-off-by: Frank Li <Frank.Li@freescale.com>
---
Documentation/devicetree/bindings/net/fsl-fec.txt | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/Documentation/devicetree/bindings/net/fsl-fec.txt b/Documentation/devicetree/bindings/net/fsl-fec.txt
index 8a2c7b5..4da148d 100644
--- a/Documentation/devicetree/bindings/net/fsl-fec.txt
+++ b/Documentation/devicetree/bindings/net/fsl-fec.txt
@@ -16,6 +16,12 @@ Optional properties:
- phy-handle : phandle to the PHY device connected to this device.
- fixed-link : Assume a fixed link. See fixed-link.txt in the same directory.
Use instead of phy-handle.
+- fsl,num_tx_queues : The property is valid for enet-avb IP, which supports
+ hw multi queues. Should specify the tx queue number, otherwise set tx queue
+ number to 1.
+- fsl,num_rx_queues : The property is valid for enet-avb IP, which supports
+ hw multi queues. Should specify the rx queue number, otherwise set rx queue
+ number to 1.
Optional subnodes:
- mdio : specifies the mdio bus in the FEC, used as a container for phy nodes
--
1.9.1
^ permalink raw reply related
* Re: [patch net-next 01/13] openvswitch: split flow structures into ovs specific and generic ones
From: Jamal Hadi Salim @ 2014-09-03 21:11 UTC (permalink / raw)
To: John Fastabend, Jiri Pirko
Cc: netdev, davem, nhorman, andy, tgraf, dborkman, ogerlitz, jesse,
pshelar, azhou, ben, stephen, jeffrey.t.kirsher, vyasevic,
xiyou.wangcong, john.r.fastabend, edumazet, sfeldma, f.fainelli,
roopa, linville, dev, jasowang, ebiederm, nicolas.dichtel,
ryazanov.s.a, buytenh, aviadr, nbd, alexei.starovoitov,
Neil.Jerram, ronye
In-Reply-To: <540731B9.4010603@gmail.com>
On 09/03/14 11:20, John Fastabend wrote:
> Also I have some filters that can match on offset/length/mask
> tuples. As far as I can tell this is going to have to be yet
> another interface? Or would it be worth the effort to define
> the flow key more generically. My initial guess is I'll just
> write a separate interface. I think this is what Jamal referred
> to as another "classifier".
>
Exactly. I have more complex classifiers as stated earlier.
I am afraid these patches again are not satisfying that need.
In any case - we are taking a different tact than these patches
do and hopefully at some point we can merge thoughts.
cheers,
jamal
^ permalink raw reply
* [Patch net-next 02/11] net:fec: add enet AVB feature macro define for imx6sx
From: y @ 2014-09-03 20:34 UTC (permalink / raw)
To: b38611, davem, netdev, lznuaa
Cc: shawn.guo, linux-arm-kernel, devicetree, Fugang Duan, Frank Li
In-Reply-To: <1409776486-1403-1-git-send-email-y>
From: Fugang Duan <B38611@freescale.com>
Add enet AVB feature macro define for imx6sx.
Signed-off-by: Fugang Duan <B38611@freescale.com>
Signed-off-by: Frank Li <Frank.Li@freescale.com>
---
drivers/net/ethernet/freescale/fec_main.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index c21ecff1..ee9f04f 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -104,6 +104,16 @@ static void set_multicast_list(struct net_device *ndev);
* ENET_TDAR[TDAR].
*/
#define FEC_QUIRK_ERR006358 (1 << 7)
+/* ENET IP hw AVB
+ *
+ * i.MX6SX ENET IP add Audio Video Bridging (AVB) feature support.
+ * - Two class indicators on receive with configurable priority
+ * - Two class indicators and line speed timer on transmit allowing
+ * implementation class credit based shapers externally
+ * - Additional DMA registers provisioned to allow managing up to 3
+ * independent rings
+ */
+#define FEC_QUIRK_HAS_AVB (1 << 8)
static struct platform_device_id fec_devtype[] = {
{
@@ -128,6 +138,12 @@ static struct platform_device_id fec_devtype[] = {
.name = "mvf600-fec",
.driver_data = FEC_QUIRK_ENET_MAC,
}, {
+ .name = "imx6sx-fec",
+ .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT |
+ FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM |
+ FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR006358 |
+ FEC_QUIRK_HAS_AVB,
+ }, {
/* sentinel */
}
};
--
1.9.1
^ permalink raw reply related
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