* [PATCH] atm: iphase: fix newline escape and minor tweak to source formatting
From: Colin King @ 2016-09-12 9:29 UTC (permalink / raw)
To: Chas Williams, linux-atm-general, netdev; +Cc: linux-kernel
From: Colin Ian King <colin.king@canonical.com>
The newline escape is incorrect and needs fixing. Also adjust source
formatting / indentation and remove trailing white space.
Signed-off-by: Colin Ian King <colin.king@canonical.com>
---
drivers/atm/iphase.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/drivers/atm/iphase.c b/drivers/atm/iphase.c
index 9d8807e..256b191 100644
--- a/drivers/atm/iphase.c
+++ b/drivers/atm/iphase.c
@@ -1885,9 +1885,8 @@ static int open_tx(struct atm_vcc *vcc)
if ((ret = ia_cbr_setup (iadev, vcc)) < 0) {
return ret;
}
- }
- else
- printk("iadev: Non UBR, ABR and CBR traffic not supportedn");
+ } else
+ printk("iadev: Non UBR, ABR and CBR traffic not supported\n");
iadev->testTable[vcc->vci]->vc_status |= VC_ACTIVE;
IF_EVENT(printk("ia open_tx returning \n");)
--
2.9.3
^ permalink raw reply related
* Re: [PATCH v2] cfg80211: Remove deprecated create_singlethread_workqueue
From: Johannes Berg @ 2016-09-12 9:21 UTC (permalink / raw)
To: Bhaktipriya Shridhar, David S. Miller
Cc: Tejun Heo, linux-wireless, netdev, linux-kernel
In-Reply-To: <20160830190507.GA11493@Karyakshetra>
On Wed, 2016-08-31 at 00:35 +0530, Bhaktipriya Shridhar wrote:
> The workqueue "cfg80211_wq" is involved in cleanup, scan and event
> related
> works. It queues multiple work items &rdev->event_work,
> &rdev->dfs_update_channels_wk,
> &wiphy_to_rdev(request->wiphy)->scan_done_wk,
> &wiphy_to_rdev(wiphy)->sched_scan_results_wk, which require strict
> execution ordering.
> Hence, an ordered dedicated workqueue has been used.
>
> Since it's a wireless driver, WQ_MEM_RECLAIM has been set to ensure
> forward progress under memory pressure.
>
Applied, thanks.
johannes
^ permalink raw reply
* Re: [PATCH 25/26] pch_gbe: constify local structures
From: Julia Lawall @ 2016-09-12 9:07 UTC (permalink / raw)
To: David Miller; +Cc: netdev, joe, kernel-janitors, linux-kernel
In-Reply-To: <20160911.194819.1394072668837372328.davem@davemloft.net>
On Sun, 11 Sep 2016, David Miller wrote:
>
> Julia, I went over the networking driver patches in this series and
> I have to say that I'd rather see these changes be more durable
> and self-checking.
>
> By this I mean that I want you to also make the driver private pointer
> that holds these structures be const too.
Sorry, I'm not sure what you are asking for. In these cases, we often end
up with something like:
static const struct foo = { ... };
and then later
xxx.ops = foo;
So foo is protected, but its lifetime of interest is quite short. But we
can't set the ops field of the type of xxx to be const either, because it
is obviously not - the code above modifies it. Everything would be fine
if ops were of pointer type but not structure type, but that is not the
case in this patch series, because the semantic patch disallows &foo.
There is the __ro_after_init annotation that might help in some cases, but
I have often seen these assignments in probe functions that are not
__init. Kees Cook mentioned some code that could be inserted before and
after an assignment to make a field temporarily writeable, but I haven't
looked into that possibility yet.
Have I misunderstood something?
thanks,
julia
> Then if there are really any assignments to the objects being marked
> const, it will show immediately.
>
> Thank you.
> --
> To unsubscribe from this list: send the line "unsubscribe kernel-janitors" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply
* Re: [PATCH v4 15/16] IB: Add PVRDMA driver
From: kbuild test robot @ 2016-09-12 9:00 UTC (permalink / raw)
To: Adit Ranadive
Cc: kbuild-all-JC7UmRfGjtg, dledford-H+wXaHxf7aLQT0dZR+AlfA,
linux-rdma-u79uwXL29TY76Z2rM5mHXA,
pv-drivers-pghWNbHTmq7QT0dZR+AlfA, Adit Ranadive,
netdev-u79uwXL29TY76Z2rM5mHXA, linux-pci-u79uwXL29TY76Z2rM5mHXA,
jhansen-pghWNbHTmq7QT0dZR+AlfA, asarwade-pghWNbHTmq7QT0dZR+AlfA,
georgezhang-pghWNbHTmq7QT0dZR+AlfA,
bryantan-pghWNbHTmq7QT0dZR+AlfA
In-Reply-To: <1473655766-31628-16-git-send-email-aditr-pghWNbHTmq7QT0dZR+AlfA@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 3830 bytes --]
Hi Adit,
[auto build test WARNING on next-20160909]
[cannot apply to rdma/master net-next/master net/master v4.8-rc6 v4.8-rc5 v4.8-rc4 v4.8-rc6]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
[Suggest to use git(>=2.9.0) format-patch --base=<commit> (or --base=auto for convenience) to record what (public, well-known) commit your patch series was built on]
[Check https://git-scm.com/docs/git-format-patch for more information]
url: https://github.com/0day-ci/linux/commits/Adit-Ranadive/Add-Paravirtual-RDMA-Driver/20160912-125631
config: xtensa-allyesconfig (attached as .config)
compiler: xtensa-linux-gcc (GCC) 4.9.0
reproduce:
wget https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
make.cross ARCH=xtensa
All warnings (new ones prefixed by >>):
drivers/infiniband/hw/pvrdma/pvrdma_misc.c: In function 'pvrdma_page_dir_init':
>> drivers/infiniband/hw/pvrdma/pvrdma_misc.c:74:21: warning: passing argument 3 of 'dma_alloc_coherent' from incompatible pointer type
pdir->tables[i] = dma_alloc_coherent(&dev->pdev->dev, PAGE_SIZE,
^
In file included from include/linux/pci-dma-compat.h:7:0,
from include/linux/pci.h:2112,
from drivers/infiniband/hw/pvrdma/pvrdma.h:53,
from drivers/infiniband/hw/pvrdma/pvrdma_misc.c:50:
include/linux/dma-mapping.h:450:21: note: expected 'dma_addr_t *' but argument is of type 'u64 *'
static inline void *dma_alloc_coherent(struct device *dev, size_t size,
^
vim +/dma_alloc_coherent +74 drivers/infiniband/hw/pvrdma/pvrdma_misc.c
3f4cd1e6 Adit Ranadive 2016-09-11 58 return -EINVAL;
3f4cd1e6 Adit Ranadive 2016-09-11 59
3f4cd1e6 Adit Ranadive 2016-09-11 60 memset(pdir, 0, sizeof(*pdir));
3f4cd1e6 Adit Ranadive 2016-09-11 61
3f4cd1e6 Adit Ranadive 2016-09-11 62 pdir->dir = dma_alloc_coherent(&dev->pdev->dev, PAGE_SIZE,
3f4cd1e6 Adit Ranadive 2016-09-11 63 &pdir->dir_dma, GFP_KERNEL);
3f4cd1e6 Adit Ranadive 2016-09-11 64 if (!pdir->dir)
3f4cd1e6 Adit Ranadive 2016-09-11 65 goto err;
3f4cd1e6 Adit Ranadive 2016-09-11 66
3f4cd1e6 Adit Ranadive 2016-09-11 67 pdir->ntables = PVRDMA_PAGE_DIR_TABLE(npages - 1) + 1;
3f4cd1e6 Adit Ranadive 2016-09-11 68 pdir->tables = kcalloc(pdir->ntables, sizeof(*pdir->tables),
3f4cd1e6 Adit Ranadive 2016-09-11 69 GFP_KERNEL);
3f4cd1e6 Adit Ranadive 2016-09-11 70 if (!pdir->tables)
3f4cd1e6 Adit Ranadive 2016-09-11 71 goto err;
3f4cd1e6 Adit Ranadive 2016-09-11 72
3f4cd1e6 Adit Ranadive 2016-09-11 73 for (i = 0; i < pdir->ntables; i++) {
3f4cd1e6 Adit Ranadive 2016-09-11 @74 pdir->tables[i] = dma_alloc_coherent(&dev->pdev->dev, PAGE_SIZE,
3f4cd1e6 Adit Ranadive 2016-09-11 75 &pdir->dir[i], GFP_KERNEL);
3f4cd1e6 Adit Ranadive 2016-09-11 76 if (!pdir->tables[i])
3f4cd1e6 Adit Ranadive 2016-09-11 77 goto err;
3f4cd1e6 Adit Ranadive 2016-09-11 78 }
3f4cd1e6 Adit Ranadive 2016-09-11 79
3f4cd1e6 Adit Ranadive 2016-09-11 80 pdir->npages = npages;
3f4cd1e6 Adit Ranadive 2016-09-11 81
3f4cd1e6 Adit Ranadive 2016-09-11 82 if (alloc_pages) {
:::::: The code at line 74 was first introduced by commit
:::::: 3f4cd1e6dfbd35bd0392c639ef00eb665bcfb308 IB/pvrdma: Add helper functions
:::::: TO: Adit Ranadive <aditr-pghWNbHTmq7QT0dZR+AlfA@public.gmane.org>
:::::: CC: 0day robot <fengguang.wu-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
[-- Attachment #2: .config.gz --]
[-- Type: application/octet-stream, Size: 46794 bytes --]
^ permalink raw reply
* Re: [PATCH net-next 2/7] cxgb4: add common api support for configuring filters
From: Jiri Pirko @ 2016-09-12 8:57 UTC (permalink / raw)
To: Rahul Lakkireddy; +Cc: netdev, davem, hariprasad, leedom, nirranjan, indranil
In-Reply-To: <05ef6c530086d3df5b8f4128141e3c12d3812105.1473667613.git.rahul.lakkireddy@chelsio.com>
Mon, Sep 12, 2016 at 10:12:35AM CEST, rahul.lakkireddy@chelsio.com wrote:
>Enable filters for non-offload configuration and add common api support
>for setting and deleting filters in LE-TCAM region of the hardware.
>
>IPv4 filters occupy one slot. IPv6 filters occupy 4 slots and must
>be on a 4-slot boundary. IPv4 filters can not occupy a slot belonging
>to IPv6 and the vice-versa is also true.
>
>Filters are set and deleted asynchronously. Use completion to wait
>for reply from firmware in order to allow for synchronization if needed.
>
>Signed-off-by: Rahul Lakkireddy <rahul.lakkireddy@chelsio.com>
>Signed-off-by: Hariprasad Shenai <hariprasad@chelsio.com>
>---
> drivers/net/ethernet/chelsio/cxgb4/cxgb4.h | 3 +
> drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c | 424 +++++++++++++++++++++-
> drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h | 1 +
> drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 33 +-
> drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h | 23 +-
> 5 files changed, 453 insertions(+), 31 deletions(-)
>
>diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
>index 053976f..fbd593a 100644
>--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
>+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
>@@ -1055,7 +1055,10 @@ struct filter_entry {
>
> u32 pending:1; /* filter action is pending firmware reply */
> u32 smtidx:8; /* Source MAC Table index for smac */
>+ struct filter_ctx *ctx; /* Caller's completion hook */
> struct l2t_entry *l2t; /* Layer Two Table entry for dmac */
>+ struct net_device *dev; /* Associated net device */
>+ u32 tid; /* This will store the actual tid */
>
> /* The filter itself. Most of this is a straight copy of information
> * provided by the extended ioctl(). Some fields are translated to
>diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c
>index 2e86902..490bd94 100644
>--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c
>+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c
>@@ -33,10 +33,138 @@
> */
>
> #include "cxgb4.h"
>+#include "t4_regs.h"
> #include "l2t.h"
> #include "t4fw_api.h"
> #include "cxgb4_filter.h"
>
>+/* Validate filter spec against configuration done on the card.
>+ */
>+static int validate_filter(struct net_device *dev,
>+ struct ch_filter_specification *fs)
>+{
>+ struct adapter *adapter = netdev2adap(dev);
>+ u32 fconf, iconf;
>+
>+ /* Check for unconfigured fields being used. */
>+ fconf = adapter->params.tp.vlan_pri_map;
>+ iconf = adapter->params.tp.ingress_config;
>+
>+ #define S(_field) \
>+ (fs->val._field || fs->mask._field)
>+ #define U(_mask, _field) \
>+ (!(fconf & (_mask)) && S(_field))
Wow, this is really odd. Please replace these mysterious "S" and "U"
with some properly named static helper function.
>+
>+ if (U(FCOE_F, fcoe) || U(PORT_F, iport) || U(TOS_F, tos) ||
>+ U(ETHERTYPE_F, ethtype) || U(MACMATCH_F, macidx) ||
>+ U(MPSHITTYPE_F, matchtype) || U(FRAGMENTATION_F, frag) ||
>+ U(PROTOCOL_F, proto) ||
>+ U(VNIC_ID_F, pfvf_vld) ||
>+ U(VNIC_ID_F, ovlan_vld) ||
>+ U(VLAN_F, ivlan_vld))
>+ return -EOPNOTSUPP;
>+
>+ /* T4 inconveniently uses the same FT_VNIC_ID_W bits for both the Outer
>+ * VLAN Tag and PF/VF/VFvld fields based on VNIC_F being set
>+ * in TP_INGRESS_CONFIG. Hense the somewhat crazy checks
>+ * below. Additionally, since the T4 firmware interface also
>+ * carries that overlap, we need to translate any PF/VF
>+ * specification into that internal format below.
>+ */
>+ if (S(pfvf_vld) && S(ovlan_vld))
>+ return -EOPNOTSUPP;
>+ if ((S(pfvf_vld) && !(iconf & VNIC_F)) ||
>+ (S(ovlan_vld) && (iconf & VNIC_F)))
>+ return -EOPNOTSUPP;
>+ if (fs->val.pf > 0x7 || fs->val.vf > 0x7f)
>+ return -ERANGE;
>+ fs->mask.pf &= 0x7;
>+ fs->mask.vf &= 0x7f;
>+
>+ #undef S
>+ #undef U
>+
^ permalink raw reply
* Re: README: [PATCH RFC 11/11] net/mlx5e: XDP TX xmit more
From: Jesper Dangaard Brouer via iovisor-dev @ 2016-09-12 8:56 UTC (permalink / raw)
To: Alexei Starovoitov
Cc: Tom Herbert, iovisor-dev, Jamal Hadi Salim, Saeed Mahameed,
Eric Dumazet, netdev-u79uwXL29TY76Z2rM5mHXA, Edward Cree
In-Reply-To: <20160909063048.GA67375-+o4/htvd0TDFYCXBM6kdu7fOX0fSgVTm@public.gmane.org>
On Thu, 8 Sep 2016 23:30:50 -0700
Alexei Starovoitov <alexei.starovoitov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> On Fri, Sep 09, 2016 at 07:36:52AM +0200, Jesper Dangaard Brouer wrote:
> > > > Lets do bundling/bulking from the start!
> > >
> > > mlx4 already does bulking and this proposed mlx5 set of patches
> > > does bulking as well.
> > > See nothing wrong about it. RX side processes the packets and
> > > when it's done it tells TX to xmit whatever it collected.
> >
> > This is doing "hidden" bulking and not really taking advantage of using
> > the icache more effeciently.
> >
> > Let me explain the problem I see, little more clear then, so you
> > hopefully see where I'm going.
> >
> > Imagine you have packets intermixed towards the stack and XDP_TX.
> > Every time you call the stack code, then you flush your icache. When
> > returning to the driver code, you will have to reload all the icache
> > associated with the XDP_TX, this is a costly operation.
>
> correct. And why is that a problem?
It is good that you can see and acknowledge the I-cache problem.
XDP is all about performance. What I hear is, that you are arguing
against a model that will yield better performance, that does not make
sense to me. Let me explain this again, in another way.
This is a mental model switch. Stop seeing the lowest driver RX as
something that works on a per packet basis. Maybe is it is easier to
understand if we instead see this as vector processing? This is about
having a vector of packets, where we apply some action/operation.
This is about using the CPU more efficiently, getting it to do more
instructions per cycle (directly measurable with perf, while I-cache
is not directly measurable).
Lets assume everything fits into the I-cache (XDP+driver code). The
CPU-frontend still have to decode the instructions from the I-cache
into micro-ops. The next level of optimizations is to reuse the
decoded I-cache by running it on all elements in the packet-vector.
The Intel "64 and IA-32 Architectures Optimization Reference Manual"
(section 3.4.2.6 "Optimization for Decoded ICache"[1][2]), states make
sure each hot code block is less than about 500 instructions. Thus,
the different "stages" working on the packet-vector, need to be rather
small and compact.
[1] http://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-optimization-manual.html
[2] http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-optimization-manual.pdf
Notice: The same mental model switch applies to delivery packets to
the regular netstack. I've brought this up before[3]. Instead of
flushing the drivers I-cache for every packet, by calling the stack,
let instead bundle up N-packets in the driver before calling the
stack. I showed 10% speedup by a naive implementation of this
approach. Edward Cree also showed[4] a 10% performance boost, and
went further into the stack, showing a 25% increase.
A goal is also, to make optimizing netstack code-size independent of
the driver code-size, by separating the netstacks I-cache usage from
the drivers.
[3] http://lists.openwall.net/netdev/2016/01/15/51
[4] http://lists.openwall.net/netdev/2016/04/19/89
--
Best regards,
Jesper Dangaard Brouer
MSc.CS, Principal Kernel Engineer at Red Hat
Author of http://www.iptv-analyzer.org
LinkedIn: http://www.linkedin.com/in/brouer
^ permalink raw reply
* Re: [PATCH 23/26] sh_eth: constify local structures
From: Julia Lawall @ 2016-09-12 8:55 UTC (permalink / raw)
To: Sergei Shtylyov
Cc: joe, kernel-janitors, netdev, linux-renesas-soc, linux-kernel
In-Reply-To: <cb443416-4fb9-5593-2529-66211299c9a8@cogentembedded.com>
On Sun, 11 Sep 2016, Sergei Shtylyov wrote:
> On 09/11/2016 04:06 PM, Julia Lawall wrote:
> > For structure types defined in the same file or local header files, find
> > top-level static structure declarations that have the following
> > properties:
> > 1. Never reassigned.
>
> Really?
>
> > 2. Address never taken
>
> Really?
>
> > 3. Not passed to a top-level macro call
> > 4. No pointer or array-typed field passed to a function or stored in a
> > variable.
> > Declare structures having all of these properties as const.
> >
> > Done using Coccinelle.
> > Based on a suggestion by Joe Perches <joe@perches.com>.
> >
> > Signed-off-by: Julia Lawall <Julia.Lawall@lip6.fr>
>
> NAK, see sh_eth_set_default_cpu_data().
Thanks for the feedback. I will check on why this slipped through.
Perhaps it is a variability (ifdef) problem.
julia
>
> MBR, Sergei
>
>
^ permalink raw reply
* Re: [PATCH 00/26] constify local structures
From: Julia Lawall @ 2016-09-12 8:54 UTC (permalink / raw)
To: Jarkko Sakkinen
Cc: Julia Lawall, linux-renesas-soc, joe, kernel-janitors,
Sergei Shtylyov, linux-pm, platform-driver-x86, linux-media,
linux-can, Tatyana Nikolova, Shiraz Saleem, Mustafa Ismail,
Chien Tin Tung, linux-rdma, netdev, devel, alsa-devel,
linux-kernel, linux-fbdev, linux-wireless, Jason Gunthorpe,
tpmdd-devel, linux-scsi
In-Reply-To: <20160911172105.GB5493@intel.com>
On Sun, 11 Sep 2016, Jarkko Sakkinen wrote:
> On Sun, Sep 11, 2016 at 03:05:42PM +0200, Julia Lawall wrote:
> > Constify local structures.
> >
> > The semantic patch that makes this change is as follows:
> > (http://coccinelle.lip6.fr/)
>
> Just my two cents but:
>
> 1. You *can* use a static analysis too to find bugs or other issues.
> 2. However, you should manually do the commits and proper commit
> messages to subsystems based on your findings. And I generally think
> that if one contributes code one should also at least smoke test changes
> somehow.
>
> I don't know if I'm alone with my opinion. I just think that one should
> also do the analysis part and not blindly create and submit patches.
All of the patches are compile tested. And the individual patches are
submitted to the relevant maintainers. The individual commit messages
give a more detailed explanation of the strategy used to decide that the
structure was constifiable. It seemed redundant to put that in the cover
letter, which will not be committed anyway.
julia
>
> Anyway, I'll apply the TPM change at some point. As I said they were
> for better. Thanks.
>
> /Jarkko
>
> > // <smpl>
> > // The first rule ignores some cases that posed problems
> > @r disable optional_qualifier@
> > identifier s != {peri_clk_data,threshold_attr,tracer_flags,tracer};
> > identifier i != {s5k5baf_cis_rect,smtcfb_fix};
> > position p;
> > @@
> > static struct s i@p = { ... };
> >
> > @lstruct@
> > identifier r.s;
> > @@
> > struct s { ... };
> >
> > @used depends on lstruct@
> > identifier r.i;
> > @@
> > i
> >
> > @bad1@
> > expression e;
> > identifier r.i;
> > assignment operator a;
> > @@
> > (<+...i...+>) a e
> >
> > @bad2@
> > identifier r.i;
> > @@
> > &(<+...i...+>)
> >
> > @bad3@
> > identifier r.i;
> > declarer d;
> > @@
> > d(...,<+...i...+>,...);
> >
> > @bad4@
> > identifier r.i;
> > type T;
> > T[] e;
> > identifier f;
> > position p;
> > @@
> >
> > f@p(...,
> > (
> > (<+...i...+>)
> > &
> > e
> > )
> > ,...)
> >
> > @bad4a@
> > identifier r.i;
> > type T;
> > T *e;
> > identifier f;
> > position p;
> > @@
> >
> > f@p(...,
> > (
> > (<+...i...+>)
> > &
> > e
> > )
> > ,...)
> >
> > @ok5@
> > expression *e;
> > identifier r.i;
> > position p;
> > @@
> > e =@p i
> >
> > @bad5@
> > expression *e;
> > identifier r.i;
> > position p != ok5.p;
> > @@
> > e =@p (<+...i...+>)
> >
> > @rr depends on used && !bad1 && !bad2 && !bad3 && !bad4 && !bad4a && !bad5@
> > identifier s,r.i;
> > position r.p;
> > @@
> >
> > static
> > +const
> > struct s i@p = { ... };
> >
> > @depends on used && !bad1 && !bad2 && !bad3 && !bad4 && !bad4a && !bad5
> > disable optional_qualifier@
> > identifier rr.s,r.i;
> > @@
> >
> > static
> > +const
> > struct s i;
> > // </smpl>
> >
> > ---
> >
> > drivers/acpi/acpi_apd.c | 8 +++---
> > drivers/char/tpm/tpm-interface.c | 10 ++++----
> > drivers/char/tpm/tpm-sysfs.c | 2 -
> > drivers/cpufreq/intel_pstate.c | 8 +++---
> > drivers/infiniband/hw/i40iw/i40iw_uk.c | 6 ++---
> > drivers/media/i2c/tvp514x.c | 2 -
> > drivers/media/pci/ddbridge/ddbridge-core.c | 18 +++++++--------
> > drivers/media/pci/ngene/ngene-cards.c | 14 ++++++------
> > drivers/media/pci/smipcie/smipcie-main.c | 8 +++---
> > drivers/misc/sgi-xp/xpc_uv.c | 2 -
> > drivers/net/arcnet/com20020-pci.c | 10 ++++----
> > drivers/net/can/c_can/c_can_pci.c | 4 +--
> > drivers/net/can/sja1000/plx_pci.c | 20 ++++++++---------
> > drivers/net/ethernet/mellanox/mlx4/main.c | 4 +--
> > drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c | 2 -
> > drivers/net/ethernet/renesas/sh_eth.c | 14 ++++++------
> > drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c | 2 -
> > drivers/net/wireless/ath/dfs_pattern_detector.c | 2 -
> > drivers/net/wireless/intel/iwlegacy/3945.c | 4 +--
> > drivers/net/wireless/realtek/rtlwifi/rtl8188ee/sw.c | 2 -
> > drivers/net/wireless/realtek/rtlwifi/rtl8192ce/sw.c | 2 -
> > drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.c | 2 -
> > drivers/net/wireless/realtek/rtlwifi/rtl8192ee/sw.c | 2 -
> > drivers/net/wireless/realtek/rtlwifi/rtl8192se/sw.c | 2 -
> > drivers/net/wireless/realtek/rtlwifi/rtl8723ae/sw.c | 2 -
> > drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c | 2 -
> > drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c | 2 -
> > drivers/platform/chrome/chromeos_laptop.c | 22 +++++++++----------
> > drivers/platform/x86/intel_scu_ipc.c | 6 ++---
> > drivers/platform/x86/intel_telemetry_debugfs.c | 2 -
> > drivers/scsi/esas2r/esas2r_flash.c | 2 -
> > drivers/scsi/hptiop.c | 6 ++---
> > drivers/spi/spi-dw-pci.c | 4 +--
> > drivers/staging/rtl8192e/rtl8192e/rtl_core.c | 2 -
> > drivers/usb/misc/ezusb.c | 2 -
> > drivers/video/fbdev/matrox/matroxfb_g450.c | 2 -
> > lib/crc64_ecma.c | 2 -
> > sound/pci/ctxfi/ctatc.c | 2 -
> > sound/pci/hda/patch_ca0132.c | 10 ++++----
> > sound/pci/riptide/riptide.c | 2 -
> > 40 files changed, 110 insertions(+), 110 deletions(-)
>
^ permalink raw reply
* Re: [PATCH net-next 7/7] cxgb4: add support for drop and redirect actions
From: Jiri Pirko @ 2016-09-12 8:52 UTC (permalink / raw)
To: Rahul Lakkireddy; +Cc: netdev, davem, hariprasad, leedom, nirranjan, indranil
In-Reply-To: <13800cc26e45257999003861820b0bc65ab7a8d1.1473667613.git.rahul.lakkireddy@chelsio.com>
Mon, Sep 12, 2016 at 10:12:40AM CEST, rahul.lakkireddy@chelsio.com wrote:
>Add support for dropping matched packets in hardware. Also add support
>for re-directing matched packets to a specified port in hardware.
>
>Signed-off-by: Rahul Lakkireddy <rahul.lakkireddy@chelsio.com>
>Signed-off-by: Hariprasad Shenai <hariprasad@chelsio.com>
>---
> drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c | 63 +++++++++++++++++++++++
> 1 file changed, 63 insertions(+)
>
>diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c
>index 31847e3..584ccb3 100644
>--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c
>+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c
>@@ -32,6 +32,9 @@
> * SOFTWARE.
> */
>
>+#include <net/tc_act/tc_gact.h>
>+#include <net/tc_act/tc_mirred.h>
>+
> #include "cxgb4.h"
> #include "cxgb4_tc_u32_parse.h"
> #include "cxgb4_tc_u32.h"
>@@ -83,6 +86,59 @@ static int fill_match_fields(struct adapter *adap,
> return 0;
> }
>
>+/* Fill ch_filter_specification with parsed action. */
>+static int fill_action_fields(struct adapter *adap,
>+ struct ch_filter_specification *fs,
>+ struct tc_cls_u32_offload *cls)
>+{
>+ const struct tc_action *a;
>+ struct tcf_exts *exts;
>+ LIST_HEAD(actions);
>+ unsigned int num_actions = 0;
>+ bool found = false;
>+
>+ exts = cls->knode.exts;
>+ if (tc_no_actions(exts))
>+ return -EINVAL;
>+
>+ tcf_exts_to_list(exts, &actions);
>+ list_for_each_entry(a, &actions, list) {
>+ /* Don't allow more than one action per rule. */
>+ if (num_actions)
>+ return -EINVAL;
Looking at this, unrelated to this patch, we really need some advanced
reporting to user about what went wrong. Otherwise he's playing a
guessing game.
>+
>+ /* Drop in hardware. */
>+ if (is_tcf_gact_shot(a)) {
>+ fs->action = FILTER_DROP;
>+ found = true;
>+ }
>+
>+ /* Re-direct to specified port in hardware. */
>+ if (is_tcf_mirred_redirect(a)) {
else if ?
>+ struct net_device *n_dev;
>+ unsigned int i, index;
>+
>+ index = tcf_mirred_ifindex(a);
>+ for_each_port(adap, i) {
>+ n_dev = adap->port[i];
>+ if (index == n_dev->ifindex) {
>+ fs->action = FILTER_SWITCH;
>+ fs->eport = i;
>+ break;
>+ }
>+ }
>+ found = true;
>+ }
You need to report an error in case you don't support the action.
>+
>+ num_actions++;
>+ }
>+
>+ if (!found)
>+ return -EINVAL;
Oh, I guess this is to fail on unsupported action. Odd. Rather just
return directly the list_for_each_entry loop
>+
>+ return 0;
>+}
>+
> int cxgb4_config_knode(struct net_device *dev, __be16 protocol,
> struct tc_cls_u32_offload *cls)
> {
>@@ -236,6 +292,13 @@ int cxgb4_config_knode(struct net_device *dev, __be16 protocol,
> if (ret)
> goto out;
>
>+ /* Fill ch_filter_specification action fields to be shipped to
>+ * hardware.
>+ */
>+ ret = fill_action_fields(adapter, &fs, cls);
>+ if (ret)
>+ goto out;
>+
> /* The filter spec has been completely built from the info
> * provided from u32. We now set some default fields in the
> * spec for sanity.
>--
>2.5.3
>
^ permalink raw reply
* Re: [PATCH net-next 6/7] cxgb4: add support for deleting u32 filters
From: Jiri Pirko @ 2016-09-12 8:47 UTC (permalink / raw)
To: Rahul Lakkireddy; +Cc: netdev, davem, hariprasad, leedom, nirranjan, indranil
In-Reply-To: <01b1dd8d57eb3ebb2e8a5f338a956f300de5f841.1473667613.git.rahul.lakkireddy@chelsio.com>
Mon, Sep 12, 2016 at 10:12:39AM CEST, rahul.lakkireddy@chelsio.com wrote:
>Add support for deleting an offloaded u32 filter from hardware. If a
>link is deleted, then all corresponding filters associated with the link
>are also deleted. Also enable hardware tc offload as a supported
>feature.
>
>Signed-off-by: Rahul Lakkireddy <rahul.lakkireddy@chelsio.com>
>Signed-off-by: Hariprasad Shenai <hariprasad@chelsio.com>
I don't understand why add and delete are 2 separate patches. I believe
it should go in one.
>---
> drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 5 +-
> drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c | 92 +++++++++++++++++++++++
> drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h | 2 +
> 3 files changed, 98 insertions(+), 1 deletion(-)
>
>diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
>index 28396f5..087066a 100644
>--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
>+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
>@@ -3047,6 +3047,8 @@ int cxgb_setup_tc(struct net_device *dev, u32 handle, __be16 proto,
> case TC_CLSU32_NEW_KNODE:
> case TC_CLSU32_REPLACE_KNODE:
> return cxgb4_config_knode(dev, proto, tc->cls_u32);
>+ case TC_CLSU32_DELETE_KNODE:
>+ return cxgb4_delete_knode(dev, proto, tc->cls_u32);
> default:
> return -EOPNOTSUPP;
> }
>@@ -5155,7 +5157,8 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
> netdev->hw_features = NETIF_F_SG | TSO_FLAGS |
> NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
> NETIF_F_RXCSUM | NETIF_F_RXHASH |
>- NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX;
>+ NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX |
>+ NETIF_F_HW_TC;
> if (highdma)
> netdev->hw_features |= NETIF_F_HIGHDMA;
> netdev->features |= netdev->hw_features;
>diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c
>index 62c1695..31847e3 100644
>--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c
>+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c
>@@ -279,6 +279,98 @@ out:
> return ret;
> }
>
>+int cxgb4_delete_knode(struct net_device *dev, __be16 protocol,
>+ struct tc_cls_u32_offload *cls)
>+{
>+ struct adapter *adapter = netdev2adap(dev);
>+ struct cxgb4_tc_u32_table *t;
>+ struct cxgb4_link *link = NULL;
>+ struct filter_ctx ctx;
>+ u32 handle, uhtid;
>+ unsigned int filter_id;
>+ unsigned int max_tids;
>+ unsigned int i, j;
>+ int ret;
>+
>+ if (!can_tc_u32_offload(dev))
>+ return -EOPNOTSUPP;
>+
>+ /* Fetch the location to delete the filter. */
>+ filter_id = (cls->knode.handle & 0xFFFFF);
Again with the (). Looks like you have it all over the place.
>+
>+ if (filter_id > adapter->tids.nftids) {
>+ dev_err(adapter->pdev_dev,
>+ "Location %d out of range for deletion. Max: %d\n",
>+ filter_id, adapter->tids.nftids);
>+ return -ERANGE;
>+ }
>+
>+ t = adapter->tc_u32;
>+ handle = cls->knode.handle;
>+ uhtid = TC_U32_USERHTID(cls->knode.handle);
>+
>+ /* Ensure that uhtid is either root u32 (i.e. 0x800)
>+ * or a a valid linked bucket.
>+ */
>+ if (uhtid != 0x800 && uhtid >= t->size)
>+ return -EINVAL;
>+
>+ /* Delete the specified filter */
>+ if (uhtid != 0x800) {
>+ link = &t->table[uhtid - 1];
>+ if (!link->link_handle)
>+ return -EINVAL;
>+
>+ if (!test_bit(filter_id, link->tid_map))
>+ return -EINVAL;
>+ }
>+
>+ init_completion(&ctx.completion);
>+
>+ ret = cxgb4_del_filter(dev, filter_id, &ctx);
>+ if (ret)
>+ goto out;
>+
>+ /* Wait for reply */
>+ ret = wait_for_completion_timeout(&ctx.completion, 10 * HZ);
>+ if (!ret)
>+ return -ETIMEDOUT;
Hmm, I think it would be nicer to have the completion dance wrapped
inside cxgb4_del_filter and have __cxgb4_del_filter for case you don't
need it.
>+
>+ ret = ctx.result;
>+ if (!ret && link)
>+ clear_bit(filter_id, link->tid_map);
>+
>+ /* If a link is being deleted, then delete all filters
>+ * associated with the link.
>+ */
>+ max_tids = adapter->tids.nftids;
>+ for (i = 0; i < t->size; i++) {
>+ link = &t->table[i];
>+
>+ if (link->link_handle == handle) {
>+ for (j = 0; j < max_tids; j++) {
>+ if (!test_bit(j, link->tid_map))
>+ continue;
>+
>+ ret = cxgb4_del_filter(dev, j, NULL);
>+ if (ret)
>+ goto out;
>+
>+ clear_bit(j, link->tid_map);
>+ }
>+
>+ /* Clear the link state */
>+ link->match_field = NULL;
>+ link->link_handle = 0;
>+ memset(&link->fs, 0, sizeof(link->fs));
>+ break;
>+ }
>+ }
>+
>+out:
>+ return ret;
>+}
>+
> void cxgb4_cleanup_tc_u32(struct adapter *adap)
> {
> struct cxgb4_tc_u32_table *t;
>diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h
>index 46575843..6bdc885 100644
>--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h
>+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h
>@@ -48,6 +48,8 @@ static inline bool can_tc_u32_offload(struct net_device *dev)
>
> int cxgb4_config_knode(struct net_device *dev, __be16 protocol,
> struct tc_cls_u32_offload *cls);
>+int cxgb4_delete_knode(struct net_device *dev, __be16 protocol,
>+ struct tc_cls_u32_offload *cls);
>
> void cxgb4_cleanup_tc_u32(struct adapter *adapter);
> struct cxgb4_tc_u32_table *cxgb4_init_tc_u32(struct adapter *adap,
>--
>2.5.3
>
^ permalink raw reply
* Re: [PATCH net-next 5/7] cxgb4: add support for setting u32 filters
From: Jiri Pirko @ 2016-09-12 8:40 UTC (permalink / raw)
To: Rahul Lakkireddy; +Cc: netdev, davem, hariprasad, leedom, nirranjan, indranil
In-Reply-To: <6a12fb1798c4f18afee3f8181bb633296a6727ee.1473667613.git.rahul.lakkireddy@chelsio.com>
Mon, Sep 12, 2016 at 10:12:38AM CEST, rahul.lakkireddy@chelsio.com wrote:
>Add support for offloading u32 filter onto hardware. Links are stored
>in a jump table to perform necessary jumps to match TCP/UDP header.
>When inserting rules in the linked bucket, the TCP/UDP match fields
>in the corresponding entry of the jump table are appended to the filter
>rule before insertion.
>
>Signed-off-by: Rahul Lakkireddy <rahul.lakkireddy@chelsio.com>
>Signed-off-by: Hariprasad Shenai <hariprasad@chelsio.com>
>---
> drivers/net/ethernet/chelsio/cxgb4/Makefile | 2 +-
> drivers/net/ethernet/chelsio/cxgb4/cxgb4.h | 3 +
> drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 36 +++
> drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c | 343 +++++++++++++++++++++
> drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h | 55 ++++
> .../ethernet/chelsio/cxgb4/cxgb4_tc_u32_parse.h | 12 +
> 6 files changed, 450 insertions(+), 1 deletion(-)
> create mode 100644 drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c
> create mode 100644 drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h
>
>diff --git a/drivers/net/ethernet/chelsio/cxgb4/Makefile b/drivers/net/ethernet/chelsio/cxgb4/Makefile
>index da88981..c6b71f6 100644
>--- a/drivers/net/ethernet/chelsio/cxgb4/Makefile
>+++ b/drivers/net/ethernet/chelsio/cxgb4/Makefile
>@@ -4,7 +4,7 @@
>
> obj-$(CONFIG_CHELSIO_T4) += cxgb4.o
>
>-cxgb4-objs := cxgb4_main.o l2t.o t4_hw.o sge.o clip_tbl.o cxgb4_ethtool.o cxgb4_uld.o sched.o cxgb4_filter.o
>+cxgb4-objs := cxgb4_main.o l2t.o t4_hw.o sge.o clip_tbl.o cxgb4_ethtool.o cxgb4_uld.o sched.o cxgb4_filter.o cxgb4_tc_u32.o
> cxgb4-$(CONFIG_CHELSIO_T4_DCB) += cxgb4_dcb.o
> cxgb4-$(CONFIG_CHELSIO_T4_FCOE) += cxgb4_fcoe.o
> cxgb4-$(CONFIG_DEBUG_FS) += cxgb4_debugfs.o
>diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
>index fbd593a..1adb28f 100644
>--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
>+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
>@@ -867,6 +867,9 @@ struct adapter {
>
> spinlock_t stats_lock;
> spinlock_t win0_lock ____cacheline_aligned_in_smp;
>+
>+ /* TC u32 offload */
>+ struct cxgb4_tc_u32_table *tc_u32;
> };
>
> /* Support for "sched-class" command to allow a TX Scheduling Class to be
>diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
>index af07f9d..28396f5 100644
>--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
>+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
>@@ -78,6 +78,7 @@
> #include "clip_tbl.h"
> #include "l2t.h"
> #include "sched.h"
>+#include "cxgb4_tc_u32.h"
>
> char cxgb4_driver_name[] = KBUILD_MODNAME;
>
>@@ -3027,6 +3028,33 @@ static int cxgb_set_tx_maxrate(struct net_device *dev, int index, u32 rate)
> return err;
> }
>
>+int cxgb_setup_tc(struct net_device *dev, u32 handle, __be16 proto,
>+ struct tc_to_netdev *tc)
>+{
>+ struct adapter *adap = netdev2adap(dev);
>+ struct port_info *pi = netdev2pinfo(dev);
>+
>+ if (!(adap->flags & FULL_INIT_DONE)) {
>+ dev_err(adap->pdev_dev,
>+ "Failed to setup tc on port %d. Link Down?\n",
>+ pi->port_id);
>+ return -EINVAL;
>+ }
>+
>+ if (TC_H_MAJ(handle) == TC_H_MAJ(TC_H_INGRESS) &&
>+ tc->type == TC_SETUP_CLSU32) {
>+ switch (tc->cls_u32->command) {
>+ case TC_CLSU32_NEW_KNODE:
>+ case TC_CLSU32_REPLACE_KNODE:
>+ return cxgb4_config_knode(dev, proto, tc->cls_u32);
>+ default:
>+ return -EOPNOTSUPP;
>+ }
>+ }
>+
>+ return -EOPNOTSUPP;
>+}
>+
> static const struct net_device_ops cxgb4_netdev_ops = {
> .ndo_open = cxgb_open,
> .ndo_stop = cxgb_close,
>@@ -3050,6 +3078,7 @@ static const struct net_device_ops cxgb4_netdev_ops = {
> .ndo_busy_poll = cxgb_busy_poll,
> #endif
> .ndo_set_tx_maxrate = cxgb_set_tx_maxrate,
>+ .ndo_setup_tc = cxgb_setup_tc,
> };
>
> #ifdef CONFIG_PCI_IOV
>@@ -4781,6 +4810,7 @@ static void free_some_resources(struct adapter *adapter)
> t4_free_mem(adapter->l2t);
> t4_cleanup_sched(adapter);
> t4_free_mem(adapter->tids.tid_tab);
>+ cxgb4_cleanup_tc_u32(adapter);
> kfree(adapter->sge.egr_map);
> kfree(adapter->sge.ingr_map);
> kfree(adapter->sge.starving_fl);
>@@ -5213,6 +5243,12 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
> dev_warn(&pdev->dev, "could not allocate TID table, "
> "continuing\n");
> adapter->params.offload = 0;
>+ } else {
>+ adapter->tc_u32 = cxgb4_init_tc_u32(adapter,
>+ CXGB4_MAX_LINK_HANDLE);
>+ if (!adapter->tc_u32)
>+ dev_warn(&pdev->dev,
>+ "could not offload tc u32, continuing\n");
> }
>
> if (is_offload(adapter)) {
>diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c
>new file mode 100644
>index 0000000..62c1695
>--- /dev/null
>+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c
>@@ -0,0 +1,343 @@
>+/*
>+ * This file is part of the Chelsio T4 Ethernet driver for Linux.
>+ *
>+ * Copyright (c) 2016 Chelsio Communications, Inc. All rights reserved.
>+ *
>+ * This software is available to you under a choice of one of two
>+ * licenses. You may choose to be licensed under the terms of the GNU
>+ * General Public License (GPL) Version 2, available from the file
>+ * COPYING in the main directory of this source tree, or the
>+ * OpenIB.org BSD license below:
>+ *
>+ * Redistribution and use in source and binary forms, with or
>+ * without modification, are permitted provided that the following
>+ * conditions are met:
>+ *
>+ * - Redistributions of source code must retain the above
>+ * copyright notice, this list of conditions and the following
>+ * disclaimer.
>+ *
>+ * - Redistributions in binary form must reproduce the above
>+ * copyright notice, this list of conditions and the following
>+ * disclaimer in the documentation and/or other materials
>+ * provided with the distribution.
>+ *
>+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
>+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
>+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
>+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
>+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
>+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
>+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
>+ * SOFTWARE.
>+ */
>+
>+#include "cxgb4.h"
>+#include "cxgb4_tc_u32_parse.h"
>+#include "cxgb4_tc_u32.h"
>+
>+/* Fill ch_filter_specification with parsed match value/mask pair. */
>+static int fill_match_fields(struct adapter *adap,
>+ struct ch_filter_specification *fs,
>+ struct tc_cls_u32_offload *cls,
>+ const struct cxgb4_match_field *entry,
>+ bool next_header)
>+{
>+ unsigned int i, j;
>+ int off;
>+ u32 val, mask;
>+ int err;
>+ bool found = false;
>+
>+ for (i = 0; i < cls->knode.sel->nkeys; i++) {
>+ off = cls->knode.sel->keys[i].off;
>+ val = cls->knode.sel->keys[i].val;
>+ mask = cls->knode.sel->keys[i].mask;
>+
>+ if (next_header) {
>+ /* For next headers, parse only keys with offmask */
>+ if (!cls->knode.sel->keys[i].offmask)
>+ continue;
>+ } else {
>+ /* For the remaining, parse only keys without offmask */
>+ if (cls->knode.sel->keys[i].offmask)
>+ continue;
>+ }
>+
>+ found = false;
>+
>+ for (j = 0; entry[j].val; j++) {
>+ if (off == entry[j].off) {
>+ found = true;
>+ err = entry[j].val(fs, val, mask);
>+ if (err)
>+ return err;
>+ break;
>+ }
>+ }
>+
>+ if (!found)
>+ return -EINVAL;
>+ }
>+
>+ return 0;
>+}
>+
>+int cxgb4_config_knode(struct net_device *dev, __be16 protocol,
>+ struct tc_cls_u32_offload *cls)
>+{
>+ struct adapter *adapter = netdev2adap(dev);
>+ struct cxgb4_tc_u32_table *t;
>+ const struct cxgb4_match_field *start, *link_start = NULL;
>+ struct cxgb4_link *link;
>+ struct ch_filter_specification fs;
>+ struct filter_ctx ctx;
>+ unsigned int filter_id;
>+ u32 uhtid, link_uhtid;
>+ int ret;
>+ bool is_ipv6 = false;
>+
>+ if (!can_tc_u32_offload(dev))
>+ return -EOPNOTSUPP;
>+
>+ if ((protocol != htons(ETH_P_IP)) && (protocol != htons(ETH_P_IPV6)))
You use a lot of unnecessary ()
>+ return -EOPNOTSUPP;
>+
>+ /* Fetch the location to insert the filter. */
>+ filter_id = (cls->knode.handle & 0xFFFFF);
here as well
>+
>+ if (filter_id > adapter->tids.nftids) {
>+ dev_err(adapter->pdev_dev,
>+ "Location %d out of range for insertion. Max: %d\n",
>+ filter_id, adapter->tids.nftids);
>+ return -ERANGE;
>+ }
>+
>+ t = adapter->tc_u32;
>+ uhtid = TC_U32_USERHTID(cls->knode.handle);
>+ link_uhtid = TC_U32_USERHTID(cls->knode.link_handle);
>+
>+ /* Ensure that uhtid is either root u32 (i.e. 0x800)
>+ * or a a valid linked bucket.
>+ */
>+ if (uhtid != 0x800 && uhtid >= t->size)
>+ return -EINVAL;
>+
>+ /* Ensure link handle uhtid is sane, if specified. */
>+ if (link_uhtid >= t->size)
>+ return -EINVAL;
>+
>+ memset(&fs, 0, sizeof(fs));
>+
>+ if (protocol == htons(ETH_P_IPV6)) {
>+ start = cxgb4_ipv6_fields;
>+ is_ipv6 = true;
>+ } else {
>+ start = cxgb4_ipv4_fields;
>+ is_ipv6 = false;
>+ }
>+
>+ if (uhtid != 0x800) {
>+ /* Link must exist from root node before insertion. */
>+ if (!t->table[uhtid - 1].link_handle)
>+ return -EINVAL;
>+
>+ /* Link must have a valid supported next header. */
>+ link_start = (&t->table[uhtid - 1])->match_field;
here as well
>+ if (!link_start)
>+ return -EINVAL;
>+ }
>+
>+ /* Parse links and record them for subsequent jumps to valid
>+ * next headers.
>+ */
>+ if (link_uhtid) {
>+ const struct cxgb4_next_header *next;
>+ unsigned int i, j;
>+ int off;
>+ u32 val, mask;
>+ bool found = false;
>+
>+ if (t->table[link_uhtid - 1].link_handle) {
>+ dev_err(adapter->pdev_dev,
>+ "Link handle exists for: 0x%x\n",
>+ link_uhtid);
>+ return -EINVAL;
>+ }
>+
>+ next = is_ipv6 ? cxgb4_ipv6_jumps : cxgb4_ipv4_jumps;
>+
>+ /* Try to find matches that allow jumps to next header. */
>+ for (i = 0; next[i].jump; i++) {
>+ if (next[i].offoff != cls->knode.sel->offoff ||
>+ next[i].shift != cls->knode.sel->offshift ||
>+ next[i].mask != cls->knode.sel->offmask ||
>+ next[i].offset != cls->knode.sel->off)
>+ continue;
>+
>+ /* Found a possible candidate. Find a key that
>+ * matches the corresponding offset, value, and
>+ * mask to jump to next header.
>+ */
>+ for (j = 0; j < cls->knode.sel->nkeys; j++) {
>+ off = cls->knode.sel->keys[j].off;
>+ val = cls->knode.sel->keys[j].val;
>+ mask = cls->knode.sel->keys[j].mask;
>+
>+ if (next[i].match_off == off &&
>+ next[i].match_val == val &&
>+ next[i].match_mask == mask) {
>+ found = true;
>+ break;
>+ }
>+ }
>+
>+ if (!found)
>+ continue; /* Try next candidate. */
>+
>+ /* Candidate to jump to next header found.
>+ * Translate all keys to internal specification
>+ * and store them in jump table. This spec is copied
>+ * later to set the actual filters.
>+ */
>+ ret = fill_match_fields(adapter, &fs, cls,
>+ start, false);
>+ if (ret)
>+ goto out;
>+
>+ link = &t->table[link_uhtid - 1];
>+ link->match_field = next[i].jump;
>+ link->link_handle = cls->knode.handle;
>+ memcpy(&link->fs, &fs, sizeof(fs));
>+ break;
>+ }
>+
>+ /* No candidate found to jump to next header. */
>+ if (!found)
>+ return -EINVAL;
>+
>+ return 0;
>+ }
>+
>+ /* Fill ch_filter_specification match fields to be shipped to hardware.
>+ * Copy the linked spec (if any) first. And then update the spec as
>+ * needed.
>+ */
>+ if ((uhtid != 0x800) && t->table[uhtid - 1].link_handle) {
>+ /* Copy linked ch_filter_specification */
>+ memcpy(&fs, &t->table[uhtid - 1].fs, sizeof(fs));
>+ ret = fill_match_fields(adapter, &fs, cls,
>+ link_start, true);
>+ if (ret)
>+ goto out;
>+ }
>+
>+ ret = fill_match_fields(adapter, &fs, cls, start, false);
>+ if (ret)
>+ goto out;
>+
>+ /* The filter spec has been completely built from the info
>+ * provided from u32. We now set some default fields in the
>+ * spec for sanity.
>+ */
>+
>+ /* Match only packets coming from the ingress port where this
>+ * filter will be created.
>+ */
>+ fs.val.iport = netdev2pinfo(dev)->port_id;
>+ fs.mask.iport = ~0;
>+
>+ /* Enable filter hit counts. */
>+ fs.hitcnts = 1;
>+
>+ /* Set type of filter - IPv6 or IPv4 */
>+ fs.type = is_ipv6 ? 1 : 0;
>+
>+ init_completion(&ctx.completion);
>+
>+ /* Set the filter */
>+ ret = cxgb4_set_filter(dev, filter_id, &fs, &ctx);
>+ if (ret)
>+ goto out;
>+
>+ /* Wait for reply */
>+ ret = wait_for_completion_timeout(&ctx.completion, 10 * HZ);
>+ if (!ret)
>+ return -ETIMEDOUT;
>+
>+ ret = ctx.result;
>+ if (!ret) {
>+ /* If this is a linked bucket, then set the corresponding
>+ * entry in the bitmap to mark it as belonging to this linked
>+ * bucket.
>+ */
>+ if ((uhtid != 0x800) && t->table[uhtid - 1].link_handle)
>+ set_bit(filter_id, (&t->table[uhtid - 1])->tid_map);
>+ }
>+
>+out:
>+ return ret;
>+}
>+
>+void cxgb4_cleanup_tc_u32(struct adapter *adap)
>+{
>+ struct cxgb4_tc_u32_table *t;
>+ unsigned int i;
>+
>+ if (!adap->tc_u32)
>+ return;
>+
>+ /* Free up all allocated memory. */
>+ t = adap->tc_u32;
>+ for (i = 0; i < t->size; i++) {
>+ struct cxgb4_link *link = &t->table[i];
>+
>+ t4_free_mem(link->tid_map);
>+ }
>+ t4_free_mem(adap->tc_u32);
>+}
>+
>+struct cxgb4_tc_u32_table *cxgb4_init_tc_u32(struct adapter *adap,
>+ unsigned int size)
>+{
>+ struct cxgb4_tc_u32_table *t;
>+ unsigned int i;
>+
>+ if (!size)
>+ return NULL;
>+
>+ t = t4_alloc_mem(sizeof(*t) +
>+ (size * sizeof(struct cxgb4_link)));
>+ if (!t)
>+ return NULL;
>+
>+ t->size = size;
>+
>+ for (i = 0; i < t->size; i++) {
>+ struct cxgb4_link *link = &t->table[i];
>+ unsigned int bmap_size;
>+ unsigned int max_tids;
>+
>+ max_tids = adap->tids.nftids;
>+ bmap_size = BITS_TO_LONGS(max_tids);
>+ link->tid_map = t4_alloc_mem(sizeof(unsigned long) * bmap_size);
>+ if (!link->tid_map)
>+ goto out_no_mem;
>+ bitmap_zero(link->tid_map, max_tids);
>+ }
>+
>+ return t;
>+
>+out_no_mem:
>+ for (i = 0; i < t->size; i++) {
>+ struct cxgb4_link *link = &t->table[i];
>+
>+ if (link->tid_map)
>+ t4_free_mem(link->tid_map);
>+ }
>+
>+ if (t)
>+ t4_free_mem(t);
>+
>+ return NULL;
>+}
>diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h
>new file mode 100644
>index 0000000..46575843
>--- /dev/null
>+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h
>@@ -0,0 +1,55 @@
>+/*
>+ * This file is part of the Chelsio T4 Ethernet driver for Linux.
>+ *
>+ * Copyright (c) 2016 Chelsio Communications, Inc. All rights reserved.
>+ *
>+ * This software is available to you under a choice of one of two
>+ * licenses. You may choose to be licensed under the terms of the GNU
>+ * General Public License (GPL) Version 2, available from the file
>+ * COPYING in the main directory of this source tree, or the
>+ * OpenIB.org BSD license below:
>+ *
>+ * Redistribution and use in source and binary forms, with or
>+ * without modification, are permitted provided that the following
>+ * conditions are met:
>+ *
>+ * - Redistributions of source code must retain the above
>+ * copyright notice, this list of conditions and the following
>+ * disclaimer.
>+ *
>+ * - Redistributions in binary form must reproduce the above
>+ * copyright notice, this list of conditions and the following
>+ * disclaimer in the documentation and/or other materials
>+ * provided with the distribution.
>+ *
>+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
>+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
>+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
>+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
>+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
>+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
>+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
>+ * SOFTWARE.
>+ */
>+
>+#ifndef __CXGB4_TC_U32_H
>+#define __CXGB4_TC_U32_H
>+
>+#include <net/pkt_cls.h>
>+
>+#define CXGB4_MAX_LINK_HANDLE 32
>+
>+static inline bool can_tc_u32_offload(struct net_device *dev)
>+{
>+ struct adapter *adap = netdev2adap(dev);
>+
>+ return (dev->features & NETIF_F_HW_TC) && adap->tc_u32 ? true : false;
>+}
>+
>+int cxgb4_config_knode(struct net_device *dev, __be16 protocol,
>+ struct tc_cls_u32_offload *cls);
>+
>+void cxgb4_cleanup_tc_u32(struct adapter *adapter);
>+struct cxgb4_tc_u32_table *cxgb4_init_tc_u32(struct adapter *adap,
>+ unsigned int size);
>+#endif /* __CXGB4_TC_U32_H */
>diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32_parse.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32_parse.h
>index 261aa4a..de321bf 100644
>--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32_parse.h
>+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32_parse.h
>@@ -279,4 +279,16 @@ static const struct cxgb4_next_header cxgb4_ipv6_jumps[] = {
> .jump = cxgb4_udp_fields },
> { .jump = NULL }
> };
>+
>+struct cxgb4_link {
>+ const struct cxgb4_match_field *match_field; /* Next header */
>+ struct ch_filter_specification fs; /* Match spec associated with link */
>+ u32 link_handle; /* Knode handle associated with the link */
>+ unsigned long *tid_map; /* Bitmap for filter tids */
>+};
>+
>+struct cxgb4_tc_u32_table {
>+ unsigned int size; /* number of entries in table */
>+ struct cxgb4_link table[0]; /* Jump table */
>+};
> #endif /* __CXGB4_TC_U32_PARSE_H */
>--
>2.5.3
>
^ permalink raw reply
* [PATCH 3/3] net-next: dsa: add new driver for qca8xxx family
From: John Crispin @ 2016-09-12 8:35 UTC (permalink / raw)
To: David S. Miller, Andrew Lunn, Florian Fainelli
Cc: netdev, linux-kernel, qsdk-review, John Crispin
In-Reply-To: <1473669337-21221-1-git-send-email-john@phrozen.org>
This patch contains initial support for the QCA8337 switch. It
will detect a QCA8337 switch, if present and declared in the DT.
Each port will be represented through a standalone net_device interface,
as for other DSA switches. CPU can communicate with any of the ports by
setting an IP@ on ethN interface. Most of the extra callbacks of the DSA
subsystem are already supported, such as bridge offloading, stp, fdb.
Signed-off-by: John Crispin <john@phrozen.org>
---
drivers/net/dsa/Kconfig | 9 +
drivers/net/dsa/Makefile | 1 +
drivers/net/dsa/qca8k.c | 969 ++++++++++++++++++++++++++++++++++++++++++++++
drivers/net/dsa/qca8k.h | 201 ++++++++++
4 files changed, 1180 insertions(+)
create mode 100644 drivers/net/dsa/qca8k.c
create mode 100644 drivers/net/dsa/qca8k.h
diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
index de6d044..0659846 100644
--- a/drivers/net/dsa/Kconfig
+++ b/drivers/net/dsa/Kconfig
@@ -25,4 +25,13 @@ source "drivers/net/dsa/b53/Kconfig"
source "drivers/net/dsa/mv88e6xxx/Kconfig"
+config NET_DSA_QCA8K
+ tristate "Qualcomm Atheros QCA8K Ethernet switch family support"
+ depends on NET_DSA
+ select NET_DSA_TAG_QCA
+ select REGMAP
+ ---help---
+ This enables support for the Qualcomm Atheros QCA8K Ethernet
+ switch chips.
+
endmenu
diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile
index ca1e71b..8346e4f 100644
--- a/drivers/net/dsa/Makefile
+++ b/drivers/net/dsa/Makefile
@@ -1,5 +1,6 @@
obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e6060.o
obj-$(CONFIG_NET_DSA_BCM_SF2) += bcm_sf2.o
+obj-$(CONFIG_NET_DSA_QCA8K) += qca8k.o
obj-y += b53/
obj-y += mv88e6xxx/
diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c
new file mode 100644
index 0000000..4d0c138
--- /dev/null
+++ b/drivers/net/dsa/qca8k.c
@@ -0,0 +1,969 @@
+/*
+ * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
+ * Copyright (C) 2011-2012 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016 John Crispin <john@phrozen.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/phy.h>
+#include <linux/netdevice.h>
+#include <net/dsa.h>
+#include <net/switchdev.h>
+#include <linux/phy.h>
+#include <linux/of_net.h>
+#include <linux/of_platform.h>
+#include <linux/if_bridge.h>
+#include <linux/mdio.h>
+#include <linux/etherdevice.h>
+
+#include "qca8k.h"
+
+#define MIB_DESC(_s, _o, _n) \
+ { \
+ .size = (_s), \
+ .offset = (_o), \
+ .name = (_n), \
+ }
+
+static const struct qca8k_mib_desc ar8327_mib[] = {
+ MIB_DESC(1, 0x00, "RxBroad"),
+ MIB_DESC(1, 0x04, "RxPause"),
+ MIB_DESC(1, 0x08, "RxMulti"),
+ MIB_DESC(1, 0x0c, "RxFcsErr"),
+ MIB_DESC(1, 0x10, "RxAlignErr"),
+ MIB_DESC(1, 0x14, "RxRunt"),
+ MIB_DESC(1, 0x18, "RxFragment"),
+ MIB_DESC(1, 0x1c, "Rx64Byte"),
+ MIB_DESC(1, 0x20, "Rx128Byte"),
+ MIB_DESC(1, 0x24, "Rx256Byte"),
+ MIB_DESC(1, 0x28, "Rx512Byte"),
+ MIB_DESC(1, 0x2c, "Rx1024Byte"),
+ MIB_DESC(1, 0x30, "Rx1518Byte"),
+ MIB_DESC(1, 0x34, "RxMaxByte"),
+ MIB_DESC(1, 0x38, "RxTooLong"),
+ MIB_DESC(2, 0x3c, "RxGoodByte"),
+ MIB_DESC(2, 0x44, "RxBadByte"),
+ MIB_DESC(1, 0x4c, "RxOverFlow"),
+ MIB_DESC(1, 0x50, "Filtered"),
+ MIB_DESC(1, 0x54, "TxBroad"),
+ MIB_DESC(1, 0x58, "TxPause"),
+ MIB_DESC(1, 0x5c, "TxMulti"),
+ MIB_DESC(1, 0x60, "TxUnderRun"),
+ MIB_DESC(1, 0x64, "Tx64Byte"),
+ MIB_DESC(1, 0x68, "Tx128Byte"),
+ MIB_DESC(1, 0x6c, "Tx256Byte"),
+ MIB_DESC(1, 0x70, "Tx512Byte"),
+ MIB_DESC(1, 0x74, "Tx1024Byte"),
+ MIB_DESC(1, 0x78, "Tx1518Byte"),
+ MIB_DESC(1, 0x7c, "TxMaxByte"),
+ MIB_DESC(1, 0x80, "TxOverSize"),
+ MIB_DESC(2, 0x84, "TxByte"),
+ MIB_DESC(1, 0x8c, "TxCollision"),
+ MIB_DESC(1, 0x90, "TxAbortCol"),
+ MIB_DESC(1, 0x94, "TxMultiCol"),
+ MIB_DESC(1, 0x98, "TxSingleCol"),
+ MIB_DESC(1, 0x9c, "TxExcDefer"),
+ MIB_DESC(1, 0xa0, "TxDefer"),
+ MIB_DESC(1, 0xa4, "TxLateCol"),
+};
+
+/* The 32bit switch registers are accessed indirectly. To achieve this we need
+ * to set the page of the register. Track the last page that was set to reduce
+ * mdio writes
+ */
+static u16 qca8k_current_page = 0xffff;
+
+static u32
+qca8k_mii_read32(struct mii_bus *bus, int phy_id, u32 regnum)
+{
+ u16 lo, hi;
+
+ lo = bus->read(bus, phy_id, regnum);
+ hi = bus->read(bus, phy_id, regnum + 1);
+
+ return (hi << 16) | lo;
+}
+
+static void
+qca8k_mii_write32(struct mii_bus *bus, int phy_id, u32 regnum, u32 val)
+{
+ u16 lo, hi;
+
+ lo = val & 0xffff;
+ hi = (u16)(val >> 16);
+
+ bus->write(bus, phy_id, regnum, lo);
+ bus->write(bus, phy_id, regnum + 1, hi);
+}
+
+static void
+qca8k_set_page(struct mii_bus *bus, u16 page)
+{
+ if (page == qca8k_current_page)
+ return;
+
+ bus->write(bus, 0x18, 0, page);
+ udelay(5);
+ qca8k_current_page = page;
+}
+
+static u32
+qca8k_read(struct qca8k_priv *priv, u32 reg)
+{
+ u16 r1, r2, page;
+ u32 val;
+
+ qca8k_split_addr(reg, &r1, &r2, &page);
+
+ mutex_lock(&priv->bus->mdio_lock);
+
+ qca8k_set_page(priv->bus, page);
+ val = qca8k_mii_read32(priv->bus, 0x10 | r2, r1);
+
+ mutex_unlock(&priv->bus->mdio_lock);
+
+ return val;
+}
+
+static void
+qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val)
+{
+ u16 r1, r2, page;
+
+ qca8k_split_addr(reg, &r1, &r2, &page);
+
+ mutex_lock(&priv->bus->mdio_lock);
+
+ qca8k_set_page(priv->bus, page);
+ qca8k_mii_write32(priv->bus, 0x10 | r2, r1, val);
+
+ mutex_unlock(&priv->bus->mdio_lock);
+}
+
+static u32
+qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 val)
+{
+ u16 r1, r2, page;
+ u32 ret;
+
+ qca8k_split_addr(reg, &r1, &r2, &page);
+
+ mutex_lock(&priv->bus->mdio_lock);
+
+ qca8k_set_page(priv->bus, page);
+ ret = qca8k_mii_read32(priv->bus, 0x10 | r2, r1);
+ ret &= ~mask;
+ ret |= val;
+ qca8k_mii_write32(priv->bus, 0x10 | r2, r1, ret);
+
+ mutex_unlock(&priv->bus->mdio_lock);
+
+ return ret;
+}
+
+static inline void
+qca8k_reg_set(struct qca8k_priv *priv, u32 reg, u32 val)
+{
+ qca8k_rmw(priv, reg, 0, val);
+}
+
+static inline void
+qca8k_reg_clear(struct qca8k_priv *priv, u32 reg, u32 val)
+{
+ qca8k_rmw(priv, reg, val, 0);
+}
+
+static u16
+qca8k_phy_mmd_read(struct qca8k_priv *priv, int phy_addr, u16 addr, u16 reg)
+{
+ u16 data;
+
+ mutex_lock(&priv->bus->mdio_lock);
+
+ priv->bus->write(priv->bus, phy_addr, MII_ATH_MMD_ADDR, addr);
+ priv->bus->write(priv->bus, phy_addr, MII_ATH_MMD_DATA, reg);
+ priv->bus->write(priv->bus, phy_addr, MII_ATH_MMD_ADDR, addr | 0x4000);
+ data = priv->bus->read(priv->bus, phy_addr, MII_ATH_MMD_DATA);
+
+ mutex_unlock(&priv->bus->mdio_lock);
+
+ return data;
+}
+
+static int
+qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *val)
+{
+ struct qca8k_priv *priv = (struct qca8k_priv *)ctx;
+
+ *val = qca8k_read(priv, reg);
+
+ return 0;
+}
+
+static int
+qca8k_regmap_write(void *ctx, uint32_t reg, uint32_t val)
+{
+ struct qca8k_priv *priv = (struct qca8k_priv *)ctx;
+
+ qca8k_write(priv, reg, val);
+
+ return 0;
+}
+
+static const struct regmap_range qca8k_readable_ranges[] = {
+ regmap_reg_range(0x0000, 0x00e4), /* Global control */
+ regmap_reg_range(0x0100, 0x0168), /* EEE control */
+ regmap_reg_range(0x0200, 0x0270), /* Parser control */
+ regmap_reg_range(0x0400, 0x0454), /* ACL */
+ regmap_reg_range(0x0600, 0x0718), /* Lookup */
+ regmap_reg_range(0x0800, 0x0b70), /* QM */
+ regmap_reg_range(0x0c00, 0x0c80), /* PKT */
+ regmap_reg_range(0x0e00, 0x0e98), /* L3 */
+ regmap_reg_range(0x1000, 0x10ac), /* MIB - Port0 */
+ regmap_reg_range(0x1100, 0x11ac), /* MIB - Port1 */
+ regmap_reg_range(0x1200, 0x12ac), /* MIB - Port2 */
+ regmap_reg_range(0x1300, 0x13ac), /* MIB - Port3 */
+ regmap_reg_range(0x1400, 0x14ac), /* MIB - Port4 */
+ regmap_reg_range(0x1500, 0x15ac), /* MIB - Port5 */
+ regmap_reg_range(0x1600, 0x16ac), /* MIB - Port6 */
+
+};
+
+static struct regmap_access_table qca8k_readable_table = {
+ .yes_ranges = qca8k_readable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(qca8k_readable_ranges),
+};
+
+struct regmap_config qca8k_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = 0x16ac, /* end MIB - Port6 range */
+ .reg_read = qca8k_regmap_read,
+ .reg_write = qca8k_regmap_write,
+ .rd_table = &qca8k_readable_table,
+};
+
+static int
+qca8k_fdb_busy_wait(struct qca8k_priv *priv)
+{
+ unsigned long timeout;
+
+ timeout = jiffies + msecs_to_jiffies(20);
+
+ /* loop until the busy flag has cleared */
+ do {
+ u32 reg = qca8k_read(priv, QCA8K_REG_ATU_FUNC);
+ int busy = reg & QCA8K_ATU_FUNC_BUSY;
+
+ if (!busy)
+ break;
+ } while (!time_after_eq(jiffies, timeout));
+
+ return time_after_eq(jiffies, timeout);
+}
+
+static void
+qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb)
+{
+ u32 reg[4];
+ int i;
+
+ /* load the ARL table into an array */
+ for (i = 0; i < 4; i++)
+ reg[i] = qca8k_read(priv, QCA8K_REG_ATU_DATA0 + (i * 4));
+
+ /* vid - 83:72 */
+ fdb->vid = (reg[2] >> QCA8K_ATU_VID_S) & QCA8K_ATU_VID_M;
+ /* aging - 67:64 */
+ fdb->aging = reg[2] & QCA8K_ATU_STATUS_M;
+ /* portmask - 54:48 */
+ fdb->port_mask = (reg[1] >> QCA8K_ATU_PORT_S) & QCA8K_ATU_PORT_M;
+ /* mac - 47:0 */
+ fdb->mac[0] = (reg[1] >> QCA8K_ATU_ADDR0_S) & 0xff;
+ fdb->mac[1] = reg[1] & 0xff;
+ fdb->mac[2] = (reg[0] >> QCA8K_ATU_ADDR2_S) & 0xff;
+ fdb->mac[3] = (reg[0] >> QCA8K_ATU_ADDR3_S) & 0xff;
+ fdb->mac[4] = (reg[0] >> QCA8K_ATU_ADDR4_S) & 0xff;
+ fdb->mac[5] = reg[0] & 0xff;
+}
+
+static void
+qca8k_fdb_write(struct qca8k_priv *priv, u16 vid, u8 port_mask, const u8 *mac,
+ u8 aging)
+{
+ u32 reg[3] = { 0 };
+ int i;
+
+ /* vid - 83:72 */
+ reg[2] = (vid & QCA8K_ATU_VID_M) << QCA8K_ATU_VID_S;
+ /* aging - 67:64 */
+ reg[2] |= aging & QCA8K_ATU_STATUS_M;
+ /* portmask - 54:48 */
+ reg[1] = (port_mask & QCA8K_ATU_PORT_M) << QCA8K_ATU_PORT_S;
+ /* mac - 47:0 */
+ reg[1] |= mac[0] << QCA8K_ATU_ADDR0_S;
+ reg[1] |= mac[1];
+ reg[0] |= mac[2] << QCA8K_ATU_ADDR2_S;
+ reg[0] |= mac[3] << QCA8K_ATU_ADDR3_S;
+ reg[0] |= mac[4] << QCA8K_ATU_ADDR4_S;
+ reg[0] |= mac[5];
+
+ /* load the array into the ARL table */
+ for (i = 0; i < 3; i++)
+ qca8k_write(priv, QCA8K_REG_ATU_DATA0 + (i * 4), reg[i]);
+}
+
+static int
+qca8k_fdb_access(struct qca8k_priv *priv, enum qca8k_fdb_cmd cmd, int port)
+{
+ u32 reg;
+
+ /* Set the command and FDB index */
+ reg = QCA8K_ATU_FUNC_BUSY;
+ reg |= cmd;
+ if (port >= 0) {
+ reg |= QCA8K_ATU_FUNC_PORT_EN;
+ reg |= (port && QCA8K_ATU_FUNC_PORT_M) << QCA8K_ATU_FUNC_PORT_S;
+ }
+
+ /* Write the function register triggering the table access */
+ qca8k_write(priv, QCA8K_REG_ATU_FUNC, reg);
+
+ /* wait for completion */
+ if (qca8k_fdb_busy_wait(priv))
+ return -1;
+
+ return 0;
+}
+
+static int
+qca8k_fdb_next(struct qca8k_priv *priv, struct qca8k_fdb *fdb, int port)
+{
+ int ret;
+
+ qca8k_fdb_write(priv, fdb->vid, fdb->port_mask, fdb->mac, fdb->aging);
+ ret = qca8k_fdb_access(priv, QCA8K_FDB_NEXT, port);
+ if (ret >= 0)
+ qca8k_fdb_read(priv, fdb);
+
+ return ret;
+}
+
+static void
+qca8k_fdb_flush(struct qca8k_priv *priv)
+{
+ qca8k_fdb_access(priv, QCA8K_FDB_FLUSH, -1);
+}
+
+/* The switch has 2 CPU ports. These can alternatively be configured to
+ * connected directly to one of the PHYs, bypassing the switching core
+ */
+static int
+qca8k_set_pad_ctrl(struct qca8k_priv *priv, int port, int mode)
+{
+ u32 reg;
+
+ switch (port) {
+ case 0:
+ reg = QCA8K_REG_PORT0_PAD_CTRL;
+ break;
+ case 6:
+ reg = QCA8K_REG_PORT6_PAD_CTRL;
+ break;
+ default:
+ pr_err("Can't set PAD_CTRL on port %d\n", port);
+ return -EINVAL;
+ }
+
+ /* Configure a port to be directly connected to a PHY */
+ switch (mode) {
+ case PHY_INTERFACE_MODE_RGMII:
+ qca8k_write(priv, reg,
+ QCA8K_PORT_PAD_RGMII_EN |
+ QCA8K_PORT_PAD_RGMII_TX_DELAY(3) |
+ QCA8K_PORT_PAD_RGMII_RX_DELAY(3));
+
+ /* According to the datasheet, RGMII delay is enabled through
+ * PORT5_PAD_CTRL for all ports, rather than individual port
+ * registers
+ */
+ qca8k_write(priv, QCA8K_REG_PORT5_PAD_CTRL,
+ QCA8K_PORT_PAD_RGMII_RX_DELAY_EN);
+ break;
+ case PHY_INTERFACE_MODE_SGMII:
+ qca8k_write(priv, reg, QCA8K_PORT_PAD_SGMII_EN);
+ break;
+ default:
+ pr_err("xMII mode %d not supported\n", mode);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int
+qca8k_setup(struct dsa_switch *ds)
+{
+ struct qca8k_priv *priv = qca8k_to_priv(ds);
+ int ret, i, phy_mode = -1;
+
+ /* Keep track of which port is the connected to the cpu. This can be
+ * phy 11 / port 0 or phy 5 / port 6.
+ */
+ switch (dsa_upstream_port(ds)) {
+ case 11:
+ priv->cpu_port = 0;
+ break;
+ case 5:
+ priv->cpu_port = 6;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ /* Start by setting up the register mapping */
+ priv->regmap = devm_regmap_init(ds->dev, NULL, priv,
+ &qca8k_regmap_config);
+
+ if (IS_ERR(priv->regmap))
+ pr_warn("regmap initialization failed");
+
+ /* Initialize CPU port pad mode (xMII type, delays...) */
+ phy_mode = of_get_phy_mode(ds->ports[ds->dst->cpu_port].dn);
+ if (phy_mode < 0) {
+ pr_err("Can't find phy-mode for master device\n");
+ return phy_mode;
+ }
+ ret = qca8k_set_pad_ctrl(priv, priv->cpu_port, phy_mode);
+ if (ret < 0)
+ return ret;
+
+ /* Enable CPU Port */
+ qca8k_reg_set(priv, QCA8K_REG_GLOBAL_FW_CTRL0,
+ QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN);
+
+ /* Enable MIB counters */
+ qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP);
+ qca8k_write(priv, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_MIB);
+
+ /* Enable QCA header mode on Port 0 */
+ qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(priv->cpu_port),
+ QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_TX_S |
+ QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_RX_S);
+
+ /* Disable forwarding by default on all ports */
+ for (i = 0; i < QCA8K_NUM_PORTS; i++)
+ qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
+ QCA8K_PORT_LOOKUP_MEMBER, 0);
+
+ /* Disable MAC by default on all ports */
+ for (i = 0; i < QCA8K_NUM_PORTS; i++) {
+ int port = qca8k_phy_to_port(i);
+
+ if (ds->enabled_port_mask & BIT(i))
+ qca8k_rmw(priv, QCA8K_REG_PORT_STATUS(port),
+ QCA8K_PORT_STATUS_LINK_AUTO |
+ QCA8K_PORT_STATUS_TXMAC, 0);
+ }
+
+ /* Forward all unknown frames to CPU port for Linux processing */
+ qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1,
+ BIT(0) << QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_S |
+ BIT(0) << QCA8K_GLOBAL_FW_CTRL1_BC_DP_S |
+ BIT(0) << QCA8K_GLOBAL_FW_CTRL1_MC_DP_S |
+ BIT(0) << QCA8K_GLOBAL_FW_CTRL1_UC_DP_S);
+
+ /* Setup connection between CPU ports & PHYs */
+ for (i = 0; i < DSA_MAX_PORTS; i++) {
+ /* CPU port gets connected to all PHYs in the switch */
+ if (dsa_is_cpu_port(ds, i)) {
+ qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(priv->cpu_port),
+ QCA8K_PORT_LOOKUP_MEMBER,
+ ds->enabled_port_mask << 1);
+ }
+
+ /* Invividual PHYs gets connected to CPU port only */
+ if (ds->enabled_port_mask & BIT(i)) {
+ int port = qca8k_phy_to_port(i);
+ int shift = 16 * (port % 2);
+
+ qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
+ QCA8K_PORT_LOOKUP_MEMBER,
+ BIT(priv->cpu_port));
+
+ /* Enable ARP Auto-learning by default */
+ qca8k_reg_set(priv, QCA8K_PORT_LOOKUP_CTRL(port),
+ QCA8K_PORT_LOOKUP_LEARN);
+
+ /* For port based vlans to work we need to set the
+ * default egress vid
+ */
+ qca8k_rmw(priv, AR8337_EGRESS_VLAN(port),
+ 0xffff << shift, 1 << shift);
+ qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(port),
+ QCA8K_PORT_VLAN_CVID(1) |
+ QCA8K_PORT_VLAN_SVID(1));
+ }
+ }
+
+ /* Flush the FDB table */
+ qca8k_fdb_flush(priv);
+
+ return 0;
+}
+
+static int
+qca8k_set_addr(struct dsa_switch *ds, u8 *addr)
+{
+ /* The subsystem always calls this function so add an empty stub */
+ return 0;
+}
+
+static int
+qca8k_phy_read(struct dsa_switch *ds, int phy, int regnum)
+{
+ struct qca8k_priv *priv = qca8k_to_priv(ds);
+
+ return mdiobus_read(priv->bus, phy, regnum);
+}
+
+static int
+qca8k_phy_write(struct dsa_switch *ds, int phy, int regnum, u16 val)
+{
+ struct qca8k_priv *priv = qca8k_to_priv(ds);
+
+ return mdiobus_write(priv->bus, phy, regnum, val);
+}
+
+static void
+qca8k_get_strings(struct dsa_switch *ds, int phy, uint8_t *data)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ar8327_mib); i++)
+ strncpy(data + i * ETH_GSTRING_LEN, ar8327_mib[i].name,
+ ETH_GSTRING_LEN);
+}
+
+static void
+qca8k_get_ethtool_stats(struct dsa_switch *ds, int phy,
+ uint64_t *data)
+{
+ struct qca8k_priv *priv = qca8k_to_priv(ds);
+ const struct qca8k_mib_desc *mib;
+ u32 reg, i, port;
+ u64 hi;
+
+ port = qca8k_phy_to_port(phy);
+
+ for (i = 0; i < ARRAY_SIZE(ar8327_mib); i++) {
+ mib = &ar8327_mib[i];
+ reg = QCA8K_PORT_MIB_COUNTER(port) + mib->offset;
+
+ data[i] = qca8k_read(priv, reg);
+ if (mib->size == 2) {
+ hi = qca8k_read(priv, reg + 4);
+ data[i] |= hi << 32;
+ }
+ }
+}
+
+static int
+qca8k_get_sset_count(struct dsa_switch *ds)
+{
+ return ARRAY_SIZE(ar8327_mib);
+}
+
+static void
+qca8k_eee_enable_set(struct dsa_switch *ds, int port, bool enable)
+{
+ struct qca8k_priv *priv = qca8k_to_priv(ds);
+ u32 lpi_en = QCA8K_REG_EEE_CTRL_LPI_EN(qca8k_phy_to_port(port));
+ u32 reg;
+
+ reg = qca8k_read(priv, QCA8K_REG_EEE_CTRL);
+ if (enable)
+ reg |= lpi_en;
+ else
+ reg &= ~lpi_en;
+ qca8k_write(priv, QCA8K_REG_EEE_CTRL, reg);
+}
+
+static int
+qca8k_eee_init(struct dsa_switch *ds, int port,
+ struct phy_device *phy)
+{
+ struct qca8k_priv *priv = qca8k_to_priv(ds);
+ struct ethtool_eee *p = &priv->port_sts[qca8k_phy_to_port(port)].eee;
+ int ret;
+
+ p->supported = (SUPPORTED_1000baseT_Full | SUPPORTED_100baseT_Full);
+
+ ret = phy_init_eee(phy, 0);
+ if (ret)
+ return 0;
+
+ qca8k_eee_enable_set(ds, port, true);
+
+ return 1;
+}
+
+static int
+qca8k_set_eee(struct dsa_switch *ds, int port,
+ struct phy_device *phydev,
+ struct ethtool_eee *e)
+{
+ struct qca8k_priv *priv = qca8k_to_priv(ds);
+ struct ethtool_eee *p = &priv->port_sts[qca8k_phy_to_port(port)].eee;
+ int ret = 0;
+
+ p->eee_enabled = e->eee_enabled;
+
+ if (e->eee_enabled) {
+ p->eee_enabled = qca8k_eee_init(ds, port, phydev);
+ if (!p->eee_enabled)
+ ret = -EOPNOTSUPP;
+ }
+ qca8k_eee_enable_set(ds, port, p->eee_enabled);
+
+ return ret;
+}
+
+static int
+qca8k_get_eee(struct dsa_switch *ds, int port,
+ struct ethtool_eee *e)
+{
+ struct qca8k_priv *priv = qca8k_to_priv(ds);
+ struct ethtool_eee *p = &priv->port_sts[qca8k_phy_to_port(port)].eee;
+ u32 lp, adv, supported;
+ u16 val;
+
+ /* The switch has no way to tell the result of the AN so we need to
+ * read the result directly from the PHYs MMD registers
+ */
+ val = qca8k_phy_mmd_read(priv, port, MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE);
+ supported = mmd_eee_cap_to_ethtool_sup_t(val);
+
+ val = qca8k_phy_mmd_read(priv, port, MDIO_MMD_AN, MDIO_AN_EEE_ADV);
+ adv = mmd_eee_adv_to_ethtool_adv_t(val);
+
+ val = qca8k_phy_mmd_read(priv, port, MDIO_MMD_AN, MDIO_AN_EEE_LPABLE);
+ lp = mmd_eee_adv_to_ethtool_adv_t(val);
+
+ e->eee_enabled = p->eee_enabled;
+ e->eee_active = !!(supported & adv & lp);
+
+ return 0;
+}
+
+static void
+ar8xxx_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
+{
+ struct qca8k_priv *priv = qca8k_to_priv(ds);
+ u32 stp_state;
+
+ switch (state) {
+ case BR_STATE_DISABLED:
+ stp_state = QCA8K_PORT_LOOKUP_STATE_DISABLED;
+ break;
+ case BR_STATE_BLOCKING:
+ stp_state = QCA8K_PORT_LOOKUP_STATE_BLOCKING;
+ break;
+ case BR_STATE_LISTENING:
+ stp_state = QCA8K_PORT_LOOKUP_STATE_LISTENING;
+ break;
+ case BR_STATE_LEARNING:
+ stp_state = QCA8K_PORT_LOOKUP_STATE_LEARNING;
+ break;
+ case BR_STATE_FORWARDING:
+ default:
+ stp_state = QCA8K_PORT_LOOKUP_STATE_FORWARD;
+ break;
+ }
+
+ qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(qca8k_phy_to_port(port)),
+ QCA8K_PORT_LOOKUP_STATE_MASK, stp_state);
+}
+
+static int
+qca8k_port_bridge_join(struct dsa_switch *ds, int _port,
+ struct net_device *bridge)
+{
+ struct qca8k_priv *priv = qca8k_to_priv(ds);
+ int port_mask = BIT(priv->cpu_port);
+ int port = qca8k_phy_to_port(_port);
+ int i;
+
+ priv->port_sts[port].bridge_dev = bridge;
+
+ for (i = 0; i < QCA8K_NUM_PORTS; i++) {
+ if (priv->port_sts[i].bridge_dev != bridge)
+ continue;
+ /* Add this port to the portvlan mask of the other ports
+ * in the bridge
+ */
+ qca8k_reg_set(priv,
+ QCA8K_PORT_LOOKUP_CTRL(qca8k_phy_to_port(i)),
+ BIT(port));
+ if (i != port)
+ port_mask |= BIT(qca8k_phy_to_port(i));
+ }
+ /* Add all other ports to this ports portvlan mask */
+ qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
+ QCA8K_PORT_LOOKUP_MEMBER, port_mask);
+
+ return 0;
+}
+
+static void
+qca8k_port_bridge_leave(struct dsa_switch *ds, int _port)
+{
+ struct qca8k_priv *priv = qca8k_to_priv(ds);
+ int port = qca8k_phy_to_port(_port);
+ int i;
+
+ for (i = 0; i < QCA8K_NUM_PORTS; i++) {
+ if (priv->port_sts[i].bridge_dev !=
+ priv->port_sts[port].bridge_dev)
+ continue;
+ /* Remove this port to the portvlan mask of the other ports
+ * in the bridge
+ */
+ qca8k_reg_clear(priv,
+ QCA8K_PORT_LOOKUP_CTRL(qca8k_phy_to_port(i)),
+ BIT(port));
+ }
+ priv->port_sts[port].bridge_dev = NULL;
+ /* Set the cpu port to be the only one in the portvlan mask of
+ * this port
+ */
+ qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
+ QCA8K_PORT_LOOKUP_MEMBER, BIT(priv->cpu_port));
+}
+
+static int
+qca8k_port_enable(struct dsa_switch *ds, int _port,
+ struct phy_device *phy)
+{
+ struct qca8k_priv *priv = qca8k_to_priv(ds);
+ int port = qca8k_phy_to_port(_port);
+
+ qca8k_reg_set(priv, QCA8K_REG_PORT_STATUS(port),
+ QCA8K_PORT_STATUS_LINK_AUTO | QCA8K_PORT_STATUS_TXMAC);
+
+ return 0;
+}
+
+static void
+qca8k_port_disable(struct dsa_switch *ds, int port,
+ struct phy_device *phy)
+{
+ struct qca8k_priv *priv = qca8k_to_priv(ds);
+
+ qca8k_reg_clear(priv, QCA8K_REG_PORT_STATUS(port),
+ QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_LINK_AUTO);
+}
+
+static int
+qca8k_fdb_prepare(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_fdb *fdb,
+ struct switchdev_trans *trans)
+{
+ /* We do not need to do anything specific here yet */
+ return 0;
+}
+
+static void
+qca8k_fdb_add(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_fdb *fdb,
+ struct switchdev_trans *trans)
+{
+ struct qca8k_priv *priv = qca8k_to_priv(ds);
+ u16 port_mask = BIT(qca8k_phy_to_port(port));
+
+ qca8k_fdb_write(priv, fdb->vid, port_mask, fdb->addr,
+ QCA8K_ATU_STATUS_STATIC);
+
+ qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1);
+}
+
+static int
+qca8k_fdb_del(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_fdb *fdb)
+{
+ struct qca8k_priv *priv = qca8k_to_priv(ds);
+ u16 port_mask = BIT(qca8k_phy_to_port(port));
+
+ qca8k_fdb_write(priv, fdb->vid, port_mask, fdb->addr, 0);
+
+ return qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1);
+}
+
+static int
+qca8k_fdb_dump(struct dsa_switch *ds, int port,
+ struct switchdev_obj_port_fdb *fdb,
+ int (*cb)(struct switchdev_obj *obj))
+{
+ struct qca8k_priv *priv = qca8k_to_priv(ds);
+ struct qca8k_fdb _fdb = { 0 };
+ int cnt = QCA8K_NUM_FDB_RECORDS;
+
+ while (cnt-- && !qca8k_fdb_next(priv, &_fdb, qca8k_phy_to_port(port))) {
+ int ret;
+
+ if (!_fdb.aging)
+ break;
+
+ ether_addr_copy(fdb->addr, _fdb.mac);
+ fdb->vid = _fdb.vid;
+ if (_fdb.aging == QCA8K_ATU_STATUS_STATIC)
+ fdb->ndm_state = NUD_NOARP;
+ else
+ fdb->ndm_state = NUD_REACHABLE;
+
+ ret = cb(&fdb->obj);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static enum dsa_tag_protocol
+qca8k_get_tag_protocol(struct dsa_switch *ds)
+{
+ return DSA_TAG_PROTO_QCA;
+}
+
+static struct dsa_switch_ops qca8k_switch_ops = {
+ .get_tag_protocol = qca8k_get_tag_protocol,
+ .setup = qca8k_setup,
+ .set_addr = qca8k_set_addr,
+ .phy_read = qca8k_phy_read,
+ .phy_write = qca8k_phy_write,
+ .get_strings = qca8k_get_strings,
+ .get_ethtool_stats = qca8k_get_ethtool_stats,
+ .get_sset_count = qca8k_get_sset_count,
+ .get_eee = qca8k_get_eee,
+ .set_eee = qca8k_set_eee,
+ .port_enable = qca8k_port_enable,
+ .port_disable = qca8k_port_disable,
+ .port_stp_state_set = ar8xxx_port_stp_state_set,
+ .port_bridge_join = qca8k_port_bridge_join,
+ .port_bridge_leave = qca8k_port_bridge_leave,
+ .port_fdb_prepare = qca8k_fdb_prepare,
+ .port_fdb_add = qca8k_fdb_add,
+ .port_fdb_del = qca8k_fdb_del,
+ .port_fdb_dump = qca8k_fdb_dump,
+};
+
+static int
+qca8k_sw_probe(struct mdio_device *mdiodev)
+{
+ struct qca8k_priv *priv;
+ u32 phy_id;
+
+ /* sw_addr is irrelevant as the switch occupies the MDIO bus from
+ * addresses 0 to 4 (PHYs) and 16-23 (for MDIO 32bits protocol). So
+ * we'll probe address 0 to see if we see the right switch family.
+ */
+ phy_id = mdiobus_read(mdiodev->bus, 0, MII_PHYSID1) << 16;
+ phy_id |= mdiobus_read(mdiodev->bus, 0, MII_PHYSID2);
+
+ switch (phy_id) {
+ case PHY_ID_QCA8337:
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), GFP_KERNEL);
+ if (!priv->ds)
+ return -ENOMEM;
+
+ priv->ds->priv = priv;
+ priv->ds->dev = &mdiodev->dev;
+ priv->ds->ops = &qca8k_switch_ops;
+ priv->bus = mdiodev->bus;
+ dev_set_drvdata(&mdiodev->dev, priv);
+
+ return dsa_register_switch(priv->ds, priv->ds->dev->of_node);
+}
+
+static void
+qca8k_sw_remove(struct mdio_device *mdiodev)
+{
+ struct qca8k_priv *priv = dev_get_drvdata(&mdiodev->dev);
+
+ dsa_unregister_switch(priv->ds);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int qca8k_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct qca8k_priv *priv = platform_get_drvdata(pdev);
+
+ return dsa_switch_suspend(priv->ds);
+}
+
+static int qca8k_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct qca8k_priv *priv = platform_get_drvdata(pdev);
+
+ return dsa_switch_resume(priv->ds);
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static SIMPLE_DEV_PM_OPS(qca8k_pm_ops,
+ qca8k_suspend, qca8k_resume);
+
+static const struct of_device_id qca8k_of_match[] = {
+ { .compatible = "qca,qca8337" },
+ { /* sentinel */ },
+};
+
+static struct mdio_driver qca8kmdio_driver = {
+ .probe = qca8k_sw_probe,
+ .remove = qca8k_sw_remove,
+ .mdiodrv.driver = {
+ .name = "qca8k",
+ .of_match_table = qca8k_of_match,
+ .pm = &qca8k_pm_ops,
+ },
+};
+
+static int __init
+qca8kmdio_driver_register(void)
+{
+ return mdio_driver_register(&qca8kmdio_driver);
+}
+module_init(qca8kmdio_driver_register);
+
+static void __exit
+qca8kmdio_driver_unregister(void)
+{
+ mdio_driver_unregister(&qca8kmdio_driver);
+}
+module_exit(qca8kmdio_driver_unregister);
+
+MODULE_AUTHOR("Mathieu Olivari, John Crispin <john@phrozen.org>");
+MODULE_DESCRIPTION("Driver for QCA8K ethernet switch family");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:qca8k");
diff --git a/drivers/net/dsa/qca8k.h b/drivers/net/dsa/qca8k.h
new file mode 100644
index 0000000..7913e03
--- /dev/null
+++ b/drivers/net/dsa/qca8k.h
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
+ * Copyright (C) 2011-2012 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __QCA8K_H
+#define __QCA8K_H
+
+#include <linux/delay.h>
+#include <linux/regmap.h>
+
+#define QCA8K_NUM_PORTS 7
+
+#define PHY_ID_QCA8337 0x004dd036
+
+#define QCA8K_NUM_FDB_RECORDS 2048
+
+/* Global control registers */
+#define QCA8K_REG_PORT0_PAD_CTRL 0x004
+#define QCA8K_REG_PORT5_PAD_CTRL 0x008
+#define QCA8K_REG_PORT6_PAD_CTRL 0x00c
+#define QCA8K_PORT_PAD_RGMII_EN BIT(26)
+#define QCA8K_PORT_PAD_RGMII_TX_DELAY(x) \
+ ((0x8 + (x & 0x3)) << 22)
+#define QCA8K_PORT_PAD_RGMII_RX_DELAY(x) \
+ ((0x10 + (x & 0x3)) << 20)
+#define QCA8K_PORT_PAD_RGMII_RX_DELAY_EN BIT(24)
+#define QCA8K_PORT_PAD_SGMII_EN BIT(7)
+#define QCA8K_REG_MODULE_EN 0x030
+#define QCA8K_MODULE_EN_MIB BIT(0)
+#define QCA8K_REG_MIB 0x034
+#define QCA8K_MIB_CPU_KEEP BIT(20)
+#define QCA8K_GOL_MAC_ADDR0 0x60
+#define QCA8K_GOL_MAC_ADDR1 0x64
+#define QCA8K_REG_PORT_STATUS(_i) (0x07c + (_i) * 4)
+#define QCA8K_PORT_STATUS_SPEED GENMASK(2, 0)
+#define QCA8K_PORT_STATUS_SPEED_S 0
+#define QCA8K_PORT_STATUS_TXMAC BIT(2)
+#define QCA8K_PORT_STATUS_RXMAC BIT(3)
+#define QCA8K_PORT_STATUS_TXFLOW BIT(4)
+#define QCA8K_PORT_STATUS_RXFLOW BIT(5)
+#define QCA8K_PORT_STATUS_DUPLEX BIT(6)
+#define QCA8K_PORT_STATUS_LINK_UP BIT(8)
+#define QCA8K_PORT_STATUS_LINK_AUTO BIT(9)
+#define QCA8K_PORT_STATUS_LINK_PAUSE BIT(10)
+#define QCA8K_REG_PORT_HDR_CTRL(_i) (0x9c + (_i * 4))
+#define QCA8K_PORT_HDR_CTRL_RX_MASK GENMASK(3, 2)
+#define QCA8K_PORT_HDR_CTRL_RX_S 2
+#define QCA8K_PORT_HDR_CTRL_TX_MASK GENMASK(1, 0)
+#define QCA8K_PORT_HDR_CTRL_TX_S 0
+#define QCA8K_PORT_HDR_CTRL_ALL 2
+#define QCA8K_PORT_HDR_CTRL_MGMT 1
+#define QCA8K_PORT_HDR_CTRL_NONE 0
+
+/* EEE control registers */
+#define QCA8K_REG_EEE_CTRL 0x100
+#define QCA8K_REG_EEE_CTRL_LPI_EN(_i) ((_i + 1) * 2)
+
+/* ACL registers */
+#define QCA8K_REG_PORT_VLAN_CTRL0(_i) (0x420 + (_i * 8))
+#define QCA8K_PORT_VLAN_CVID(x) (x << 16)
+#define QCA8K_PORT_VLAN_SVID(x) x
+#define QCA8K_REG_PORT_VLAN_CTRL1(_i) (0x424 + (_i * 8))
+#define QCA8K_REG_IPV4_PRI_BASE_ADDR 0x470
+#define QCA8K_REG_IPV4_PRI_ADDR_MASK 0x474
+
+/* Lookup registers */
+#define QCA8K_REG_ATU_DATA0 0x600
+#define QCA8K_ATU_ADDR2_S 24
+#define QCA8K_ATU_ADDR3_S 16
+#define QCA8K_ATU_ADDR4_S 8
+#define QCA8K_REG_ATU_DATA1 0x604
+#define QCA8K_ATU_PORT_M 0x7f
+#define QCA8K_ATU_PORT_S 16
+#define QCA8K_ATU_ADDR0_S 8
+#define QCA8K_REG_ATU_DATA2 0x608
+#define QCA8K_ATU_VID_M 0xfff
+#define QCA8K_ATU_VID_S 8
+#define QCA8K_ATU_STATUS_M 0xf
+#define QCA8K_ATU_STATUS_STATIC 0xf
+#define QCA8K_REG_ATU_FUNC 0x60c
+#define QCA8K_ATU_FUNC_BUSY BIT(31)
+#define QCA8K_ATU_FUNC_PORT_EN BIT(14)
+#define QCA8K_ATU_FUNC_PORT_M 0xf
+#define QCA8K_ATU_FUNC_PORT_S 8
+#define QCA8K_REG_GLOBAL_FW_CTRL0 0x620
+#define QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN BIT(10)
+#define QCA8K_REG_GLOBAL_FW_CTRL1 0x624
+#define QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_S 24
+#define QCA8K_GLOBAL_FW_CTRL1_BC_DP_S 16
+#define QCA8K_GLOBAL_FW_CTRL1_MC_DP_S 8
+#define QCA8K_GLOBAL_FW_CTRL1_UC_DP_S 0
+#define QCA8K_PORT_LOOKUP_CTRL(_i) (0x660 + (_i) * 0xc)
+#define QCA8K_PORT_LOOKUP_MEMBER GENMASK(6, 0)
+#define QCA8K_PORT_LOOKUP_STATE_MASK GENMASK(18, 16)
+#define QCA8K_PORT_LOOKUP_STATE_DISABLED (0 << 16)
+#define QCA8K_PORT_LOOKUP_STATE_BLOCKING (1 << 16)
+#define QCA8K_PORT_LOOKUP_STATE_LISTENING (2 << 16)
+#define QCA8K_PORT_LOOKUP_STATE_LEARNING (3 << 16)
+#define QCA8K_PORT_LOOKUP_STATE_FORWARD (4 << 16)
+#define QCA8K_PORT_LOOKUP_STATE GENMASK(18, 16)
+#define QCA8K_PORT_LOOKUP_LEARN BIT(20)
+
+/* Pkt edit registers */
+#define AR8337_EGRESS_VLAN(x) (0x0c70 + (4 * (x / 2)))
+
+/* L3 registers */
+#define QCA8K_HROUTER_CONTROL 0xe00
+#define QCA8K_HROUTER_CONTROL_GLB_LOCKTIME_M GENMASK(17, 16)
+#define QCA8K_HROUTER_CONTROL_GLB_LOCKTIME_S 16
+#define QCA8K_HROUTER_CONTROL_ARP_AGE_MODE 1
+#define QCA8K_HROUTER_PBASED_CONTROL1 0xe08
+#define QCA8K_HROUTER_PBASED_CONTROL2 0xe0c
+#define QCA8K_HNAT_CONTROL 0xe38
+
+/* MIB registers */
+#define QCA8K_PORT_MIB_COUNTER(_i) (0x1000 + (_i) * 0x100)
+
+/* QCA specific MII registers */
+#define MII_ATH_MMD_ADDR 0x0d
+#define MII_ATH_MMD_DATA 0x0e
+
+enum {
+ QCA8K_PORT_SPEED_10M = 0,
+ QCA8K_PORT_SPEED_100M = 1,
+ QCA8K_PORT_SPEED_1000M = 2,
+ QCA8K_PORT_SPEED_ERR = 3,
+};
+
+enum qca8k_fdb_cmd {
+ QCA8K_FDB_FLUSH = 1,
+ QCA8K_FDB_LOAD = 2,
+ QCA8K_FDB_PURGE = 3,
+ QCA8K_FDB_NEXT = 6,
+ QCA8K_FDB_SEARCH = 7,
+};
+
+struct ar8xxx_port_status {
+ struct ethtool_eee eee;
+ struct net_device *bridge_dev;
+};
+
+struct qca8k_priv {
+ struct regmap *regmap;
+ struct mii_bus *bus;
+ struct ar8xxx_port_status port_sts[QCA8K_NUM_PORTS];
+ int cpu_port;
+ struct dsa_switch *ds;
+};
+
+struct qca8k_mib_desc {
+ unsigned int size;
+ unsigned int offset;
+ const char *name;
+};
+
+struct qca8k_fdb {
+ u16 vid;
+ u8 port_mask;
+ u8 aging;
+ u8 mac[6];
+};
+
+static inline struct qca8k_priv *qca8k_to_priv(struct dsa_switch *ds)
+{
+ struct qca8k_priv *priv = ds->priv;
+
+ return priv;
+}
+
+static inline void qca8k_split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page)
+{
+ regaddr >>= 1;
+ *r1 = regaddr & 0x1e;
+
+ regaddr >>= 5;
+ *r2 = regaddr & 0x7;
+
+ regaddr >>= 3;
+ *page = regaddr & 0x3ff;
+}
+
+static inline int qca8k_phy_to_port(int phy)
+{
+ if (phy < 5)
+ return phy + 1;
+
+ return -1;
+}
+
+#endif /* __QCA8K_H */
--
1.7.10.4
^ permalink raw reply related
* [PATCH 0/3] net-next: dsa: add QCA8K support
From: John Crispin @ 2016-09-12 8:35 UTC (permalink / raw)
To: David S. Miller, Andrew Lunn, Florian Fainelli
Cc: netdev, linux-kernel, qsdk-review, John Crispin
This series is based on the AR8xxx series posted by Matthieu Olivari in may
2015. The following changes were made since then
* fixed the nitpicks from the previous review
* updated to latest API
* turned it into an mdio device
* added callbacks for fdb, bridge offloading, stp, eee, port status
* fixed several minor issues to the port setup and arp learning
* changed the namespacing as this driver to qca8k
The driver has so far only been tested on qca8337/N. It should work on other QCA
switches such as the qca8327 with minor changes.
John Crispin (3):
Documentation: devicetree: add qca8k binding
net-next: dsa: add Qualcomm tag RX/TX handler
net-next: dsa: add new driver for qca8xxx family
.../devicetree/bindings/net/dsa/qca8k.txt | 53 ++
drivers/net/dsa/Kconfig | 9 +
drivers/net/dsa/Makefile | 1 +
drivers/net/dsa/qca8k.c | 969 ++++++++++++++++++++
drivers/net/dsa/qca8k.h | 201 ++++
include/net/dsa.h | 1 +
net/dsa/Kconfig | 3 +
net/dsa/Makefile | 1 +
net/dsa/dsa.c | 3 +
net/dsa/dsa_priv.h | 2 +
net/dsa/tag_qca.c | 158 ++++
11 files changed, 1401 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/dsa/qca8k.txt
create mode 100644 drivers/net/dsa/qca8k.c
create mode 100644 drivers/net/dsa/qca8k.h
create mode 100644 net/dsa/tag_qca.c
--
1.7.10.4
^ permalink raw reply
* Re: [PATCH net-next 3/7] cxgb4: add debugfs support to dump filter debug logs
From: Jiri Pirko @ 2016-09-12 8:36 UTC (permalink / raw)
To: Rahul Lakkireddy; +Cc: netdev, davem, hariprasad, leedom, nirranjan, indranil
In-Reply-To: <f10743c39acd854257a066f554f595a0cfa9f820.1473667613.git.rahul.lakkireddy@chelsio.com>
Mon, Sep 12, 2016 at 10:12:36AM CEST, rahul.lakkireddy@chelsio.com wrote:
>Add debugfs support to dump filter debug information.
Please no debugfs. Why would you want to use it?
Use a well defined user api instead. If not available, please introduce it.
>
>Signed-off-by: Rahul Lakkireddy <rahul.lakkireddy@chelsio.com>
>Signed-off-by: Hariprasad Shenai <hariprasad@chelsio.com>
>---
> drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c | 4 +-
> drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c | 415 +++++++++++++++++++++
> drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h | 2 +
> drivers/net/ethernet/chelsio/cxgb4/t4_values.h | 5 +-
> 4 files changed, 424 insertions(+), 2 deletions(-)
>
>diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c
>index 91fb508..72cf3de2 100644
>--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c
>+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c
>@@ -1,7 +1,7 @@
> /*
> * This file is part of the Chelsio T4 Ethernet driver for Linux.
> *
>- * Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved.
>+ * Copyright (c) 2003-2016 Chelsio Communications, Inc. All rights reserved.
> *
> * This software is available to you under a choice of one of two
> * licenses. You may choose to be licensed under the terms of the GNU
>@@ -45,6 +45,7 @@
> #include "cxgb4_debugfs.h"
> #include "clip_tbl.h"
> #include "l2t.h"
>+#include "cxgb4_filter.h"
>
> /* generic seq_file support for showing a table of size rows x width. */
> static void *seq_tab_get_idx(struct seq_tab *tb, loff_t pos)
>@@ -3272,6 +3273,7 @@ int t4_setup_debugfs(struct adapter *adap)
> { "tids", &tid_info_debugfs_fops, S_IRUSR, 0},
> { "blocked_fl", &blocked_fl_fops, S_IRUSR | S_IWUSR, 0 },
> { "meminfo", &meminfo_fops, S_IRUSR, 0 },
>+ { "filters", &filters_debugfs_fops, S_IRUSR, 0 },
> };
>
> /* Debug FS nodes common to all T5 and later adapters.
>diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c
>index 490bd94..51b6745 100644
>--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c
>+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c
>@@ -34,6 +34,7 @@
>
> #include "cxgb4.h"
> #include "t4_regs.h"
>+#include "t4_values.h"
> #include "l2t.h"
> #include "t4fw_api.h"
> #include "cxgb4_filter.h"
>@@ -669,3 +670,417 @@ void filter_rpl(struct adapter *adap, const struct cpl_set_tcb_rpl *rpl)
> complete(&ctx->completion);
> }
> }
>+
>+/* Retrieve the packet count for the specified filter. */
>+int cxgb4_get_filter_count(struct adapter *adapter, unsigned int fidx,
>+ u64 *c, int hash, bool get_byte)
>+{
>+ struct filter_entry *f;
>+ unsigned int tcb_base, tcbaddr;
>+ unsigned int max_ftids;
>+ int ret;
>+
>+ tcb_base = t4_read_reg(adapter, TP_CMM_TCB_BASE_A);
>+ max_ftids = adapter->tids.nftids;
>+ if ((fidx != (max_ftids + adapter->tids.nsftids - 1)) &&
>+ (fidx >= max_ftids))
>+ return -E2BIG;
>+
>+ f = &adapter->tids.ftid_tab[fidx];
>+ if (!f->valid)
>+ return -EINVAL;
>+
>+ tcbaddr = tcb_base + f->tid * TCB_SIZE;
>+
>+ if (is_t4(adapter->params.chip)) {
>+ /* For T4, the Filter Packet Hit Count is maintained as a
>+ * 64-bit Big Endian value in the TCB fields
>+ * {t_rtt_ts_recent_age, t_rtseq_recent} ... The format in
>+ * memory is swizzled/mapped in a manner such that instead
>+ * of having this 64-bit counter show up at offset 24
>+ * ((TCB_T_RTT_TS_RECENT_AGE_W == 6) * sizeof(u32)), it
>+ * actually shows up at offset 16. Hence the constant "4"
>+ * below instead of TCB_T_RTT_TS_RECENT_AGE_W.
>+ */
>+ if (get_byte) {
>+ unsigned int word_offset = 4;
>+ __be64 be64_byte_count;
>+
>+ spin_lock(&adapter->win0_lock);
>+ ret = t4_memory_rw(adapter, MEMWIN_NIC, MEM_EDC0,
>+ tcbaddr +
>+ (word_offset * sizeof(__be32)),
>+ sizeof(be64_byte_count),
>+ &be64_byte_count,
>+ T4_MEMORY_READ);
>+ spin_unlock(&adapter->win0_lock);
>+ if (ret < 0)
>+ return ret;
>+ *c = be64_to_cpu(be64_byte_count);
>+ } else {
>+ unsigned int word_offset = 4;
>+ __be64 be64_count;
>+
>+ spin_lock(&adapter->win0_lock);
>+ ret = t4_memory_rw(adapter, MEMWIN_NIC, MEM_EDC0,
>+ tcbaddr +
>+ (word_offset * sizeof(__be32)),
>+ sizeof(be64_count),
>+ (__be32 *)&be64_count,
>+ T4_MEMORY_READ);
>+ spin_unlock(&adapter->win0_lock);
>+ if (ret < 0)
>+ return ret;
>+ *c = be64_to_cpu(be64_count);
>+ }
>+ } else {
>+ /* For T5, the Filter Packet Hit Count is maintained as a
>+ * 32-bit Big Endian value in the TCB field {timestamp}.
>+ * Instead of the filter hit count showing up at offset 20
>+ * ((TCB_TIMESTAMP_W == 5) * sizeof(u32)), it actually shows
>+ * up at offset 24. Hence the constant "6" below.
>+ */
>+ if (get_byte) {
>+ unsigned int word_offset = 4;
>+ __be64 be64_byte_count;
>+
>+ spin_lock(&adapter->win0_lock);
>+ ret = t4_memory_rw(adapter, MEMWIN_NIC, MEM_EDC0,
>+ tcbaddr +
>+ (word_offset * sizeof(__be32)),
>+ sizeof(be64_byte_count),
>+ &be64_byte_count,
>+ T4_MEMORY_READ);
>+ spin_unlock(&adapter->win0_lock);
>+ if (ret < 0)
>+ return ret;
>+ *c = be64_to_cpu(be64_byte_count);
>+ } else {
>+ unsigned int word_offset = 6;
>+ __be32 be32_count;
>+
>+ spin_lock(&adapter->win0_lock);
>+ ret = t4_memory_rw(adapter, MEMWIN_NIC, MEM_EDC0,
>+ tcbaddr +
>+ (word_offset * sizeof(__be32)),
>+ sizeof(be32_count), &be32_count,
>+ T4_MEMORY_READ);
>+ spin_unlock(&adapter->win0_lock);
>+ if (ret < 0)
>+ return ret;
>+ *c = (u64)be32_to_cpu(be32_count);
>+ }
>+ }
>+
>+ return 0;
>+}
>+
>+/* Filter Table. */
>+static void filters_show_ipaddr(struct seq_file *seq,
>+ int type, u8 *addr, u8 *addrm)
>+{
>+ int noctets, octet;
>+
>+ seq_puts(seq, " ");
>+ if (type == 0) {
>+ noctets = 4;
>+ seq_printf(seq, "%48s", " ");
>+ } else {
>+ noctets = 16;
>+ }
>+
>+ for (octet = 0; octet < noctets; octet++)
>+ seq_printf(seq, "%02x", addr[octet]);
>+ seq_puts(seq, "/");
>+ for (octet = 0; octet < noctets; octet++)
>+ seq_printf(seq, "%02x", addrm[octet]);
>+}
>+
>+static void filters_display(struct seq_file *seq, unsigned int fidx,
>+ struct filter_entry *f, int hash)
>+{
>+ struct adapter *adapter = seq->private;
>+ u32 fconf = adapter->params.tp.vlan_pri_map;
>+ u32 tpiconf = adapter->params.tp.ingress_config;
>+ int i;
>+
>+ /* Filter index */
>+ seq_printf(seq, "%4d%c%c", fidx,
>+ (!f->locked ? ' ' : '!'),
>+ (!f->pending ? ' ' : (!f->valid ? '+' : '-')));
>+
>+ if (f->fs.hitcnts) {
>+ u64 hitcnt;
>+ int ret;
>+
>+ ret = cxgb4_get_filter_count(adapter, fidx, &hitcnt,
>+ hash, false);
>+ if (ret)
>+ seq_printf(seq, " %20s", "hits={ERROR}");
>+ else
>+ seq_printf(seq, " %20llu", hitcnt);
>+ } else {
>+ seq_printf(seq, " %20s", "Disabled");
>+ }
>+
>+ /* Compressed header portion of filter. */
>+ for (i = FT_FIRST_S; i <= FT_LAST_S; i++) {
>+ switch (fconf & (1 << i)) {
>+ case 0:
>+ /* compressed filter field not enabled */
>+ break;
>+
>+ case FCOE_F:
>+ seq_printf(seq, " %1d/%1d",
>+ f->fs.val.fcoe, f->fs.mask.fcoe);
>+ break;
>+
>+ case PORT_F:
>+ seq_printf(seq, " %1d/%1d",
>+ f->fs.val.iport, f->fs.mask.iport);
>+ break;
>+
>+ case VNIC_ID_F:
>+ if ((tpiconf & VNIC_F) == 0)
>+ seq_printf(seq, " %1d:%04x/%1d:%04x",
>+ f->fs.val.ovlan_vld,
>+ f->fs.val.ovlan,
>+ f->fs.mask.ovlan_vld,
>+ f->fs.mask.ovlan);
>+ else
>+ seq_printf(seq, " %1d:%1x:%02x/%1d:%1x:%02x",
>+ f->fs.val.ovlan_vld,
>+ (f->fs.val.ovlan >> 13) & 0x7,
>+ f->fs.val.ovlan & 0x7f,
>+ f->fs.mask.ovlan_vld,
>+ (f->fs.mask.ovlan >> 13) & 0x7,
>+ f->fs.mask.ovlan & 0x7f);
>+ break;
>+
>+ case VLAN_F:
>+ seq_printf(seq, " %1d:%04x/%1d:%04x",
>+ f->fs.val.ivlan_vld,
>+ f->fs.val.ivlan,
>+ f->fs.mask.ivlan_vld,
>+ f->fs.mask.ivlan);
>+ break;
>+
>+ case TOS_F:
>+ seq_printf(seq, " %02x/%02x",
>+ f->fs.val.tos, f->fs.mask.tos);
>+ break;
>+
>+ case PROTOCOL_F:
>+ seq_printf(seq, " %02x/%02x",
>+ f->fs.val.proto, f->fs.mask.proto);
>+ break;
>+
>+ case ETHERTYPE_F:
>+ seq_printf(seq, " %04x/%04x",
>+ f->fs.val.ethtype, f->fs.mask.ethtype);
>+ break;
>+
>+ case MACMATCH_F:
>+ seq_printf(seq, " %03x/%03x",
>+ f->fs.val.macidx, f->fs.mask.macidx);
>+ break;
>+
>+ case MPSHITTYPE_F:
>+ seq_printf(seq, " %1x/%1x",
>+ f->fs.val.matchtype,
>+ f->fs.mask.matchtype);
>+ break;
>+
>+ case FRAGMENTATION_F:
>+ seq_printf(seq, " %1d/%1d",
>+ f->fs.val.frag, f->fs.mask.frag);
>+ break;
>+ }
>+ }
>+
>+ /* Fixed portion of filter. */
>+ filters_show_ipaddr(seq, f->fs.type,
>+ f->fs.val.lip, f->fs.mask.lip);
>+ filters_show_ipaddr(seq, f->fs.type,
>+ f->fs.val.fip, f->fs.mask.fip);
>+ seq_printf(seq, " %04x/%04x %04x/%04x",
>+ f->fs.val.lport, f->fs.mask.lport,
>+ f->fs.val.fport, f->fs.mask.fport);
>+
>+ /* Variable length filter action. */
>+ if (f->fs.action == FILTER_DROP) {
>+ seq_puts(seq, " Drop");
>+ } else if (f->fs.action == FILTER_SWITCH) {
>+ seq_printf(seq, " Switch: port=%d", f->fs.eport);
>+ if (f->fs.newdmac)
>+ seq_printf(seq,
>+ ", dmac=%02x:%02x:%02x:%02x:%02x:%02x, l2tidx=%d",
>+ f->fs.dmac[0], f->fs.dmac[1],
>+ f->fs.dmac[2], f->fs.dmac[3],
>+ f->fs.dmac[4], f->fs.dmac[5],
>+ f->l2t->idx);
>+ if (f->fs.newsmac)
>+ seq_printf(seq,
>+ ", smac=%02x:%02x:%02x:%02x:%02x:%02x, smtidx=%d",
>+ f->fs.smac[0], f->fs.smac[1],
>+ f->fs.smac[2], f->fs.smac[3],
>+ f->fs.smac[4], f->fs.smac[5],
>+ f->smtidx);
>+ if (f->fs.newvlan == VLAN_REMOVE)
>+ seq_puts(seq, ", vlan=none");
>+ else if (f->fs.newvlan == VLAN_INSERT)
>+ seq_printf(seq, ", vlan=insert(%x)",
>+ f->fs.vlan);
>+ else if (f->fs.newvlan == VLAN_REWRITE)
>+ seq_printf(seq, ", vlan=rewrite(%x)",
>+ f->fs.vlan);
>+ } else {
>+ seq_puts(seq, " Pass: Q=");
>+ if (f->fs.dirsteer == 0) {
>+ seq_puts(seq, "RSS");
>+ if (f->fs.maskhash)
>+ seq_puts(seq, "(TCB=hash)");
>+ } else {
>+ seq_printf(seq, "%d", f->fs.iq);
>+ if (f->fs.dirsteerhash == 0)
>+ seq_puts(seq, "(QID)");
>+ else
>+ seq_puts(seq, "(hash)");
>+ }
>+ }
>+ if (f->fs.prio)
>+ seq_puts(seq, " Prio");
>+ if (f->fs.rpttid)
>+ seq_puts(seq, " RptTID");
>+ seq_puts(seq, "\n");
>+}
>+
>+static int filters_show(struct seq_file *seq, void *v)
>+{
>+ struct adapter *adapter = seq->private;
>+ u32 fconf = adapter->params.tp.vlan_pri_map;
>+ u32 tpiconf = adapter->params.tp.ingress_config;
>+ int i;
>+
>+ if (v == SEQ_START_TOKEN) {
>+ seq_puts(seq, "[[Legend: '!' => locked; '+' => pending set; '-' => pending clear]]\n");
>+ seq_puts(seq, " Idx Hits");
>+ for (i = FT_FIRST_S; i <= FT_LAST_S; i++) {
>+ switch (fconf & (1 << i)) {
>+ case 0:
>+ /* compressed filter field not enabled */
>+ break;
>+
>+ case FCOE_F:
>+ seq_puts(seq, " FCoE");
>+ break;
>+
>+ case PORT_F:
>+ seq_puts(seq, " Port");
>+ break;
>+
>+ case VNIC_ID_F:
>+ if ((tpiconf & VNIC_F) == 0)
>+ seq_puts(seq, " vld:oVLAN");
>+ else
>+ seq_puts(seq, " VFvld:PF:VF");
>+ break;
>+
>+ case VLAN_F:
>+ seq_puts(seq, " vld:iVLAN");
>+ break;
>+
>+ case TOS_F:
>+ seq_puts(seq, " TOS");
>+ break;
>+
>+ case PROTOCOL_F:
>+ seq_puts(seq, " Prot");
>+ break;
>+
>+ case ETHERTYPE_F:
>+ seq_puts(seq, " EthType");
>+ break;
>+
>+ case MACMATCH_F:
>+ seq_puts(seq, " MACIdx");
>+ break;
>+
>+ case MPSHITTYPE_F:
>+ seq_puts(seq, " MPS");
>+ break;
>+
>+ case FRAGMENTATION_F:
>+ seq_puts(seq, " Frag");
>+ break;
>+ }
>+ }
>+ seq_printf(seq, " %65s %65s %9s %9s %s\n",
>+ "LIP", "FIP", "LPORT", "FPORT", "Action");
>+ } else {
>+ int fidx = (uintptr_t)v - 2;
>+ struct filter_entry *f = &adapter->tids.ftid_tab[fidx];
>+
>+ /* if this entry isn't filled in just return */
>+ if (!f->valid && !f->pending)
>+ return 0;
>+
>+ filters_display(seq, fidx, f, 0);
>+ }
>+ return 0;
>+}
>+
>+static inline void *filters_get_idx(struct adapter *adapter, loff_t pos)
>+{
>+ if (pos > (adapter->tids.nftids + adapter->tids.nsftids))
>+ return NULL;
>+
>+ return (void *)(uintptr_t)(pos + 1);
>+}
>+
>+static void *filters_start(struct seq_file *seq, loff_t *pos)
>+{
>+ struct adapter *adapter = seq->private;
>+
>+ return *pos ? filters_get_idx(adapter, *pos) : SEQ_START_TOKEN;
>+}
>+
>+static void *filters_next(struct seq_file *seq, void *v, loff_t *pos)
>+{
>+ struct adapter *adapter = seq->private;
>+
>+ (*pos)++;
>+ return filters_get_idx(adapter, *pos);
>+}
>+
>+static void filters_stop(struct seq_file *seq, void *v)
>+{
>+}
>+
>+static const struct seq_operations filters_seq_ops = {
>+ .start = filters_start,
>+ .next = filters_next,
>+ .stop = filters_stop,
>+ .show = filters_show
>+};
>+
>+int filters_open(struct inode *inode, struct file *file)
>+{
>+ struct adapter *adapter = inode->i_private;
>+ int res;
>+
>+ res = seq_open(file, &filters_seq_ops);
>+ if (!res) {
>+ struct seq_file *seq = file->private_data;
>+
>+ seq->private = adapter;
>+ }
>+ return res;
>+}
>+
>+const struct file_operations filters_debugfs_fops = {
>+ .owner = THIS_MODULE,
>+ .open = filters_open,
>+ .read = seq_read,
>+ .llseek = seq_lseek,
>+};
>diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h
>index 23742cb..e801e0b 100644
>--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h
>+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h
>@@ -37,6 +37,8 @@
>
> #include "t4_msg.h"
>
>+extern const struct file_operations filters_debugfs_fops;
>+
> void filter_rpl(struct adapter *adap, const struct cpl_set_tcb_rpl *rpl);
> void clear_filter(struct adapter *adap, struct filter_entry *f);
>
>diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_values.h b/drivers/net/ethernet/chelsio/cxgb4/t4_values.h
>index 36cf307..0115222 100644
>--- a/drivers/net/ethernet/chelsio/cxgb4/t4_values.h
>+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_values.h
>@@ -1,7 +1,7 @@
> /*
> * This file is part of the Chelsio T4 Ethernet driver for Linux.
> *
>- * Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved.
>+ * Copyright (c) 2003-2016 Chelsio Communications, Inc. All rights reserved.
> *
> * This software is available to you under a choice of one of two
> * licenses. You may choose to be licensed under the terms of the GNU
>@@ -121,6 +121,9 @@
> * selects for a particular field being present. These fields, when present
> * in the Compressed Filter Tuple, have the following widths in bits.
> */
>+#define FT_FIRST_S FCOE_S
>+#define FT_LAST_S FRAGMENTATION_S
>+
> #define FT_FCOE_W 1
> #define FT_PORT_W 3
> #define FT_VNIC_ID_W 17
>--
>2.5.3
>
^ permalink raw reply
* [PATCH 2/3] net-next: dsa: add Qualcomm tag RX/TX handler
From: John Crispin @ 2016-09-12 8:35 UTC (permalink / raw)
To: David S. Miller, Andrew Lunn, Florian Fainelli
Cc: netdev, linux-kernel, qsdk-review, John Crispin
In-Reply-To: <1473669337-21221-1-git-send-email-john@phrozen.org>
Add support for the 2-bytes Qualcomm tag that gigabit switches such as
the QCA8337/N might insert when receiving packets, or that we need
to insert while targeting specific switch ports. The tag is inserted
directly behind the ethernet header.
Signed-off-by: John Crispin <john@phrozen.org>
---
include/net/dsa.h | 1 +
net/dsa/Kconfig | 3 +
net/dsa/Makefile | 1 +
net/dsa/dsa.c | 3 +
net/dsa/dsa_priv.h | 2 +
net/dsa/tag_qca.c | 158 ++++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 168 insertions(+)
create mode 100644 net/dsa/tag_qca.c
diff --git a/include/net/dsa.h b/include/net/dsa.h
index 9d97c52..7556646 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -26,6 +26,7 @@ enum dsa_tag_protocol {
DSA_TAG_PROTO_TRAILER,
DSA_TAG_PROTO_EDSA,
DSA_TAG_PROTO_BRCM,
+ DSA_TAG_PROTO_QCA,
DSA_TAG_LAST, /* MUST BE LAST */
};
diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig
index ff7736f..96e47c5 100644
--- a/net/dsa/Kconfig
+++ b/net/dsa/Kconfig
@@ -38,4 +38,7 @@ config NET_DSA_TAG_EDSA
config NET_DSA_TAG_TRAILER
bool
+config NET_DSA_TAG_QCA
+ bool
+
endif
diff --git a/net/dsa/Makefile b/net/dsa/Makefile
index 8af4ded..a3380ed 100644
--- a/net/dsa/Makefile
+++ b/net/dsa/Makefile
@@ -7,3 +7,4 @@ dsa_core-$(CONFIG_NET_DSA_TAG_BRCM) += tag_brcm.o
dsa_core-$(CONFIG_NET_DSA_TAG_DSA) += tag_dsa.o
dsa_core-$(CONFIG_NET_DSA_TAG_EDSA) += tag_edsa.o
dsa_core-$(CONFIG_NET_DSA_TAG_TRAILER) += tag_trailer.o
+dsa_core-$(CONFIG_NET_DSA_TAG_QCA) += tag_qca.o
diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c
index d8d267e..66e31ac 100644
--- a/net/dsa/dsa.c
+++ b/net/dsa/dsa.c
@@ -54,6 +54,9 @@ const struct dsa_device_ops *dsa_device_ops[DSA_TAG_LAST] = {
#ifdef CONFIG_NET_DSA_TAG_BRCM
[DSA_TAG_PROTO_BRCM] = &brcm_netdev_ops,
#endif
+#ifdef CONFIG_NET_DSA_TAG_QCA
+ [DSA_TAG_PROTO_QCA] = &qca_netdev_ops,
+#endif
[DSA_TAG_PROTO_NONE] = &none_ops,
};
diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h
index 00077a9..6cfd738 100644
--- a/net/dsa/dsa_priv.h
+++ b/net/dsa/dsa_priv.h
@@ -81,5 +81,7 @@ extern const struct dsa_device_ops trailer_netdev_ops;
/* tag_brcm.c */
extern const struct dsa_device_ops brcm_netdev_ops;
+/* tag_qca.c */
+extern const struct dsa_device_ops qca_netdev_ops;
#endif
diff --git a/net/dsa/tag_qca.c b/net/dsa/tag_qca.c
new file mode 100644
index 0000000..8d75663
--- /dev/null
+++ b/net/dsa/tag_qca.c
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/etherdevice.h>
+#include "dsa_priv.h"
+
+#define QCA_HDR_LEN 2
+#define QCA_HDR_VERSION 0x2
+
+#define QCA_HDR_RECV_VERSION_MASK GENMASK(15, 14)
+#define QCA_HDR_RECV_VERSION_S 14
+#define QCA_HDR_RECV_PRIORITY_MASK GENMASK(13, 11)
+#define QCA_HDR_RECV_PRIORITY_S 11
+#define QCA_HDR_RECV_TYPE_MASK GENMASK(10, 6)
+#define QCA_HDR_RECV_TYPE_S 6
+#define QCA_HDR_RECV_FRAME_IS_TAGGED BIT(3)
+#define QCA_HDR_RECV_SOURCE_PORT_MASK GENMASK(2, 0)
+
+#define QCA_HDR_XMIT_VERSION_MASK GENMASK(15, 14)
+#define QCA_HDR_XMIT_VERSION_S 14
+#define QCA_HDR_XMIT_PRIORITY_MASK GENMASK(13, 11)
+#define QCA_HDR_XMIT_PRIORITY_S 11
+#define QCA_HDR_XMIT_CONTROL_MASK GENMASK(10, 8)
+#define QCA_HDR_XMIT_CONTROL_S 8
+#define QCA_HDR_XMIT_FROM_CPU BIT(7)
+#define QCA_HDR_XMIT_DP_BIT_MASK GENMASK(6, 0)
+
+static inline int reg_to_port(int reg)
+{
+ if (reg < 5)
+ return reg + 1;
+
+ return -1;
+}
+
+static inline int port_to_reg(int port)
+{
+ if (port >= 1 && port <= 6)
+ return port - 1;
+
+ return -1;
+}
+
+static struct sk_buff *qca_tag_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct dsa_slave_priv *p = netdev_priv(dev);
+ u16 *phdr, hdr;
+
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += skb->len;
+
+ if (skb_cow_head(skb, 0) < 0)
+ goto out_free;
+
+ skb_push(skb, QCA_HDR_LEN);
+
+ memmove(skb->data, skb->data + QCA_HDR_LEN, 2 * ETH_ALEN);
+ phdr = (u16 *)(skb->data + 2 * ETH_ALEN);
+
+ /* Set the version field, and set destination port information */
+ hdr = QCA_HDR_VERSION << QCA_HDR_XMIT_VERSION_S |
+ QCA_HDR_XMIT_FROM_CPU |
+ 1 << reg_to_port(p->port);
+
+ *phdr = htons(hdr);
+
+ //skb->dev = p->parent->dst->master_netdev;
+ //dev_queue_xmit(skb);
+
+ return skb;
+
+out_free:
+ kfree_skb(skb);
+ return NULL;
+}
+
+static int qca_tag_rcv(struct sk_buff *skb, struct net_device *dev,
+ struct packet_type *pt, struct net_device *orig_dev)
+{
+ struct dsa_switch_tree *dst = dev->dsa_ptr;
+ struct dsa_switch *ds;
+ u8 ver;
+ int port, phy;
+ __be16 *phdr, hdr;
+
+ if (unlikely(!dst))
+ goto out_drop;
+
+ skb = skb_unshare(skb, GFP_ATOMIC);
+ if (!skb)
+ goto out;
+
+ if (unlikely(!pskb_may_pull(skb, QCA_HDR_LEN)))
+ goto out_drop;
+
+ /* Ethernet is added by the switch between src addr and Ethertype
+ * At this point, skb->data points to ethertype so header should be
+ * right before
+ */
+ phdr = (__be16 *)(skb->data - 2);
+ hdr = ntohs(*phdr);
+
+ /* Make sure the version is correct */
+ ver = (hdr & QCA_HDR_RECV_VERSION_MASK) >> QCA_HDR_RECV_VERSION_S;
+ if (unlikely(ver != QCA_HDR_VERSION))
+ goto out_drop;
+
+ /* Remove QCA tag and recalculate checksum */
+ skb_pull_rcsum(skb, QCA_HDR_LEN);
+ memmove(skb->data - ETH_HLEN, skb->data - ETH_HLEN - QCA_HDR_LEN,
+ ETH_HLEN - QCA_HDR_LEN);
+
+ /* This protocol doesn't support cascading multiple switches so it's
+ * safe to assume the switch is first in the tree
+ */
+ ds = dst->ds[0];
+ if (!ds)
+ goto out_drop;
+
+ /* Get source port information */
+ port = (hdr & QCA_HDR_RECV_SOURCE_PORT_MASK);
+ phy = port_to_reg(port);
+ if (unlikely(phy < 0) || !ds->ports[phy].netdev)
+ goto out_drop;
+
+ /* Update skb & forward the frame accordingly */
+ skb_push(skb, ETH_HLEN);
+ skb->pkt_type = PACKET_HOST;
+ skb->dev = ds->ports[phy].netdev;
+ skb->protocol = eth_type_trans(skb, skb->dev);
+
+ skb->dev->stats.rx_packets++;
+ skb->dev->stats.rx_bytes += skb->len;
+
+ netif_receive_skb(skb);
+
+ return 0;
+
+out_drop:
+ kfree_skb(skb);
+out:
+ return 0;
+}
+
+const struct dsa_device_ops qca_netdev_ops = {
+ .xmit = qca_tag_xmit,
+ .rcv = qca_tag_rcv,
+};
--
1.7.10.4
^ permalink raw reply related
* [PATCH 1/3] Documentation: devicetree: add qca8k binding
From: John Crispin @ 2016-09-12 8:35 UTC (permalink / raw)
To: David S. Miller, Andrew Lunn, Florian Fainelli
Cc: netdev, linux-kernel, qsdk-review, John Crispin, devicetree
In-Reply-To: <1473669337-21221-1-git-send-email-john@phrozen.org>
Add device-tree binding for ar8xxx switch families.
Cc: devicetree@vger.kernel.org
Signed-off-by: John Crispin <john@phrozen.org>
---
.../devicetree/bindings/net/dsa/qca8k.txt | 53 ++++++++++++++++++++
1 file changed, 53 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/dsa/qca8k.txt
diff --git a/Documentation/devicetree/bindings/net/dsa/qca8k.txt b/Documentation/devicetree/bindings/net/dsa/qca8k.txt
new file mode 100644
index 0000000..2a1ad06
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/dsa/qca8k.txt
@@ -0,0 +1,53 @@
+* Qualcomm Atheros QCA8xxx switch family
+
+Required properties:
+
+- compatible: should be "qca,qca8337"
+- #size-cells: must be 0
+- #address-cells: must be 1
+
+Subnodes:
+
+The integrated switch subnode should be specified according to the binding
+described in dsa/dsa.txt.
+
+Example:
+
+ &mdio0 {
+ switch@0 {
+ compatible = "qca,qca8337";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <30>;
+
+ ports {
+ port@0 {
+ reg = <11>;
+ label = "cpu";
+ ethernet = <&gmac1>;
+ phy-mode = "rgmii";
+ };
+
+ port@1 {
+ reg = <0>;
+ label = "lan1";
+ };
+
+ port@2 {
+ reg = <1>;
+ label = "lan2";
+ };
+
+ port@3 {
+ reg = <2>;
+ label = "lan3";
+ };
+
+ port@4 {
+ reg = <3>;
+ label = "lan4";
+ };
+ }
+ };
+ };
--
1.7.10.4
^ permalink raw reply related
* [PATCH net-next 7/7] cxgb4: add support for drop and redirect actions
From: Rahul Lakkireddy @ 2016-09-12 8:12 UTC (permalink / raw)
To: netdev; +Cc: davem, hariprasad, leedom, nirranjan, indranil, Rahul Lakkireddy
In-Reply-To: <cover.1473667613.git.rahul.lakkireddy@chelsio.com>
Add support for dropping matched packets in hardware. Also add support
for re-directing matched packets to a specified port in hardware.
Signed-off-by: Rahul Lakkireddy <rahul.lakkireddy@chelsio.com>
Signed-off-by: Hariprasad Shenai <hariprasad@chelsio.com>
---
drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c | 63 +++++++++++++++++++++++
1 file changed, 63 insertions(+)
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c
index 31847e3..584ccb3 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c
@@ -32,6 +32,9 @@
* SOFTWARE.
*/
+#include <net/tc_act/tc_gact.h>
+#include <net/tc_act/tc_mirred.h>
+
#include "cxgb4.h"
#include "cxgb4_tc_u32_parse.h"
#include "cxgb4_tc_u32.h"
@@ -83,6 +86,59 @@ static int fill_match_fields(struct adapter *adap,
return 0;
}
+/* Fill ch_filter_specification with parsed action. */
+static int fill_action_fields(struct adapter *adap,
+ struct ch_filter_specification *fs,
+ struct tc_cls_u32_offload *cls)
+{
+ const struct tc_action *a;
+ struct tcf_exts *exts;
+ LIST_HEAD(actions);
+ unsigned int num_actions = 0;
+ bool found = false;
+
+ exts = cls->knode.exts;
+ if (tc_no_actions(exts))
+ return -EINVAL;
+
+ tcf_exts_to_list(exts, &actions);
+ list_for_each_entry(a, &actions, list) {
+ /* Don't allow more than one action per rule. */
+ if (num_actions)
+ return -EINVAL;
+
+ /* Drop in hardware. */
+ if (is_tcf_gact_shot(a)) {
+ fs->action = FILTER_DROP;
+ found = true;
+ }
+
+ /* Re-direct to specified port in hardware. */
+ if (is_tcf_mirred_redirect(a)) {
+ struct net_device *n_dev;
+ unsigned int i, index;
+
+ index = tcf_mirred_ifindex(a);
+ for_each_port(adap, i) {
+ n_dev = adap->port[i];
+ if (index == n_dev->ifindex) {
+ fs->action = FILTER_SWITCH;
+ fs->eport = i;
+ break;
+ }
+ }
+ found = true;
+ }
+
+ num_actions++;
+ }
+
+ if (!found)
+ return -EINVAL;
+
+ return 0;
+}
+
int cxgb4_config_knode(struct net_device *dev, __be16 protocol,
struct tc_cls_u32_offload *cls)
{
@@ -236,6 +292,13 @@ int cxgb4_config_knode(struct net_device *dev, __be16 protocol,
if (ret)
goto out;
+ /* Fill ch_filter_specification action fields to be shipped to
+ * hardware.
+ */
+ ret = fill_action_fields(adapter, &fs, cls);
+ if (ret)
+ goto out;
+
/* The filter spec has been completely built from the info
* provided from u32. We now set some default fields in the
* spec for sanity.
--
2.5.3
^ permalink raw reply related
* [PATCH net-next 6/7] cxgb4: add support for deleting u32 filters
From: Rahul Lakkireddy @ 2016-09-12 8:12 UTC (permalink / raw)
To: netdev; +Cc: davem, hariprasad, leedom, nirranjan, indranil, Rahul Lakkireddy
In-Reply-To: <cover.1473667613.git.rahul.lakkireddy@chelsio.com>
Add support for deleting an offloaded u32 filter from hardware. If a
link is deleted, then all corresponding filters associated with the link
are also deleted. Also enable hardware tc offload as a supported
feature.
Signed-off-by: Rahul Lakkireddy <rahul.lakkireddy@chelsio.com>
Signed-off-by: Hariprasad Shenai <hariprasad@chelsio.com>
---
drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 5 +-
drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c | 92 +++++++++++++++++++++++
drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h | 2 +
3 files changed, 98 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
index 28396f5..087066a 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
@@ -3047,6 +3047,8 @@ int cxgb_setup_tc(struct net_device *dev, u32 handle, __be16 proto,
case TC_CLSU32_NEW_KNODE:
case TC_CLSU32_REPLACE_KNODE:
return cxgb4_config_knode(dev, proto, tc->cls_u32);
+ case TC_CLSU32_DELETE_KNODE:
+ return cxgb4_delete_knode(dev, proto, tc->cls_u32);
default:
return -EOPNOTSUPP;
}
@@ -5155,7 +5157,8 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
netdev->hw_features = NETIF_F_SG | TSO_FLAGS |
NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
NETIF_F_RXCSUM | NETIF_F_RXHASH |
- NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX;
+ NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX |
+ NETIF_F_HW_TC;
if (highdma)
netdev->hw_features |= NETIF_F_HIGHDMA;
netdev->features |= netdev->hw_features;
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c
index 62c1695..31847e3 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c
@@ -279,6 +279,98 @@ out:
return ret;
}
+int cxgb4_delete_knode(struct net_device *dev, __be16 protocol,
+ struct tc_cls_u32_offload *cls)
+{
+ struct adapter *adapter = netdev2adap(dev);
+ struct cxgb4_tc_u32_table *t;
+ struct cxgb4_link *link = NULL;
+ struct filter_ctx ctx;
+ u32 handle, uhtid;
+ unsigned int filter_id;
+ unsigned int max_tids;
+ unsigned int i, j;
+ int ret;
+
+ if (!can_tc_u32_offload(dev))
+ return -EOPNOTSUPP;
+
+ /* Fetch the location to delete the filter. */
+ filter_id = (cls->knode.handle & 0xFFFFF);
+
+ if (filter_id > adapter->tids.nftids) {
+ dev_err(adapter->pdev_dev,
+ "Location %d out of range for deletion. Max: %d\n",
+ filter_id, adapter->tids.nftids);
+ return -ERANGE;
+ }
+
+ t = adapter->tc_u32;
+ handle = cls->knode.handle;
+ uhtid = TC_U32_USERHTID(cls->knode.handle);
+
+ /* Ensure that uhtid is either root u32 (i.e. 0x800)
+ * or a a valid linked bucket.
+ */
+ if (uhtid != 0x800 && uhtid >= t->size)
+ return -EINVAL;
+
+ /* Delete the specified filter */
+ if (uhtid != 0x800) {
+ link = &t->table[uhtid - 1];
+ if (!link->link_handle)
+ return -EINVAL;
+
+ if (!test_bit(filter_id, link->tid_map))
+ return -EINVAL;
+ }
+
+ init_completion(&ctx.completion);
+
+ ret = cxgb4_del_filter(dev, filter_id, &ctx);
+ if (ret)
+ goto out;
+
+ /* Wait for reply */
+ ret = wait_for_completion_timeout(&ctx.completion, 10 * HZ);
+ if (!ret)
+ return -ETIMEDOUT;
+
+ ret = ctx.result;
+ if (!ret && link)
+ clear_bit(filter_id, link->tid_map);
+
+ /* If a link is being deleted, then delete all filters
+ * associated with the link.
+ */
+ max_tids = adapter->tids.nftids;
+ for (i = 0; i < t->size; i++) {
+ link = &t->table[i];
+
+ if (link->link_handle == handle) {
+ for (j = 0; j < max_tids; j++) {
+ if (!test_bit(j, link->tid_map))
+ continue;
+
+ ret = cxgb4_del_filter(dev, j, NULL);
+ if (ret)
+ goto out;
+
+ clear_bit(j, link->tid_map);
+ }
+
+ /* Clear the link state */
+ link->match_field = NULL;
+ link->link_handle = 0;
+ memset(&link->fs, 0, sizeof(link->fs));
+ break;
+ }
+ }
+
+out:
+ return ret;
+}
+
void cxgb4_cleanup_tc_u32(struct adapter *adap)
{
struct cxgb4_tc_u32_table *t;
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h
index 46575843..6bdc885 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h
@@ -48,6 +48,8 @@ static inline bool can_tc_u32_offload(struct net_device *dev)
int cxgb4_config_knode(struct net_device *dev, __be16 protocol,
struct tc_cls_u32_offload *cls);
+int cxgb4_delete_knode(struct net_device *dev, __be16 protocol,
+ struct tc_cls_u32_offload *cls);
void cxgb4_cleanup_tc_u32(struct adapter *adapter);
struct cxgb4_tc_u32_table *cxgb4_init_tc_u32(struct adapter *adap,
--
2.5.3
^ permalink raw reply related
* [PATCH net-next 5/7] cxgb4: add support for setting u32 filters
From: Rahul Lakkireddy @ 2016-09-12 8:12 UTC (permalink / raw)
To: netdev; +Cc: davem, hariprasad, leedom, nirranjan, indranil, Rahul Lakkireddy
In-Reply-To: <cover.1473667613.git.rahul.lakkireddy@chelsio.com>
Add support for offloading u32 filter onto hardware. Links are stored
in a jump table to perform necessary jumps to match TCP/UDP header.
When inserting rules in the linked bucket, the TCP/UDP match fields
in the corresponding entry of the jump table are appended to the filter
rule before insertion.
Signed-off-by: Rahul Lakkireddy <rahul.lakkireddy@chelsio.com>
Signed-off-by: Hariprasad Shenai <hariprasad@chelsio.com>
---
drivers/net/ethernet/chelsio/cxgb4/Makefile | 2 +-
drivers/net/ethernet/chelsio/cxgb4/cxgb4.h | 3 +
drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 36 +++
drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c | 343 +++++++++++++++++++++
drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h | 55 ++++
.../ethernet/chelsio/cxgb4/cxgb4_tc_u32_parse.h | 12 +
6 files changed, 450 insertions(+), 1 deletion(-)
create mode 100644 drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c
create mode 100644 drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h
diff --git a/drivers/net/ethernet/chelsio/cxgb4/Makefile b/drivers/net/ethernet/chelsio/cxgb4/Makefile
index da88981..c6b71f6 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/Makefile
+++ b/drivers/net/ethernet/chelsio/cxgb4/Makefile
@@ -4,7 +4,7 @@
obj-$(CONFIG_CHELSIO_T4) += cxgb4.o
-cxgb4-objs := cxgb4_main.o l2t.o t4_hw.o sge.o clip_tbl.o cxgb4_ethtool.o cxgb4_uld.o sched.o cxgb4_filter.o
+cxgb4-objs := cxgb4_main.o l2t.o t4_hw.o sge.o clip_tbl.o cxgb4_ethtool.o cxgb4_uld.o sched.o cxgb4_filter.o cxgb4_tc_u32.o
cxgb4-$(CONFIG_CHELSIO_T4_DCB) += cxgb4_dcb.o
cxgb4-$(CONFIG_CHELSIO_T4_FCOE) += cxgb4_fcoe.o
cxgb4-$(CONFIG_DEBUG_FS) += cxgb4_debugfs.o
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
index fbd593a..1adb28f 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
@@ -867,6 +867,9 @@ struct adapter {
spinlock_t stats_lock;
spinlock_t win0_lock ____cacheline_aligned_in_smp;
+
+ /* TC u32 offload */
+ struct cxgb4_tc_u32_table *tc_u32;
};
/* Support for "sched-class" command to allow a TX Scheduling Class to be
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
index af07f9d..28396f5 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
@@ -78,6 +78,7 @@
#include "clip_tbl.h"
#include "l2t.h"
#include "sched.h"
+#include "cxgb4_tc_u32.h"
char cxgb4_driver_name[] = KBUILD_MODNAME;
@@ -3027,6 +3028,33 @@ static int cxgb_set_tx_maxrate(struct net_device *dev, int index, u32 rate)
return err;
}
+int cxgb_setup_tc(struct net_device *dev, u32 handle, __be16 proto,
+ struct tc_to_netdev *tc)
+{
+ struct adapter *adap = netdev2adap(dev);
+ struct port_info *pi = netdev2pinfo(dev);
+
+ if (!(adap->flags & FULL_INIT_DONE)) {
+ dev_err(adap->pdev_dev,
+ "Failed to setup tc on port %d. Link Down?\n",
+ pi->port_id);
+ return -EINVAL;
+ }
+
+ if (TC_H_MAJ(handle) == TC_H_MAJ(TC_H_INGRESS) &&
+ tc->type == TC_SETUP_CLSU32) {
+ switch (tc->cls_u32->command) {
+ case TC_CLSU32_NEW_KNODE:
+ case TC_CLSU32_REPLACE_KNODE:
+ return cxgb4_config_knode(dev, proto, tc->cls_u32);
+ default:
+ return -EOPNOTSUPP;
+ }
+ }
+
+ return -EOPNOTSUPP;
+}
+
static const struct net_device_ops cxgb4_netdev_ops = {
.ndo_open = cxgb_open,
.ndo_stop = cxgb_close,
@@ -3050,6 +3078,7 @@ static const struct net_device_ops cxgb4_netdev_ops = {
.ndo_busy_poll = cxgb_busy_poll,
#endif
.ndo_set_tx_maxrate = cxgb_set_tx_maxrate,
+ .ndo_setup_tc = cxgb_setup_tc,
};
#ifdef CONFIG_PCI_IOV
@@ -4781,6 +4810,7 @@ static void free_some_resources(struct adapter *adapter)
t4_free_mem(adapter->l2t);
t4_cleanup_sched(adapter);
t4_free_mem(adapter->tids.tid_tab);
+ cxgb4_cleanup_tc_u32(adapter);
kfree(adapter->sge.egr_map);
kfree(adapter->sge.ingr_map);
kfree(adapter->sge.starving_fl);
@@ -5213,6 +5243,12 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
dev_warn(&pdev->dev, "could not allocate TID table, "
"continuing\n");
adapter->params.offload = 0;
+ } else {
+ adapter->tc_u32 = cxgb4_init_tc_u32(adapter,
+ CXGB4_MAX_LINK_HANDLE);
+ if (!adapter->tc_u32)
+ dev_warn(&pdev->dev,
+ "could not offload tc u32, continuing\n");
}
if (is_offload(adapter)) {
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c
new file mode 100644
index 0000000..62c1695
--- /dev/null
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c
@@ -0,0 +1,343 @@
+/*
+ * This file is part of the Chelsio T4 Ethernet driver for Linux.
+ *
+ * Copyright (c) 2016 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "cxgb4.h"
+#include "cxgb4_tc_u32_parse.h"
+#include "cxgb4_tc_u32.h"
+
+/* Fill ch_filter_specification with parsed match value/mask pair. */
+static int fill_match_fields(struct adapter *adap,
+ struct ch_filter_specification *fs,
+ struct tc_cls_u32_offload *cls,
+ const struct cxgb4_match_field *entry,
+ bool next_header)
+{
+ unsigned int i, j;
+ int off;
+ u32 val, mask;
+ int err;
+ bool found = false;
+
+ for (i = 0; i < cls->knode.sel->nkeys; i++) {
+ off = cls->knode.sel->keys[i].off;
+ val = cls->knode.sel->keys[i].val;
+ mask = cls->knode.sel->keys[i].mask;
+
+ if (next_header) {
+ /* For next headers, parse only keys with offmask */
+ if (!cls->knode.sel->keys[i].offmask)
+ continue;
+ } else {
+ /* For the remaining, parse only keys without offmask */
+ if (cls->knode.sel->keys[i].offmask)
+ continue;
+ }
+
+ found = false;
+
+ for (j = 0; entry[j].val; j++) {
+ if (off == entry[j].off) {
+ found = true;
+ err = entry[j].val(fs, val, mask);
+ if (err)
+ return err;
+ break;
+ }
+ }
+
+ if (!found)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int cxgb4_config_knode(struct net_device *dev, __be16 protocol,
+ struct tc_cls_u32_offload *cls)
+{
+ struct adapter *adapter = netdev2adap(dev);
+ struct cxgb4_tc_u32_table *t;
+ const struct cxgb4_match_field *start, *link_start = NULL;
+ struct cxgb4_link *link;
+ struct ch_filter_specification fs;
+ struct filter_ctx ctx;
+ unsigned int filter_id;
+ u32 uhtid, link_uhtid;
+ int ret;
+ bool is_ipv6 = false;
+
+ if (!can_tc_u32_offload(dev))
+ return -EOPNOTSUPP;
+
+ if ((protocol != htons(ETH_P_IP)) && (protocol != htons(ETH_P_IPV6)))
+ return -EOPNOTSUPP;
+
+ /* Fetch the location to insert the filter. */
+ filter_id = (cls->knode.handle & 0xFFFFF);
+
+ if (filter_id > adapter->tids.nftids) {
+ dev_err(adapter->pdev_dev,
+ "Location %d out of range for insertion. Max: %d\n",
+ filter_id, adapter->tids.nftids);
+ return -ERANGE;
+ }
+
+ t = adapter->tc_u32;
+ uhtid = TC_U32_USERHTID(cls->knode.handle);
+ link_uhtid = TC_U32_USERHTID(cls->knode.link_handle);
+
+ /* Ensure that uhtid is either root u32 (i.e. 0x800)
+ * or a a valid linked bucket.
+ */
+ if (uhtid != 0x800 && uhtid >= t->size)
+ return -EINVAL;
+
+ /* Ensure link handle uhtid is sane, if specified. */
+ if (link_uhtid >= t->size)
+ return -EINVAL;
+
+ memset(&fs, 0, sizeof(fs));
+
+ if (protocol == htons(ETH_P_IPV6)) {
+ start = cxgb4_ipv6_fields;
+ is_ipv6 = true;
+ } else {
+ start = cxgb4_ipv4_fields;
+ is_ipv6 = false;
+ }
+
+ if (uhtid != 0x800) {
+ /* Link must exist from root node before insertion. */
+ if (!t->table[uhtid - 1].link_handle)
+ return -EINVAL;
+
+ /* Link must have a valid supported next header. */
+ link_start = (&t->table[uhtid - 1])->match_field;
+ if (!link_start)
+ return -EINVAL;
+ }
+
+ /* Parse links and record them for subsequent jumps to valid
+ * next headers.
+ */
+ if (link_uhtid) {
+ const struct cxgb4_next_header *next;
+ unsigned int i, j;
+ int off;
+ u32 val, mask;
+ bool found = false;
+
+ if (t->table[link_uhtid - 1].link_handle) {
+ dev_err(adapter->pdev_dev,
+ "Link handle exists for: 0x%x\n",
+ link_uhtid);
+ return -EINVAL;
+ }
+
+ next = is_ipv6 ? cxgb4_ipv6_jumps : cxgb4_ipv4_jumps;
+
+ /* Try to find matches that allow jumps to next header. */
+ for (i = 0; next[i].jump; i++) {
+ if (next[i].offoff != cls->knode.sel->offoff ||
+ next[i].shift != cls->knode.sel->offshift ||
+ next[i].mask != cls->knode.sel->offmask ||
+ next[i].offset != cls->knode.sel->off)
+ continue;
+
+ /* Found a possible candidate. Find a key that
+ * matches the corresponding offset, value, and
+ * mask to jump to next header.
+ */
+ for (j = 0; j < cls->knode.sel->nkeys; j++) {
+ off = cls->knode.sel->keys[j].off;
+ val = cls->knode.sel->keys[j].val;
+ mask = cls->knode.sel->keys[j].mask;
+
+ if (next[i].match_off == off &&
+ next[i].match_val == val &&
+ next[i].match_mask == mask) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ continue; /* Try next candidate. */
+
+ /* Candidate to jump to next header found.
+ * Translate all keys to internal specification
+ * and store them in jump table. This spec is copied
+ * later to set the actual filters.
+ */
+ ret = fill_match_fields(adapter, &fs, cls,
+ start, false);
+ if (ret)
+ goto out;
+
+ link = &t->table[link_uhtid - 1];
+ link->match_field = next[i].jump;
+ link->link_handle = cls->knode.handle;
+ memcpy(&link->fs, &fs, sizeof(fs));
+ break;
+ }
+
+ /* No candidate found to jump to next header. */
+ if (!found)
+ return -EINVAL;
+
+ return 0;
+ }
+
+ /* Fill ch_filter_specification match fields to be shipped to hardware.
+ * Copy the linked spec (if any) first. And then update the spec as
+ * needed.
+ */
+ if ((uhtid != 0x800) && t->table[uhtid - 1].link_handle) {
+ /* Copy linked ch_filter_specification */
+ memcpy(&fs, &t->table[uhtid - 1].fs, sizeof(fs));
+ ret = fill_match_fields(adapter, &fs, cls,
+ link_start, true);
+ if (ret)
+ goto out;
+ }
+
+ ret = fill_match_fields(adapter, &fs, cls, start, false);
+ if (ret)
+ goto out;
+
+ /* The filter spec has been completely built from the info
+ * provided from u32. We now set some default fields in the
+ * spec for sanity.
+ */
+
+ /* Match only packets coming from the ingress port where this
+ * filter will be created.
+ */
+ fs.val.iport = netdev2pinfo(dev)->port_id;
+ fs.mask.iport = ~0;
+
+ /* Enable filter hit counts. */
+ fs.hitcnts = 1;
+
+ /* Set type of filter - IPv6 or IPv4 */
+ fs.type = is_ipv6 ? 1 : 0;
+
+ init_completion(&ctx.completion);
+
+ /* Set the filter */
+ ret = cxgb4_set_filter(dev, filter_id, &fs, &ctx);
+ if (ret)
+ goto out;
+
+ /* Wait for reply */
+ ret = wait_for_completion_timeout(&ctx.completion, 10 * HZ);
+ if (!ret)
+ return -ETIMEDOUT;
+
+ ret = ctx.result;
+ if (!ret) {
+ /* If this is a linked bucket, then set the corresponding
+ * entry in the bitmap to mark it as belonging to this linked
+ * bucket.
+ */
+ if ((uhtid != 0x800) && t->table[uhtid - 1].link_handle)
+ set_bit(filter_id, (&t->table[uhtid - 1])->tid_map);
+ }
+
+out:
+ return ret;
+}
+
+void cxgb4_cleanup_tc_u32(struct adapter *adap)
+{
+ struct cxgb4_tc_u32_table *t;
+ unsigned int i;
+
+ if (!adap->tc_u32)
+ return;
+
+ /* Free up all allocated memory. */
+ t = adap->tc_u32;
+ for (i = 0; i < t->size; i++) {
+ struct cxgb4_link *link = &t->table[i];
+
+ t4_free_mem(link->tid_map);
+ }
+ t4_free_mem(adap->tc_u32);
+}
+
+struct cxgb4_tc_u32_table *cxgb4_init_tc_u32(struct adapter *adap,
+ unsigned int size)
+{
+ struct cxgb4_tc_u32_table *t;
+ unsigned int i;
+
+ if (!size)
+ return NULL;
+
+ t = t4_alloc_mem(sizeof(*t) +
+ (size * sizeof(struct cxgb4_link)));
+ if (!t)
+ return NULL;
+
+ t->size = size;
+
+ for (i = 0; i < t->size; i++) {
+ struct cxgb4_link *link = &t->table[i];
+ unsigned int bmap_size;
+ unsigned int max_tids;
+
+ max_tids = adap->tids.nftids;
+ bmap_size = BITS_TO_LONGS(max_tids);
+ link->tid_map = t4_alloc_mem(sizeof(unsigned long) * bmap_size);
+ if (!link->tid_map)
+ goto out_no_mem;
+ bitmap_zero(link->tid_map, max_tids);
+ }
+
+ return t;
+
+out_no_mem:
+ for (i = 0; i < t->size; i++) {
+ struct cxgb4_link *link = &t->table[i];
+
+ if (link->tid_map)
+ t4_free_mem(link->tid_map);
+ }
+
+ if (t)
+ t4_free_mem(t);
+
+ return NULL;
+}
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h
new file mode 100644
index 0000000..46575843
--- /dev/null
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h
@@ -0,0 +1,55 @@
+/*
+ * This file is part of the Chelsio T4 Ethernet driver for Linux.
+ *
+ * Copyright (c) 2016 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __CXGB4_TC_U32_H
+#define __CXGB4_TC_U32_H
+
+#include <net/pkt_cls.h>
+
+#define CXGB4_MAX_LINK_HANDLE 32
+
+static inline bool can_tc_u32_offload(struct net_device *dev)
+{
+ struct adapter *adap = netdev2adap(dev);
+
+ return (dev->features & NETIF_F_HW_TC) && adap->tc_u32 ? true : false;
+}
+
+int cxgb4_config_knode(struct net_device *dev, __be16 protocol,
+ struct tc_cls_u32_offload *cls);
+
+void cxgb4_cleanup_tc_u32(struct adapter *adapter);
+struct cxgb4_tc_u32_table *cxgb4_init_tc_u32(struct adapter *adap,
+ unsigned int size);
+#endif /* __CXGB4_TC_U32_H */
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32_parse.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32_parse.h
index 261aa4a..de321bf 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32_parse.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32_parse.h
@@ -279,4 +279,16 @@ static const struct cxgb4_next_header cxgb4_ipv6_jumps[] = {
.jump = cxgb4_udp_fields },
{ .jump = NULL }
};
+
+struct cxgb4_link {
+ const struct cxgb4_match_field *match_field; /* Next header */
+ struct ch_filter_specification fs; /* Match spec associated with link */
+ u32 link_handle; /* Knode handle associated with the link */
+ unsigned long *tid_map; /* Bitmap for filter tids */
+};
+
+struct cxgb4_tc_u32_table {
+ unsigned int size; /* number of entries in table */
+ struct cxgb4_link table[0]; /* Jump table */
+};
#endif /* __CXGB4_TC_U32_PARSE_H */
--
2.5.3
^ permalink raw reply related
* [PATCH net-next 4/7] cxgb4: add parser to translate u32 filters to internal spec
From: Rahul Lakkireddy @ 2016-09-12 8:12 UTC (permalink / raw)
To: netdev; +Cc: davem, hariprasad, leedom, nirranjan, indranil, Rahul Lakkireddy
In-Reply-To: <cover.1473667613.git.rahul.lakkireddy@chelsio.com>
Parse information sent by u32 into internal filter specification.
Add support for parsing several fields in IPv4, IPv6, TCP, and UDP.
Signed-off-by: Rahul Lakkireddy <rahul.lakkireddy@chelsio.com>
Signed-off-by: Hariprasad Shenai <hariprasad@chelsio.com>
---
.../ethernet/chelsio/cxgb4/cxgb4_tc_u32_parse.h | 282 +++++++++++++++++++++
1 file changed, 282 insertions(+)
create mode 100644 drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32_parse.h
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32_parse.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32_parse.h
new file mode 100644
index 0000000..261aa4a
--- /dev/null
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32_parse.h
@@ -0,0 +1,282 @@
+/*
+ * This file is part of the Chelsio T4 Ethernet driver for Linux.
+ *
+ * Copyright (c) 2016 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __CXGB4_TC_U32_PARSE_H
+#define __CXGB4_TC_U32_PARSE_H
+
+struct cxgb4_match_field {
+ int off; /* Offset from the beginning of the header to match */
+ /* Fill the value/mask pair in the spec if matched */
+ int (*val)(struct ch_filter_specification *f, u32 val, u32 mask);
+};
+
+/* IPv4 match fields */
+static inline int cxgb4_fill_ipv4_tos(struct ch_filter_specification *f,
+ u32 val, u32 mask)
+{
+ f->val.tos = (ntohl(val) >> 16) & 0x000000FF;
+ f->mask.tos = (ntohl(mask) >> 16) & 0x000000FF;
+
+ return 0;
+}
+
+static inline int cxgb4_fill_ipv4_frag(struct ch_filter_specification *f,
+ u32 val, u32 mask)
+{
+ u8 frag_val;
+ u32 mask_val;
+
+ frag_val = (ntohl(val) >> 13) & 0x00000007;
+ mask_val = ntohl(mask) & 0x0000FFFF;
+
+ if (frag_val == 0x1 && mask_val != 0x3FFF) { /* MF set */
+ f->val.frag = 1;
+ f->mask.frag = 1;
+ } else if (frag_val == 0x2 && mask_val != 0x3FFF) { /* DF set */
+ f->val.frag = 0;
+ f->mask.frag = 1;
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static inline int cxgb4_fill_ipv4_proto(struct ch_filter_specification *f,
+ u32 val, u32 mask)
+{
+ f->val.proto = (ntohl(val) >> 16) & 0x000000FF;
+ f->mask.proto = (ntohl(mask) >> 16) & 0x000000FF;
+
+ return 0;
+}
+
+static inline int cxgb4_fill_ipv4_src_ip(struct ch_filter_specification *f,
+ u32 val, u32 mask)
+{
+ memcpy(&f->val.fip[0], &val, sizeof(u32));
+ memcpy(&f->mask.fip[0], &mask, sizeof(u32));
+
+ return 0;
+}
+
+static inline int cxgb4_fill_ipv4_dst_ip(struct ch_filter_specification *f,
+ u32 val, u32 mask)
+{
+ memcpy(&f->val.lip[0], &val, sizeof(u32));
+ memcpy(&f->mask.lip[0], &mask, sizeof(u32));
+
+ return 0;
+}
+
+static const struct cxgb4_match_field cxgb4_ipv4_fields[] = {
+ { .off = 0, .val = cxgb4_fill_ipv4_tos },
+ { .off = 4, .val = cxgb4_fill_ipv4_frag },
+ { .off = 8, .val = cxgb4_fill_ipv4_proto },
+ { .off = 12, .val = cxgb4_fill_ipv4_src_ip },
+ { .off = 16, .val = cxgb4_fill_ipv4_dst_ip },
+ { .val = NULL }
+};
+
+/* IPv6 match fields */
+static inline int cxgb4_fill_ipv6_tos(struct ch_filter_specification *f,
+ u32 val, u32 mask)
+{
+ f->val.tos = (ntohl(val) >> 20) & 0x000000FF;
+ f->mask.tos = (ntohl(mask) >> 20) & 0x000000FF;
+
+ return 0;
+}
+
+static inline int cxgb4_fill_ipv6_proto(struct ch_filter_specification *f,
+ u32 val, u32 mask)
+{
+ f->val.proto = (ntohl(val) >> 8) & 0x000000FF;
+ f->mask.proto = (ntohl(mask) >> 8) & 0x000000FF;
+
+ return 0;
+}
+
+static inline int cxgb4_fill_ipv6_src_ip0(struct ch_filter_specification *f,
+ u32 val, u32 mask)
+{
+ memcpy(&f->val.fip[0], &val, sizeof(u32));
+ memcpy(&f->mask.fip[0], &mask, sizeof(u32));
+
+ return 0;
+}
+
+static inline int cxgb4_fill_ipv6_src_ip1(struct ch_filter_specification *f,
+ u32 val, u32 mask)
+{
+ memcpy(&f->val.fip[4], &val, sizeof(u32));
+ memcpy(&f->mask.fip[4], &mask, sizeof(u32));
+
+ return 0;
+}
+
+static inline int cxgb4_fill_ipv6_src_ip2(struct ch_filter_specification *f,
+ u32 val, u32 mask)
+{
+ memcpy(&f->val.fip[8], &val, sizeof(u32));
+ memcpy(&f->mask.fip[8], &mask, sizeof(u32));
+
+ return 0;
+}
+
+static inline int cxgb4_fill_ipv6_src_ip3(struct ch_filter_specification *f,
+ u32 val, u32 mask)
+{
+ memcpy(&f->val.fip[12], &val, sizeof(u32));
+ memcpy(&f->mask.fip[12], &mask, sizeof(u32));
+
+ return 0;
+}
+
+static inline int cxgb4_fill_ipv6_dst_ip0(struct ch_filter_specification *f,
+ u32 val, u32 mask)
+{
+ memcpy(&f->val.lip[0], &val, sizeof(u32));
+ memcpy(&f->mask.lip[0], &mask, sizeof(u32));
+
+ return 0;
+}
+
+static inline int cxgb4_fill_ipv6_dst_ip1(struct ch_filter_specification *f,
+ u32 val, u32 mask)
+{
+ memcpy(&f->val.lip[4], &val, sizeof(u32));
+ memcpy(&f->mask.lip[4], &mask, sizeof(u32));
+
+ return 0;
+}
+
+static inline int cxgb4_fill_ipv6_dst_ip2(struct ch_filter_specification *f,
+ u32 val, u32 mask)
+{
+ memcpy(&f->val.lip[8], &val, sizeof(u32));
+ memcpy(&f->mask.lip[8], &mask, sizeof(u32));
+
+ return 0;
+}
+
+static inline int cxgb4_fill_ipv6_dst_ip3(struct ch_filter_specification *f,
+ u32 val, u32 mask)
+{
+ memcpy(&f->val.lip[12], &val, sizeof(u32));
+ memcpy(&f->mask.lip[12], &mask, sizeof(u32));
+
+ return 0;
+}
+
+static const struct cxgb4_match_field cxgb4_ipv6_fields[] = {
+ { .off = 0, .val = cxgb4_fill_ipv6_tos },
+ { .off = 4, .val = cxgb4_fill_ipv6_proto },
+ { .off = 8, .val = cxgb4_fill_ipv6_src_ip0 },
+ { .off = 12, .val = cxgb4_fill_ipv6_src_ip1 },
+ { .off = 16, .val = cxgb4_fill_ipv6_src_ip2 },
+ { .off = 20, .val = cxgb4_fill_ipv6_src_ip3 },
+ { .off = 24, .val = cxgb4_fill_ipv6_dst_ip0 },
+ { .off = 28, .val = cxgb4_fill_ipv6_dst_ip1 },
+ { .off = 32, .val = cxgb4_fill_ipv6_dst_ip2 },
+ { .off = 36, .val = cxgb4_fill_ipv6_dst_ip3 },
+ { .val = NULL }
+};
+
+/* TCP/UDP match */
+static inline int cxgb4_fill_l4_ports(struct ch_filter_specification *f,
+ u32 val, u32 mask)
+{
+ f->val.fport = ntohl(val) >> 16;
+ f->mask.fport = ntohl(mask) >> 16;
+ f->val.lport = ntohl(val) & 0x0000FFFF;
+ f->mask.lport = ntohl(mask) & 0x0000FFFF;
+
+ return 0;
+};
+
+static const struct cxgb4_match_field cxgb4_tcp_fields[] = {
+ { .off = 0, .val = cxgb4_fill_l4_ports },
+ { .val = NULL }
+};
+
+static const struct cxgb4_match_field cxgb4_udp_fields[] = {
+ { .off = 0, .val = cxgb4_fill_l4_ports },
+ { .val = NULL }
+};
+
+struct cxgb4_next_header {
+ unsigned int offset; /* Offset to next header */
+ /* offset, shift, and mask added to offset above
+ * to get to next header. Useful when using a header
+ * field's value to jump to next header such as IHL field
+ * in IPv4 header.
+ */
+ unsigned int offoff;
+ u32 shift;
+ u32 mask;
+ /* match criteria to make this jump */
+ unsigned int match_off;
+ u32 match_val;
+ u32 match_mask;
+ /* location of jump to make */
+ const struct cxgb4_match_field *jump;
+};
+
+/* Accept a rule with a jump to transport layer header based on IHL field in
+ * IPv4 header.
+ */
+static const struct cxgb4_next_header cxgb4_ipv4_jumps[] = {
+ { .offset = 0, .offoff = 0, .shift = 6, .mask = 0xF,
+ .match_off = 8, .match_val = 0x600, .match_mask = 0xFF00,
+ .jump = cxgb4_tcp_fields },
+ { .offset = 0, .offoff = 0, .shift = 6, .mask = 0xF,
+ .match_off = 8, .match_val = 0x1100, .match_mask = 0xFF00,
+ .jump = cxgb4_udp_fields },
+ { .jump = NULL }
+};
+
+/* Accept a rule with a jump directly past the 40 Bytes of IPv6 fixed header
+ * to get to transport layer header.
+ */
+static const struct cxgb4_next_header cxgb4_ipv6_jumps[] = {
+ { .offset = 0x28, .offoff = 0, .shift = 0, .mask = 0,
+ .match_off = 4, .match_val = 0x60000, .match_mask = 0xFF0000,
+ .jump = cxgb4_tcp_fields },
+ { .offset = 0x28, .offoff = 0, .shift = 0, .mask = 0,
+ .match_off = 4, .match_val = 0x110000, .match_mask = 0xFF0000,
+ .jump = cxgb4_udp_fields },
+ { .jump = NULL }
+};
+#endif /* __CXGB4_TC_U32_PARSE_H */
--
2.5.3
^ permalink raw reply related
* [PATCH net-next 3/7] cxgb4: add debugfs support to dump filter debug logs
From: Rahul Lakkireddy @ 2016-09-12 8:12 UTC (permalink / raw)
To: netdev; +Cc: davem, hariprasad, leedom, nirranjan, indranil, Rahul Lakkireddy
In-Reply-To: <cover.1473667613.git.rahul.lakkireddy@chelsio.com>
Add debugfs support to dump filter debug information.
Signed-off-by: Rahul Lakkireddy <rahul.lakkireddy@chelsio.com>
Signed-off-by: Hariprasad Shenai <hariprasad@chelsio.com>
---
drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c | 4 +-
drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c | 415 +++++++++++++++++++++
drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h | 2 +
drivers/net/ethernet/chelsio/cxgb4/t4_values.h | 5 +-
4 files changed, 424 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c
index 91fb508..72cf3de2 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c
@@ -1,7 +1,7 @@
/*
* This file is part of the Chelsio T4 Ethernet driver for Linux.
*
- * Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved.
+ * Copyright (c) 2003-2016 Chelsio Communications, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
@@ -45,6 +45,7 @@
#include "cxgb4_debugfs.h"
#include "clip_tbl.h"
#include "l2t.h"
+#include "cxgb4_filter.h"
/* generic seq_file support for showing a table of size rows x width. */
static void *seq_tab_get_idx(struct seq_tab *tb, loff_t pos)
@@ -3272,6 +3273,7 @@ int t4_setup_debugfs(struct adapter *adap)
{ "tids", &tid_info_debugfs_fops, S_IRUSR, 0},
{ "blocked_fl", &blocked_fl_fops, S_IRUSR | S_IWUSR, 0 },
{ "meminfo", &meminfo_fops, S_IRUSR, 0 },
+ { "filters", &filters_debugfs_fops, S_IRUSR, 0 },
};
/* Debug FS nodes common to all T5 and later adapters.
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c
index 490bd94..51b6745 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c
@@ -34,6 +34,7 @@
#include "cxgb4.h"
#include "t4_regs.h"
+#include "t4_values.h"
#include "l2t.h"
#include "t4fw_api.h"
#include "cxgb4_filter.h"
@@ -669,3 +670,417 @@ void filter_rpl(struct adapter *adap, const struct cpl_set_tcb_rpl *rpl)
complete(&ctx->completion);
}
}
+
+/* Retrieve the packet count for the specified filter. */
+int cxgb4_get_filter_count(struct adapter *adapter, unsigned int fidx,
+ u64 *c, int hash, bool get_byte)
+{
+ struct filter_entry *f;
+ unsigned int tcb_base, tcbaddr;
+ unsigned int max_ftids;
+ int ret;
+
+ tcb_base = t4_read_reg(adapter, TP_CMM_TCB_BASE_A);
+ max_ftids = adapter->tids.nftids;
+ if ((fidx != (max_ftids + adapter->tids.nsftids - 1)) &&
+ (fidx >= max_ftids))
+ return -E2BIG;
+
+ f = &adapter->tids.ftid_tab[fidx];
+ if (!f->valid)
+ return -EINVAL;
+
+ tcbaddr = tcb_base + f->tid * TCB_SIZE;
+
+ if (is_t4(adapter->params.chip)) {
+ /* For T4, the Filter Packet Hit Count is maintained as a
+ * 64-bit Big Endian value in the TCB fields
+ * {t_rtt_ts_recent_age, t_rtseq_recent} ... The format in
+ * memory is swizzled/mapped in a manner such that instead
+ * of having this 64-bit counter show up at offset 24
+ * ((TCB_T_RTT_TS_RECENT_AGE_W == 6) * sizeof(u32)), it
+ * actually shows up at offset 16. Hence the constant "4"
+ * below instead of TCB_T_RTT_TS_RECENT_AGE_W.
+ */
+ if (get_byte) {
+ unsigned int word_offset = 4;
+ __be64 be64_byte_count;
+
+ spin_lock(&adapter->win0_lock);
+ ret = t4_memory_rw(adapter, MEMWIN_NIC, MEM_EDC0,
+ tcbaddr +
+ (word_offset * sizeof(__be32)),
+ sizeof(be64_byte_count),
+ &be64_byte_count,
+ T4_MEMORY_READ);
+ spin_unlock(&adapter->win0_lock);
+ if (ret < 0)
+ return ret;
+ *c = be64_to_cpu(be64_byte_count);
+ } else {
+ unsigned int word_offset = 4;
+ __be64 be64_count;
+
+ spin_lock(&adapter->win0_lock);
+ ret = t4_memory_rw(adapter, MEMWIN_NIC, MEM_EDC0,
+ tcbaddr +
+ (word_offset * sizeof(__be32)),
+ sizeof(be64_count),
+ (__be32 *)&be64_count,
+ T4_MEMORY_READ);
+ spin_unlock(&adapter->win0_lock);
+ if (ret < 0)
+ return ret;
+ *c = be64_to_cpu(be64_count);
+ }
+ } else {
+ /* For T5, the Filter Packet Hit Count is maintained as a
+ * 32-bit Big Endian value in the TCB field {timestamp}.
+ * Instead of the filter hit count showing up at offset 20
+ * ((TCB_TIMESTAMP_W == 5) * sizeof(u32)), it actually shows
+ * up at offset 24. Hence the constant "6" below.
+ */
+ if (get_byte) {
+ unsigned int word_offset = 4;
+ __be64 be64_byte_count;
+
+ spin_lock(&adapter->win0_lock);
+ ret = t4_memory_rw(adapter, MEMWIN_NIC, MEM_EDC0,
+ tcbaddr +
+ (word_offset * sizeof(__be32)),
+ sizeof(be64_byte_count),
+ &be64_byte_count,
+ T4_MEMORY_READ);
+ spin_unlock(&adapter->win0_lock);
+ if (ret < 0)
+ return ret;
+ *c = be64_to_cpu(be64_byte_count);
+ } else {
+ unsigned int word_offset = 6;
+ __be32 be32_count;
+
+ spin_lock(&adapter->win0_lock);
+ ret = t4_memory_rw(adapter, MEMWIN_NIC, MEM_EDC0,
+ tcbaddr +
+ (word_offset * sizeof(__be32)),
+ sizeof(be32_count), &be32_count,
+ T4_MEMORY_READ);
+ spin_unlock(&adapter->win0_lock);
+ if (ret < 0)
+ return ret;
+ *c = (u64)be32_to_cpu(be32_count);
+ }
+ }
+
+ return 0;
+}
+
+/* Filter Table. */
+static void filters_show_ipaddr(struct seq_file *seq,
+ int type, u8 *addr, u8 *addrm)
+{
+ int noctets, octet;
+
+ seq_puts(seq, " ");
+ if (type == 0) {
+ noctets = 4;
+ seq_printf(seq, "%48s", " ");
+ } else {
+ noctets = 16;
+ }
+
+ for (octet = 0; octet < noctets; octet++)
+ seq_printf(seq, "%02x", addr[octet]);
+ seq_puts(seq, "/");
+ for (octet = 0; octet < noctets; octet++)
+ seq_printf(seq, "%02x", addrm[octet]);
+}
+
+static void filters_display(struct seq_file *seq, unsigned int fidx,
+ struct filter_entry *f, int hash)
+{
+ struct adapter *adapter = seq->private;
+ u32 fconf = adapter->params.tp.vlan_pri_map;
+ u32 tpiconf = adapter->params.tp.ingress_config;
+ int i;
+
+ /* Filter index */
+ seq_printf(seq, "%4d%c%c", fidx,
+ (!f->locked ? ' ' : '!'),
+ (!f->pending ? ' ' : (!f->valid ? '+' : '-')));
+
+ if (f->fs.hitcnts) {
+ u64 hitcnt;
+ int ret;
+
+ ret = cxgb4_get_filter_count(adapter, fidx, &hitcnt,
+ hash, false);
+ if (ret)
+ seq_printf(seq, " %20s", "hits={ERROR}");
+ else
+ seq_printf(seq, " %20llu", hitcnt);
+ } else {
+ seq_printf(seq, " %20s", "Disabled");
+ }
+
+ /* Compressed header portion of filter. */
+ for (i = FT_FIRST_S; i <= FT_LAST_S; i++) {
+ switch (fconf & (1 << i)) {
+ case 0:
+ /* compressed filter field not enabled */
+ break;
+
+ case FCOE_F:
+ seq_printf(seq, " %1d/%1d",
+ f->fs.val.fcoe, f->fs.mask.fcoe);
+ break;
+
+ case PORT_F:
+ seq_printf(seq, " %1d/%1d",
+ f->fs.val.iport, f->fs.mask.iport);
+ break;
+
+ case VNIC_ID_F:
+ if ((tpiconf & VNIC_F) == 0)
+ seq_printf(seq, " %1d:%04x/%1d:%04x",
+ f->fs.val.ovlan_vld,
+ f->fs.val.ovlan,
+ f->fs.mask.ovlan_vld,
+ f->fs.mask.ovlan);
+ else
+ seq_printf(seq, " %1d:%1x:%02x/%1d:%1x:%02x",
+ f->fs.val.ovlan_vld,
+ (f->fs.val.ovlan >> 13) & 0x7,
+ f->fs.val.ovlan & 0x7f,
+ f->fs.mask.ovlan_vld,
+ (f->fs.mask.ovlan >> 13) & 0x7,
+ f->fs.mask.ovlan & 0x7f);
+ break;
+
+ case VLAN_F:
+ seq_printf(seq, " %1d:%04x/%1d:%04x",
+ f->fs.val.ivlan_vld,
+ f->fs.val.ivlan,
+ f->fs.mask.ivlan_vld,
+ f->fs.mask.ivlan);
+ break;
+
+ case TOS_F:
+ seq_printf(seq, " %02x/%02x",
+ f->fs.val.tos, f->fs.mask.tos);
+ break;
+
+ case PROTOCOL_F:
+ seq_printf(seq, " %02x/%02x",
+ f->fs.val.proto, f->fs.mask.proto);
+ break;
+
+ case ETHERTYPE_F:
+ seq_printf(seq, " %04x/%04x",
+ f->fs.val.ethtype, f->fs.mask.ethtype);
+ break;
+
+ case MACMATCH_F:
+ seq_printf(seq, " %03x/%03x",
+ f->fs.val.macidx, f->fs.mask.macidx);
+ break;
+
+ case MPSHITTYPE_F:
+ seq_printf(seq, " %1x/%1x",
+ f->fs.val.matchtype,
+ f->fs.mask.matchtype);
+ break;
+
+ case FRAGMENTATION_F:
+ seq_printf(seq, " %1d/%1d",
+ f->fs.val.frag, f->fs.mask.frag);
+ break;
+ }
+ }
+
+ /* Fixed portion of filter. */
+ filters_show_ipaddr(seq, f->fs.type,
+ f->fs.val.lip, f->fs.mask.lip);
+ filters_show_ipaddr(seq, f->fs.type,
+ f->fs.val.fip, f->fs.mask.fip);
+ seq_printf(seq, " %04x/%04x %04x/%04x",
+ f->fs.val.lport, f->fs.mask.lport,
+ f->fs.val.fport, f->fs.mask.fport);
+
+ /* Variable length filter action. */
+ if (f->fs.action == FILTER_DROP) {
+ seq_puts(seq, " Drop");
+ } else if (f->fs.action == FILTER_SWITCH) {
+ seq_printf(seq, " Switch: port=%d", f->fs.eport);
+ if (f->fs.newdmac)
+ seq_printf(seq,
+ ", dmac=%02x:%02x:%02x:%02x:%02x:%02x, l2tidx=%d",
+ f->fs.dmac[0], f->fs.dmac[1],
+ f->fs.dmac[2], f->fs.dmac[3],
+ f->fs.dmac[4], f->fs.dmac[5],
+ f->l2t->idx);
+ if (f->fs.newsmac)
+ seq_printf(seq,
+ ", smac=%02x:%02x:%02x:%02x:%02x:%02x, smtidx=%d",
+ f->fs.smac[0], f->fs.smac[1],
+ f->fs.smac[2], f->fs.smac[3],
+ f->fs.smac[4], f->fs.smac[5],
+ f->smtidx);
+ if (f->fs.newvlan == VLAN_REMOVE)
+ seq_puts(seq, ", vlan=none");
+ else if (f->fs.newvlan == VLAN_INSERT)
+ seq_printf(seq, ", vlan=insert(%x)",
+ f->fs.vlan);
+ else if (f->fs.newvlan == VLAN_REWRITE)
+ seq_printf(seq, ", vlan=rewrite(%x)",
+ f->fs.vlan);
+ } else {
+ seq_puts(seq, " Pass: Q=");
+ if (f->fs.dirsteer == 0) {
+ seq_puts(seq, "RSS");
+ if (f->fs.maskhash)
+ seq_puts(seq, "(TCB=hash)");
+ } else {
+ seq_printf(seq, "%d", f->fs.iq);
+ if (f->fs.dirsteerhash == 0)
+ seq_puts(seq, "(QID)");
+ else
+ seq_puts(seq, "(hash)");
+ }
+ }
+ if (f->fs.prio)
+ seq_puts(seq, " Prio");
+ if (f->fs.rpttid)
+ seq_puts(seq, " RptTID");
+ seq_puts(seq, "\n");
+}
+
+static int filters_show(struct seq_file *seq, void *v)
+{
+ struct adapter *adapter = seq->private;
+ u32 fconf = adapter->params.tp.vlan_pri_map;
+ u32 tpiconf = adapter->params.tp.ingress_config;
+ int i;
+
+ if (v == SEQ_START_TOKEN) {
+ seq_puts(seq, "[[Legend: '!' => locked; '+' => pending set; '-' => pending clear]]\n");
+ seq_puts(seq, " Idx Hits");
+ for (i = FT_FIRST_S; i <= FT_LAST_S; i++) {
+ switch (fconf & (1 << i)) {
+ case 0:
+ /* compressed filter field not enabled */
+ break;
+
+ case FCOE_F:
+ seq_puts(seq, " FCoE");
+ break;
+
+ case PORT_F:
+ seq_puts(seq, " Port");
+ break;
+
+ case VNIC_ID_F:
+ if ((tpiconf & VNIC_F) == 0)
+ seq_puts(seq, " vld:oVLAN");
+ else
+ seq_puts(seq, " VFvld:PF:VF");
+ break;
+
+ case VLAN_F:
+ seq_puts(seq, " vld:iVLAN");
+ break;
+
+ case TOS_F:
+ seq_puts(seq, " TOS");
+ break;
+
+ case PROTOCOL_F:
+ seq_puts(seq, " Prot");
+ break;
+
+ case ETHERTYPE_F:
+ seq_puts(seq, " EthType");
+ break;
+
+ case MACMATCH_F:
+ seq_puts(seq, " MACIdx");
+ break;
+
+ case MPSHITTYPE_F:
+ seq_puts(seq, " MPS");
+ break;
+
+ case FRAGMENTATION_F:
+ seq_puts(seq, " Frag");
+ break;
+ }
+ }
+ seq_printf(seq, " %65s %65s %9s %9s %s\n",
+ "LIP", "FIP", "LPORT", "FPORT", "Action");
+ } else {
+ int fidx = (uintptr_t)v - 2;
+ struct filter_entry *f = &adapter->tids.ftid_tab[fidx];
+
+ /* if this entry isn't filled in just return */
+ if (!f->valid && !f->pending)
+ return 0;
+
+ filters_display(seq, fidx, f, 0);
+ }
+ return 0;
+}
+
+static inline void *filters_get_idx(struct adapter *adapter, loff_t pos)
+{
+ if (pos > (adapter->tids.nftids + adapter->tids.nsftids))
+ return NULL;
+
+ return (void *)(uintptr_t)(pos + 1);
+}
+
+static void *filters_start(struct seq_file *seq, loff_t *pos)
+{
+ struct adapter *adapter = seq->private;
+
+ return *pos ? filters_get_idx(adapter, *pos) : SEQ_START_TOKEN;
+}
+
+static void *filters_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+ struct adapter *adapter = seq->private;
+
+ (*pos)++;
+ return filters_get_idx(adapter, *pos);
+}
+
+static void filters_stop(struct seq_file *seq, void *v)
+{
+}
+
+static const struct seq_operations filters_seq_ops = {
+ .start = filters_start,
+ .next = filters_next,
+ .stop = filters_stop,
+ .show = filters_show
+};
+
+int filters_open(struct inode *inode, struct file *file)
+{
+ struct adapter *adapter = inode->i_private;
+ int res;
+
+ res = seq_open(file, &filters_seq_ops);
+ if (!res) {
+ struct seq_file *seq = file->private_data;
+
+ seq->private = adapter;
+ }
+ return res;
+}
+
+const struct file_operations filters_debugfs_fops = {
+ .owner = THIS_MODULE,
+ .open = filters_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+};
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h
index 23742cb..e801e0b 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h
@@ -37,6 +37,8 @@
#include "t4_msg.h"
+extern const struct file_operations filters_debugfs_fops;
+
void filter_rpl(struct adapter *adap, const struct cpl_set_tcb_rpl *rpl);
void clear_filter(struct adapter *adap, struct filter_entry *f);
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_values.h b/drivers/net/ethernet/chelsio/cxgb4/t4_values.h
index 36cf307..0115222 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4_values.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_values.h
@@ -1,7 +1,7 @@
/*
* This file is part of the Chelsio T4 Ethernet driver for Linux.
*
- * Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved.
+ * Copyright (c) 2003-2016 Chelsio Communications, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
@@ -121,6 +121,9 @@
* selects for a particular field being present. These fields, when present
* in the Compressed Filter Tuple, have the following widths in bits.
*/
+#define FT_FIRST_S FCOE_S
+#define FT_LAST_S FRAGMENTATION_S
+
#define FT_FCOE_W 1
#define FT_PORT_W 3
#define FT_VNIC_ID_W 17
--
2.5.3
^ permalink raw reply related
* [PATCH net-next 2/7] cxgb4: add common api support for configuring filters
From: Rahul Lakkireddy @ 2016-09-12 8:12 UTC (permalink / raw)
To: netdev; +Cc: davem, hariprasad, leedom, nirranjan, indranil, Rahul Lakkireddy
In-Reply-To: <cover.1473667613.git.rahul.lakkireddy@chelsio.com>
Enable filters for non-offload configuration and add common api support
for setting and deleting filters in LE-TCAM region of the hardware.
IPv4 filters occupy one slot. IPv6 filters occupy 4 slots and must
be on a 4-slot boundary. IPv4 filters can not occupy a slot belonging
to IPv6 and the vice-versa is also true.
Filters are set and deleted asynchronously. Use completion to wait
for reply from firmware in order to allow for synchronization if needed.
Signed-off-by: Rahul Lakkireddy <rahul.lakkireddy@chelsio.com>
Signed-off-by: Hariprasad Shenai <hariprasad@chelsio.com>
---
drivers/net/ethernet/chelsio/cxgb4/cxgb4.h | 3 +
drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c | 424 +++++++++++++++++++++-
drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h | 1 +
drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 33 +-
drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h | 23 +-
5 files changed, 453 insertions(+), 31 deletions(-)
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
index 053976f..fbd593a 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
@@ -1055,7 +1055,10 @@ struct filter_entry {
u32 pending:1; /* filter action is pending firmware reply */
u32 smtidx:8; /* Source MAC Table index for smac */
+ struct filter_ctx *ctx; /* Caller's completion hook */
struct l2t_entry *l2t; /* Layer Two Table entry for dmac */
+ struct net_device *dev; /* Associated net device */
+ u32 tid; /* This will store the actual tid */
/* The filter itself. Most of this is a straight copy of information
* provided by the extended ioctl(). Some fields are translated to
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c
index 2e86902..490bd94 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c
@@ -33,10 +33,138 @@
*/
#include "cxgb4.h"
+#include "t4_regs.h"
#include "l2t.h"
#include "t4fw_api.h"
#include "cxgb4_filter.h"
+/* Validate filter spec against configuration done on the card.
+ */
+static int validate_filter(struct net_device *dev,
+ struct ch_filter_specification *fs)
+{
+ struct adapter *adapter = netdev2adap(dev);
+ u32 fconf, iconf;
+
+ /* Check for unconfigured fields being used. */
+ fconf = adapter->params.tp.vlan_pri_map;
+ iconf = adapter->params.tp.ingress_config;
+
+ #define S(_field) \
+ (fs->val._field || fs->mask._field)
+ #define U(_mask, _field) \
+ (!(fconf & (_mask)) && S(_field))
+
+ if (U(FCOE_F, fcoe) || U(PORT_F, iport) || U(TOS_F, tos) ||
+ U(ETHERTYPE_F, ethtype) || U(MACMATCH_F, macidx) ||
+ U(MPSHITTYPE_F, matchtype) || U(FRAGMENTATION_F, frag) ||
+ U(PROTOCOL_F, proto) ||
+ U(VNIC_ID_F, pfvf_vld) ||
+ U(VNIC_ID_F, ovlan_vld) ||
+ U(VLAN_F, ivlan_vld))
+ return -EOPNOTSUPP;
+
+ /* T4 inconveniently uses the same FT_VNIC_ID_W bits for both the Outer
+ * VLAN Tag and PF/VF/VFvld fields based on VNIC_F being set
+ * in TP_INGRESS_CONFIG. Hense the somewhat crazy checks
+ * below. Additionally, since the T4 firmware interface also
+ * carries that overlap, we need to translate any PF/VF
+ * specification into that internal format below.
+ */
+ if (S(pfvf_vld) && S(ovlan_vld))
+ return -EOPNOTSUPP;
+ if ((S(pfvf_vld) && !(iconf & VNIC_F)) ||
+ (S(ovlan_vld) && (iconf & VNIC_F)))
+ return -EOPNOTSUPP;
+ if (fs->val.pf > 0x7 || fs->val.vf > 0x7f)
+ return -ERANGE;
+ fs->mask.pf &= 0x7;
+ fs->mask.vf &= 0x7f;
+
+ #undef S
+ #undef U
+
+ /* If the user is requesting that the filter action loop
+ * matching packets back out one of our ports, make sure that
+ * the egress port is in range.
+ */
+ if (fs->action == FILTER_SWITCH &&
+ fs->eport >= adapter->params.nports)
+ return -ERANGE;
+
+ /* Don't allow various trivially obvious bogus out-of-range values... */
+ if (fs->val.iport >= adapter->params.nports)
+ return -ERANGE;
+
+ /* T4 doesn't support removing VLAN Tags for loop back filters. */
+ if (is_t4(adapter->params.chip) &&
+ fs->action == FILTER_SWITCH &&
+ (fs->newvlan == VLAN_REMOVE ||
+ fs->newvlan == VLAN_REWRITE))
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+static unsigned int get_filter_steerq(struct net_device *dev,
+ struct ch_filter_specification *fs)
+{
+ struct adapter *adapter = netdev2adap(dev);
+ unsigned int iq;
+
+ /* If the user has requested steering matching Ingress Packets
+ * to a specific Queue Set, we need to make sure it's in range
+ * for the port and map that into the Absolute Queue ID of the
+ * Queue Set's Response Queue.
+ */
+ if (!fs->dirsteer) {
+ if (fs->iq)
+ return -EINVAL;
+ iq = 0;
+ } else {
+ struct port_info *pi = netdev_priv(dev);
+
+ /* If the iq id is greater than the number of qsets,
+ * then assume it is an absolute qid.
+ */
+ if (fs->iq < pi->nqsets)
+ iq = adapter->sge.ethrxq[pi->first_qset +
+ fs->iq].rspq.abs_id;
+ else
+ iq = fs->iq;
+ }
+
+ return iq;
+}
+
+static int cxgb4_set_ftid(struct tid_info *t, int fidx, int family)
+{
+ spin_lock_bh(&t->ftid_lock);
+
+ if (test_bit(fidx, t->ftid_bmap)) {
+ spin_unlock_bh(&t->ftid_lock);
+ return -EBUSY;
+ }
+
+ if (family == PF_INET)
+ __set_bit(fidx, t->ftid_bmap);
+ else
+ bitmap_allocate_region(t->ftid_bmap, fidx, 2);
+
+ spin_unlock_bh(&t->ftid_lock);
+ return 0;
+}
+
+static void cxgb4_clear_ftid(struct tid_info *t, int fidx, int family)
+{
+ spin_lock_bh(&t->ftid_lock);
+ if (family == PF_INET)
+ __clear_bit(fidx, t->ftid_bmap);
+ else
+ bitmap_release_region(t->ftid_bmap, fidx, 2);
+ spin_unlock_bh(&t->ftid_lock);
+}
+
/* Delete the filter at a specified index.
*/
static int del_filter_wr(struct adapter *adapter, int fidx)
@@ -44,17 +172,16 @@ static int del_filter_wr(struct adapter *adapter, int fidx)
struct filter_entry *f = &adapter->tids.ftid_tab[fidx];
struct sk_buff *skb;
struct fw_filter_wr *fwr;
- unsigned int len, ftid;
+ unsigned int len;
len = sizeof(*fwr);
- ftid = adapter->tids.ftid_base + fidx;
skb = alloc_skb(len, GFP_KERNEL);
if (!skb)
return -ENOMEM;
fwr = (struct fw_filter_wr *)__skb_put(skb, len);
- t4_mk_filtdelwr(ftid, fwr, adapter->sge.fw_evtq.abs_id);
+ t4_mk_filtdelwr(f->tid, fwr, adapter->sge.fw_evtq.abs_id);
/* Mark the filter as "pending" and ship off the Filter Work Request.
* When we get the Work Request Reply we'll clear the pending status.
@@ -75,7 +202,6 @@ int set_filter_wr(struct adapter *adapter, int fidx)
struct filter_entry *f = &adapter->tids.ftid_tab[fidx];
struct sk_buff *skb;
struct fw_filter_wr *fwr;
- unsigned int ftid;
skb = alloc_skb(sizeof(*fwr), GFP_KERNEL);
if (!skb)
@@ -95,8 +221,6 @@ int set_filter_wr(struct adapter *adapter, int fidx)
}
}
- ftid = adapter->tids.ftid_base + fidx;
-
fwr = (struct fw_filter_wr *)__skb_put(skb, sizeof(*fwr));
memset(fwr, 0, sizeof(*fwr));
@@ -111,7 +235,7 @@ int set_filter_wr(struct adapter *adapter, int fidx)
fwr->op_pkd = htonl(FW_WR_OP_V(FW_FILTER_WR));
fwr->len16_pkd = htonl(FW_WR_LEN16_V(sizeof(*fwr) / 16));
fwr->tid_to_iq =
- htonl(FW_FILTER_WR_TID_V(ftid) |
+ htonl(FW_FILTER_WR_TID_V(f->tid) |
FW_FILTER_WR_RQTYPE_V(f->fs.type) |
FW_FILTER_WR_NOREPLY_V(0) |
FW_FILTER_WR_IQ_V(f->fs.iq));
@@ -237,34 +361,300 @@ void clear_filter(struct adapter *adap, struct filter_entry *f)
memset(f, 0, sizeof(*f));
}
+void clear_all_filters(struct adapter *adapter)
+{
+ unsigned int i;
+
+ if (adapter->tids.ftid_tab) {
+ struct filter_entry *f = &adapter->tids.ftid_tab[0];
+ unsigned int max_ftid = adapter->tids.nftids +
+ adapter->tids.nsftids;
+
+ for (i = 0; i < max_ftid; i++, f++)
+ if (f->valid || f->pending)
+ clear_filter(adapter, f);
+ }
+}
+
+/* Fill up default masks for set match fields. */
+static void fill_default_mask(struct ch_filter_specification *fs)
+{
+ unsigned int i;
+ unsigned int lip = 0, lip_mask = 0;
+ unsigned int fip = 0, fip_mask = 0;
+
+ if (fs->val.iport && !fs->mask.iport)
+ fs->mask.iport |= ~0;
+ if (fs->val.fcoe && !fs->mask.fcoe)
+ fs->mask.fcoe |= ~0;
+ if (fs->val.matchtype && !fs->mask.matchtype)
+ fs->mask.matchtype |= ~0;
+ if (fs->val.macidx && !fs->mask.macidx)
+ fs->mask.macidx |= ~0;
+ if (fs->val.ethtype && !fs->mask.ethtype)
+ fs->mask.ethtype |= ~0;
+ if (fs->val.ivlan && !fs->mask.ivlan)
+ fs->mask.ivlan |= ~0;
+ if (fs->val.ovlan && !fs->mask.ovlan)
+ fs->mask.ovlan |= ~0;
+ if (fs->val.frag && !fs->mask.frag)
+ fs->mask.frag |= ~0;
+ if (fs->val.tos && !fs->mask.tos)
+ fs->mask.tos |= ~0;
+ if (fs->val.proto && !fs->mask.proto)
+ fs->mask.proto |= ~0;
+
+ for (i = 0; i < ARRAY_SIZE(fs->val.lip); i++) {
+ lip |= fs->val.lip[i];
+ lip_mask |= fs->mask.lip[i];
+ fip |= fs->val.fip[i];
+ fip_mask |= fs->mask.fip[i];
+ }
+
+ if (lip && !lip_mask)
+ memset(fs->mask.lip, ~0, sizeof(fs->mask.lip));
+
+ if (fip && !fip_mask)
+ memset(fs->mask.fip, ~0, sizeof(fs->mask.lip));
+
+ if (fs->val.lport && !fs->mask.lport)
+ fs->mask.lport = ~0;
+ if (fs->val.fport && !fs->mask.fport)
+ fs->mask.fport = ~0;
+}
+
+/* Check a Chelsio Filter Request for validity, convert it into our internal
+ * format and send it to the hardware. Return 0 on success, an error number
+ * otherwise. We attach any provided filter operation context to the internal
+ * filter specification in order to facilitate signaling completion of the
+ * operation.
+ */
+int cxgb4_set_filter(struct net_device *dev, int filter_id,
+ struct ch_filter_specification *fs,
+ struct filter_ctx *ctx)
+{
+ struct adapter *adapter = netdev2adap(dev);
+ struct filter_entry *f;
+ u32 iconf;
+ unsigned int fidx, iq;
+ unsigned int max_fidx;
+ int ret;
+
+ max_fidx = adapter->tids.nftids;
+ if ((filter_id != (max_fidx + adapter->tids.nsftids - 1)) &&
+ (filter_id >= max_fidx))
+ return -E2BIG;
+
+ fill_default_mask(fs);
+
+ ret = validate_filter(dev, fs);
+ if (ret)
+ return ret;
+
+ iq = get_filter_steerq(dev, fs);
+ if (iq < 0)
+ return iq;
+
+ /* IPv6 filters occupy four slots and must be aligned on
+ * four-slot boundaries. IPv4 filters only occupy a single
+ * slot and have no alignment requirements but writing a new
+ * IPv4 filter into the middle of an existing IPv6 filter
+ * requires clearing the old IPv6 filter and hence we prevent
+ * insertion.
+ */
+ if (fs->type == 0) { /* IPv4 */
+ /* If our IPv4 filter isn't being written to a
+ * multiple of four filter index and there's an IPv6
+ * filter at the multiple of 4 base slot, then we
+ * prevent insertion.
+ */
+ fidx = filter_id & ~0x3;
+ if (fidx != filter_id &&
+ adapter->tids.ftid_tab[fidx].fs.type) {
+ f = &adapter->tids.ftid_tab[fidx];
+ if (f->valid) {
+ dev_err(adapter->pdev_dev,
+ "Invalid location. IPv6 requires 4 slots and is occupying slots %u to %u\n",
+ fidx, fidx + 3);
+ return -EINVAL;
+ }
+ }
+ } else { /* IPv6 */
+ /* Ensure that the IPv6 filter is aligned on a
+ * multiple of 4 boundary.
+ */
+ if (filter_id & 0x3) {
+ dev_err(adapter->pdev_dev,
+ "Invalid location. IPv6 must be aligned on a 4-slot boundary\n");
+ return -EINVAL;
+ }
+
+ /* Check all except the base overlapping IPv4 filter slots. */
+ for (fidx = filter_id + 1; fidx < filter_id + 4; fidx++) {
+ f = &adapter->tids.ftid_tab[fidx];
+ if (f->valid) {
+ dev_err(adapter->pdev_dev,
+ "Invalid location. IPv6 requires 4 slots and an IPv4 filter exists at %u\n",
+ fidx);
+ return -EINVAL;
+ }
+ }
+ }
+
+ /* Check to make sure that provided filter index is not
+ * already in use by someone else
+ */
+ f = &adapter->tids.ftid_tab[filter_id];
+ if (f->valid)
+ return -EBUSY;
+
+ fidx = filter_id + adapter->tids.ftid_base;
+ ret = cxgb4_set_ftid(&adapter->tids, filter_id,
+ fs->type ? PF_INET6 : PF_INET);
+ if (ret)
+ return ret;
+
+ /* Check to make sure the filter requested is writable ... */
+ ret = writable_filter(f);
+ if (ret) {
+ /* Clear the bits we have set above */
+ cxgb4_clear_ftid(&adapter->tids, filter_id,
+ fs->type ? PF_INET6 : PF_INET);
+ return ret;
+ }
+
+ /* Clear out any old resources being used by the filter before
+ * we start constructing the new filter.
+ */
+ if (f->valid)
+ clear_filter(adapter, f);
+
+ /* Convert the filter specification into our internal format.
+ * We copy the PF/VF specification into the Outer VLAN field
+ * here so the rest of the code -- including the interface to
+ * the firmware -- doesn't have to constantly do these checks.
+ */
+ f->fs = *fs;
+ f->fs.iq = iq;
+ f->dev = dev;
+
+ iconf = adapter->params.tp.ingress_config;
+ if (iconf & VNIC_F) {
+ f->fs.val.ovlan = (fs->val.pf << 13) | fs->val.vf;
+ f->fs.mask.ovlan = (fs->mask.pf << 13) | fs->mask.vf;
+ f->fs.val.ovlan_vld = fs->val.pfvf_vld;
+ f->fs.mask.ovlan_vld = fs->mask.pfvf_vld;
+ }
+
+ /* Attempt to set the filter. If we don't succeed, we clear
+ * it and return the failure.
+ */
+ f->ctx = ctx;
+ f->tid = fidx; /* Save the actual tid */
+ ret = set_filter_wr(adapter, filter_id);
+ if (ret) {
+ cxgb4_clear_ftid(&adapter->tids, filter_id,
+ fs->type ? PF_INET6 : PF_INET);
+ clear_filter(adapter, f);
+ }
+
+ return ret;
+}
+
+/* Check a delete filter request for validity and send it to the hardware.
+ * Return 0 on success, an error number otherwise. We attach any provided
+ * filter operation context to the internal filter specification in order to
+ * facilitate signaling completion of the operation.
+ */
+int cxgb4_del_filter(struct net_device *dev, int filter_id,
+ struct filter_ctx *ctx)
+{
+ struct adapter *adapter = netdev2adap(dev);
+ struct filter_entry *f;
+ unsigned int max_fidx;
+ int ret = 0;
+
+ max_fidx = adapter->tids.nftids;
+ if ((filter_id != (max_fidx + adapter->tids.nsftids - 1)) &&
+ (filter_id >= max_fidx))
+ return -E2BIG;
+
+ f = &adapter->tids.ftid_tab[filter_id];
+ ret = writable_filter(f);
+ if (ret)
+ return ret;
+
+ if (f->valid) {
+ f->ctx = ctx;
+ cxgb4_clear_ftid(&adapter->tids, filter_id,
+ f->fs.type ? PF_INET6 : PF_INET);
+ return del_filter_wr(adapter, filter_id);
+ }
+
+ /* If the caller has passed in a Completion Context then we need to
+ * mark it as a successful completion so they don't stall waiting
+ * for it.
+ */
+ if (ctx) {
+ ctx->result = 0;
+ complete(&ctx->completion);
+ }
+ return ret;
+}
+
/* Handle a filter write/deletion reply.
*/
void filter_rpl(struct adapter *adap, const struct cpl_set_tcb_rpl *rpl)
{
- unsigned int idx = GET_TID(rpl);
- unsigned int nidx = idx - adap->tids.ftid_base;
- unsigned int ret;
- struct filter_entry *f;
+ unsigned int tid = GET_TID(rpl);
+ struct filter_entry *f = NULL;
+ unsigned int max_fidx;
+ int idx;
- if (idx >= adap->tids.ftid_base && nidx <
- (adap->tids.nftids + adap->tids.nsftids)) {
- idx = nidx;
- ret = TCB_COOKIE_G(rpl->cookie);
+ max_fidx = adap->tids.nftids + adap->tids.nsftids;
+ /* Get the corresponding filter entry for this tid */
+ if (adap->tids.ftid_tab) {
+ /* Check this in normal filter region */
+ idx = tid - adap->tids.ftid_base;
+ if (idx >= max_fidx)
+ return;
f = &adap->tids.ftid_tab[idx];
+ if (f->tid != tid)
+ return;
+ }
+
+ /* We found the filter entry for this tid */
+ if (f) {
+ unsigned int ret = TCB_COOKIE_G(rpl->cookie);
+ struct filter_ctx *ctx;
+
+ /* Pull off any filter operation context attached to the
+ * filter.
+ */
+ ctx = f->ctx;
+ f->ctx = NULL;
if (ret == FW_FILTER_WR_FLT_DELETED) {
/* Clear the filter when we get confirmation from the
* hardware that the filter has been deleted.
*/
clear_filter(adap, f);
+ if (ctx)
+ ctx->result = 0;
} else if (ret == FW_FILTER_WR_SMT_TBL_FULL) {
dev_err(adap->pdev_dev, "filter %u setup failed due to full SMT\n",
idx);
clear_filter(adap, f);
+ if (ctx)
+ ctx->result = -ENOMEM;
} else if (ret == FW_FILTER_WR_FLT_ADDED) {
f->smtidx = (be64_to_cpu(rpl->oldval) >> 24) & 0xff;
f->pending = 0; /* asynchronous setup completed */
f->valid = 1;
+ if (ctx) {
+ ctx->result = 0;
+ ctx->tid = idx;
+ }
} else {
/* Something went wrong. Issue a warning about the
* problem and clear everything out.
@@ -272,6 +662,10 @@ void filter_rpl(struct adapter *adap, const struct cpl_set_tcb_rpl *rpl)
dev_err(adap->pdev_dev, "filter %u setup failed with error %u\n",
idx, ret);
clear_filter(adap, f);
+ if (ctx)
+ ctx->result = -EINVAL;
}
+ if (ctx)
+ complete(&ctx->completion);
}
}
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h
index f6bd0bf..23742cb 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h
@@ -44,4 +44,5 @@ int set_filter_wr(struct adapter *adapter, int fidx);
int delete_filter(struct adapter *adapter, unsigned int fidx);
int writable_filter(struct filter_entry *f);
+void clear_all_filters(struct adapter *adapter);
#endif /* __CXGB4_FILTER_H */
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
index 00f4d7f..af07f9d 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
@@ -1498,17 +1498,20 @@ static int tid_init(struct tid_info *t)
{
size_t size;
unsigned int stid_bmap_size;
+ unsigned int ftid_bmap_size;
unsigned int natids = t->natids;
+ unsigned int max_ftids = t->nftids + t->nsftids;
struct adapter *adap = container_of(t, struct adapter, tids);
stid_bmap_size = BITS_TO_LONGS(t->nstids + t->nsftids);
+ ftid_bmap_size = BITS_TO_LONGS(t->nftids);
size = t->ntids * sizeof(*t->tid_tab) +
natids * sizeof(*t->atid_tab) +
t->nstids * sizeof(*t->stid_tab) +
t->nsftids * sizeof(*t->stid_tab) +
stid_bmap_size * sizeof(long) +
- t->nftids * sizeof(*t->ftid_tab) +
- t->nsftids * sizeof(*t->ftid_tab);
+ max_ftids * sizeof(*t->ftid_tab) +
+ ftid_bmap_size * sizeof(long);
t->tid_tab = t4_alloc_mem(size);
if (!t->tid_tab)
@@ -1518,8 +1521,10 @@ static int tid_init(struct tid_info *t)
t->stid_tab = (struct serv_entry *)&t->atid_tab[natids];
t->stid_bmap = (unsigned long *)&t->stid_tab[t->nstids + t->nsftids];
t->ftid_tab = (struct filter_entry *)&t->stid_bmap[stid_bmap_size];
+ t->ftid_bmap = (unsigned long *)&t->ftid_tab[max_ftids];
spin_lock_init(&t->stid_lock);
spin_lock_init(&t->atid_lock);
+ spin_lock_init(&t->ftid_lock);
t->stids_in_use = 0;
t->sftids_in_use = 0;
@@ -1534,12 +1539,16 @@ static int tid_init(struct tid_info *t)
t->atid_tab[natids - 1].next = &t->atid_tab[natids];
t->afree = t->atid_tab;
}
- bitmap_zero(t->stid_bmap, t->nstids + t->nsftids);
- /* Reserve stid 0 for T4/T5 adapters */
- if (!t->stid_base &&
- (CHELSIO_CHIP_VERSION(adap->params.chip) <= CHELSIO_T5))
- __set_bit(0, t->stid_bmap);
+ if (is_offload(adap)) {
+ bitmap_zero(t->stid_bmap, t->nstids + t->nsftids);
+ /* Reserve stid 0 for T4/T5 adapters */
+ if (!t->stid_base &&
+ (CHELSIO_CHIP_VERSION(adap->params.chip) <= CHELSIO_T5))
+ __set_bit(0, t->stid_bmap);
+ }
+
+ bitmap_zero(t->ftid_bmap, t->nftids);
return 0;
}
@@ -5200,7 +5209,7 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
i);
}
- if (is_offload(adapter) && tid_init(&adapter->tids) < 0) {
+ if (tid_init(&adapter->tids) < 0) {
dev_warn(&pdev->dev, "could not allocate TID table, "
"continuing\n");
adapter->params.offload = 0;
@@ -5383,13 +5392,7 @@ static void remove_one(struct pci_dev *pdev)
/* If we allocated filters, free up state associated with any
* valid filters ...
*/
- if (adapter->tids.ftid_tab) {
- struct filter_entry *f = &adapter->tids.ftid_tab[0];
- for (i = 0; i < (adapter->tids.nftids +
- adapter->tids.nsftids); i++, f++)
- if (f->valid)
- clear_filter(adapter, f);
- }
+ clear_all_filters(adapter);
if (adapter->flags & FULL_INIT_DONE)
cxgb_down(adapter);
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h
index ab40372..7efb2d0 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h
@@ -1,7 +1,7 @@
/*
* This file is part of the Chelsio T4 Ethernet driver for Linux.
*
- * Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved.
+ * Copyright (c) 2003-2016 Chelsio Communications, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
@@ -104,6 +104,7 @@ struct tid_info {
unsigned int atid_base;
struct filter_entry *ftid_tab;
+ unsigned long *ftid_bmap;
unsigned int nftids;
unsigned int ftid_base;
unsigned int aftid_base;
@@ -124,6 +125,8 @@ struct tid_info {
atomic_t tids_in_use;
/* TIDs in the HASH */
atomic_t hash_tids_in_use;
+ /* lock for setting/clearing filter bitmap */
+ spinlock_t ftid_lock;
};
static inline void *lookup_tid(const struct tid_info *t, unsigned int tid)
@@ -183,6 +186,24 @@ int cxgb4_create_server_filter(const struct net_device *dev, unsigned int stid,
int cxgb4_remove_server_filter(const struct net_device *dev, unsigned int stid,
unsigned int queue, bool ipv6);
+/* Filter operation context to allow callers of cxgb4_set_filter() and
+ * cxgb4_del_filter() to wait for an asynchronous completion.
+ */
+struct filter_ctx {
+ struct completion completion; /* completion rendezvous */
+ void *closure; /* caller's opaque information */
+ int result; /* result of operation */
+ u32 tid; /* to store tid */
+};
+
+struct ch_filter_specification;
+
+int cxgb4_set_filter(struct net_device *dev, int filter_id,
+ struct ch_filter_specification *fs,
+ struct filter_ctx *ctx);
+int cxgb4_del_filter(struct net_device *dev, int filter_id,
+ struct filter_ctx *ctx);
+
static inline void set_wr_txq(struct sk_buff *skb, int prio, int queue)
{
skb_set_queue_mapping(skb, (queue << 1) | prio);
--
2.5.3
^ permalink raw reply related
* [PATCH net-next 1/7] cxgb4: move common filter code to separate file
From: Rahul Lakkireddy @ 2016-09-12 8:12 UTC (permalink / raw)
To: netdev; +Cc: davem, hariprasad, leedom, nirranjan, indranil, Rahul Lakkireddy
In-Reply-To: <cover.1473667613.git.rahul.lakkireddy@chelsio.com>
Move common filter code to separate files. Also fix the following
checkpatch checks.
CHECK: Comparison to NULL could be written "!f->l2t"
+ if (f->l2t == NULL) {
CHECK: spaces preferred around that '/' (ctx:VxV)
+ fwr->len16_pkd = htonl(FW_WR_LEN16_V(sizeof(*fwr)/16));
Signed-off-by: Rahul Lakkireddy <rahul.lakkireddy@chelsio.com>
Signed-off-by: Hariprasad Shenai <hariprasad@chelsio.com>
---
drivers/net/ethernet/chelsio/cxgb4/Makefile | 2 +-
drivers/net/ethernet/chelsio/cxgb4/cxgb4.h | 24 ++
drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c | 277 ++++++++++++++++++++++
drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h | 47 ++++
drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 264 +--------------------
5 files changed, 350 insertions(+), 264 deletions(-)
create mode 100644 drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c
create mode 100644 drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h
diff --git a/drivers/net/ethernet/chelsio/cxgb4/Makefile b/drivers/net/ethernet/chelsio/cxgb4/Makefile
index 2461296..da88981 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/Makefile
+++ b/drivers/net/ethernet/chelsio/cxgb4/Makefile
@@ -4,7 +4,7 @@
obj-$(CONFIG_CHELSIO_T4) += cxgb4.o
-cxgb4-objs := cxgb4_main.o l2t.o t4_hw.o sge.o clip_tbl.o cxgb4_ethtool.o cxgb4_uld.o sched.o
+cxgb4-objs := cxgb4_main.o l2t.o t4_hw.o sge.o clip_tbl.o cxgb4_ethtool.o cxgb4_uld.o sched.o cxgb4_filter.o
cxgb4-$(CONFIG_CHELSIO_T4_DCB) += cxgb4_dcb.o
cxgb4-$(CONFIG_CHELSIO_T4_FCOE) += cxgb4_fcoe.o
cxgb4-$(CONFIG_DEBUG_FS) += cxgb4_debugfs.o
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
index 4595569..053976f 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
@@ -1041,6 +1041,30 @@ enum {
VLAN_REWRITE
};
+/* Host shadow copy of ingress filter entry. This is in host native format
+ * and doesn't match the ordering or bit order, etc. of the hardware of the
+ * firmware command. The use of bit-field structure elements is purely to
+ * remind ourselves of the field size limitations and save memory in the case
+ * where the filter table is large.
+ */
+struct filter_entry {
+ /* Administrative fields for filter.
+ */
+ u32 valid:1; /* filter allocated and valid */
+ u32 locked:1; /* filter is administratively locked */
+
+ u32 pending:1; /* filter action is pending firmware reply */
+ u32 smtidx:8; /* Source MAC Table index for smac */
+ struct l2t_entry *l2t; /* Layer Two Table entry for dmac */
+
+ /* The filter itself. Most of this is a straight copy of information
+ * provided by the extended ioctl(). Some fields are translated to
+ * internal forms -- for instance the Ingress Queue ID passed in from
+ * the ioctl() is translated into the Absolute Ingress Queue ID.
+ */
+ struct ch_filter_specification fs;
+};
+
static inline int is_offload(const struct adapter *adap)
{
return adap->params.offload;
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c
new file mode 100644
index 0000000..2e86902
--- /dev/null
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c
@@ -0,0 +1,277 @@
+/*
+ * This file is part of the Chelsio T4 Ethernet driver for Linux.
+ *
+ * Copyright (c) 2003-2016 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "cxgb4.h"
+#include "l2t.h"
+#include "t4fw_api.h"
+#include "cxgb4_filter.h"
+
+/* Delete the filter at a specified index.
+ */
+static int del_filter_wr(struct adapter *adapter, int fidx)
+{
+ struct filter_entry *f = &adapter->tids.ftid_tab[fidx];
+ struct sk_buff *skb;
+ struct fw_filter_wr *fwr;
+ unsigned int len, ftid;
+
+ len = sizeof(*fwr);
+ ftid = adapter->tids.ftid_base + fidx;
+
+ skb = alloc_skb(len, GFP_KERNEL);
+ if (!skb)
+ return -ENOMEM;
+
+ fwr = (struct fw_filter_wr *)__skb_put(skb, len);
+ t4_mk_filtdelwr(ftid, fwr, adapter->sge.fw_evtq.abs_id);
+
+ /* Mark the filter as "pending" and ship off the Filter Work Request.
+ * When we get the Work Request Reply we'll clear the pending status.
+ */
+ f->pending = 1;
+ t4_mgmt_tx(adapter, skb);
+ return 0;
+}
+
+/* Send a Work Request to write the filter at a specified index. We construct
+ * a Firmware Filter Work Request to have the work done and put the indicated
+ * filter into "pending" mode which will prevent any further actions against
+ * it till we get a reply from the firmware on the completion status of the
+ * request.
+ */
+int set_filter_wr(struct adapter *adapter, int fidx)
+{
+ struct filter_entry *f = &adapter->tids.ftid_tab[fidx];
+ struct sk_buff *skb;
+ struct fw_filter_wr *fwr;
+ unsigned int ftid;
+
+ skb = alloc_skb(sizeof(*fwr), GFP_KERNEL);
+ if (!skb)
+ return -ENOMEM;
+
+ /* If the new filter requires loopback Destination MAC and/or VLAN
+ * rewriting then we need to allocate a Layer 2 Table (L2T) entry for
+ * the filter.
+ */
+ if (f->fs.newdmac || f->fs.newvlan) {
+ /* allocate L2T entry for new filter */
+ f->l2t = t4_l2t_alloc_switching(adapter, f->fs.vlan,
+ f->fs.eport, f->fs.dmac);
+ if (!f->l2t) {
+ kfree_skb(skb);
+ return -ENOMEM;
+ }
+ }
+
+ ftid = adapter->tids.ftid_base + fidx;
+
+ fwr = (struct fw_filter_wr *)__skb_put(skb, sizeof(*fwr));
+ memset(fwr, 0, sizeof(*fwr));
+
+ /* It would be nice to put most of the following in t4_hw.c but most
+ * of the work is translating the cxgbtool ch_filter_specification
+ * into the Work Request and the definition of that structure is
+ * currently in cxgbtool.h which isn't appropriate to pull into the
+ * common code. We may eventually try to come up with a more neutral
+ * filter specification structure but for now it's easiest to simply
+ * put this fairly direct code in line ...
+ */
+ fwr->op_pkd = htonl(FW_WR_OP_V(FW_FILTER_WR));
+ fwr->len16_pkd = htonl(FW_WR_LEN16_V(sizeof(*fwr) / 16));
+ fwr->tid_to_iq =
+ htonl(FW_FILTER_WR_TID_V(ftid) |
+ FW_FILTER_WR_RQTYPE_V(f->fs.type) |
+ FW_FILTER_WR_NOREPLY_V(0) |
+ FW_FILTER_WR_IQ_V(f->fs.iq));
+ fwr->del_filter_to_l2tix =
+ htonl(FW_FILTER_WR_RPTTID_V(f->fs.rpttid) |
+ FW_FILTER_WR_DROP_V(f->fs.action == FILTER_DROP) |
+ FW_FILTER_WR_DIRSTEER_V(f->fs.dirsteer) |
+ FW_FILTER_WR_MASKHASH_V(f->fs.maskhash) |
+ FW_FILTER_WR_DIRSTEERHASH_V(f->fs.dirsteerhash) |
+ FW_FILTER_WR_LPBK_V(f->fs.action == FILTER_SWITCH) |
+ FW_FILTER_WR_DMAC_V(f->fs.newdmac) |
+ FW_FILTER_WR_SMAC_V(f->fs.newsmac) |
+ FW_FILTER_WR_INSVLAN_V(f->fs.newvlan == VLAN_INSERT ||
+ f->fs.newvlan == VLAN_REWRITE) |
+ FW_FILTER_WR_RMVLAN_V(f->fs.newvlan == VLAN_REMOVE ||
+ f->fs.newvlan == VLAN_REWRITE) |
+ FW_FILTER_WR_HITCNTS_V(f->fs.hitcnts) |
+ FW_FILTER_WR_TXCHAN_V(f->fs.eport) |
+ FW_FILTER_WR_PRIO_V(f->fs.prio) |
+ FW_FILTER_WR_L2TIX_V(f->l2t ? f->l2t->idx : 0));
+ fwr->ethtype = htons(f->fs.val.ethtype);
+ fwr->ethtypem = htons(f->fs.mask.ethtype);
+ fwr->frag_to_ovlan_vldm =
+ (FW_FILTER_WR_FRAG_V(f->fs.val.frag) |
+ FW_FILTER_WR_FRAGM_V(f->fs.mask.frag) |
+ FW_FILTER_WR_IVLAN_VLD_V(f->fs.val.ivlan_vld) |
+ FW_FILTER_WR_OVLAN_VLD_V(f->fs.val.ovlan_vld) |
+ FW_FILTER_WR_IVLAN_VLDM_V(f->fs.mask.ivlan_vld) |
+ FW_FILTER_WR_OVLAN_VLDM_V(f->fs.mask.ovlan_vld));
+ fwr->smac_sel = 0;
+ fwr->rx_chan_rx_rpl_iq =
+ htons(FW_FILTER_WR_RX_CHAN_V(0) |
+ FW_FILTER_WR_RX_RPL_IQ_V(adapter->sge.fw_evtq.abs_id));
+ fwr->maci_to_matchtypem =
+ htonl(FW_FILTER_WR_MACI_V(f->fs.val.macidx) |
+ FW_FILTER_WR_MACIM_V(f->fs.mask.macidx) |
+ FW_FILTER_WR_FCOE_V(f->fs.val.fcoe) |
+ FW_FILTER_WR_FCOEM_V(f->fs.mask.fcoe) |
+ FW_FILTER_WR_PORT_V(f->fs.val.iport) |
+ FW_FILTER_WR_PORTM_V(f->fs.mask.iport) |
+ FW_FILTER_WR_MATCHTYPE_V(f->fs.val.matchtype) |
+ FW_FILTER_WR_MATCHTYPEM_V(f->fs.mask.matchtype));
+ fwr->ptcl = f->fs.val.proto;
+ fwr->ptclm = f->fs.mask.proto;
+ fwr->ttyp = f->fs.val.tos;
+ fwr->ttypm = f->fs.mask.tos;
+ fwr->ivlan = htons(f->fs.val.ivlan);
+ fwr->ivlanm = htons(f->fs.mask.ivlan);
+ fwr->ovlan = htons(f->fs.val.ovlan);
+ fwr->ovlanm = htons(f->fs.mask.ovlan);
+ memcpy(fwr->lip, f->fs.val.lip, sizeof(fwr->lip));
+ memcpy(fwr->lipm, f->fs.mask.lip, sizeof(fwr->lipm));
+ memcpy(fwr->fip, f->fs.val.fip, sizeof(fwr->fip));
+ memcpy(fwr->fipm, f->fs.mask.fip, sizeof(fwr->fipm));
+ fwr->lp = htons(f->fs.val.lport);
+ fwr->lpm = htons(f->fs.mask.lport);
+ fwr->fp = htons(f->fs.val.fport);
+ fwr->fpm = htons(f->fs.mask.fport);
+ if (f->fs.newsmac)
+ memcpy(fwr->sma, f->fs.smac, sizeof(fwr->sma));
+
+ /* Mark the filter as "pending" and ship off the Filter Work Request.
+ * When we get the Work Request Reply we'll clear the pending status.
+ */
+ f->pending = 1;
+ set_wr_txq(skb, CPL_PRIORITY_CONTROL, f->fs.val.iport & 0x3);
+ t4_ofld_send(adapter, skb);
+ return 0;
+}
+
+/* Return an error number if the indicated filter isn't writable ...
+ */
+int writable_filter(struct filter_entry *f)
+{
+ if (f->locked)
+ return -EPERM;
+ if (f->pending)
+ return -EBUSY;
+
+ return 0;
+}
+
+/* Delete the filter at the specified index (if valid). The checks for all
+ * the common problems with doing this like the filter being locked, currently
+ * pending in another operation, etc.
+ */
+int delete_filter(struct adapter *adapter, unsigned int fidx)
+{
+ struct filter_entry *f;
+ int ret;
+
+ if (fidx >= adapter->tids.nftids + adapter->tids.nsftids)
+ return -EINVAL;
+
+ f = &adapter->tids.ftid_tab[fidx];
+ ret = writable_filter(f);
+ if (ret)
+ return ret;
+ if (f->valid)
+ return del_filter_wr(adapter, fidx);
+
+ return 0;
+}
+
+/* Clear a filter and release any of its resources that we own. This also
+ * clears the filter's "pending" status.
+ */
+void clear_filter(struct adapter *adap, struct filter_entry *f)
+{
+ /* If the new or old filter have loopback rewriteing rules then we'll
+ * need to free any existing Layer Two Table (L2T) entries of the old
+ * filter rule. The firmware will handle freeing up any Source MAC
+ * Table (SMT) entries used for rewriting Source MAC Addresses in
+ * loopback rules.
+ */
+ if (f->l2t)
+ cxgb4_l2t_release(f->l2t);
+
+ /* The zeroing of the filter rule below clears the filter valid,
+ * pending, locked flags, l2t pointer, etc. so it's all we need for
+ * this operation.
+ */
+ memset(f, 0, sizeof(*f));
+}
+
+/* Handle a filter write/deletion reply.
+ */
+void filter_rpl(struct adapter *adap, const struct cpl_set_tcb_rpl *rpl)
+{
+ unsigned int idx = GET_TID(rpl);
+ unsigned int nidx = idx - adap->tids.ftid_base;
+ unsigned int ret;
+ struct filter_entry *f;
+
+ if (idx >= adap->tids.ftid_base && nidx <
+ (adap->tids.nftids + adap->tids.nsftids)) {
+ idx = nidx;
+ ret = TCB_COOKIE_G(rpl->cookie);
+ f = &adap->tids.ftid_tab[idx];
+
+ if (ret == FW_FILTER_WR_FLT_DELETED) {
+ /* Clear the filter when we get confirmation from the
+ * hardware that the filter has been deleted.
+ */
+ clear_filter(adap, f);
+ } else if (ret == FW_FILTER_WR_SMT_TBL_FULL) {
+ dev_err(adap->pdev_dev, "filter %u setup failed due to full SMT\n",
+ idx);
+ clear_filter(adap, f);
+ } else if (ret == FW_FILTER_WR_FLT_ADDED) {
+ f->smtidx = (be64_to_cpu(rpl->oldval) >> 24) & 0xff;
+ f->pending = 0; /* asynchronous setup completed */
+ f->valid = 1;
+ } else {
+ /* Something went wrong. Issue a warning about the
+ * problem and clear everything out.
+ */
+ dev_err(adap->pdev_dev, "filter %u setup failed with error %u\n",
+ idx, ret);
+ clear_filter(adap, f);
+ }
+ }
+}
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h
new file mode 100644
index 0000000..f6bd0bf
--- /dev/null
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h
@@ -0,0 +1,47 @@
+/*
+ * This file is part of the Chelsio T4 Ethernet driver for Linux.
+ *
+ * Copyright (c) 2003-2016 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __CXGB4_FILTER_H
+#define __CXGB4_FILTER_H
+
+#include "t4_msg.h"
+
+void filter_rpl(struct adapter *adap, const struct cpl_set_tcb_rpl *rpl);
+void clear_filter(struct adapter *adap, struct filter_entry *f);
+
+int set_filter_wr(struct adapter *adapter, int fidx);
+int delete_filter(struct adapter *adapter, unsigned int fidx);
+
+int writable_filter(struct filter_entry *f);
+#endif /* __CXGB4_FILTER_H */
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
index 44cc976..00f4d7f 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
@@ -67,6 +67,7 @@
#include <linux/crash_dump.h>
#include "cxgb4.h"
+#include "cxgb4_filter.h"
#include "t4_regs.h"
#include "t4_values.h"
#include "t4_msg.h"
@@ -87,30 +88,6 @@ char cxgb4_driver_name[] = KBUILD_MODNAME;
const char cxgb4_driver_version[] = DRV_VERSION;
#define DRV_DESC "Chelsio T4/T5/T6 Network Driver"
-/* Host shadow copy of ingress filter entry. This is in host native format
- * and doesn't match the ordering or bit order, etc. of the hardware of the
- * firmware command. The use of bit-field structure elements is purely to
- * remind ourselves of the field size limitations and save memory in the case
- * where the filter table is large.
- */
-struct filter_entry {
- /* Administrative fields for filter.
- */
- u32 valid:1; /* filter allocated and valid */
- u32 locked:1; /* filter is administratively locked */
-
- u32 pending:1; /* filter action is pending firmware reply */
- u32 smtidx:8; /* Source MAC Table index for smac */
- struct l2t_entry *l2t; /* Layer Two Table entry for dmac */
-
- /* The filter itself. Most of this is a straight copy of information
- * provided by the extended ioctl(). Some fields are translated to
- * internal forms -- for instance the Ingress Queue ID passed in from
- * the ioctl() is translated into the Absolute Ingress Queue ID.
- */
- struct ch_filter_specification fs;
-};
-
#define DFLT_MSG_ENABLE (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK | \
NETIF_MSG_TIMER | NETIF_MSG_IFDOWN | NETIF_MSG_IFUP |\
NETIF_MSG_RX_ERR | NETIF_MSG_TX_ERR)
@@ -532,66 +509,6 @@ static void dcb_rpl(struct adapter *adap, const struct fw_port_cmd *pcmd)
}
#endif /* CONFIG_CHELSIO_T4_DCB */
-/* Clear a filter and release any of its resources that we own. This also
- * clears the filter's "pending" status.
- */
-static void clear_filter(struct adapter *adap, struct filter_entry *f)
-{
- /* If the new or old filter have loopback rewriteing rules then we'll
- * need to free any existing Layer Two Table (L2T) entries of the old
- * filter rule. The firmware will handle freeing up any Source MAC
- * Table (SMT) entries used for rewriting Source MAC Addresses in
- * loopback rules.
- */
- if (f->l2t)
- cxgb4_l2t_release(f->l2t);
-
- /* The zeroing of the filter rule below clears the filter valid,
- * pending, locked flags, l2t pointer, etc. so it's all we need for
- * this operation.
- */
- memset(f, 0, sizeof(*f));
-}
-
-/* Handle a filter write/deletion reply.
- */
-static void filter_rpl(struct adapter *adap, const struct cpl_set_tcb_rpl *rpl)
-{
- unsigned int idx = GET_TID(rpl);
- unsigned int nidx = idx - adap->tids.ftid_base;
- unsigned int ret;
- struct filter_entry *f;
-
- if (idx >= adap->tids.ftid_base && nidx <
- (adap->tids.nftids + adap->tids.nsftids)) {
- idx = nidx;
- ret = TCB_COOKIE_G(rpl->cookie);
- f = &adap->tids.ftid_tab[idx];
-
- if (ret == FW_FILTER_WR_FLT_DELETED) {
- /* Clear the filter when we get confirmation from the
- * hardware that the filter has been deleted.
- */
- clear_filter(adap, f);
- } else if (ret == FW_FILTER_WR_SMT_TBL_FULL) {
- dev_err(adap->pdev_dev, "filter %u setup failed due to full SMT\n",
- idx);
- clear_filter(adap, f);
- } else if (ret == FW_FILTER_WR_FLT_ADDED) {
- f->smtidx = (be64_to_cpu(rpl->oldval) >> 24) & 0xff;
- f->pending = 0; /* asynchronous setup completed */
- f->valid = 1;
- } else {
- /* Something went wrong. Issue a warning about the
- * problem and clear everything out.
- */
- dev_err(adap->pdev_dev, "filter %u setup failed with error %u\n",
- idx, ret);
- clear_filter(adap, f);
- }
- }
-}
-
/* Response queue handler for the FW event queue.
*/
static int fwevtq_handler(struct sge_rspq *q, const __be64 *rsp,
@@ -1198,151 +1115,6 @@ void t4_free_mem(void *addr)
kvfree(addr);
}
-/* Send a Work Request to write the filter at a specified index. We construct
- * a Firmware Filter Work Request to have the work done and put the indicated
- * filter into "pending" mode which will prevent any further actions against
- * it till we get a reply from the firmware on the completion status of the
- * request.
- */
-static int set_filter_wr(struct adapter *adapter, int fidx)
-{
- struct filter_entry *f = &adapter->tids.ftid_tab[fidx];
- struct sk_buff *skb;
- struct fw_filter_wr *fwr;
- unsigned int ftid;
-
- skb = alloc_skb(sizeof(*fwr), GFP_KERNEL);
- if (!skb)
- return -ENOMEM;
-
- /* If the new filter requires loopback Destination MAC and/or VLAN
- * rewriting then we need to allocate a Layer 2 Table (L2T) entry for
- * the filter.
- */
- if (f->fs.newdmac || f->fs.newvlan) {
- /* allocate L2T entry for new filter */
- f->l2t = t4_l2t_alloc_switching(adapter, f->fs.vlan,
- f->fs.eport, f->fs.dmac);
- if (f->l2t == NULL) {
- kfree_skb(skb);
- return -ENOMEM;
- }
- }
-
- ftid = adapter->tids.ftid_base + fidx;
-
- fwr = (struct fw_filter_wr *)__skb_put(skb, sizeof(*fwr));
- memset(fwr, 0, sizeof(*fwr));
-
- /* It would be nice to put most of the following in t4_hw.c but most
- * of the work is translating the cxgbtool ch_filter_specification
- * into the Work Request and the definition of that structure is
- * currently in cxgbtool.h which isn't appropriate to pull into the
- * common code. We may eventually try to come up with a more neutral
- * filter specification structure but for now it's easiest to simply
- * put this fairly direct code in line ...
- */
- fwr->op_pkd = htonl(FW_WR_OP_V(FW_FILTER_WR));
- fwr->len16_pkd = htonl(FW_WR_LEN16_V(sizeof(*fwr)/16));
- fwr->tid_to_iq =
- htonl(FW_FILTER_WR_TID_V(ftid) |
- FW_FILTER_WR_RQTYPE_V(f->fs.type) |
- FW_FILTER_WR_NOREPLY_V(0) |
- FW_FILTER_WR_IQ_V(f->fs.iq));
- fwr->del_filter_to_l2tix =
- htonl(FW_FILTER_WR_RPTTID_V(f->fs.rpttid) |
- FW_FILTER_WR_DROP_V(f->fs.action == FILTER_DROP) |
- FW_FILTER_WR_DIRSTEER_V(f->fs.dirsteer) |
- FW_FILTER_WR_MASKHASH_V(f->fs.maskhash) |
- FW_FILTER_WR_DIRSTEERHASH_V(f->fs.dirsteerhash) |
- FW_FILTER_WR_LPBK_V(f->fs.action == FILTER_SWITCH) |
- FW_FILTER_WR_DMAC_V(f->fs.newdmac) |
- FW_FILTER_WR_SMAC_V(f->fs.newsmac) |
- FW_FILTER_WR_INSVLAN_V(f->fs.newvlan == VLAN_INSERT ||
- f->fs.newvlan == VLAN_REWRITE) |
- FW_FILTER_WR_RMVLAN_V(f->fs.newvlan == VLAN_REMOVE ||
- f->fs.newvlan == VLAN_REWRITE) |
- FW_FILTER_WR_HITCNTS_V(f->fs.hitcnts) |
- FW_FILTER_WR_TXCHAN_V(f->fs.eport) |
- FW_FILTER_WR_PRIO_V(f->fs.prio) |
- FW_FILTER_WR_L2TIX_V(f->l2t ? f->l2t->idx : 0));
- fwr->ethtype = htons(f->fs.val.ethtype);
- fwr->ethtypem = htons(f->fs.mask.ethtype);
- fwr->frag_to_ovlan_vldm =
- (FW_FILTER_WR_FRAG_V(f->fs.val.frag) |
- FW_FILTER_WR_FRAGM_V(f->fs.mask.frag) |
- FW_FILTER_WR_IVLAN_VLD_V(f->fs.val.ivlan_vld) |
- FW_FILTER_WR_OVLAN_VLD_V(f->fs.val.ovlan_vld) |
- FW_FILTER_WR_IVLAN_VLDM_V(f->fs.mask.ivlan_vld) |
- FW_FILTER_WR_OVLAN_VLDM_V(f->fs.mask.ovlan_vld));
- fwr->smac_sel = 0;
- fwr->rx_chan_rx_rpl_iq =
- htons(FW_FILTER_WR_RX_CHAN_V(0) |
- FW_FILTER_WR_RX_RPL_IQ_V(adapter->sge.fw_evtq.abs_id));
- fwr->maci_to_matchtypem =
- htonl(FW_FILTER_WR_MACI_V(f->fs.val.macidx) |
- FW_FILTER_WR_MACIM_V(f->fs.mask.macidx) |
- FW_FILTER_WR_FCOE_V(f->fs.val.fcoe) |
- FW_FILTER_WR_FCOEM_V(f->fs.mask.fcoe) |
- FW_FILTER_WR_PORT_V(f->fs.val.iport) |
- FW_FILTER_WR_PORTM_V(f->fs.mask.iport) |
- FW_FILTER_WR_MATCHTYPE_V(f->fs.val.matchtype) |
- FW_FILTER_WR_MATCHTYPEM_V(f->fs.mask.matchtype));
- fwr->ptcl = f->fs.val.proto;
- fwr->ptclm = f->fs.mask.proto;
- fwr->ttyp = f->fs.val.tos;
- fwr->ttypm = f->fs.mask.tos;
- fwr->ivlan = htons(f->fs.val.ivlan);
- fwr->ivlanm = htons(f->fs.mask.ivlan);
- fwr->ovlan = htons(f->fs.val.ovlan);
- fwr->ovlanm = htons(f->fs.mask.ovlan);
- memcpy(fwr->lip, f->fs.val.lip, sizeof(fwr->lip));
- memcpy(fwr->lipm, f->fs.mask.lip, sizeof(fwr->lipm));
- memcpy(fwr->fip, f->fs.val.fip, sizeof(fwr->fip));
- memcpy(fwr->fipm, f->fs.mask.fip, sizeof(fwr->fipm));
- fwr->lp = htons(f->fs.val.lport);
- fwr->lpm = htons(f->fs.mask.lport);
- fwr->fp = htons(f->fs.val.fport);
- fwr->fpm = htons(f->fs.mask.fport);
- if (f->fs.newsmac)
- memcpy(fwr->sma, f->fs.smac, sizeof(fwr->sma));
-
- /* Mark the filter as "pending" and ship off the Filter Work Request.
- * When we get the Work Request Reply we'll clear the pending status.
- */
- f->pending = 1;
- set_wr_txq(skb, CPL_PRIORITY_CONTROL, f->fs.val.iport & 0x3);
- t4_ofld_send(adapter, skb);
- return 0;
-}
-
-/* Delete the filter at a specified index.
- */
-static int del_filter_wr(struct adapter *adapter, int fidx)
-{
- struct filter_entry *f = &adapter->tids.ftid_tab[fidx];
- struct sk_buff *skb;
- struct fw_filter_wr *fwr;
- unsigned int len, ftid;
-
- len = sizeof(*fwr);
- ftid = adapter->tids.ftid_base + fidx;
-
- skb = alloc_skb(len, GFP_KERNEL);
- if (!skb)
- return -ENOMEM;
-
- fwr = (struct fw_filter_wr *)__skb_put(skb, len);
- t4_mk_filtdelwr(ftid, fwr, adapter->sge.fw_evtq.abs_id);
-
- /* Mark the filter as "pending" and ship off the Filter Work Request.
- * When we get the Work Request Reply we'll clear the pending status.
- */
- f->pending = 1;
- t4_mgmt_tx(adapter, skb);
- return 0;
-}
-
static u16 cxgb_select_queue(struct net_device *dev, struct sk_buff *skb,
void *accel_priv, select_queue_fallback_t fallback)
{
@@ -2830,40 +2602,6 @@ static int cxgb_close(struct net_device *dev)
return t4_enable_vi(adapter, adapter->pf, pi->viid, false, false);
}
-/* Return an error number if the indicated filter isn't writable ...
- */
-static int writable_filter(struct filter_entry *f)
-{
- if (f->locked)
- return -EPERM;
- if (f->pending)
- return -EBUSY;
-
- return 0;
-}
-
-/* Delete the filter at the specified index (if valid). The checks for all
- * the common problems with doing this like the filter being locked, currently
- * pending in another operation, etc.
- */
-static int delete_filter(struct adapter *adapter, unsigned int fidx)
-{
- struct filter_entry *f;
- int ret;
-
- if (fidx >= adapter->tids.nftids + adapter->tids.nsftids)
- return -EINVAL;
-
- f = &adapter->tids.ftid_tab[fidx];
- ret = writable_filter(f);
- if (ret)
- return ret;
- if (f->valid)
- return del_filter_wr(adapter, fidx);
-
- return 0;
-}
-
int cxgb4_create_server_filter(const struct net_device *dev, unsigned int stid,
__be32 sip, __be16 sport, __be16 vlan,
unsigned int queue, unsigned char port, unsigned char mask)
--
2.5.3
^ permalink raw reply related
* [PATCH net-next 0/7] cxgb4: add support for offloading TC u32 filters
From: Rahul Lakkireddy @ 2016-09-12 8:12 UTC (permalink / raw)
To: netdev; +Cc: davem, hariprasad, leedom, nirranjan, indranil, Rahul Lakkireddy
This series of patches add support to offload TC u32 filters onto
Chelsio NICs.
Patch 1 moves current common filter code to separate files
in order to provide a common api for performing packet classification
and filtering in Chelsio NICs.
Patch 2 enables filters for normal NIC configuration and implements
common api for setting and deleting filters.
Patch 3 provides a debugfs for dumping filter information.
Patches 4-7 add support for TC u32 offload via ndo_setup_tc.
Rahul Lakkireddy (7):
cxgb4: move common filter code to separate file
cxgb4: add common api support for configuring filters
cxgb4: add debugfs support to dump filter debug logs
cxgb4: add parser to translate u32 filters to internal spec
cxgb4: add support for setting u32 filters
cxgb4: add support for deleting u32 filters
cxgb4: add support for drop and redirect actions
drivers/net/ethernet/chelsio/cxgb4/Makefile | 2 +-
drivers/net/ethernet/chelsio/cxgb4/cxgb4.h | 30 +
drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c | 4 +-
drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c | 1086 ++++++++++++++++++++
drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h | 50 +
drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 338 ++----
drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c | 498 +++++++++
drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h | 57 +
.../ethernet/chelsio/cxgb4/cxgb4_tc_u32_parse.h | 294 ++++++
drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h | 23 +-
drivers/net/ethernet/chelsio/cxgb4/t4_values.h | 5 +-
11 files changed, 2104 insertions(+), 283 deletions(-)
create mode 100644 drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c
create mode 100644 drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h
create mode 100644 drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c
create mode 100644 drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h
create mode 100644 drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32_parse.h
--
2.5.3
^ permalink raw reply
* Re: [PATCH -next] net: macb: fix missing unlock on error in macb_start_xmit()
From: Nicolas Ferre @ 2016-09-12 7:38 UTC (permalink / raw)
To: Wei Yongjun, Helmut Buchsbaum; +Cc: Wei Yongjun, netdev
In-Reply-To: <1473506277-31304-1-git-send-email-weiyj.lk@gmail.com>
Le 10/09/2016 à 13:17, Wei Yongjun a écrit :
> From: Wei Yongjun <weiyongjun1@huawei.com>
>
> Fix missing unlock before return from function macb_start_xmit()
> in the error handling case.
>
> Fixes: 007e4ba3ee13 ("net: macb: initialize checksum when using
> checksum offloading")
> Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com>
Yes,
Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Thanks. Best regards,
> ---
> drivers/net/ethernet/cadence/macb.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c
> index 0294b6a..63144bb 100644
> --- a/drivers/net/ethernet/cadence/macb.c
> +++ b/drivers/net/ethernet/cadence/macb.c
> @@ -1398,7 +1398,7 @@ static int macb_start_xmit(struct sk_buff *skb, struct net_device *dev)
>
> if (macb_clear_csum(skb)) {
> dev_kfree_skb_any(skb);
> - return NETDEV_TX_OK;
> + goto unlock;
> }
>
> /* Map socket buffer for DMA transfer */
>
>
--
Nicolas Ferre
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox