Netdev List
 help / color / mirror / Atom feed
* [PATCH 3/3] net: ethernet: sun4i-emac: Read rxhdr in CPU byte-order
From: Michael Weiser @ 2016-11-14 17:58 UTC (permalink / raw)
  To: netdev; +Cc: Michael Weiser, Maxime Ripard
In-Reply-To: <20161114175807.4747-1-michael.weiser@gmx.de>

The EMAC EMAC_RX_IO_DATA_REG data register is dual-purpose: On one hand
it is used to move actual packet data off the wire. This will be in
wire-format and accepted as such by higher layers such as IP. Therefore
it is correctly read as-is (i.e. raw) using readsl.

On the other hand it provides metadata about incoming transfers to the
driver such as length and checksum validation status. This data is
little-endian, always and it is interpreted by the driver. Therefore it
needs to be swapped to CPU endianness to make sense to the driver. This
is already done for the "receive header" but not rxhdr.

Read rxhdr using readl in order for sun4i-emac to work correctly when
running a big-endian kernel.

Signed-off-by: Michael Weiser <michael.weiser@gmx.de>
Cc: Maxime Ripard <maxime.ripard@free-electrons.com>
Cc: netdev@vger.kernel.org
---
 drivers/net/ethernet/allwinner/sun4i-emac.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/allwinner/sun4i-emac.c b/drivers/net/ethernet/allwinner/sun4i-emac.c
index cd08885..87d0b87 100644
--- a/drivers/net/ethernet/allwinner/sun4i-emac.c
+++ b/drivers/net/ethernet/allwinner/sun4i-emac.c
@@ -592,8 +592,7 @@ static void emac_rx(struct net_device *dev)
 		/* A packet ready now  & Get status/length */
 		good_packet = true;
 
-		emac_inblk_32bit(db->membase + EMAC_RX_IO_DATA_REG,
-				&rxhdr, sizeof(rxhdr));
+		rxhdr = readl(db->membase + EMAC_RX_IO_DATA_REG);
 
 		if (netif_msg_rx_status(db))
 			dev_dbg(db->dev, "rxhdr: %x\n", *((int *)(&rxhdr)));
-- 
2.9.3 (Apple Git-75)

^ permalink raw reply related

* Re: [PATCH 2/3] net: ethernet: sun4i-emac: Allow to enable netif messages
From: Maxime Ripard @ 2016-11-14 19:00 UTC (permalink / raw)
  To: Michael Weiser; +Cc: netdev
In-Reply-To: <20161114175807.4747-3-michael.weiser@gmx.de>

[-- Attachment #1: Type: text/plain, Size: 741 bytes --]

On Mon, Nov 14, 2016 at 06:58:06PM +0100, Michael Weiser wrote:
> sun4i-emac has the ability to print a number of diagnostic messages using
> dev_dbg depending on message level settings implemented using netif_msg_*
> macros. But there's no way to actually enable them.
> 
> Add the ability to switch diagnostic messages on using either a module
> parameter debug or ethtool -s <netif> msglvl <flags>.
> 
> Signed-off-by: Michael Weiser <michael.weiser@gmx.de>
> Cc: Maxime Ripard <maxime.ripard@free-electrons.com>
> Cc: netdev@vger.kernel.org

Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>

Thanks!
Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 801 bytes --]

^ permalink raw reply

* [PATCH 0/3 v5] Fixes for running a big-endian kernel on Cubieboard2
From: Michael Weiser @ 2016-11-14 17:58 UTC (permalink / raw)
  To: netdev; +Cc: Michael Weiser

the following patches are what remains to be fixed in order to allow running a
big-endian kernel on the Cubieboard2.

The first patch fixes up endianness problems with DMA descriptors in
the stmmac driver preventing it from working correctly when runnning a
big-endian kernel.

The second patch adds the ability to enable diagnostic messages in the
sun4i-emac driver which were instrumental in finding the problem fixed
by patch number three: Endianness confusion caused by dual-purpose I/O
register usage in sun4i-emac.

All of these have been tested successfully on a Cubieboard2 DualCard.

Changes since v4:
- Rebased to current master
- Removed already applied patches to sunxi-mmc and sunxi-Kconfig

Changes since v3:
- Rebased sunxi-mmc patch against Ulf's mmc.git/next
- Changed Kconfig change to enable big-endian support only for sun7i
  devices

Changes since v2:
- Fixed typo in stmmac patch causing a build failure
- Added sun4i-emac patches

Changes since v1:
- Fixed checkpatch niggles
- Added respective Cc:s

Regards,
Michael

Michael Weiser (3):
  net: ethernet: stmmac: change dma descriptors to __le32
  net: ethernet: sun4i-emac: Allow to enable netif messages
  net: ethernet: sun4i-emac: Read rxhdr in CPU byte-order

 drivers/net/ethernet/allwinner/sun4i-emac.c        | 25 ++++++++-
 drivers/net/ethernet/stmicro/stmmac/chain_mode.c   | 55 ++++++++++----------
 drivers/net/ethernet/stmicro/stmmac/descs.h        | 20 ++++----
 drivers/net/ethernet/stmicro/stmmac/descs_com.h    | 48 +++++++++--------
 drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c | 60 +++++++++++-----------
 drivers/net/ethernet/stmicro/stmmac/enh_desc.c     | 55 ++++++++++----------
 drivers/net/ethernet/stmicro/stmmac/norm_desc.c    | 48 ++++++++---------
 drivers/net/ethernet/stmicro/stmmac/ring_mode.c    | 39 +++++++-------
 drivers/net/ethernet/stmicro/stmmac/stmmac_main.c  | 51 +++++++++---------
 9 files changed, 218 insertions(+), 183 deletions(-)

-- 
2.9.3 (Apple Git-75)

^ permalink raw reply

* Re: [PATCH 3/3] net: ethernet: sun4i-emac: Read rxhdr in CPU byte-order
From: Maxime Ripard @ 2016-11-14 18:59 UTC (permalink / raw)
  To: Michael Weiser; +Cc: netdev
In-Reply-To: <20161114175807.4747-4-michael.weiser@gmx.de>

[-- Attachment #1: Type: text/plain, Size: 1139 bytes --]

On Mon, Nov 14, 2016 at 06:58:07PM +0100, Michael Weiser wrote:
> The EMAC EMAC_RX_IO_DATA_REG data register is dual-purpose: On one hand
> it is used to move actual packet data off the wire. This will be in
> wire-format and accepted as such by higher layers such as IP. Therefore
> it is correctly read as-is (i.e. raw) using readsl.
> 
> On the other hand it provides metadata about incoming transfers to the
> driver such as length and checksum validation status. This data is
> little-endian, always and it is interpreted by the driver. Therefore it
> needs to be swapped to CPU endianness to make sense to the driver. This
> is already done for the "receive header" but not rxhdr.
> 
> Read rxhdr using readl in order for sun4i-emac to work correctly when
> running a big-endian kernel.
> 
> Signed-off-by: Michael Weiser <michael.weiser@gmx.de>
> Cc: Maxime Ripard <maxime.ripard@free-electrons.com>
> Cc: netdev@vger.kernel.org

Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>

Thanks!
Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 801 bytes --]

^ permalink raw reply

* Re: [PATCH net-next] bpf: fix range arithmetic for bpf map access
From: Josef Bacik @ 2016-11-14 18:52 UTC (permalink / raw)
  To: Alexei Starovoitov; +Cc: jannh, ast, daniel, davem, netdev
In-Reply-To: <20161112031338.GA86010@ast-mbp.thefacebook.com>

On 11/11/2016 10:13 PM, Alexei Starovoitov wrote:
> On Fri, Nov 11, 2016 at 04:47:39PM -0500, Josef Bacik wrote:
>> I made some invalid assumptions with BPF_AND and BPF_MOD that could result in
>> invalid accesses to bpf map entries.  Fix this up by doing a few things
>>
>> 1) Kill BPF_MOD support.  This doesn't actually get used by the compiler in real
>> life and just adds extra complexity.
>>
>> 2) Fix the logic for BPF_AND, don't allow AND of negative numbers and set the
>> minimum value to 0 for positive AND's.
>>
>> 3) Don't do operations on the ranges if they are set to the limits, as they are
>> by definition undefined, and allowing arithmetic operations on those values
>> could make them appear valid when they really aren't.
>>
>> This fixes the testcase provided by Jann as well as a few other theoretical
>> problems.
>>
>> Reported-by: Jann Horn <jannh@google.com>
>> Signed-off-by: Josef Bacik <jbacik@fb.com>
>> ---
>>  include/linux/bpf_verifier.h |  3 +-
>>  kernel/bpf/verifier.c        | 70 +++++++++++++++++++++++++++++---------------
>>  2 files changed, 49 insertions(+), 24 deletions(-)
>>
>> diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
>> index ac5b393..15ceb7f 100644
>> --- a/include/linux/bpf_verifier.h
>> +++ b/include/linux/bpf_verifier.h
>> @@ -22,7 +22,8 @@ struct bpf_reg_state {
>>  	 * Used to determine if any memory access using this register will
>>  	 * result in a bad access.
>>  	 */
>> -	u64 min_value, max_value;
>> +	s64 min_value;
>> +	u64 max_value;
>>  	u32 id;
>>  	union {
>>  		/* valid when type == CONST_IMM | PTR_TO_STACK | UNKNOWN_VALUE */
>> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
>> index 89f787c..709fe0e 100644
>> --- a/kernel/bpf/verifier.c
>> +++ b/kernel/bpf/verifier.c
>> @@ -234,8 +234,8 @@ static void print_verifier_state(struct bpf_verifier_state *state)
>>  				reg->map_ptr->value_size,
>>  				reg->id);
>>  		if (reg->min_value != BPF_REGISTER_MIN_RANGE)
>> -			verbose(",min_value=%llu",
>> -				(unsigned long long)reg->min_value);
>> +			verbose(",min_value=%lld",
>> +				(long long)reg->min_value);
>>  		if (reg->max_value != BPF_REGISTER_MAX_RANGE)
>>  			verbose(",max_value=%llu",
>>  				(unsigned long long)reg->max_value);
>> @@ -778,7 +778,7 @@ static int check_mem_access(struct bpf_verifier_env *env, u32 regno, int off,
>>  			 * index'es we need to make sure that whatever we use
>>  			 * will have a set floor within our range.
>>  			 */
>> -			if ((s64)reg->min_value < 0) {
>> +			if (reg->min_value < 0) {
>>  				verbose("R%d min value is negative, either use unsigned index or do a if (index >=0) check.\n",
>>  					regno);
>>  				return -EACCES;
>> @@ -1490,7 +1490,8 @@ static void check_reg_overflow(struct bpf_reg_state *reg)
>>  {
>>  	if (reg->max_value > BPF_REGISTER_MAX_RANGE)
>>  		reg->max_value = BPF_REGISTER_MAX_RANGE;
>> -	if ((s64)reg->min_value < BPF_REGISTER_MIN_RANGE)
>> +	if (reg->min_value < BPF_REGISTER_MIN_RANGE ||
>> +	    reg->min_value > BPF_REGISTER_MAX_RANGE)
>>  		reg->min_value = BPF_REGISTER_MIN_RANGE;
>>  }
>>
>> @@ -1498,7 +1499,8 @@ static void adjust_reg_min_max_vals(struct bpf_verifier_env *env,
>>  				    struct bpf_insn *insn)
>>  {
>>  	struct bpf_reg_state *regs = env->cur_state.regs, *dst_reg;
>> -	u64 min_val = BPF_REGISTER_MIN_RANGE, max_val = BPF_REGISTER_MAX_RANGE;
>> +	s64 min_val = BPF_REGISTER_MIN_RANGE;
>> +	u64 max_val = BPF_REGISTER_MAX_RANGE;
>>  	u8 opcode = BPF_OP(insn->code);
>>
>>  	dst_reg = &regs[insn->dst_reg];
>> @@ -1532,22 +1534,43 @@ static void adjust_reg_min_max_vals(struct bpf_verifier_env *env,
>>  		return;
>>  	}
>>
>> +	/* If one of our values was at the end of our ranges then we can't just
>> +	 * do our normal operations to the register, we need to set the values
>> +	 * to the min/max since they are undefined.
>> +	 */
>> +	if (min_val == BPF_REGISTER_MIN_RANGE)
>> +		dst_reg->min_value = BPF_REGISTER_MIN_RANGE;
>> +	if (max_val == BPF_REGISTER_MAX_RANGE)
>> +		dst_reg->max_value = BPF_REGISTER_MAX_RANGE;
>> +
>>  	switch (opcode) {
>>  	case BPF_ADD:
>> -		dst_reg->min_value += min_val;
>> -		dst_reg->max_value += max_val;
>> +		if (dst_reg->min_value != BPF_REGISTER_MIN_RANGE)
>> +			dst_reg->min_value += min_val;
>> +		if (dst_reg->max_value != BPF_REGISTER_MAX_RANGE)
>> +			dst_reg->max_value += max_val;
>>  		break;
>>  	case BPF_SUB:
>> -		dst_reg->min_value -= min_val;
>> -		dst_reg->max_value -= max_val;
>> +		if (dst_reg->min_value != BPF_REGISTER_MIN_RANGE)
>> +			dst_reg->min_value -= min_val;
>> +		if (dst_reg->max_value != BPF_REGISTER_MAX_RANGE)
>> +			dst_reg->max_value -= max_val;
>>  		break;
>>  	case BPF_MUL:
>> -		dst_reg->min_value *= min_val;
>> -		dst_reg->max_value *= max_val;
>> +		if (dst_reg->min_value != BPF_REGISTER_MIN_RANGE)
>> +			dst_reg->min_value *= min_val;
>
> looks to be few issues here with negative values as well.
> If dst_reg range [-2, 5] and right hand side range is [-2, 10],
> then above will be computed as -2 * -2 == 4
> but even if we do -1 * abs(dst_reg->min) * abs(min), it's still
> incorrect, since dst_reg could be 5 and multiplied by -2 (== -10),
> it will be less than above simple math on min values...
> so I'd suggest to disable negative values everywhere.
>
>> +		if (dst_reg->max_value != BPF_REGISTER_MAX_RANGE)
>> +			dst_reg->max_value *= max_val;
>>  		break;
>>  	case BPF_AND:
>> -		/* & is special since it could end up with 0 bits set. */
>> -		dst_reg->min_value &= min_val;
>> +		/* Disallow AND'ing of negative numbers, ain't nobody got time
>> +		 * for that.  Otherwise the minimum is 0 and the max is the max
>> +		 * value we could AND against.
>> +		 */
>> +		if (min_val < 0)
>> +			dst_reg->min_value = BPF_REGISTER_MIN_RANGE;
>> +		else
>> +			dst_reg->min_value = 0;
>>  		dst_reg->max_value = max_val;
>>  		break;
>>  	case BPF_LSH:
>> @@ -1557,24 +1580,25 @@ static void adjust_reg_min_max_vals(struct bpf_verifier_env *env,
>>  		 */
>>  		if (min_val > ilog2(BPF_REGISTER_MAX_RANGE))
>>  			dst_reg->min_value = BPF_REGISTER_MIN_RANGE;
>> -		else
>> +		else if (dst_reg->min_value != BPF_REGISTER_MIN_RANGE)
>>  			dst_reg->min_value <<= min_val;
>>
>>  		if (max_val > ilog2(BPF_REGISTER_MAX_RANGE))
>>  			dst_reg->max_value = BPF_REGISTER_MAX_RANGE;
>> -		else
>> +		else if (dst_reg->max_value != BPF_REGISTER_MAX_RANGE)
>>  			dst_reg->max_value <<= max_val;
>>  		break;
>>  	case BPF_RSH:
>> -		dst_reg->min_value >>= min_val;
>> -		dst_reg->max_value >>= max_val;
>> -		break;
>> -	case BPF_MOD:
>> -		/* % is special since it is an unsigned modulus, so the floor
>> -		 * will always be 0.
>> +		/* RSH by a negative number is undefined, and the BPF_RSH is an
>> +		 * unsigned shift, so make the appropriate casts.
>>  		 */
>> -		dst_reg->min_value = 0;
>> -		dst_reg->max_value = max_val - 1;
>> +		if (min_val < 0)
>> +			dst_reg->min_value = BPF_REGISTER_MIN_RANGE;
>> +		else if (dst_reg->min_value != BPF_REGISTER_MIN_RANGE)
>> +			dst_reg->min_value =
>> +				(u64)(dst_reg->min_value) >> min_val;
>
> when min_val is negative both >> and << are undefined,
> so we need to avoid negative values for these cases as well.
>
>> +		if (dst_reg->max_value != BPF_REGISTER_MAX_RANGE)
>> +			dst_reg->max_value >>= max_val;
>
> and for max_val too we need to make sure that max_val >= 0.

Well it's unsigned, so if somebody sets it to a negative value it'll be > 
BPF_REGISTER_MAX_RANGE and that'll get caught by the overflow logic above.

>
> To address all of it I'm thinking it will be easier to set
> BPF_REGISTER_MIN_RANGE to -1.
> I don't think we can kill tracking of min_val completely
> and assume valid min starts at zero, since we need either min
> tracking or boolean flag that indicates negative overflow and
> min tracking is imo cleaner (though valid min will always be >=0
> and invalid min is -1)
>
> Also this patch has to go to 'net' tree, so rebasing with net-next
> wasn't necessary.
>

Yeah I'm fine with killing negative values altogether, it does seem a bit silly 
to support it and isn't likely to be used in any sort of normal scenario.  Thanks,

Josef

^ permalink raw reply

* Re: [PATCH net 2/3] bpf, mlx5: fix various refcount/prog issues in mlx5e_xdp_set
From: Saeed Mahameed @ 2016-11-14 18:27 UTC (permalink / raw)
  To: Daniel Borkmann, davem
  Cc: alexei.starovoitov, bblanco, tariqt, zhiyisun, ranas, netdev
In-Reply-To: <03741f7075af64e83d23add379bdab41204396b0.1479080215.git.daniel@iogearbox.net>



On 11/14/2016 02:43 AM, Daniel Borkmann wrote:
> There are multiple issues in mlx5e_xdp_set():
> 
> 1) prog can be NULL, so calling unconditionally into bpf_prog_add(prog,
>    priv->params.num_channels) can end badly.

not correct, if prog is null we will never get to bpf_prog_add:

        reset = (!priv->xdp_prog || !prog);
        [...]
	if (!test_bit(MLX5E_STATE_OPENED, &priv->state) || reset)
		goto unlock;
        bpf_prog_add...
         

> 
> 2) The batched bpf_prog_add() should be done at an earlier point in
>    time. This makes sure that we cannot fail anymore at the time we
>    want to set the program for each channel. This only means that we
>    have to undo the bpf_prog_add() in case we return early due to
>    reset or device not in MLX5E_STATE_OPENED yet. Note, err is 0 here.
> 

It is delayed for a reason, we do delayed batched bpf_prog_add() 
only when reset is not required (exchanging prog/old_prg) when both prog and old_prog are not null,
which means the only thing that could fail in this case is bpf_prog_add.

so i don't see any reason for changing the logic, checking for  bpf_prog_add return value would be sufficient.

Sorry I need to go for now, I will continue reviewing this patch later.  but this patch looks a little bit exaggerated.

> 3) When swapping the priv->xdp_prog, then no extra reference count must
>    be taken since we got that from call path via dev_change_xdp_fd()
>    already. Otherwise, we'd never be able to free the program. Also,
>    bpf_prog_add() without checking the return code could fail.
> 
> Fixes: 86994156c736 ("net/mlx5e: XDP fast RX drop bpf programs support")
> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
> ---
>  drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 25 ++++++++++++++++++-----
>  include/linux/bpf.h                               |  5 +++++
>  kernel/bpf/syscall.c                              | 11 ++++++++++
>  3 files changed, 36 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
> index 2b83667..c90610a 100644
> --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
> +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
> @@ -3125,6 +3125,17 @@ static int mlx5e_xdp_set(struct net_device *netdev, struct bpf_prog *prog)
>  		goto unlock;
>  	}
>  
> +	if (prog) {
> +		/* num_channels is invariant here, so we can take the
> +		 * batched reference right upfront.
> +		 */
> +		prog = bpf_prog_add(prog, priv->params.num_channels);
> +		if (IS_ERR(prog)) {
> +			err = PTR_ERR(prog);
> +			goto unlock;
> +		}
> +	}
> +
>  	was_opened = test_bit(MLX5E_STATE_OPENED, &priv->state);
>  	/* no need for full reset when exchanging programs */
>  	reset = (!priv->xdp_prog || !prog);
> @@ -3132,10 +3143,10 @@ static int mlx5e_xdp_set(struct net_device *netdev, struct bpf_prog *prog)
>  	if (was_opened && reset)
>  		mlx5e_close_locked(netdev);
>  
> -	/* exchange programs */
> +	/* exchange programs, extra prog reference we got from caller
> +	 * as long as we don't fail from this point onwards.
> +	 */
>  	old_prog = xchg(&priv->xdp_prog, prog);
> -	if (prog)
> -		bpf_prog_add(prog, 1);
>  	if (old_prog)
>  		bpf_prog_put(old_prog);
>  
> @@ -3146,12 +3157,11 @@ static int mlx5e_xdp_set(struct net_device *netdev, struct bpf_prog *prog)
>  		mlx5e_open_locked(netdev);
>  
>  	if (!test_bit(MLX5E_STATE_OPENED, &priv->state) || reset)
> -		goto unlock;
> +		goto unlock_put;
>  
>  	/* exchanging programs w/o reset, we update ref counts on behalf
>  	 * of the channels RQs here.
>  	 */
> -	bpf_prog_add(prog, priv->params.num_channels);
>  	for (i = 0; i < priv->params.num_channels; i++) {
>  		struct mlx5e_channel *c = priv->channel[i];
>  
> @@ -3173,6 +3183,11 @@ static int mlx5e_xdp_set(struct net_device *netdev, struct bpf_prog *prog)
>  unlock:
>  	mutex_unlock(&priv->state_lock);
>  	return err;
> +unlock_put:
> +	/* reference on priv->xdp_prog is still held at this point */
> +	if (prog)
> +		bpf_prog_sub(prog, priv->params.num_channels);
> +	goto unlock;
>  }
>  
>  static bool mlx5e_xdp_attached(struct net_device *dev)
> diff --git a/include/linux/bpf.h b/include/linux/bpf.h
> index c201017..ca495fd 100644
> --- a/include/linux/bpf.h
> +++ b/include/linux/bpf.h
> @@ -234,6 +234,7 @@ u64 bpf_event_output(struct bpf_map *map, u64 flags, void *meta, u64 meta_size,
>  struct bpf_prog *bpf_prog_get(u32 ufd);
>  struct bpf_prog *bpf_prog_get_type(u32 ufd, enum bpf_prog_type type);
>  struct bpf_prog *bpf_prog_add(struct bpf_prog *prog, int i);
> +void bpf_prog_sub(struct bpf_prog *prog, int i);
>  struct bpf_prog *bpf_prog_inc(struct bpf_prog *prog);
>  void bpf_prog_put(struct bpf_prog *prog);
>  
> @@ -303,6 +304,10 @@ static inline struct bpf_prog *bpf_prog_add(struct bpf_prog *prog, int i)
>  	return ERR_PTR(-EOPNOTSUPP);
>  }
>  
> +static inline void bpf_prog_sub(struct bpf_prog *prog, int i)
> +{
> +}
> +
>  static inline void bpf_prog_put(struct bpf_prog *prog)
>  {
>  }
> diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
> index 751e806..a0fca9f 100644
> --- a/kernel/bpf/syscall.c
> +++ b/kernel/bpf/syscall.c
> @@ -682,6 +682,17 @@ struct bpf_prog *bpf_prog_add(struct bpf_prog *prog, int i)
>  }
>  EXPORT_SYMBOL_GPL(bpf_prog_add);
>  
> +void bpf_prog_sub(struct bpf_prog *prog, int i)
> +{
> +	/* Only to be used for undoing previous bpf_prog_add() in some
> +	 * error path. We still know that another entity in our call
> +	 * path holds a reference to the program, thus atomic_sub() can
> +	 * be safely used in such cases!
> +	 */
> +	WARN_ON(atomic_sub_return(i, &prog->aux->refcnt) == 0);
> +}
> +EXPORT_SYMBOL_GPL(bpf_prog_sub);
> +
>  struct bpf_prog *bpf_prog_inc(struct bpf_prog *prog)
>  {
>  	return bpf_prog_add(prog, 1);
> 

^ permalink raw reply

* Re: [PATCH v2 net-next 3/6] bpf: Refactor codes handling percpu map
From: Alexei Starovoitov @ 2016-11-14 18:43 UTC (permalink / raw)
  To: Martin KaFai Lau
  Cc: netdev, David Miller, Alexei Starovoitov, Daniel Borkmann,
	Kernel Team
In-Reply-To: <1478890511-1346984-4-git-send-email-kafai@fb.com>

On Fri, Nov 11, 2016 at 10:55:08AM -0800, Martin KaFai Lau wrote:
> Refactor the codes that populate the value
> of a htab_elem in a BPF_MAP_TYPE_PERCPU_HASH
> typed bpf_map.
> 
> Signed-off-by: Martin KaFai Lau <kafai@fb.com>

Acked-by: Alexei Starovoitov <ast@kernel.org>

^ permalink raw reply

* Re: Debugging Ethernet issues
From: Florian Fainelli @ 2016-11-14 18:42 UTC (permalink / raw)
  To: Sebastian Frias, Mason, Andrew Lunn
  Cc: netdev, Mans Rullgard, Sergei Shtylyov, Tom Lendacky, Zach Brown,
	Shaohui Xie, Tim Beale, Brian Hill, Vince Bridgers,
	Balakumaran Kannan, David S. Miller, Kirill Kapranov
In-Reply-To: <2187db98-dc5a-7a3c-7965-7ccbeffc0fa1@gmail.com>

On 11/14/2016 10:20 AM, Florian Fainelli wrote:
> On 11/14/2016 09:59 AM, Sebastian Frias wrote:
>> On 11/14/2016 06:32 PM, Florian Fainelli wrote:
>>> On 11/14/2016 07:33 AM, Mason wrote:
>>>> On 14/11/2016 15:58, Mason wrote:
>>>>
>>>>> nb8800 26000.ethernet eth0: Link is Up - 100Mbps/Full - flow control rx/tx
>>>>> vs
>>>>> nb8800 26000.ethernet eth0: Link is Up - 100Mbps/Full - flow control off
>>>>>
>>>>> I'm not sure whether "flow control" is relevant...
>>>>
>>>> Based on phy_print_status()
>>>> phydev->pause ? "rx/tx" : "off"
>>>> I added the following patch.
>>>>
>>>> diff --git a/drivers/net/ethernet/aurora/nb8800.c b/drivers/net/ethernet/aurora/nb8800.c
>>>> index defc22a15f67..4e758c1cfa4e 100644
>>>> --- a/drivers/net/ethernet/aurora/nb8800.c
>>>> +++ b/drivers/net/ethernet/aurora/nb8800.c
>>>> @@ -667,6 +667,8 @@ static void nb8800_link_reconfigure(struct net_device *dev)
>>>>         struct phy_device *phydev = priv->phydev;
>>>>         int change = 0;
>>>>  
>>>> +       printk("%s from %pf\n", __func__, __builtin_return_address(0));
>>>> +
>>>>         if (phydev->link) {
>>>>                 if (phydev->speed != priv->speed) {
>>>>                         priv->speed = phydev->speed;
>>>> @@ -1274,9 +1276,9 @@ static int nb8800_hw_init(struct net_device *dev)
>>>>         nb8800_writeb(priv, NB8800_PQ2, val & 0xff);
>>>>  
>>>>         /* Auto-negotiate by default */
>>>> -       priv->pause_aneg = true;
>>>> -       priv->pause_rx = true;
>>>> -       priv->pause_tx = true;
>>>> +       priv->pause_aneg = false;
>>>> +       priv->pause_rx = false;
>>>> +       priv->pause_tx = false;
>>>>  
>>>>         nb8800_mc_init(dev, 0);
>>>>  
>>>>
>>>> Connected to 1000 Mbps switch:
>>>>
>>>> # time udhcpc | while read LINE; do date; echo $LINE; done
>>>> Thu Jan  1 00:00:22 UTC 1970
>>>> udhcpc (v1.22.1) started
>>>> Thu Jan  1 00:00:22 UTC 1970
>>>> Sending discover...
>>>> [   24.565346] nb8800_link_reconfigure from phy_state_machine
>>>> Thu Jan  1 00:00:25 UTC 1970
>>>> Sending discover...
>>>> [   26.575402] nb8800_link_reconfigure from phy_state_machine
>>>> [   26.580972] nb8800 26000.ethernet eth0: Link is Up - 1Gbps/Full - flow control rx/tx
>>>> Thu Jan  1 00:00:28 UTC 1970
>>>> Sending discover...
>>>> Thu Jan  1 00:00:29 UTC 1970
>>>> Sending select for 172.27.64.58...
>>>> Thu Jan  1 00:00:29 UTC 1970
>>>> Lease of 172.27.64.58 obtained, lease time 604800
>>>> Thu Jan  1 00:00:29 UTC 1970
>>>> deleting routers
>>>> Thu Jan  1 00:00:29 UTC 1970
>>>> adding dns 172.27.0.17
>>>>
>>>> real    0m7.388s
>>>> user    0m0.040s
>>>> sys     0m0.090s
>>>>
>>>>
>>>>
>>>> Connected to 100 Mbps switch:
>>>>
>>>> # time udhcpc | while read LINE; do date; echo $LINE; done
>>>> Thu Jan  1 00:00:14 UTC 1970
>>>> udhcpc (v1.22.1) started
>>>> Thu Jan  1 00:00:15 UTC 1970
>>>> Sending discover...
>>>> [   16.968621] nb8800_link_reconfigure from phy_state_machine
>>>> [   17.975359] nb8800_link_reconfigure from phy_state_machine
>>>> [   17.980923] nb8800 26000.ethernet eth0: Link is Up - 100Mbps/Full - flow control rx/tx
>>>> Thu Jan  1 00:00:18 UTC 1970
>>>> Sending discover...
>>>> Thu Jan  1 00:00:19 UTC 1970
>>>> Sending select for 172.27.64.58...
>>>> Thu Jan  1 00:00:19 UTC 1970
>>>> Lease of 172.27.64.58 obtained, lease time 604800
>>>> Thu Jan  1 00:00:19 UTC 1970
>>>> deleting routers
>>>> Thu Jan  1 00:00:19 UTC 1970
>>>> adding dns 172.27.0.17
>>>>
>>>> real    0m4.355s
>>>> user    0m0.043s
>>>> sys     0m0.083s
>>>>
>>>
>>> And the time difference is clearly accounted for auto-negotiation time
>>> here, as you can see it takes about 3 seconds for Gigabit Ethernet to
>>> auto-negotiate and that seems completely acceptable and normal to me
>>> since it is a more involved process than lower speeds.
>>>
>>>>
>>>>
>>>> OK, so now it works (by accident?) even on 100 Mbps switch, but it still
>>>> prints "flow control rx/tx"...
>>>
>>> Because your link partner advertises flow control, and that's what
>>> phydev->pause and phydev->asym_pause report (I know it's confusing, but
>>> that's what it is at the moment).
>>
>> Thanks.
>> Could you confirm that Mason's patch is correct and/or that it does not
>> has negative side-effects?
> 
> The patch is not correct nor incorrect per-se, it changes the default
> policy of having pause frames advertised by default to not having them
> advertised by default. This influences both your Ethernet MAC and the
> link partner in that the result is either flow control is enabled
> (before) or it is not (with the patch). There must be something amiss if
> you see packet loss or some kind of problem like that with an early
> exchange such as DHCP. Flow control tend to kick in under higher packet
> rates (at least, that's what you expect).
> 
> 
>>
>> Right now we know that Mason's patch makes this work, but we do not understand
>> why nor its implications.
> 
> You need to understand why, right now, the way this problem is
> presented, you came up with a workaround, not with the root cause or the
> solution. What does your link partner (switch?) reports, that is, what
> is the ethtool output when you have a link up from  your nb8800 adapter?

Actually, nb8800_pause_config() seems to be doing a complete MAC/DMA
reconfiguration when pause frames get auto-negotiated while the link is
UP, and it does not differentiate being called from
ethtool::set_pauseparam or the PHYLIB adjust_link callback (which it
probably should), wondering if there is a not a remote chance you can
get the reply to arrive right when you just got signaled a link UP?
-- 
Florian

^ permalink raw reply

* Re: [PATCH] ps3_gelic: fix spelling mistake in debug message
From: David Miller @ 2016-11-14 18:39 UTC (permalink / raw)
  To: colin.king
  Cc: benh, paulus, mpe, falakreyaz, christophe.jaillet, jarod, netdev,
	linuxppc-dev, linux-kernel
In-Reply-To: <20161112172030.7583-1-colin.king@canonical.com>

From: Colin King <colin.king@canonical.com>
Date: Sat, 12 Nov 2016 17:20:30 +0000

> From: Colin Ian King <colin.king@canonical.com>
> 
> Trivial fix to spelling mistake "unmached" to "unmatched" in
> debug message.
> 
> Signed-off-by: Colin Ian King <colin.king@canonical.com>

Applied.

^ permalink raw reply

* Re: [PATCH] net: atheros: atl2: use new api ethtool_{get|set}_link_ksettings
From: David Miller @ 2016-11-14 18:38 UTC (permalink / raw)
  To: tremyfr; +Cc: jcliburn, chris.snook, jarod, ben, netdev, linux-kernel
In-Reply-To: <1479059608-32456-1-git-send-email-tremyfr@gmail.com>

From: Philippe Reynes <tremyfr@gmail.com>
Date: Sun, 13 Nov 2016 18:53:28 +0100

> The ethtool api {get|set}_settings is deprecated.
> We move this driver to new api {get|set}_link_ksettings.
> 
> The previous implementation of set_settings was modifying
> the value of advertising, but with the new API, it's not
> possible. The structure ethtool_link_ksettings is defined
> as const.
> 
> Signed-off-by: Philippe Reynes <tremyfr@gmail.com>

Applied.

^ permalink raw reply

* Re: [PATCH] net: atheros: atl1: use new api ethtool_{get|set}_link_ksettings
From: David Miller @ 2016-11-14 18:38 UTC (permalink / raw)
  To: tremyfr; +Cc: jcliburn, chris.snook, jarod, netdev, linux-kernel
In-Reply-To: <1479058514-22438-1-git-send-email-tremyfr@gmail.com>

From: Philippe Reynes <tremyfr@gmail.com>
Date: Sun, 13 Nov 2016 18:35:14 +0100

> The ethtool api {get|set}_settings is deprecated.
> We move this driver to new api {get|set}_link_ksettings.
> 
> The previous implementation of set_settings was modifying
> the value of advertising, but with the new API, it's not
> possible. The structure ethtool_link_ksettings is defined
> as const.
> 
> Signed-off-by: Philippe Reynes <tremyfr@gmail.com>

Applied.

^ permalink raw reply

* Re: [PATCH] net: atheros: atl1c: use new api ethtool_{get|set}_link_ksettings
From: David Miller @ 2016-11-14 18:38 UTC (permalink / raw)
  To: tremyfr; +Cc: jcliburn, chris.snook, netdev, linux-kernel
In-Reply-To: <1478968369-25034-1-git-send-email-tremyfr@gmail.com>

From: Philippe Reynes <tremyfr@gmail.com>
Date: Sat, 12 Nov 2016 17:32:49 +0100

> The ethtool api {get|set}_settings is deprecated.
> We move this driver to new api {get|set}_link_ksettings.
> 
> Signed-off-by: Philippe Reynes <tremyfr@gmail.com>

Applied.

^ permalink raw reply

* Re: [PATCH] net: alx: use new api ethtool_{get|set}_link_ksettings
From: David Miller @ 2016-11-14 18:38 UTC (permalink / raw)
  To: tremyfr; +Cc: jcliburn, chris.snook, netdev, linux-kernel
In-Reply-To: <1478903437-9049-1-git-send-email-tremyfr@gmail.com>

From: Philippe Reynes <tremyfr@gmail.com>
Date: Fri, 11 Nov 2016 23:30:37 +0100

> The ethtool api {get|set}_settings is deprecated.
> We move this driver to new api {get|set}_link_ksettings.
> 
> Signed-off-by: Philippe Reynes <tremyfr@gmail.com>

Applied.

^ permalink raw reply

* Re: [PATCH] icmp: Restore resistence to abnormal messages
From: David Miller @ 2016-11-14 18:36 UTC (permalink / raw)
  To: googuy; +Cc: kuznet, jmorris, yoshfuji, kaber, netdev, linux-kernel
In-Reply-To: <20161111202018.13795-1-googuy@gmail.com>

From: Vicente Jimenez Aguilar <googuy@gmail.com>
Date: Fri, 11 Nov 2016 21:20:18 +0100

> @@ -819,6 +820,12 @@ static bool icmp_unreach(struct sk_buff *skb)
>  				/* fall through */
>  			case 0:
>  				info = ntohs(icmph->un.frag.mtu);
> +				/* Handle weird case where next hop MTU is
> +				 * equal to or exceeding dropped packet size
> +				 */
> +				old_mtu = ntohs(iph->tot_len);
> +				if (info >= old_mtu)
> +					info = old_mtu - 2;

This isn't something the old code did.

The old code behaved much differently.

In the case where the new mtu was smaller than 68 or larger than
the iph->tot_len value, it would do several things:

1) First it would check for a BSD 4.2 anomaly and subtract old_mtu
   by the IP header length.

2) Second, it would try to guess the intended MTU using the
   mtu_plateau table.

I don't see any code where a subtraction by a fixed constant of 2
occurred.

Nor can I figure out what that might accomplish.  If you really
want to do this, you have to docuement what this 2 means, what
it is accomplishing, and why you have choosen to accomplish it
this way.

Thanks.

^ permalink raw reply

* Re: [PATCH] [v2] net: phy: phy drivers should not set SUPPORTED_[Asym_]Pause
From: Florian Fainelli @ 2016-11-14 18:35 UTC (permalink / raw)
  To: Timur Tabi, David Miller, jon.mason, netdev, Andrew Lunn
In-Reply-To: <1478821561-26498-1-git-send-email-timur@codeaurora.org>

On 11/10/2016 03:46 PM, Timur Tabi wrote:
> Instead of having individual PHY drivers set the SUPPORTED_Pause and
> SUPPORTED_Asym_Pause flags, phylib itself should set those flags.
> During autonegotiation, the PHYs will determine whether to enable
> pause frame support.
> 
> Pause frames are a feature that is supported by the MAC.  It is the MAC
> that generates the frames and that processes them.  The PHY can only be
> configured to allow them to pass through.
> 
> So the new process is:
> 
> 1) Phylib sets the SUPPORTED_Pause and SUPPORTED_AsymPause bits in
> phydev->supported.  This indicates that the PHY supports pause frames.
> 
> 2) The MAC driver checks phydev->supported before it calls phy_start().
> If (SUPPORTED_Pause | SUPPORTED_AsymPause) is set, then the MAC driver
> sets those bits in phydev->advertising, if it wants to enable pause
> frame support.
> 
> 3) When the link state changes, the MAC driver checks phydev->pause and
> phydev->asym_pause,  If the bits are set, then it enables the corresponding
> features in the MAC.  The algorithm is:
> 
> 	if (phydev->pause)
> 		The MAC should be programmed to receive and honor
>                 pause frames it receives, i.e. enable receive flow control.
> 
> 	if (phydev->pause != phydev->asym_pause)
> 		The MAC should be programmed to transmit pause
> 		frames when needed, i.e. enable transmit flow control.
> 
> Signed-off-by: Timur Tabi <timur@codeaurora.org>
> ---

> diff --git a/drivers/net/phy/bcm63xx.c b/drivers/net/phy/bcm63xx.c
> index e741bf6..5e9922e 100644
> --- a/drivers/net/phy/bcm63xx.c
> +++ b/drivers/net/phy/bcm63xx.c
> @@ -48,8 +48,7 @@ static int bcm63xx_config_init(struct phy_device *phydev)
>  	.phy_id		= 0x00406000,
>  	.phy_id_mask	= 0xfffffc00,
>  	.name		= "Broadcom BCM63XX (1)",
> -	/* ASYM_PAUSE bit is marked RO in datasheet, so don't cheat */
> -	.features	= (PHY_BASIC_FEATURES | SUPPORTED_Pause),
> +	.features	= PHY_BASIC_FEATURES,

Humm that's actually a pretty important piece of information here that
we are going to lose if we unconditionally move the setting of the
SUPPORTED_Pause/Asym_Pause settings into the core. I don't have the HW
in a state where I could try a mainline kernel, but I suspect that the
following could happen though:

- we would try to set the SUPPORTED_AsymPause bit, and it would not be
taken into account, since the bit is RO
- the auto-negotiation results should still show up as symmetric pause
being supported only
- the driver would properly react to that

NB: this also applies to drivers/net/phy/ste10Xp.c.

So maybe, for theses drivers specifically, what we can do, is preserve
the entry as-is, to convey that only symmetric Pause frames can be
advertised, and have the logic in PHYLIB do something like this
(pseudo-code):

if (!(drv->features & (SUPPORTED_Pause | SUPPORTED_AsymPause))
	phydev->supported |= SUPPORTED_Pause | SUPPORTED_AsymPause;
else if ((drv->features & (SUPPORTED_Pause) && (!(drv->features &
(SUPPORTED_AsymPause)))
	phydev->supported |= SUPPORTED_Pause;

(there may be more efficient ways to do this of course).
-- 
Florian

^ permalink raw reply

* Re: [PATCH v3] ip6_output: ensure flow saddr actually belongs to device
From: Hannes Frederic Sowa @ 2016-11-14 18:33 UTC (permalink / raw)
  To: David Ahern, Jason A. Donenfeld, Netdev, WireGuard mailing list,
	LKML, YOSHIFUJI Hideaki
In-Reply-To: <0214eaf8-70c6-5a37-cddd-faa1c4268871@cumulusnetworks.com>

On Mon, Nov 14, 2016, at 18:48, David Ahern wrote:
> On 11/14/16 10:33 AM, Hannes Frederic Sowa wrote:
> >>>>> I just also quickly read up on the history (sorry was travelling last
> >>>>> week) and wonder if you ever saw a user space facing bug or if this is
> >>>>> basically some difference you saw while writing out of tree code?
> >>>>
> >>>> I checked the userspace API this morning. bind and cmsg for example check that the address is valid with calls to ipv6_chk_addr.
> >>>
> >>> Hmm, so it fixes no real bug.
> >>>
> >>> Because of translations of flowi6_oif we actually can't do a correct
> >>> check of source address for cases like the one I outlined above? Hmm,
> >>> maybe we should simply depend on user space checks.
> >>
> >> I believe Jason's case is forwarding path and the ipv6_stub->ipv6_dst_lookup API.
> > 
> > It is not a kernel API, because we don't support something like that for
> > external kernel modules. We basically exported ipv6_dst_lookup to allow
> > some IPv4 code to do ipv6 stunts when the IPv6 module is loaded. ;)
> 
> ???
> 
> ipv6_stub is exported for modules (EXPORT_SYMBOL_GPL(ipv6_stub)).
> 
> ipv6_stub->ipv6_dst_lookup is used by several modules -- geneve, tipc,
> vxlan, mpls -- for IPv6 lookups, not IPv4 code do IPv6 stunts.
> 
> So how do you say that is not an exported kernel API?

Sorry, yes, I noticed I wrote it in a confusing way.

I meant to say, we don't require the IPv6 "API" to behave in a similar
way like the IPv4 one. We do this function pointer trick to allow
_in-kernel_ tree modules to use the function dynamically, even the
kernel ipv6 module would be available but is not loaded but don't
guarante any "API like IPv4" to outside tree modules.

I tried to make the point, that it is still something internal to the
kernel if compared to out-of-tree function users. And that different
behavior by itself doesn't count as a bug.

We could as well require the users of this function to check for the
source address before or require to check the source address after the
ipv6_dst_lookup call.

vxlan currently seems wrong and would impacted by this patch in a better
way, so I am all in for such a change, but I think we need to check if
we are also correct scope-wise and not just match for the address on its
own.

Thanks,
Hannes

^ permalink raw reply

* Re: [PATCH net 1/3] bpf, mlx5: fix mlx5e_create_rq taking reference on prog
From: Daniel Borkmann @ 2016-11-14 18:26 UTC (permalink / raw)
  To: Saeed Mahameed, davem
  Cc: alexei.starovoitov, bblanco, tariqt, zhiyisun, ranas, netdev
In-Reply-To: <918902f3-1852-ae68-b12d-eaa1c45bf641@mellanox.com>

Hi Saeed,

On 11/14/2016 07:15 PM, Saeed Mahameed wrote:
> On 11/14/2016 02:43 AM, Daniel Borkmann wrote:
>> In mlx5e_create_rq(), when creating a new queue, we call bpf_prog_add() but
>> without checking the return value. bpf_prog_add() can fail, so we really
>
> Didn't know this, thanks for noticing, I wonder why taking a reference for an object would fail ?
> especially when someone is requesting from the driver to take a reference to it ndo_xdp_set ?! sounds like a bad design.
>
> Anyway I will check that later.

See 92117d8443bc ("bpf: fix refcnt overflow").

>> must check it. Take the reference right when we assign it to the rq from
>> priv->xdp_prog, and just drop the reference on error path. Destruction in
>> mlx5e_destroy_rq() looks good, though.
>>
>> Fixes: 86994156c736 ("net/mlx5e: XDP fast RX drop bpf programs support")
>> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
>> ---
>>   drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 14 +++++++++++---
>>   kernel/bpf/syscall.c                              |  1 +
>>   2 files changed, 12 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
>> index 84e8b25..2b83667 100644
>> --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
>> +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
>> @@ -489,7 +489,16 @@ static int mlx5e_create_rq(struct mlx5e_channel *c,
>>   	rq->channel = c;
>>   	rq->ix      = c->ix;
>>   	rq->priv    = c->priv;
>> +
>>   	rq->xdp_prog = priv->xdp_prog;
>
> Why keeping this assignment ? just test priv->xdp_prog.
>
>> +	if (rq->xdp_prog) {
>> +		rq->xdp_prog = bpf_prog_inc(rq->xdp_prog);
>> +		if (IS_ERR(rq->xdp_prog)) {
>> +			err = PTR_ERR(rq->xdp_prog);
>> +			rq->xdp_prog = NULL;
>> +			goto err_rq_wq_destroy;
>> +		}
>> +	}
>
> Try this, simpler and less indentations:
>
> rq->xdp_prog = priv->xdp_prog ? bpf_prog_inc(priv->xdp_prog) : NULL;
> if (IS_ERR(rq->xdp_prog)) {
> 	err = PTR_ERR(rq->xdp_prog);
> 	rq->xdp_prog = NULL;
> 	goto err_rq_wq_destroy;
> }

Sure, I don't mind. Will do.

Thanks,
Daniel

^ permalink raw reply

* Re: [PATCH net 2/3] bpf, mlx5: fix various refcount/prog issues in mlx5e_xdp_set
From: Daniel Borkmann @ 2016-11-14 18:23 UTC (permalink / raw)
  To: Alexei Starovoitov; +Cc: davem, bblanco, tariqt, zhiyisun, ranas, netdev
In-Reply-To: <20161114173525.GA98186@ast-mbp.thefacebook.com>

On 11/14/2016 06:35 PM, Alexei Starovoitov wrote:
> On Mon, Nov 14, 2016 at 09:49:49AM +0100, Daniel Borkmann wrote:
>> On 11/14/2016 03:49 AM, Alexei Starovoitov wrote:
>>> On Mon, Nov 14, 2016 at 01:43:41AM +0100, Daniel Borkmann wrote:
>> [...]
>>>> diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
>>>> index 751e806..a0fca9f 100644
>>>> --- a/kernel/bpf/syscall.c
>>>> +++ b/kernel/bpf/syscall.c
>>>> @@ -682,6 +682,17 @@ struct bpf_prog *bpf_prog_add(struct bpf_prog *prog, int i)
>>>>   }
>>>>   EXPORT_SYMBOL_GPL(bpf_prog_add);
>>>>
>>>> +void bpf_prog_sub(struct bpf_prog *prog, int i)
>>>> +{
>>>> +	/* Only to be used for undoing previous bpf_prog_add() in some
>>>> +	 * error path. We still know that another entity in our call
>>>> +	 * path holds a reference to the program, thus atomic_sub() can
>>>> +	 * be safely used in such cases!
>>>> +	 */
>>>> +	WARN_ON(atomic_sub_return(i, &prog->aux->refcnt) == 0);
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(bpf_prog_sub);
>>>
>>> the patches look good. I'm only worried about net/net-next merge
>>> conflict here. (I would have to deal with it as well).
>>> So instead of copying the above helper can we apply net-next's
>>> 'bpf, mlx4: fix prog refcount in mlx4_en_try_alloc_resources error path'
>>> patch to net without mlx4_xdp_set hunk and then apply
>>> the rest of this patch?
>>> Even better is to send this patch 2/3 to net-next?
>>> yes, it's an issue, but very small one. There is no security
>>> concern here, so I would prefer to avoid merge conflict.
>>> Did you do a test merge of net/net-next by any chance?
>>
>> Yes, I did a test merge and git resolved the above just fine w/o
>> any conflicts. I have no strong opinion whether net or net-next.
>> If preferred, I can just resend this series in the evening against
>> net-next instead, perhaps that's a bit better.
>
> I have slight preference to go via net-next, but since it merges fine,
> I don't mind net route too.

Ok, I'll rebase for net-next then.

^ permalink raw reply

* Re: Debugging Ethernet issues
From: Florian Fainelli @ 2016-11-14 18:20 UTC (permalink / raw)
  To: Sebastian Frias, Mason, Andrew Lunn
  Cc: netdev, Mans Rullgard, Sergei Shtylyov, Tom Lendacky, Zach Brown,
	Shaohui Xie, Tim Beale, Brian Hill, Vince Bridgers,
	Balakumaran Kannan, David S. Miller, Kirill Kapranov
In-Reply-To: <5829FB6F.6090106@laposte.net>

On 11/14/2016 09:59 AM, Sebastian Frias wrote:
> On 11/14/2016 06:32 PM, Florian Fainelli wrote:
>> On 11/14/2016 07:33 AM, Mason wrote:
>>> On 14/11/2016 15:58, Mason wrote:
>>>
>>>> nb8800 26000.ethernet eth0: Link is Up - 100Mbps/Full - flow control rx/tx
>>>> vs
>>>> nb8800 26000.ethernet eth0: Link is Up - 100Mbps/Full - flow control off
>>>>
>>>> I'm not sure whether "flow control" is relevant...
>>>
>>> Based on phy_print_status()
>>> phydev->pause ? "rx/tx" : "off"
>>> I added the following patch.
>>>
>>> diff --git a/drivers/net/ethernet/aurora/nb8800.c b/drivers/net/ethernet/aurora/nb8800.c
>>> index defc22a15f67..4e758c1cfa4e 100644
>>> --- a/drivers/net/ethernet/aurora/nb8800.c
>>> +++ b/drivers/net/ethernet/aurora/nb8800.c
>>> @@ -667,6 +667,8 @@ static void nb8800_link_reconfigure(struct net_device *dev)
>>>         struct phy_device *phydev = priv->phydev;
>>>         int change = 0;
>>>  
>>> +       printk("%s from %pf\n", __func__, __builtin_return_address(0));
>>> +
>>>         if (phydev->link) {
>>>                 if (phydev->speed != priv->speed) {
>>>                         priv->speed = phydev->speed;
>>> @@ -1274,9 +1276,9 @@ static int nb8800_hw_init(struct net_device *dev)
>>>         nb8800_writeb(priv, NB8800_PQ2, val & 0xff);
>>>  
>>>         /* Auto-negotiate by default */
>>> -       priv->pause_aneg = true;
>>> -       priv->pause_rx = true;
>>> -       priv->pause_tx = true;
>>> +       priv->pause_aneg = false;
>>> +       priv->pause_rx = false;
>>> +       priv->pause_tx = false;
>>>  
>>>         nb8800_mc_init(dev, 0);
>>>  
>>>
>>> Connected to 1000 Mbps switch:
>>>
>>> # time udhcpc | while read LINE; do date; echo $LINE; done
>>> Thu Jan  1 00:00:22 UTC 1970
>>> udhcpc (v1.22.1) started
>>> Thu Jan  1 00:00:22 UTC 1970
>>> Sending discover...
>>> [   24.565346] nb8800_link_reconfigure from phy_state_machine
>>> Thu Jan  1 00:00:25 UTC 1970
>>> Sending discover...
>>> [   26.575402] nb8800_link_reconfigure from phy_state_machine
>>> [   26.580972] nb8800 26000.ethernet eth0: Link is Up - 1Gbps/Full - flow control rx/tx
>>> Thu Jan  1 00:00:28 UTC 1970
>>> Sending discover...
>>> Thu Jan  1 00:00:29 UTC 1970
>>> Sending select for 172.27.64.58...
>>> Thu Jan  1 00:00:29 UTC 1970
>>> Lease of 172.27.64.58 obtained, lease time 604800
>>> Thu Jan  1 00:00:29 UTC 1970
>>> deleting routers
>>> Thu Jan  1 00:00:29 UTC 1970
>>> adding dns 172.27.0.17
>>>
>>> real    0m7.388s
>>> user    0m0.040s
>>> sys     0m0.090s
>>>
>>>
>>>
>>> Connected to 100 Mbps switch:
>>>
>>> # time udhcpc | while read LINE; do date; echo $LINE; done
>>> Thu Jan  1 00:00:14 UTC 1970
>>> udhcpc (v1.22.1) started
>>> Thu Jan  1 00:00:15 UTC 1970
>>> Sending discover...
>>> [   16.968621] nb8800_link_reconfigure from phy_state_machine
>>> [   17.975359] nb8800_link_reconfigure from phy_state_machine
>>> [   17.980923] nb8800 26000.ethernet eth0: Link is Up - 100Mbps/Full - flow control rx/tx
>>> Thu Jan  1 00:00:18 UTC 1970
>>> Sending discover...
>>> Thu Jan  1 00:00:19 UTC 1970
>>> Sending select for 172.27.64.58...
>>> Thu Jan  1 00:00:19 UTC 1970
>>> Lease of 172.27.64.58 obtained, lease time 604800
>>> Thu Jan  1 00:00:19 UTC 1970
>>> deleting routers
>>> Thu Jan  1 00:00:19 UTC 1970
>>> adding dns 172.27.0.17
>>>
>>> real    0m4.355s
>>> user    0m0.043s
>>> sys     0m0.083s
>>>
>>
>> And the time difference is clearly accounted for auto-negotiation time
>> here, as you can see it takes about 3 seconds for Gigabit Ethernet to
>> auto-negotiate and that seems completely acceptable and normal to me
>> since it is a more involved process than lower speeds.
>>
>>>
>>>
>>> OK, so now it works (by accident?) even on 100 Mbps switch, but it still
>>> prints "flow control rx/tx"...
>>
>> Because your link partner advertises flow control, and that's what
>> phydev->pause and phydev->asym_pause report (I know it's confusing, but
>> that's what it is at the moment).
> 
> Thanks.
> Could you confirm that Mason's patch is correct and/or that it does not
> has negative side-effects?

The patch is not correct nor incorrect per-se, it changes the default
policy of having pause frames advertised by default to not having them
advertised by default. This influences both your Ethernet MAC and the
link partner in that the result is either flow control is enabled
(before) or it is not (with the patch). There must be something amiss if
you see packet loss or some kind of problem like that with an early
exchange such as DHCP. Flow control tend to kick in under higher packet
rates (at least, that's what you expect).


> 
> Right now we know that Mason's patch makes this work, but we do not understand
> why nor its implications.

You need to understand why, right now, the way this problem is
presented, you came up with a workaround, not with the root cause or the
solution. What does your link partner (switch?) reports, that is, what
is the ethtool output when you have a link up from  your nb8800 adapter?
-- 
Florian

^ permalink raw reply

* Re: [PATCH v2 net-next 0/6] bpf: LRU map
From: David Miller @ 2016-11-14 18:19 UTC (permalink / raw)
  To: kafai; +Cc: netdev, ast, daniel, kernel-team
In-Reply-To: <1478890511-1346984-1-git-send-email-kafai@fb.com>

From: Martin KaFai Lau <kafai@fb.com>
Date: Fri, 11 Nov 2016 10:55:05 -0800

> This patch set adds LRU map implementation to the existing BPF map
> family.

Alexei and Daniel, can I please get some review of this series?

Thank you.

^ permalink raw reply

* Re: [Patch net-next] net: fix sleeping for sk_wait_event()
From: David Miller @ 2016-11-14 18:17 UTC (permalink / raw)
  To: xiyou.wangcong; +Cc: netdev, eric.dumazet, peterz
In-Reply-To: <1478888450-16985-1-git-send-email-xiyou.wangcong@gmail.com>

From: Cong Wang <xiyou.wangcong@gmail.com>
Date: Fri, 11 Nov 2016 10:20:50 -0800

> Similar to commit 14135f30e33c ("inet: fix sleeping inside inet_wait_for_connect()"),
> sk_wait_event() needs to fix too, because release_sock() is blocking,
> it changes the process state back to running after sleep, which breaks
> the previous prepare_to_wait().
> 
> Switch to the new wait API.
> 
> Cc: Eric Dumazet <eric.dumazet@gmail.com>
> Cc: Peter Zijlstra <peterz@infradead.org>
> Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com>

Applied, thanks.

^ permalink raw reply

* Re: Long delays creating a netns after deleting one (possibly RCU related)
From: Paul E. McKenney @ 2016-11-14 18:14 UTC (permalink / raw)
  To: Cong Wang
  Cc: Rolf Neugebauer, LKML, Linux Kernel Network Developers,
	Justin Cormack, Ian Campbell
In-Reply-To: <CAM_iQpXL+JaVG86+h2ucYs4Dm0zJKHq+4Nm+gk75wESGOzTmJQ@mail.gmail.com>

On Mon, Nov 14, 2016 at 09:44:35AM -0800, Cong Wang wrote:
> On Mon, Nov 14, 2016 at 8:24 AM, Paul E. McKenney
> <paulmck@linux.vnet.ibm.com> wrote:
> > On Sun, Nov 13, 2016 at 10:47:01PM -0800, Cong Wang wrote:
> >> On Fri, Nov 11, 2016 at 4:55 PM, Cong Wang <xiyou.wangcong@gmail.com> wrote:
> >> > On Fri, Nov 11, 2016 at 4:23 PM, Paul E. McKenney
> >> > <paulmck@linux.vnet.ibm.com> wrote:
> >> >>
> >> >> Ah!  This net_mutex is different than RTNL.  Should synchronize_net() be
> >> >> modified to check for net_mutex being held in addition to the current
> >> >> checks for RTNL being held?
> >> >>
> >> >
> >> > Good point!
> >> >
> >> > Like commit be3fc413da9eb17cce0991f214ab0, checking
> >> > for net_mutex for this case seems to be an optimization, I assume
> >> > synchronize_rcu_expedited() and synchronize_rcu() have the same
> >> > behavior...
> >>
> >> Thinking a bit more, I think commit be3fc413da9eb17cce0991f
> >> gets wrong on rtnl_is_locked(), the lock could be locked by other
> >> process not by the current one, therefore it should be
> >> lockdep_rtnl_is_held() which, however, is defined only when LOCKDEP
> >> is enabled... Sigh.
> >>
> >> I don't see any better way than letting callers decide if they want the
> >> expedited version or not, but this requires changes of all callers of
> >> synchronize_net(). Hm.
> >
> > I must confess that I don't understand how it would help to use an
> > expedited grace period when some other process is holding RTNL.
> > In contrast, I do well understand how it helps when the current process
> > is holding RTNL.
> 
> Yeah, this is exactly my point. And same for ASSERT_RTNL() which checks
> rtnl_is_locked(), clearly we need to assert "it is held by the current process"
> rather than "it is locked by whatever process".
> 
> But given *_is_held() is always defined by LOCKDEP, so we probably need
> mutex to provide such a helper directly, mutex->owner is not always defined
> either. :-/

There is always the option of making acquisition and release set a per-task
variable that can be tested.  (Where did I put that asbestos suit, anyway?)

							Thanx, Paul

^ permalink raw reply

* Re: [PATCH] [v2] net: phy: phy drivers should not set SUPPORTED_[Asym_]Pause
From: David Miller @ 2016-11-14 18:12 UTC (permalink / raw)
  To: timur; +Cc: f.fainelli, jon.mason, netdev
In-Reply-To: <1478821561-26498-1-git-send-email-timur@codeaurora.org>

From: Timur Tabi <timur@codeaurora.org>
Date: Thu, 10 Nov 2016 17:46:01 -0600

> Instead of having individual PHY drivers set the SUPPORTED_Pause and
> SUPPORTED_Asym_Pause flags, phylib itself should set those flags.
> During autonegotiation, the PHYs will determine whether to enable
> pause frame support.
> 
> Pause frames are a feature that is supported by the MAC.  It is the MAC
> that generates the frames and that processes them.  The PHY can only be
> configured to allow them to pass through.
> 
> So the new process is:
 ...
> Signed-off-by: Timur Tabi <timur@codeaurora.org>
> ---
> 
> v2: set the Pause bits in phy_probe()
>     update broadcom.c which was recently changed

Florian please review this patch.

Thank you.

^ permalink raw reply

* Re: Debugging Ethernet issues
From: Sebastian Frias @ 2016-11-14 17:59 UTC (permalink / raw)
  To: Florian Fainelli, Mason, Andrew Lunn
  Cc: netdev, Mans Rullgard, Sergei Shtylyov, Tom Lendacky, Zach Brown,
	Shaohui Xie, Tim Beale, Brian Hill, Vince Bridgers,
	Balakumaran Kannan, David S. Miller, Kirill Kapranov
In-Reply-To: <3313424a-8d45-0883-5257-ffdc250dd45b@gmail.com>

On 11/14/2016 06:32 PM, Florian Fainelli wrote:
> On 11/14/2016 07:33 AM, Mason wrote:
>> On 14/11/2016 15:58, Mason wrote:
>>
>>> nb8800 26000.ethernet eth0: Link is Up - 100Mbps/Full - flow control rx/tx
>>> vs
>>> nb8800 26000.ethernet eth0: Link is Up - 100Mbps/Full - flow control off
>>>
>>> I'm not sure whether "flow control" is relevant...
>>
>> Based on phy_print_status()
>> phydev->pause ? "rx/tx" : "off"
>> I added the following patch.
>>
>> diff --git a/drivers/net/ethernet/aurora/nb8800.c b/drivers/net/ethernet/aurora/nb8800.c
>> index defc22a15f67..4e758c1cfa4e 100644
>> --- a/drivers/net/ethernet/aurora/nb8800.c
>> +++ b/drivers/net/ethernet/aurora/nb8800.c
>> @@ -667,6 +667,8 @@ static void nb8800_link_reconfigure(struct net_device *dev)
>>         struct phy_device *phydev = priv->phydev;
>>         int change = 0;
>>  
>> +       printk("%s from %pf\n", __func__, __builtin_return_address(0));
>> +
>>         if (phydev->link) {
>>                 if (phydev->speed != priv->speed) {
>>                         priv->speed = phydev->speed;
>> @@ -1274,9 +1276,9 @@ static int nb8800_hw_init(struct net_device *dev)
>>         nb8800_writeb(priv, NB8800_PQ2, val & 0xff);
>>  
>>         /* Auto-negotiate by default */
>> -       priv->pause_aneg = true;
>> -       priv->pause_rx = true;
>> -       priv->pause_tx = true;
>> +       priv->pause_aneg = false;
>> +       priv->pause_rx = false;
>> +       priv->pause_tx = false;
>>  
>>         nb8800_mc_init(dev, 0);
>>  
>>
>> Connected to 1000 Mbps switch:
>>
>> # time udhcpc | while read LINE; do date; echo $LINE; done
>> Thu Jan  1 00:00:22 UTC 1970
>> udhcpc (v1.22.1) started
>> Thu Jan  1 00:00:22 UTC 1970
>> Sending discover...
>> [   24.565346] nb8800_link_reconfigure from phy_state_machine
>> Thu Jan  1 00:00:25 UTC 1970
>> Sending discover...
>> [   26.575402] nb8800_link_reconfigure from phy_state_machine
>> [   26.580972] nb8800 26000.ethernet eth0: Link is Up - 1Gbps/Full - flow control rx/tx
>> Thu Jan  1 00:00:28 UTC 1970
>> Sending discover...
>> Thu Jan  1 00:00:29 UTC 1970
>> Sending select for 172.27.64.58...
>> Thu Jan  1 00:00:29 UTC 1970
>> Lease of 172.27.64.58 obtained, lease time 604800
>> Thu Jan  1 00:00:29 UTC 1970
>> deleting routers
>> Thu Jan  1 00:00:29 UTC 1970
>> adding dns 172.27.0.17
>>
>> real    0m7.388s
>> user    0m0.040s
>> sys     0m0.090s
>>
>>
>>
>> Connected to 100 Mbps switch:
>>
>> # time udhcpc | while read LINE; do date; echo $LINE; done
>> Thu Jan  1 00:00:14 UTC 1970
>> udhcpc (v1.22.1) started
>> Thu Jan  1 00:00:15 UTC 1970
>> Sending discover...
>> [   16.968621] nb8800_link_reconfigure from phy_state_machine
>> [   17.975359] nb8800_link_reconfigure from phy_state_machine
>> [   17.980923] nb8800 26000.ethernet eth0: Link is Up - 100Mbps/Full - flow control rx/tx
>> Thu Jan  1 00:00:18 UTC 1970
>> Sending discover...
>> Thu Jan  1 00:00:19 UTC 1970
>> Sending select for 172.27.64.58...
>> Thu Jan  1 00:00:19 UTC 1970
>> Lease of 172.27.64.58 obtained, lease time 604800
>> Thu Jan  1 00:00:19 UTC 1970
>> deleting routers
>> Thu Jan  1 00:00:19 UTC 1970
>> adding dns 172.27.0.17
>>
>> real    0m4.355s
>> user    0m0.043s
>> sys     0m0.083s
>>
> 
> And the time difference is clearly accounted for auto-negotiation time
> here, as you can see it takes about 3 seconds for Gigabit Ethernet to
> auto-negotiate and that seems completely acceptable and normal to me
> since it is a more involved process than lower speeds.
> 
>>
>>
>> OK, so now it works (by accident?) even on 100 Mbps switch, but it still
>> prints "flow control rx/tx"...
> 
> Because your link partner advertises flow control, and that's what
> phydev->pause and phydev->asym_pause report (I know it's confusing, but
> that's what it is at the moment).

Thanks.
Could you confirm that Mason's patch is correct and/or that it does not
has negative side-effects?

Right now we know that Mason's patch makes this work, but we do not understand
why nor its implications.

> 
>>
>> # ethtool -a eth0
>> Pause parameters for eth0:
>> Autonegotiate:  off
>> RX:             off
>> TX:             off
>>
>> These values make sense considering my changes in the driver.
>>
>> Are 100 Mbps switches supposed to support these pause features,
>> and are they supposed to be able to auto-negotiate them?
> 
> Yes, switches can support flow control aka pause frames, and unless they
> are configurable, they typically advertise what their EEPROM has defined
> for them, so most likely the full auto-negotiated spectrum:
> 10/100/1000Mbps and support for flow control, but your mileage may vary
> of course.
> 

^ permalink raw reply

* [PATCH 2/2] can: spi: hi311x: Add Holt HI-311x CAN driver
From: Akshay Bhat @ 2016-11-14 17:55 UTC (permalink / raw)
  To: wg, mkl, robh+dt
  Cc: mark.rutland, linux-can, netdev, devicetree, linux-kernel,
	Akshay Bhat, Akshay Bhat
In-Reply-To: <1479146144-29143-1-git-send-email-akshay.bhat@timesys.com>

This patch adds support for the Holt HI-311x CAN controller. The HI311x
CAN controller is capable of transmitting and receiving standard data
frames, extended data frames and remote frames. The HI311x interfaces
with the host over SPI.

Datasheet: www.holtic.com/documents/371-hi-3110_v-rev-jpdf.do

Signed-off-by: Akshay Bhat <nodeax@gmail.com>
---
 drivers/net/can/spi/Kconfig  |    6 +
 drivers/net/can/spi/Makefile |    1 +
 drivers/net/can/spi/hi311x.c | 1071 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1078 insertions(+)
 create mode 100644 drivers/net/can/spi/hi311x.c

diff --git a/drivers/net/can/spi/Kconfig b/drivers/net/can/spi/Kconfig
index 148cae5..9eb1bb1 100644
--- a/drivers/net/can/spi/Kconfig
+++ b/drivers/net/can/spi/Kconfig
@@ -7,4 +7,10 @@ config CAN_MCP251X
 	---help---
 	  Driver for the Microchip MCP251x SPI CAN controllers.
 
+config CAN_HI311X
+	tristate "Holt HI311x SPI CAN controllers"
+	depends on CAN_DEV && SPI && HAS_DMA
+	---help---
+	  Driver for the Holt HI311x SPI CAN controllers.
+
 endmenu
diff --git a/drivers/net/can/spi/Makefile b/drivers/net/can/spi/Makefile
index 0e86040..eac7c3a 100644
--- a/drivers/net/can/spi/Makefile
+++ b/drivers/net/can/spi/Makefile
@@ -4,3 +4,4 @@
 
 
 obj-$(CONFIG_CAN_MCP251X)	+= mcp251x.o
+obj-$(CONFIG_CAN_HI311X)	+= hi311x.o
diff --git a/drivers/net/can/spi/hi311x.c b/drivers/net/can/spi/hi311x.c
new file mode 100644
index 0000000..1020166
--- /dev/null
+++ b/drivers/net/can/spi/hi311x.c
@@ -0,0 +1,1071 @@
+/* CAN bus driver for Holt HI3110 CAN Controller with SPI Interface
+ *
+ * Based on Microchip 251x CAN Controller (mcp251x) Linux kernel driver
+ *
+ * Copyright(C) Timesys Corporation 2016
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/can/core.h>
+#include <linux/can/dev.h>
+#include <linux/can/led.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/freezer.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/uaccess.h>
+
+#define HI3110_MASTER_RESET        0x56
+#define HI3110_READ_CTRL0          0xD2
+#define HI3110_READ_CTRL1          0xD4
+#define HI3110_READ_STATF          0xE2
+#define HI3110_WRITE_CTRL0         0x14
+#define HI3110_WRITE_CTRL1         0x16
+#define HI3110_WRITE_INTE          0x1C
+#define HI3110_WRITE_BTR0          0x18
+#define HI3110_WRITE_BTR1          0x1A
+#define HI3110_READ_BTR0           0xD6
+#define HI3110_READ_BTR1           0xD8
+#define HI3110_READ_INTF           0xDE
+#define HI3110_READ_ERR            0xDC
+#define HI3110_READ_FIFO_WOTIME    0x48
+#define HI3110_WRITE_FIFO          0x12
+#define HI3110_READ_MESSTAT        0xDA
+#define HI3110_READ_TEC            0xEC
+
+#define HI3110_CTRL0_MODE_MASK     (7 << 5)
+#define HI3110_CTRL0_NORMAL_MODE   (0 << 5)
+#define HI3110_CTRL0_LOOPBACK_MODE (1 << 5)
+#define HI3110_CTRL0_MONITOR_MODE  (2 << 5)
+#define HI3110_CTRL0_SLEEP_MODE    (3 << 5)
+#define HI3110_CTRL0_INIT_MODE     (4 << 5)
+
+#define HI3110_CTRL1_TXEN          BIT(7)
+
+#define HI3110_INT_RXTMP           BIT(7)
+#define HI3110_INT_RXFIFO          BIT(6)
+#define HI3110_INT_TXCPLT          BIT(5)
+#define HI3110_INT_BUSERR          BIT(4)
+#define HI3110_INT_MCHG            BIT(3)
+#define HI3110_INT_WAKEUP          BIT(2)
+#define HI3110_INT_F1MESS          BIT(1)
+#define HI3110_INT_F0MESS          BIT(0)
+
+#define HI3110_ERR_BUSOFF          BIT(7)
+#define HI3110_ERR_TXERRP          BIT(6)
+#define HI3110_ERR_RXERRP          BIT(5)
+#define HI3110_ERR_BITERR          BIT(4)
+#define HI3110_ERR_FRMERR          BIT(3)
+#define HI3110_ERR_CRCERR          BIT(2)
+#define HI3110_ERR_ACKERR          BIT(1)
+#define HI3110_ERR_STUFERR         BIT(0)
+#define HI3110_ERR_PROTOCOL_MASK   (0x1F)
+
+#define HI3110_STAT_RXFMTY         BIT(1)
+
+#define HI3110_BTR0_SJW_SHIFT      6
+#define HI3110_BTR0_BRP_SHIFT      0
+
+#define HI3110_BTR1_SAMP_3PERBIT   (1 << 7)
+#define HI3110_BTR1_SAMP_1PERBIT   (0 << 7)
+#define HI3110_BTR1_TSEG2_SHIFT    4
+#define HI3110_BTR1_TSEG1_SHIFT    0
+
+#define HI3110_FIFO_WOTIME_TAG_OFF 0
+#define HI3110_FIFO_WOTIME_ID_OFF  1
+#define HI3110_FIFO_WOTIME_DLC_OFF 5
+#define HI3110_FIFO_WOTIME_DAT_OFF 6
+
+#define HI3110_FIFO_WOTIME_TAG_IDE BIT(7)
+#define HI3110_FIFO_WOTIME_ID_RTR  BIT(0)
+
+#define HI3110_FIFO_TAG_OFF        0
+#define HI3110_FIFO_ID_OFF         1
+#define HI3110_FIFO_STD_DLC_OFF    3
+#define HI3110_FIFO_STD_DATA_OFF   4
+#define HI3110_FIFO_EXT_DLC_OFF    5
+#define HI3110_FIFO_EXT_DATA_OFF   6
+
+#define CAN_FRAME_MAX_DATA_LEN 8
+#define RX_BUF_LEN             15
+#define TX_STD_BUF_LEN         12
+#define TX_EXT_BUF_LEN         14
+#define CAN_FRAME_MAX_BITS     128
+
+#define TX_ECHO_SKB_MAX	1
+
+#define HI3110_OST_DELAY_MS (10)
+
+#define DEVICE_NAME "hi3110"
+
+static int hi3110_enable_dma = 1; /* Enable SPI DMA. Default: 1 (On) */
+module_param(hi3110_enable_dma, int, 0444);
+MODULE_PARM_DESC(hi3110_enable_dma, "Enable SPI DMA. Default: 1 (On)");
+
+static const struct can_bittiming_const hi3110_bittiming_const = {
+	.name = DEVICE_NAME,
+	.tseg1_min = 2,
+	.tseg1_max = 16,
+	.tseg2_min = 2,
+	.tseg2_max = 8,
+	.sjw_max = 4,
+	.brp_min = 1,
+	.brp_max = 64,
+	.brp_inc = 1,
+};
+
+enum hi3110_model {
+	CAN_HI3110_HI3110	= 0x3110,
+};
+
+struct hi3110_priv {
+	struct can_priv	   can;
+	struct net_device *net;
+	struct spi_device *spi;
+	enum hi3110_model model;
+
+	struct mutex hi3110_lock; /* SPI device lock */
+
+	u8 *spi_tx_buf;
+	u8 *spi_rx_buf;
+	dma_addr_t spi_tx_dma;
+	dma_addr_t spi_rx_dma;
+
+	struct sk_buff *tx_skb;
+	int tx_len;
+
+	struct workqueue_struct *wq;
+	struct work_struct tx_work;
+	struct work_struct restart_work;
+
+	int force_quit;
+	int after_suspend;
+#define AFTER_SUSPEND_UP 1
+#define AFTER_SUSPEND_DOWN 2
+#define AFTER_SUSPEND_POWER 4
+#define AFTER_SUSPEND_RESTART 8
+	int restart_tx;
+	struct regulator *power;
+	struct regulator *transceiver;
+	struct clk *clk;
+};
+
+static void hi3110_clean(struct net_device *net)
+{
+	struct hi3110_priv *priv = netdev_priv(net);
+
+	if (priv->tx_skb || priv->tx_len)
+		net->stats.tx_errors++;
+	if (priv->tx_skb)
+		dev_kfree_skb(priv->tx_skb);
+	if (priv->tx_len)
+		can_free_echo_skb(priv->net, 0);
+	priv->tx_skb = NULL;
+	priv->tx_len = 0;
+}
+
+/* Note about handling of error return of hi3110_spi_trans: accessing
+ * registers via SPI is not really different conceptually than using
+ * normal I/O assembler instructions, although it's much more
+ * complicated from a practical POV. So it's not advisable to always
+ * check the return value of this function. Imagine that every
+ * read{b,l}, write{b,l} and friends would be bracketed in "if ( < 0)
+ * error();", it would be a great mess (well there are some situation
+ * when exception handling C++ like could be useful after all). So we
+ * just check that transfers are OK at the beginning of our
+ * conversation with the chip and to avoid doing really nasty things
+ * (like injecting bogus packets in the network stack).
+ */
+static int hi3110_spi_trans(struct spi_device *spi, int len)
+{
+	struct hi3110_priv *priv = spi_get_drvdata(spi);
+	struct spi_transfer t = {
+		.tx_buf = priv->spi_tx_buf,
+		.rx_buf = priv->spi_rx_buf,
+		.len = len,
+		.cs_change = 0,
+	};
+	struct spi_message m;
+	int ret;
+
+	spi_message_init(&m);
+
+	if (hi3110_enable_dma) {
+		t.tx_dma = priv->spi_tx_dma;
+		t.rx_dma = priv->spi_rx_dma;
+		m.is_dma_mapped = 1;
+	}
+
+	spi_message_add_tail(&t, &m);
+
+	ret = spi_sync(spi, &m);
+
+	if (ret)
+		dev_err(&spi->dev, "spi transfer failed: ret = %d\n", ret);
+	return ret;
+}
+
+static u8 hi3110_cmd(struct spi_device *spi, uint8_t command)
+{
+	struct hi3110_priv *priv = spi_get_drvdata(spi);
+
+	priv->spi_tx_buf[0] = command;
+	dev_dbg(&spi->dev, "hi3110_cmd: %02X\n", command);
+
+	return hi3110_spi_trans(spi, 1);
+}
+
+static u8 hi3110_read(struct spi_device *spi, uint8_t command)
+{
+	struct hi3110_priv *priv = spi_get_drvdata(spi);
+	u8 val = 0;
+
+	priv->spi_tx_buf[0] = command;
+	hi3110_spi_trans(spi, 2);
+	val = priv->spi_rx_buf[1];
+	dev_dbg(&spi->dev, "hi3110_read: %02X, %02X\n", command, val);
+
+	return val;
+}
+
+static void hi3110_write(struct spi_device *spi, u8 reg, uint8_t val)
+{
+	struct hi3110_priv *priv = spi_get_drvdata(spi);
+
+	priv->spi_tx_buf[0] = reg;
+	priv->spi_tx_buf[1] = val;
+	dev_dbg(&spi->dev, "hi3110_write: %02X, %02X\n", reg, val);
+
+	hi3110_spi_trans(spi, 2);
+}
+
+static void hi3110_hw_tx_frame(struct spi_device *spi, u8 *buf, int len)
+{
+	struct hi3110_priv *priv = spi_get_drvdata(spi);
+
+	priv->spi_tx_buf[0] = HI3110_WRITE_FIFO;
+	memcpy(priv->spi_tx_buf + 1, buf, len);
+	hi3110_spi_trans(spi, len + 1);
+}
+
+static void hi3110_hw_tx(struct spi_device *spi, struct can_frame *frame)
+{
+	u8 buf[TX_EXT_BUF_LEN];
+
+	buf[HI3110_FIFO_TAG_OFF] = 0;
+
+	if (frame->can_id & CAN_EFF_FLAG) {
+		/* Extended frame */
+		buf[HI3110_FIFO_ID_OFF] = (frame->can_id & CAN_EFF_MASK) >> 21;
+		buf[HI3110_FIFO_ID_OFF + 1] =
+			((((frame->can_id & CAN_EFF_MASK) >> 18) & 0x07) << 5) |
+			0x18 | /* Recessive SRR and IDE */
+			(((frame->can_id & CAN_EFF_MASK) >> 15) & 0x07);
+		buf[HI3110_FIFO_ID_OFF + 2] =
+			(frame->can_id & CAN_EFF_MASK) >> 7;
+		buf[HI3110_FIFO_ID_OFF + 3] =
+			((frame->can_id & CAN_EFF_MASK) << 1) |
+			((frame->can_id & CAN_RTR_FLAG) ? 1 : 0);
+
+		buf[HI3110_FIFO_EXT_DLC_OFF] = frame->can_dlc;
+
+		memcpy(buf + HI3110_FIFO_EXT_DATA_OFF,
+		       frame->data, frame->can_dlc);
+
+		hi3110_hw_tx_frame(spi, buf, TX_EXT_BUF_LEN -
+				   (CAN_FRAME_MAX_DATA_LEN - frame->can_dlc));
+	} else {
+		/* Standard frame */
+		buf[HI3110_FIFO_ID_OFF] =   (frame->can_id & CAN_SFF_MASK) >> 3;
+		buf[HI3110_FIFO_ID_OFF + 1] =
+			((frame->can_id & CAN_SFF_MASK) << 5) |
+			((frame->can_id & CAN_RTR_FLAG) ? (1 << 4) : 0);
+
+		buf[HI3110_FIFO_STD_DLC_OFF] = frame->can_dlc;
+
+		memcpy(buf + HI3110_FIFO_STD_DATA_OFF,
+		       frame->data, frame->can_dlc);
+
+		hi3110_hw_tx_frame(spi, buf, TX_STD_BUF_LEN -
+				   (CAN_FRAME_MAX_DATA_LEN - frame->can_dlc));
+	}
+}
+
+static void hi3110_hw_rx_frame(struct spi_device *spi, u8 *buf)
+{
+	struct hi3110_priv *priv = spi_get_drvdata(spi);
+
+	priv->spi_tx_buf[0] = HI3110_READ_FIFO_WOTIME;
+	hi3110_spi_trans(spi, RX_BUF_LEN);
+	memcpy(buf, priv->spi_rx_buf + 1, RX_BUF_LEN - 1);
+}
+
+static void hi3110_hw_rx(struct spi_device *spi)
+{
+	struct hi3110_priv *priv = spi_get_drvdata(spi);
+	struct sk_buff *skb;
+	struct can_frame *frame;
+	u8 buf[RX_BUF_LEN - 1];
+
+	skb = alloc_can_skb(priv->net, &frame);
+	if (!skb) {
+		dev_err(&spi->dev, "cannot allocate RX skb\n");
+		priv->net->stats.rx_dropped++;
+		return;
+	}
+
+	hi3110_hw_rx_frame(spi, buf);
+	if (buf[HI3110_FIFO_WOTIME_TAG_OFF] & HI3110_FIFO_WOTIME_TAG_IDE) {
+		/* IDE is recessive (1), indicating extended 29-bit frame */
+		frame->can_id = CAN_EFF_FLAG;
+		frame->can_id |=
+		 (buf[HI3110_FIFO_WOTIME_ID_OFF] << 21) |
+		 (((buf[HI3110_FIFO_WOTIME_ID_OFF + 1] & 0xE0) >> 5) << 18) |
+		 ((buf[HI3110_FIFO_WOTIME_ID_OFF + 1] & 0x07) << 15) |
+		 (buf[HI3110_FIFO_WOTIME_ID_OFF + 2] << 7) |
+		 (buf[HI3110_FIFO_WOTIME_ID_OFF + 3] >> 1);
+	} else {
+		/* IDE is dominant (0), frame indicating standard 11-bit */
+		frame->can_id =
+			(buf[HI3110_FIFO_WOTIME_ID_OFF] << 3) |
+			((buf[HI3110_FIFO_WOTIME_ID_OFF + 1] & 0xE0) >> 5);
+	}
+
+	if (buf[HI3110_FIFO_WOTIME_ID_OFF + 3] & HI3110_FIFO_WOTIME_ID_RTR) {
+		/* RTR is recessive (1), indicating remote request frame */
+		frame->can_id |= CAN_RTR_FLAG;
+	}
+
+	/* Data length */
+	frame->can_dlc = get_can_dlc(buf[HI3110_FIFO_WOTIME_DLC_OFF] & 0x0F);
+	memcpy(frame->data, buf + HI3110_FIFO_WOTIME_DAT_OFF, frame->can_dlc);
+
+	priv->net->stats.rx_packets++;
+	priv->net->stats.rx_bytes += frame->can_dlc;
+
+	can_led_event(priv->net, CAN_LED_EVENT_RX);
+
+	netif_rx_ni(skb);
+}
+
+static void hi3110_hw_sleep(struct spi_device *spi)
+{
+	hi3110_write(spi, HI3110_WRITE_CTRL0, HI3110_CTRL0_SLEEP_MODE);
+}
+
+static netdev_tx_t hi3110_hard_start_xmit(struct sk_buff *skb,
+					  struct net_device *net)
+{
+	struct hi3110_priv *priv = netdev_priv(net);
+	struct spi_device *spi = priv->spi;
+
+	if (priv->tx_skb || priv->tx_len) {
+		dev_warn(&spi->dev, "hard_xmit called while tx busy\n");
+		return NETDEV_TX_BUSY;
+	}
+
+	if (can_dropped_invalid_skb(net, skb))
+		return NETDEV_TX_OK;
+
+	netif_stop_queue(net);
+	priv->tx_skb = skb;
+	queue_work(priv->wq, &priv->tx_work);
+
+	return NETDEV_TX_OK;
+}
+
+static int hi3110_do_set_mode(struct net_device *net, enum can_mode mode)
+{
+	struct hi3110_priv *priv = netdev_priv(net);
+
+	switch (mode) {
+	case CAN_MODE_START:
+		hi3110_clean(net);
+		/* We have to delay work since SPI I/O may sleep */
+		priv->can.state = CAN_STATE_ERROR_ACTIVE;
+		priv->restart_tx = 1;
+		if (priv->can.restart_ms == 0)
+			priv->after_suspend = AFTER_SUSPEND_RESTART;
+		queue_work(priv->wq, &priv->restart_work);
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static int hi3110_set_normal_mode(struct spi_device *spi)
+{
+	struct hi3110_priv *priv = spi_get_drvdata(spi);
+	u8 reg;
+
+	hi3110_write(spi, HI3110_WRITE_INTE, HI3110_INT_BUSERR |
+		     HI3110_INT_RXFIFO | HI3110_INT_TXCPLT);
+
+	/* Enable TX */
+	hi3110_write(spi, HI3110_WRITE_CTRL1, HI3110_CTRL1_TXEN);
+
+	if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) {
+		/* Put device into loopback mode */
+		hi3110_write(spi, HI3110_WRITE_CTRL0,
+			     HI3110_CTRL0_LOOPBACK_MODE);
+	} else if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) {
+		/* Put device into listen-only mode */
+		hi3110_write(spi, HI3110_WRITE_CTRL0,
+			     HI3110_CTRL0_MONITOR_MODE);
+	} else {
+		/* Put device into normal mode */
+		hi3110_write(spi, HI3110_WRITE_CTRL0,
+			     HI3110_CTRL0_NORMAL_MODE);
+
+		/* Wait for the device to enter normal mode */
+		mdelay(HI3110_OST_DELAY_MS);
+		reg = hi3110_read(spi, HI3110_READ_CTRL0);
+		if ((reg & HI3110_CTRL0_MODE_MASK) != HI3110_CTRL0_NORMAL_MODE)
+			return -EBUSY;
+	}
+	priv->can.state = CAN_STATE_ERROR_ACTIVE;
+	return 0;
+}
+
+static int hi3110_do_set_bittiming(struct net_device *net)
+{
+	struct hi3110_priv *priv = netdev_priv(net);
+	struct can_bittiming *bt = &priv->can.bittiming;
+	struct spi_device *spi = priv->spi;
+
+	hi3110_write(spi, HI3110_WRITE_BTR0,
+		     ((bt->sjw - 1) << HI3110_BTR0_SJW_SHIFT) |
+		     ((bt->brp - 1) << HI3110_BTR0_BRP_SHIFT));
+
+	hi3110_write(spi, HI3110_WRITE_BTR1,
+		     (priv->can.ctrlmode &
+		     CAN_CTRLMODE_3_SAMPLES ?
+		     HI3110_BTR1_SAMP_3PERBIT : HI3110_BTR1_SAMP_1PERBIT) |
+		     ((bt->phase_seg1 + bt->prop_seg - 1)
+		     << HI3110_BTR1_TSEG1_SHIFT) |
+		     ((bt->phase_seg2 - 1) << HI3110_BTR1_TSEG2_SHIFT));
+
+	dev_dbg(&spi->dev, "BT: 0x%02x 0x%02x\n",
+		hi3110_read(spi, HI3110_READ_BTR0),
+		hi3110_read(spi, HI3110_READ_BTR1));
+
+	return 0;
+}
+
+static int hi3110_setup(struct net_device *net, struct hi3110_priv *priv,
+			struct spi_device *spi)
+{
+	hi3110_do_set_bittiming(net);
+	return 0;
+}
+
+static int hi3110_hw_reset(struct spi_device *spi)
+{
+	u8 reg;
+	int ret;
+
+	/* Wait for oscillator startup timer after power up */
+	mdelay(HI3110_OST_DELAY_MS);
+
+	ret = hi3110_cmd(spi, HI3110_MASTER_RESET);
+	if (ret)
+		return ret;
+
+	/* Wait for oscillator startup timer after reset */
+	mdelay(HI3110_OST_DELAY_MS);
+
+	reg = hi3110_read(spi, HI3110_READ_CTRL0);
+	if ((reg & HI3110_CTRL0_MODE_MASK) != HI3110_CTRL0_INIT_MODE)
+		return -ENODEV;
+
+	/* As per the datasheet it appears the error flags are
+	 * not cleared on reset. Explicitly clear them by performing a read
+	 */
+	hi3110_read(spi, HI3110_READ_ERR);
+
+	return 0;
+}
+
+static int hi3110_hw_probe(struct spi_device *spi)
+{
+	u8 statf;
+
+	hi3110_hw_reset(spi);
+
+	/* Confirm correct operation by checking against reset values
+	 * in datasheet
+	 */
+	statf = hi3110_read(spi, HI3110_READ_STATF);
+
+	dev_dbg(&spi->dev, "statf: %02X\n", statf);
+
+	if (statf != 0x82)
+		return -ENODEV;
+
+	return 0;
+}
+
+static int hi3110_power_enable(struct regulator *reg, int enable)
+{
+	if (IS_ERR_OR_NULL(reg))
+		return 0;
+
+	if (enable)
+		return regulator_enable(reg);
+	else
+		return regulator_disable(reg);
+}
+
+static void hi3110_open_clean(struct net_device *net)
+{
+	struct hi3110_priv *priv = netdev_priv(net);
+	struct spi_device *spi = priv->spi;
+
+	free_irq(spi->irq, priv);
+	hi3110_hw_sleep(spi);
+	hi3110_power_enable(priv->transceiver, 0);
+	close_candev(net);
+}
+
+static int hi3110_stop(struct net_device *net)
+{
+	struct hi3110_priv *priv = netdev_priv(net);
+	struct spi_device *spi = priv->spi;
+
+	close_candev(net);
+
+	priv->force_quit = 1;
+	free_irq(spi->irq, priv);
+	destroy_workqueue(priv->wq);
+	priv->wq = NULL;
+
+	mutex_lock(&priv->hi3110_lock);
+
+	/* Disable transmit, interrupts and clear flags */
+	hi3110_write(spi, HI3110_WRITE_CTRL1, 0x0);
+	hi3110_write(spi, HI3110_WRITE_INTE, 0x0);
+	hi3110_read(spi, HI3110_READ_INTF);
+
+	hi3110_clean(net);
+
+	hi3110_hw_sleep(spi);
+
+	hi3110_power_enable(priv->transceiver, 0);
+
+	priv->can.state = CAN_STATE_STOPPED;
+
+	mutex_unlock(&priv->hi3110_lock);
+
+	can_led_event(net, CAN_LED_EVENT_STOP);
+
+	return 0;
+}
+
+static void hi3110_error_skb(struct net_device *net, int can_id,
+			     int data1, int data2)
+{
+	struct sk_buff *skb;
+	struct can_frame *frame;
+
+	skb = alloc_can_err_skb(net, &frame);
+	if (skb) {
+		frame->can_id |= can_id;
+		frame->data[1] = data1;
+		frame->data[2] = data2;
+		netif_rx_ni(skb);
+	} else {
+		netdev_err(net, "cannot allocate error skb\n");
+	}
+}
+
+static void hi3110_tx_work_handler(struct work_struct *ws)
+{
+	struct hi3110_priv *priv = container_of(ws, struct hi3110_priv,
+						 tx_work);
+	struct spi_device *spi = priv->spi;
+	struct net_device *net = priv->net;
+	struct can_frame *frame;
+
+	mutex_lock(&priv->hi3110_lock);
+	if (priv->tx_skb) {
+		if (priv->can.state == CAN_STATE_BUS_OFF) {
+			hi3110_clean(net);
+		} else {
+			frame = (struct can_frame *)priv->tx_skb->data;
+
+			if (frame->can_dlc > CAN_FRAME_MAX_DATA_LEN)
+				frame->can_dlc = CAN_FRAME_MAX_DATA_LEN;
+			hi3110_hw_tx(spi, frame);
+			priv->tx_len = 1 + frame->can_dlc;
+			can_put_echo_skb(priv->tx_skb, net, 0);
+			priv->tx_skb = NULL;
+		}
+	}
+	mutex_unlock(&priv->hi3110_lock);
+}
+
+static void hi3110_restart_work_handler(struct work_struct *ws)
+{
+	struct hi3110_priv *priv = container_of(ws, struct hi3110_priv,
+						 restart_work);
+	struct spi_device *spi = priv->spi;
+	struct net_device *net = priv->net;
+
+	mutex_lock(&priv->hi3110_lock);
+	if (priv->after_suspend) {
+		hi3110_hw_reset(spi);
+		hi3110_setup(net, priv, spi);
+		if (priv->after_suspend & AFTER_SUSPEND_RESTART) {
+			hi3110_set_normal_mode(spi);
+		} else if (priv->after_suspend & AFTER_SUSPEND_UP) {
+			netif_device_attach(net);
+			hi3110_clean(net);
+			hi3110_set_normal_mode(spi);
+			netif_wake_queue(net);
+		} else {
+			hi3110_hw_sleep(spi);
+		}
+		priv->after_suspend = 0;
+		priv->force_quit = 0;
+	}
+
+	if (priv->restart_tx) {
+		priv->restart_tx = 0;
+		hi3110_clean(net);
+		netif_wake_queue(net);
+		hi3110_error_skb(net, CAN_ERR_RESTARTED, 0, 0);
+	}
+	mutex_unlock(&priv->hi3110_lock);
+}
+
+static irqreturn_t hi3110_can_ist(int irq, void *dev_id)
+{
+	struct hi3110_priv *priv = dev_id;
+	struct spi_device *spi = priv->spi;
+	struct net_device *net = priv->net;
+
+	mutex_lock(&priv->hi3110_lock);
+
+	while (!priv->force_quit) {
+		enum can_state new_state;
+		u8 intf;
+		u8 eflag;
+		int can_id = 0, data1 = 0, data2 = 0;
+
+		while (!(HI3110_STAT_RXFMTY &
+			hi3110_read(spi, HI3110_READ_STATF))) {
+			hi3110_hw_rx(spi);
+		};
+
+		intf = hi3110_read(spi, HI3110_READ_INTF);
+		eflag = hi3110_read(spi, HI3110_READ_ERR);
+		/* Update can state */
+		if (eflag & HI3110_ERR_BUSOFF) {
+			new_state = CAN_STATE_BUS_OFF;
+			can_id |= CAN_ERR_BUSOFF;
+		} else if (eflag & HI3110_ERR_TXERRP) {
+			new_state = CAN_STATE_ERROR_PASSIVE;
+			can_id |= CAN_ERR_CRTL;
+			data1 |= CAN_ERR_CRTL_TX_PASSIVE;
+		} else if (eflag & HI3110_ERR_RXERRP) {
+			new_state = CAN_STATE_ERROR_PASSIVE;
+			can_id |= CAN_ERR_CRTL;
+			data1 |= CAN_ERR_CRTL_RX_PASSIVE;
+		} else {
+			new_state = CAN_STATE_ERROR_ACTIVE;
+		}
+
+		/* Check for protocol errors */
+		if (eflag & HI3110_ERR_PROTOCOL_MASK) {
+			can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
+			priv->can.can_stats.bus_error++;
+			priv->net->stats.rx_errors++;
+			if (eflag & HI3110_ERR_BITERR)
+				data2 |= CAN_ERR_PROT_BIT;
+			else if (eflag & HI3110_ERR_FRMERR)
+				data2 |= CAN_ERR_PROT_FORM;
+			else if (eflag & HI3110_ERR_STUFERR)
+				data2 |= CAN_ERR_PROT_STUFF;
+			else
+				data2 |= CAN_ERR_PROT_UNSPEC;
+		}
+
+		/* Update can state statistics */
+		switch (priv->can.state) {
+		case CAN_STATE_ERROR_ACTIVE:
+			if (new_state >= CAN_STATE_ERROR_WARNING &&
+			    new_state <= CAN_STATE_BUS_OFF)
+				priv->can.can_stats.error_warning++;
+		/* fallthrough */
+		case CAN_STATE_ERROR_WARNING:
+			if (new_state >= CAN_STATE_ERROR_PASSIVE &&
+			    new_state <= CAN_STATE_BUS_OFF)
+				priv->can.can_stats.error_passive++;
+			break;
+		default:
+			break;
+		}
+		priv->can.state = new_state;
+
+		if (intf & HI3110_INT_BUSERR) {
+			/* Note: HI3110 Does report overflow errors */
+			hi3110_error_skb(net, can_id, data1, data2);
+		}
+
+		if (priv->can.state == CAN_STATE_BUS_OFF) {
+			if (priv->can.restart_ms == 0) {
+				priv->force_quit = 1;
+				priv->can.can_stats.bus_off++;
+				can_bus_off(net);
+				hi3110_hw_sleep(spi);
+				break;
+			}
+		}
+
+		if (intf == 0)
+			break;
+
+		if (intf & HI3110_INT_TXCPLT) {
+			net->stats.tx_packets++;
+			net->stats.tx_bytes += priv->tx_len - 1;
+			can_led_event(net, CAN_LED_EVENT_TX);
+			if (priv->tx_len) {
+				can_get_echo_skb(net, 0);
+				priv->tx_len = 0;
+			}
+			netif_wake_queue(net);
+		}
+	}
+	mutex_unlock(&priv->hi3110_lock);
+	return IRQ_HANDLED;
+}
+
+static int hi3110_open(struct net_device *net)
+{
+	struct hi3110_priv *priv = netdev_priv(net);
+	struct spi_device *spi = priv->spi;
+	unsigned long flags = IRQF_ONESHOT | IRQF_TRIGGER_RISING;
+	int ret;
+
+	ret = open_candev(net);
+	if (ret) {
+		dev_err(&spi->dev, "unable to set initial baudrate!\n");
+		return ret;
+	}
+
+	mutex_lock(&priv->hi3110_lock);
+	hi3110_power_enable(priv->transceiver, 1);
+
+	priv->force_quit = 0;
+	priv->tx_skb = NULL;
+	priv->tx_len = 0;
+
+	ret = request_threaded_irq(spi->irq, NULL, hi3110_can_ist,
+				   flags, DEVICE_NAME, priv);
+	if (ret) {
+		dev_err(&spi->dev, "failed to acquire irq %d\n", spi->irq);
+		hi3110_power_enable(priv->transceiver, 0);
+		close_candev(net);
+		goto open_unlock;
+	}
+
+	priv->wq = alloc_workqueue("hi3110_wq", WQ_FREEZABLE | WQ_MEM_RECLAIM,
+			   0);
+	INIT_WORK(&priv->tx_work, hi3110_tx_work_handler);
+	INIT_WORK(&priv->restart_work, hi3110_restart_work_handler);
+
+	ret = hi3110_hw_reset(spi);
+	if (ret) {
+		hi3110_open_clean(net);
+		goto open_unlock;
+	}
+	ret = hi3110_setup(net, priv, spi);
+	if (ret) {
+		hi3110_open_clean(net);
+		goto open_unlock;
+	}
+	ret = hi3110_set_normal_mode(spi);
+	if (ret) {
+		hi3110_open_clean(net);
+		goto open_unlock;
+	}
+	can_led_event(net, CAN_LED_EVENT_OPEN);
+	netif_wake_queue(net);
+
+open_unlock:
+	mutex_unlock(&priv->hi3110_lock);
+	return ret;
+}
+
+static const struct net_device_ops hi3110_netdev_ops = {
+	.ndo_open = hi3110_open,
+	.ndo_stop = hi3110_stop,
+	.ndo_start_xmit = hi3110_hard_start_xmit,
+};
+
+static const struct of_device_id hi3110_of_match[] = {
+	{
+		.compatible	= "holt,hi3110",
+		.data		= (void *)CAN_HI3110_HI3110,
+	},
+	{ }
+};
+MODULE_DEVICE_TABLE(of, hi3110_of_match);
+
+static const struct spi_device_id hi3110_id_table[] = {
+	{
+		.name		= "hi3110",
+		.driver_data	= (kernel_ulong_t)CAN_HI3110_HI3110,
+	},
+	{ }
+};
+MODULE_DEVICE_TABLE(spi, hi3110_id_table);
+
+static int hi3110_can_probe(struct spi_device *spi)
+{
+	const struct of_device_id *of_id = of_match_device(hi3110_of_match,
+							   &spi->dev);
+	struct net_device *net;
+	struct hi3110_priv *priv;
+	struct clk *clk;
+	int freq, ret;
+
+	clk = devm_clk_get(&spi->dev, NULL);
+	if (IS_ERR(clk)) {
+		dev_err(&spi->dev, "no CAN clock source defined\n");
+		return PTR_ERR(clk);
+	}
+	freq = clk_get_rate(clk);
+
+	/* Sanity check */
+	if (freq > 40000000)
+		return -ERANGE;
+
+	/* Allocate can/net device */
+	net = alloc_candev(sizeof(struct hi3110_priv), TX_ECHO_SKB_MAX);
+	if (!net)
+		return -ENOMEM;
+
+	if (!IS_ERR(clk)) {
+		ret = clk_prepare_enable(clk);
+		if (ret)
+			goto out_free;
+	}
+
+	net->netdev_ops = &hi3110_netdev_ops;
+	net->flags |= IFF_ECHO;
+
+	priv = netdev_priv(net);
+	priv->can.bittiming_const = &hi3110_bittiming_const;
+	priv->can.do_set_mode = hi3110_do_set_mode;
+	priv->can.clock.freq = freq / 2;
+	priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
+		CAN_CTRLMODE_LOOPBACK | CAN_CTRLMODE_LISTENONLY;
+	if (of_id)
+		priv->model = (enum hi3110_model)of_id->data;
+	else
+		priv->model = spi_get_device_id(spi)->driver_data;
+	priv->net = net;
+	priv->clk = clk;
+
+	spi_set_drvdata(spi, priv);
+
+	/* Configure the SPI bus */
+	spi->bits_per_word = 8;
+	ret = spi_setup(spi);
+	if (ret)
+		goto out_clk;
+
+	priv->power = devm_regulator_get_optional(&spi->dev, "vdd");
+	priv->transceiver = devm_regulator_get_optional(&spi->dev, "xceiver");
+	if ((PTR_ERR(priv->power) == -EPROBE_DEFER) ||
+	    (PTR_ERR(priv->transceiver) == -EPROBE_DEFER)) {
+		ret = -EPROBE_DEFER;
+		goto out_clk;
+	}
+
+	ret = hi3110_power_enable(priv->power, 1);
+	if (ret)
+		goto out_clk;
+
+	priv->spi = spi;
+	mutex_init(&priv->hi3110_lock);
+
+	/* If requested, allocate DMA buffers */
+	if (hi3110_enable_dma) {
+		spi->dev.coherent_dma_mask = ~0;
+
+		/* Minimum coherent DMA allocation is PAGE_SIZE, so allocate
+		 * that much and share it between Tx and Rx DMA buffers.
+		 */
+		priv->spi_tx_buf = dmam_alloc_coherent(&spi->dev,
+						      PAGE_SIZE,
+						      &priv->spi_tx_dma,
+						      GFP_DMA);
+
+		if (priv->spi_tx_buf) {
+			priv->spi_rx_buf = (priv->spi_tx_buf + (PAGE_SIZE / 2));
+			priv->spi_rx_dma = (dma_addr_t)(priv->spi_tx_dma +
+							(PAGE_SIZE / 2));
+		} else {
+			/* Fall back to non-DMA */
+			hi3110_enable_dma = 0;
+		}
+	}
+
+	/* Allocate non-DMA buffers */
+	if (!hi3110_enable_dma) {
+		priv->spi_tx_buf = devm_kzalloc(&spi->dev, RX_BUF_LEN,
+				GFP_KERNEL);
+		if (!priv->spi_tx_buf) {
+			ret = -ENOMEM;
+			goto error_probe;
+		}
+		priv->spi_rx_buf = devm_kzalloc(&spi->dev, RX_BUF_LEN,
+				GFP_KERNEL);
+
+		if (!priv->spi_rx_buf) {
+			ret = -ENOMEM;
+			goto error_probe;
+		}
+	}
+
+	SET_NETDEV_DEV(net, &spi->dev);
+
+	ret = hi3110_hw_probe(spi);
+	if (ret) {
+		if (ret == -ENODEV)
+			dev_err(&spi->dev, "Cannot initialize %x. Wrong wiring?\n",
+				priv->model);
+		goto error_probe;
+	}
+	hi3110_hw_sleep(spi);
+
+	ret = register_candev(net);
+	if (ret)
+		goto error_probe;
+
+	devm_can_led_init(net);
+	netdev_info(net, "%x successfully initialized.\n", priv->model);
+
+	return 0;
+
+error_probe:
+	hi3110_power_enable(priv->power, 0);
+
+out_clk:
+	if (!IS_ERR(clk))
+		clk_disable_unprepare(clk);
+
+out_free:
+	free_candev(net);
+
+	dev_err(&spi->dev, "Probe failed, err=%d\n", -ret);
+	return ret;
+}
+
+static int hi3110_can_remove(struct spi_device *spi)
+{
+	struct hi3110_priv *priv = spi_get_drvdata(spi);
+	struct net_device *net = priv->net;
+
+	unregister_candev(net);
+
+	hi3110_power_enable(priv->power, 0);
+
+	if (!IS_ERR(priv->clk))
+		clk_disable_unprepare(priv->clk);
+
+	free_candev(net);
+
+	return 0;
+}
+
+static int __maybe_unused hi3110_can_suspend(struct device *dev)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	struct hi3110_priv *priv = spi_get_drvdata(spi);
+	struct net_device *net = priv->net;
+
+	priv->force_quit = 1;
+	disable_irq(spi->irq);
+
+	/* Note: at this point neither IST nor workqueues are running.
+	 * open/stop cannot be called anyway so locking is not needed
+	 */
+	if (netif_running(net)) {
+		netif_device_detach(net);
+
+		hi3110_hw_sleep(spi);
+		hi3110_power_enable(priv->transceiver, 0);
+		priv->after_suspend = AFTER_SUSPEND_UP;
+	} else {
+		priv->after_suspend = AFTER_SUSPEND_DOWN;
+	}
+
+	if (!IS_ERR_OR_NULL(priv->power)) {
+		regulator_disable(priv->power);
+		priv->after_suspend |= AFTER_SUSPEND_POWER;
+	}
+
+	return 0;
+}
+
+static int __maybe_unused hi3110_can_resume(struct device *dev)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	struct hi3110_priv *priv = spi_get_drvdata(spi);
+
+	if (priv->after_suspend & AFTER_SUSPEND_POWER)
+		hi3110_power_enable(priv->power, 1);
+
+	if (priv->after_suspend & AFTER_SUSPEND_UP) {
+		hi3110_power_enable(priv->transceiver, 1);
+		queue_work(priv->wq, &priv->restart_work);
+	} else {
+		priv->after_suspend = 0;
+	}
+
+	priv->force_quit = 0;
+	enable_irq(spi->irq);
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(hi3110_can_pm_ops, hi3110_can_suspend,
+	hi3110_can_resume);
+
+static struct spi_driver hi3110_can_driver = {
+	.driver = {
+		.name = DEVICE_NAME,
+		.of_match_table = hi3110_of_match,
+		.pm = &hi3110_can_pm_ops,
+	},
+	.id_table = hi3110_id_table,
+	.probe = hi3110_can_probe,
+	.remove = hi3110_can_remove,
+};
+
+module_spi_driver(hi3110_can_driver);
+
+MODULE_AUTHOR("Akshay Bhat <akshay.bhat@timesys.com>");
+MODULE_AUTHOR("Casey Fitzpatrick <casey.fitzpatrick@timesys.com>");
+MODULE_DESCRIPTION("Holt HI-3110 CAN driver");
+MODULE_LICENSE("GPL v2");
-- 
2.8.1

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox