1. 21 2月, 2018 3 次提交
    • N
      net: stmmac: WARN if tx_skbuff entries are reused before cleared · b4c9784c
      Niklas Cassel 提交于
      The current code assumes that a tx_skbuff entry has been cleared
      by stmmac_tx_clean() before stmmac_xmit()/stmmac_tso_xmit()
      assigns a new skb to that entry. However, since we never check
      the current value before overwriting it, it is theoretically
      possible that a non-NULL value is overwritten.
      
      Add WARN_ONs to verify that each entry in tx_skbuff is NULL
      before it is assigned a new value.
      Signed-off-by: NNiklas Cassel <niklas.cassel@axis.com>
      Signed-off-by: NDavid S. Miller <davem@davemloft.net>
      b4c9784c
    • N
      net: stmmac: do not clear tx_skbuff entries in stmmac_xmit()/stmmac_tso_xmit() · f66b533d
      Niklas Cassel 提交于
      tx_skbuff is initialized to NULL in init_dma_tx_desc_rings(), which is
      called from ndo_open().
      
      stmmac_tx_clean() frees any non-NULL skb, and sets the tx_skbuff
      entry to NULL. Hence, there is no need to set skbuff entries to NULL
      in stmmac_xmit()/stmmac_tso_xmit(), and doing so falsely gives the
      reader the impression that it is needed.
      Do not clear tx_skbuff entries in stmmac_xmit()/stmmac_tso_xmit().
      Signed-off-by: NNiklas Cassel <niklas.cassel@axis.com>
      Signed-off-by: NDavid S. Miller <davem@davemloft.net>
      f66b533d
    • N
      net: stmmac: set MSS for each tx DMA channel · 8d212a9e
      Niklas Cassel 提交于
      The DMA engine in dwmac4 can segment a large TSO packet to several
      smaller packets of (max) size Maximum Segment Size (MSS).
      
      The DMA engine fetches and saves the MSS via a context descriptor.
      
      This context decriptor has to be provided to each tx DMA channel.
      To ensure that this is done, move struct member mss from stmmac_priv
      to stmmac_tx_queue.
      
      stmmac_reset_queues_param() now also resets mss, together with other
      queue parameters, so reset of mss value can be removed from
      stmmac_resume().
      
      init_dma_tx_desc_rings() now also resets mss, together with other
      queue parameters, so reset of mss value can be removed from
      stmmac_open().
      
      This fixes tx queue timeouts for dwmac4, with DT property
      snps,tx-queues-to-use > 1, when running iperf3 with multiple threads.
      
      Fixes: ce736788 ("net: stmmac: adding multiple buffers for TX")
      Signed-off-by: NNiklas Cassel <niklas.cassel@axis.com>
      Signed-off-by: NDavid S. Miller <davem@davemloft.net>
      8d212a9e
  2. 23 1月, 2018 1 次提交
    • F
      net: stmmac: Fix reception of Broadcom switches tags · 8cad443e
      Florian Fainelli 提交于
      Broadcom tags inserted by Broadcom switches put a 4 byte header after
      the MAC SA and before the EtherType, which may look like some sort of 0
      length LLC/SNAP packet (tcpdump and wireshark do think that way). With
      ACS enabled in stmmac the packets were truncated to 8 bytes on
      reception, whereas clearing this bit allowed normal reception to occur.
      
      In order to make that possible, we need to pass a net_device argument to
      the different core_init() functions and we are dependent on the Broadcom
      tagger padding packets correctly (which it now does). To be as little
      invasive as possible, this is only done for gmac1000 when the network
      device is DSA-enabled (netdev_uses_dsa() returns true).
      Signed-off-by: NFlorian Fainelli <f.fainelli@gmail.com>
      Acked-by: NGiuseppe Cavallaro <peppe.cavallaro@st.com>
      Signed-off-by: NDavid S. Miller <davem@davemloft.net>
      8cad443e
  3. 05 1月, 2018 1 次提交
  4. 03 1月, 2018 1 次提交
  5. 20 12月, 2017 1 次提交
  6. 09 12月, 2017 1 次提交
    • N
      net: stmmac: fix broken dma_interrupt handling for multi-queues · 5a6a0445
      Niklas Cassel 提交于
      There is nothing that says that number of TX queues == number of RX
      queues. E.g. the ARTPEC-6 SoC has 2 TX queues and 1 RX queue.
      
      This code is obviously wrong:
      for (chan = 0; chan < tx_channel_count; chan++) {
          struct stmmac_rx_queue *rx_q = &priv->rx_queue[chan];
      
      priv->rx_queue has size MTL_MAX_RX_QUEUES, so this will send an
      uninitialized napi_struct to __napi_schedule(), causing us to
      crash in net_rx_action(), because napi_struct->poll is zero.
      
      [12846.759880] Unable to handle kernel NULL pointer dereference at virtual address 00000000
      [12846.768014] pgd = (ptrval)
      [12846.770742] [00000000] *pgd=39ec7831, *pte=00000000, *ppte=00000000
      [12846.777023] Internal error: Oops: 80000007 [#1] PREEMPT SMP ARM
      [12846.782942] Modules linked in:
      [12846.785998] CPU: 0 PID: 161 Comm: dropbear Not tainted 4.15.0-rc2-00285-gf5fb5f2f39a7 #36
      [12846.794177] Hardware name: Axis ARTPEC-6 Platform
      [12846.798879] task: (ptrval) task.stack: (ptrval)
      [12846.803407] PC is at 0x0
      [12846.805942] LR is at net_rx_action+0x274/0x43c
      [12846.810383] pc : [<00000000>]    lr : [<80bff064>]    psr: 200e0113
      [12846.816648] sp : b90d9ae8  ip : b90d9ae8  fp : b90d9b44
      [12846.821871] r10: 00000008  r9 : 0013250e  r8 : 00000100
      [12846.827094] r7 : 0000012c  r6 : 00000000  r5 : 00000001  r4 : bac84900
      [12846.833619] r3 : 00000000  r2 : b90d9b08  r1 : 00000000  r0 : bac84900
      
      Since each DMA channel can be used for rx and tx simultaneously,
      the current code should probably be rewritten so that napi_struct is
      embedded in a new struct stmmac_channel.
      That way, stmmac_poll() can call stmmac_tx_clean() on just the tx queue
      where we got the IRQ, instead of looping through all tx queues.
      This is also how the xgbe driver does it (another driver for this IP).
      
      Fixes: c22a3f48 ("net: stmmac: adding multiple napi mechanism")
      Signed-off-by: NNiklas Cassel <niklas.cassel@axis.com>
      Signed-off-by: NDavid S. Miller <davem@davemloft.net>
      5a6a0445
  7. 03 12月, 2017 1 次提交
    • L
      stmmac: reset last TSO segment size after device open · 45ab4b13
      Lars Persson 提交于
      The mss variable tracks the last max segment size sent to the TSO
      engine. We do not update the hardware as long as we receive skb:s with
      the same value in gso_size.
      
      During a network device down/up cycle (mapped to stmmac_release() and
      stmmac_open() callbacks) we issue a reset to the hardware and it
      forgets the setting for mss. However we did not zero out our mss
      variable so the next transmission of a gso packet happens with an
      undefined hardware setting.
      
      This triggers a hang in the TSO engine and eventuelly the netdev
      watchdog will bark.
      
      Fixes: f748be53 ("stmmac: support new GMAC4")
      Signed-off-by: NLars Persson <larper@axis.com>
      Signed-off-by: NDavid S. Miller <davem@davemloft.net>
      45ab4b13
  8. 22 11月, 2017 1 次提交
    • K
      treewide: setup_timer() -> timer_setup() · e99e88a9
      Kees Cook 提交于
      This converts all remaining cases of the old setup_timer() API into using
      timer_setup(), where the callback argument is the structure already
      holding the struct timer_list. These should have no behavioral changes,
      since they just change which pointer is passed into the callback with
      the same available pointers after conversion. It handles the following
      examples, in addition to some other variations.
      
      Casting from unsigned long:
      
          void my_callback(unsigned long data)
          {
              struct something *ptr = (struct something *)data;
          ...
          }
          ...
          setup_timer(&ptr->my_timer, my_callback, ptr);
      
      and forced object casts:
      
          void my_callback(struct something *ptr)
          {
          ...
          }
          ...
          setup_timer(&ptr->my_timer, my_callback, (unsigned long)ptr);
      
      become:
      
          void my_callback(struct timer_list *t)
          {
              struct something *ptr = from_timer(ptr, t, my_timer);
          ...
          }
          ...
          timer_setup(&ptr->my_timer, my_callback, 0);
      
      Direct function assignments:
      
          void my_callback(unsigned long data)
          {
              struct something *ptr = (struct something *)data;
          ...
          }
          ...
          ptr->my_timer.function = my_callback;
      
      have a temporary cast added, along with converting the args:
      
          void my_callback(struct timer_list *t)
          {
              struct something *ptr = from_timer(ptr, t, my_timer);
          ...
          }
          ...
          ptr->my_timer.function = (TIMER_FUNC_TYPE)my_callback;
      
      And finally, callbacks without a data assignment:
      
          void my_callback(unsigned long data)
          {
          ...
          }
          ...
          setup_timer(&ptr->my_timer, my_callback, 0);
      
      have their argument renamed to verify they're unused during conversion:
      
          void my_callback(struct timer_list *unused)
          {
          ...
          }
          ...
          timer_setup(&ptr->my_timer, my_callback, 0);
      
      The conversion is done with the following Coccinelle script:
      
      spatch --very-quiet --all-includes --include-headers \
      	-I ./arch/x86/include -I ./arch/x86/include/generated \
      	-I ./include -I ./arch/x86/include/uapi \
      	-I ./arch/x86/include/generated/uapi -I ./include/uapi \
      	-I ./include/generated/uapi --include ./include/linux/kconfig.h \
      	--dir . \
      	--cocci-file ~/src/data/timer_setup.cocci
      
      @fix_address_of@
      expression e;
      @@
      
       setup_timer(
      -&(e)
      +&e
       , ...)
      
      // Update any raw setup_timer() usages that have a NULL callback, but
      // would otherwise match change_timer_function_usage, since the latter
      // will update all function assignments done in the face of a NULL
      // function initialization in setup_timer().
      @change_timer_function_usage_NULL@
      expression _E;
      identifier _timer;
      type _cast_data;
      @@
      
      (
      -setup_timer(&_E->_timer, NULL, _E);
      +timer_setup(&_E->_timer, NULL, 0);
      |
      -setup_timer(&_E->_timer, NULL, (_cast_data)_E);
      +timer_setup(&_E->_timer, NULL, 0);
      |
      -setup_timer(&_E._timer, NULL, &_E);
      +timer_setup(&_E._timer, NULL, 0);
      |
      -setup_timer(&_E._timer, NULL, (_cast_data)&_E);
      +timer_setup(&_E._timer, NULL, 0);
      )
      
      @change_timer_function_usage@
      expression _E;
      identifier _timer;
      struct timer_list _stl;
      identifier _callback;
      type _cast_func, _cast_data;
      @@
      
      (
      -setup_timer(&_E->_timer, _callback, _E);
      +timer_setup(&_E->_timer, _callback, 0);
      |
      -setup_timer(&_E->_timer, &_callback, _E);
      +timer_setup(&_E->_timer, _callback, 0);
      |
      -setup_timer(&_E->_timer, _callback, (_cast_data)_E);
      +timer_setup(&_E->_timer, _callback, 0);
      |
      -setup_timer(&_E->_timer, &_callback, (_cast_data)_E);
      +timer_setup(&_E->_timer, _callback, 0);
      |
      -setup_timer(&_E->_timer, (_cast_func)_callback, _E);
      +timer_setup(&_E->_timer, _callback, 0);
      |
      -setup_timer(&_E->_timer, (_cast_func)&_callback, _E);
      +timer_setup(&_E->_timer, _callback, 0);
      |
      -setup_timer(&_E->_timer, (_cast_func)_callback, (_cast_data)_E);
      +timer_setup(&_E->_timer, _callback, 0);
      |
      -setup_timer(&_E->_timer, (_cast_func)&_callback, (_cast_data)_E);
      +timer_setup(&_E->_timer, _callback, 0);
      |
      -setup_timer(&_E._timer, _callback, (_cast_data)_E);
      +timer_setup(&_E._timer, _callback, 0);
      |
      -setup_timer(&_E._timer, _callback, (_cast_data)&_E);
      +timer_setup(&_E._timer, _callback, 0);
      |
      -setup_timer(&_E._timer, &_callback, (_cast_data)_E);
      +timer_setup(&_E._timer, _callback, 0);
      |
      -setup_timer(&_E._timer, &_callback, (_cast_data)&_E);
      +timer_setup(&_E._timer, _callback, 0);
      |
      -setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)_E);
      +timer_setup(&_E._timer, _callback, 0);
      |
      -setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)&_E);
      +timer_setup(&_E._timer, _callback, 0);
      |
      -setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)_E);
      +timer_setup(&_E._timer, _callback, 0);
      |
      -setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)&_E);
      +timer_setup(&_E._timer, _callback, 0);
      |
       _E->_timer@_stl.function = _callback;
      |
       _E->_timer@_stl.function = &_callback;
      |
       _E->_timer@_stl.function = (_cast_func)_callback;
      |
       _E->_timer@_stl.function = (_cast_func)&_callback;
      |
       _E._timer@_stl.function = _callback;
      |
       _E._timer@_stl.function = &_callback;
      |
       _E._timer@_stl.function = (_cast_func)_callback;
      |
       _E._timer@_stl.function = (_cast_func)&_callback;
      )
      
      // callback(unsigned long arg)
      @change_callback_handle_cast
       depends on change_timer_function_usage@
      identifier change_timer_function_usage._callback;
      identifier change_timer_function_usage._timer;
      type _origtype;
      identifier _origarg;
      type _handletype;
      identifier _handle;
      @@
      
       void _callback(
      -_origtype _origarg
      +struct timer_list *t
       )
       {
      (
      	... when != _origarg
      	_handletype *_handle =
      -(_handletype *)_origarg;
      +from_timer(_handle, t, _timer);
      	... when != _origarg
      |
      	... when != _origarg
      	_handletype *_handle =
      -(void *)_origarg;
      +from_timer(_handle, t, _timer);
      	... when != _origarg
      |
      	... when != _origarg
      	_handletype *_handle;
      	... when != _handle
      	_handle =
      -(_handletype *)_origarg;
      +from_timer(_handle, t, _timer);
      	... when != _origarg
      |
      	... when != _origarg
      	_handletype *_handle;
      	... when != _handle
      	_handle =
      -(void *)_origarg;
      +from_timer(_handle, t, _timer);
      	... when != _origarg
      )
       }
      
      // callback(unsigned long arg) without existing variable
      @change_callback_handle_cast_no_arg
       depends on change_timer_function_usage &&
                           !change_callback_handle_cast@
      identifier change_timer_function_usage._callback;
      identifier change_timer_function_usage._timer;
      type _origtype;
      identifier _origarg;
      type _handletype;
      @@
      
       void _callback(
      -_origtype _origarg
      +struct timer_list *t
       )
       {
      +	_handletype *_origarg = from_timer(_origarg, t, _timer);
      +
      	... when != _origarg
      -	(_handletype *)_origarg
      +	_origarg
      	... when != _origarg
       }
      
      // Avoid already converted callbacks.
      @match_callback_converted
       depends on change_timer_function_usage &&
                  !change_callback_handle_cast &&
      	    !change_callback_handle_cast_no_arg@
      identifier change_timer_function_usage._callback;
      identifier t;
      @@
      
       void _callback(struct timer_list *t)
       { ... }
      
      // callback(struct something *handle)
      @change_callback_handle_arg
       depends on change_timer_function_usage &&
      	    !match_callback_converted &&
                  !change_callback_handle_cast &&
                  !change_callback_handle_cast_no_arg@
      identifier change_timer_function_usage._callback;
      identifier change_timer_function_usage._timer;
      type _handletype;
      identifier _handle;
      @@
      
       void _callback(
      -_handletype *_handle
      +struct timer_list *t
       )
       {
      +	_handletype *_handle = from_timer(_handle, t, _timer);
      	...
       }
      
      // If change_callback_handle_arg ran on an empty function, remove
      // the added handler.
      @unchange_callback_handle_arg
       depends on change_timer_function_usage &&
      	    change_callback_handle_arg@
      identifier change_timer_function_usage._callback;
      identifier change_timer_function_usage._timer;
      type _handletype;
      identifier _handle;
      identifier t;
      @@
      
       void _callback(struct timer_list *t)
       {
      -	_handletype *_handle = from_timer(_handle, t, _timer);
       }
      
      // We only want to refactor the setup_timer() data argument if we've found
      // the matching callback. This undoes changes in change_timer_function_usage.
      @unchange_timer_function_usage
       depends on change_timer_function_usage &&
                  !change_callback_handle_cast &&
                  !change_callback_handle_cast_no_arg &&
      	    !change_callback_handle_arg@
      expression change_timer_function_usage._E;
      identifier change_timer_function_usage._timer;
      identifier change_timer_function_usage._callback;
      type change_timer_function_usage._cast_data;
      @@
      
      (
      -timer_setup(&_E->_timer, _callback, 0);
      +setup_timer(&_E->_timer, _callback, (_cast_data)_E);
      |
      -timer_setup(&_E._timer, _callback, 0);
      +setup_timer(&_E._timer, _callback, (_cast_data)&_E);
      )
      
      // If we fixed a callback from a .function assignment, fix the
      // assignment cast now.
      @change_timer_function_assignment
       depends on change_timer_function_usage &&
                  (change_callback_handle_cast ||
                   change_callback_handle_cast_no_arg ||
                   change_callback_handle_arg)@
      expression change_timer_function_usage._E;
      identifier change_timer_function_usage._timer;
      identifier change_timer_function_usage._callback;
      type _cast_func;
      typedef TIMER_FUNC_TYPE;
      @@
      
      (
       _E->_timer.function =
      -_callback
      +(TIMER_FUNC_TYPE)_callback
       ;
      |
       _E->_timer.function =
      -&_callback
      +(TIMER_FUNC_TYPE)_callback
       ;
      |
       _E->_timer.function =
      -(_cast_func)_callback;
      +(TIMER_FUNC_TYPE)_callback
       ;
      |
       _E->_timer.function =
      -(_cast_func)&_callback
      +(TIMER_FUNC_TYPE)_callback
       ;
      |
       _E._timer.function =
      -_callback
      +(TIMER_FUNC_TYPE)_callback
       ;
      |
       _E._timer.function =
      -&_callback;
      +(TIMER_FUNC_TYPE)_callback
       ;
      |
       _E._timer.function =
      -(_cast_func)_callback
      +(TIMER_FUNC_TYPE)_callback
       ;
      |
       _E._timer.function =
      -(_cast_func)&_callback
      +(TIMER_FUNC_TYPE)_callback
       ;
      )
      
      // Sometimes timer functions are called directly. Replace matched args.
      @change_timer_function_calls
       depends on change_timer_function_usage &&
                  (change_callback_handle_cast ||
                   change_callback_handle_cast_no_arg ||
                   change_callback_handle_arg)@
      expression _E;
      identifier change_timer_function_usage._timer;
      identifier change_timer_function_usage._callback;
      type _cast_data;
      @@
      
       _callback(
      (
      -(_cast_data)_E
      +&_E->_timer
      |
      -(_cast_data)&_E
      +&_E._timer
      |
      -_E
      +&_E->_timer
      )
       )
      
      // If a timer has been configured without a data argument, it can be
      // converted without regard to the callback argument, since it is unused.
      @match_timer_function_unused_data@
      expression _E;
      identifier _timer;
      identifier _callback;
      @@
      
      (
      -setup_timer(&_E->_timer, _callback, 0);
      +timer_setup(&_E->_timer, _callback, 0);
      |
      -setup_timer(&_E->_timer, _callback, 0L);
      +timer_setup(&_E->_timer, _callback, 0);
      |
      -setup_timer(&_E->_timer, _callback, 0UL);
      +timer_setup(&_E->_timer, _callback, 0);
      |
      -setup_timer(&_E._timer, _callback, 0);
      +timer_setup(&_E._timer, _callback, 0);
      |
      -setup_timer(&_E._timer, _callback, 0L);
      +timer_setup(&_E._timer, _callback, 0);
      |
      -setup_timer(&_E._timer, _callback, 0UL);
      +timer_setup(&_E._timer, _callback, 0);
      |
      -setup_timer(&_timer, _callback, 0);
      +timer_setup(&_timer, _callback, 0);
      |
      -setup_timer(&_timer, _callback, 0L);
      +timer_setup(&_timer, _callback, 0);
      |
      -setup_timer(&_timer, _callback, 0UL);
      +timer_setup(&_timer, _callback, 0);
      |
      -setup_timer(_timer, _callback, 0);
      +timer_setup(_timer, _callback, 0);
      |
      -setup_timer(_timer, _callback, 0L);
      +timer_setup(_timer, _callback, 0);
      |
      -setup_timer(_timer, _callback, 0UL);
      +timer_setup(_timer, _callback, 0);
      )
      
      @change_callback_unused_data
       depends on match_timer_function_unused_data@
      identifier match_timer_function_unused_data._callback;
      type _origtype;
      identifier _origarg;
      @@
      
       void _callback(
      -_origtype _origarg
      +struct timer_list *unused
       )
       {
      	... when != _origarg
       }
      Signed-off-by: NKees Cook <keescook@chromium.org>
      e99e88a9
  9. 28 10月, 2017 1 次提交
  10. 22 10月, 2017 3 次提交
  11. 15 10月, 2017 2 次提交
  12. 22 9月, 2017 1 次提交
  13. 20 7月, 2017 1 次提交
  14. 12 7月, 2017 3 次提交
  15. 21 6月, 2017 2 次提交
  16. 10 6月, 2017 2 次提交
  17. 07 6月, 2017 3 次提交
  18. 02 6月, 2017 2 次提交
  19. 26 5月, 2017 5 次提交
  20. 22 5月, 2017 2 次提交
  21. 15 5月, 2017 1 次提交
  22. 14 4月, 2017 1 次提交
    • N
      net: stmmac: set total length of the packet to be transmitted in TDES3 · fe6af0e1
      Niklas Cassel 提交于
      Field FL/TPL in register TDES3 is not correctly set on GMAC4.
      TX appears to be functional on GMAC 4.10a even if this field is not set,
      however, to avoid relying on undefined behavior, set the length in TDES3.
      
      The field has a different meaning depending on if the TSE bit in TDES3
      is set or not (TSO). However, regardless of the TSE bit, the field is
      not optional. The field is already set correctly when the TSE bit is set.
      
      Since there is no limit for the number of descriptors that can be
      used for a single packet, the field should be set to the sum of
      the buffers contained in:
      [<desc with First Descriptor bit set> ... <desc n> ...
      <desc with Last Descriptor bit set>], which should be equal to skb->len.
      Signed-off-by: NNiklas Cassel <niklas.cassel@axis.com>
      Acked-by: NGiuseppe Cavallaro <peppe.cavallaro@st.com>
      Signed-off-by: NDavid S. Miller <davem@davemloft.net>
      fe6af0e1
  23. 12 4月, 2017 1 次提交