13 Nov, 2020

1 commit

  • If a user unbinds and re-binds a NC-SI aware driver the kernel will
    attempt to register the netlink interface at runtime. The structure is
    marked __ro_after_init so registration fails spectacularly at this point.

    # echo 1e660000.ethernet > /sys/bus/platform/drivers/ftgmac100/unbind
    # echo 1e660000.ethernet > /sys/bus/platform/drivers/ftgmac100/bind
    ftgmac100 1e660000.ethernet: Read MAC address 52:54:00:12:34:56 from chip
    ftgmac100 1e660000.ethernet: Using NCSI interface
    8] lr : [] psr: 20000153
    sp : 8553bc80 ip : 81406244 fp : 8553bd04
    r10: 8085d12c r9 : 80a8f73c r8 : 85739000
    r7 : 00000017 r6 : 80a8f860 r5 : 80c8ab98 r4 : 80a8f858
    r3 : 00000000 r2 : 00000000 r1 : 81406130 r0 : 00000017
    Flags: nzCv IRQs on FIQs off Mode SVC_32 ISA ARM Segment none
    Control: 00c5387d Table: 85524008 DAC: 00000051
    Process sh (pid: 116, stack limit = 0x1f1988d6)
    ...
    Backtrace:
    [] (genl_register_family) from [] (ncsi_init_netlink+0x20/0x48)
    r10:8085d12c r9:80c8fb0c r8:85739000 r7:00000000 r6:81218000 r5:85739000
    r4:8121c000
    [] (ncsi_init_netlink) from [] (ncsi_register_dev+0x1b0/0x210)
    r5:8121c400 r4:8121c000
    [] (ncsi_register_dev) from [] (ftgmac100_probe+0x6e0/0x778)
    r10:00000004 r9:80950228 r8:8115bc10 r7:8115ab00 r6:9eae2c24 r5:813b6f88
    r4:85739000
    [] (ftgmac100_probe) from [] (platform_drv_probe+0x58/0xa8)
    r9:80c76bb0 r8:00000000 r7:80cd4974 r6:80c76bb0 r5:8115bc10 r4:00000000
    [] (platform_drv_probe) from [] (really_probe+0x204/0x514)
    r7:80cd4974 r6:00000000 r5:80cd4868 r4:8115bc10

    Jakub pointed out that ncsi_register_dev is obviously broken, because
    there is only one family so it would never work if there was more than
    one ncsi netdev.

    Fix the crash by registering the netlink family once on boot, and drop
    the code to unregister it.

    Fixes: 955dc68cb9b2 ("net/ncsi: Add generic netlink family")
    Signed-off-by: Joel Stanley
    Reviewed-by: Samuel Mendoza-Jonas
    Link: https://lore.kernel.org/r/20201112061210.914621-1-joel@jms.id.au
    Signed-off-by: Jakub Kicinski

    Joel Stanley
     

24 Aug, 2020

1 commit

  • Replace the existing /* fall through */ comments and its variants with
    the new pseudo-keyword macro fallthrough[1]. Also, remove unnecessary
    fall-through markings when it is the case.

    [1] https://www.kernel.org/doc/html/v5.7/process/deprecated.html?highlight=fallthrough#implicit-switch-case-fall-through

    Signed-off-by: Gustavo A. R. Silva

    Gustavo A. R. Silva
     

10 Jan, 2020

1 commit

  • Multi host Mellanox cards require MAC affinity to be set
    before receiving any config commands. All config commands
    should also have unicast address for source address in
    command header.

    Adding GMA and SMAF(Set Mac Affinity) for Mellanox card
    and call these in channel probe state machine if it is
    defined in device tree.

    Signed-off-by: Vijay Khemka
    Signed-off-by: David S. Miller

    Vijay Khemka
     

31 Dec, 2019

1 commit

  • gma_flag was set at the time of GMA command request but it should
    only be set after getting successful response. Movinng this flag
    setting in GMA response handler.

    This flag is used mainly for not repeating GMA command once
    received MAC address.

    Signed-off-by: Vijay Khemka
    Reviewed-by: Samuel Mendoza-Jonas
    Signed-off-by: David S. Miller

    Vijay Khemka
     

20 Sep, 2019

1 commit

  • Disabling multicast filtering from NCSI if it is supported. As it
    should not filter any multicast packets. In current code, multicast
    filter is enabled and with an exception of optional field supported
    by device are disabled filtering.

    Mainly I see if goal is to disable filtering for IPV6 packets then let
    it disabled for every other types as well. As we are seeing issues with
    LLDP not working with this enabled filtering. And there are other issues
    with IPV6.

    By Disabling this multicast completely, it is working for both IPV6 as
    well as LLDP.

    Signed-off-by: Vijay Khemka
    Acked-by: Samuel Mendoza-Jonas
    Signed-off-by: Jakub Kicinski

    Vijay Khemka
     

31 May, 2019

1 commit

  • Based on 1 normalized pattern(s):

    this program is free software you can redistribute it and or modify
    it under the terms of the gnu general public license as published by
    the free software foundation either version 2 of the license or at
    your option any later version

    extracted by the scancode license scanner the SPDX license identifier

    GPL-2.0-or-later

    has been chosen to replace the boilerplate/reference in 3029 file(s).

    Signed-off-by: Thomas Gleixner
    Reviewed-by: Allison Randal
    Cc: linux-spdx@vger.kernel.org
    Link: https://lkml.kernel.org/r/20190527070032.746973796@linutronix.de
    Signed-off-by: Greg Kroah-Hartman

    Thomas Gleixner
     

28 Nov, 2018

1 commit

  • This patch adds OEM Mellanox commands and response handling. It also
    defines OEM Get MAC Address handler to get and configure the device.

    ncsi_oem_gma_handler_mlx: This handler send NCSI mellanox command for
    getting mac address.
    ncsi_rsp_handler_oem_mlx: This handles response received for all
    mellanox OEM commands.
    ncsi_rsp_handler_oem_mlx_gma: This handles get mac address response and
    set it to device.

    Signed-off-by: Vijay Khemka
    Signed-off-by: David S. Miller

    Vijay Khemka
     

18 Nov, 2018

6 commits

  • This patch extends the ncsi-netlink interface with two new commands and
    three new attributes to configure multiple packages and/or channels at
    once, and configure specific failover modes.

    NCSI_CMD_SET_PACKAGE mask and NCSI_CMD_SET_CHANNEL_MASK set a whitelist
    of packages or channels allowed to be configured with the
    NCSI_ATTR_PACKAGE_MASK and NCSI_ATTR_CHANNEL_MASK attributes
    respectively. If one of these whitelists is set only packages or
    channels matching the whitelist are considered for the channel queue in
    ncsi_choose_active_channel().

    These commands may also use the NCSI_ATTR_MULTI_FLAG to signal that
    multiple packages or channels may be configured simultaneously. NCSI
    hardware arbitration (HWA) must be available in order to enable
    multi-package mode. Multi-channel mode is always available.

    If the NCSI_ATTR_CHANNEL_ID attribute is present in the
    NCSI_CMD_SET_CHANNEL_MASK command the it sets the preferred channel as
    with the NCSI_CMD_SET_INTERFACE command. The combination of preferred
    channel and channel whitelist defines a primary channel and the allowed
    failover channels.
    If the NCSI_ATTR_MULTI_FLAG attribute is also present then the preferred
    channel is configured for Tx/Rx and the other channels are enabled only
    for Rx.

    Signed-off-by: Samuel Mendoza-Jonas
    Signed-off-by: David S. Miller

    Samuel Mendoza-Jonas
     
  • When the NCSI driver is stopped with ncsi_stop_dev() the channel
    monitors are stopped and the state set to "inactive". However the
    channels are still configured and active from the perspective of the
    network controller. We should suspend each active channel but in the
    context of ncsi_stop_dev() the transmit queue has been or is about to be
    stopped so we won't have time to do so.

    Instead when ncsi_start_dev() is called if the NCSI topology has already
    been probed then call ncsi_reset_dev() to suspend any channels that were
    previously active. This resets the network controller to a known state,
    provides an up to date view of channel link state, and makes sure that
    mode flags such as NCSI_MODE_TX_ENABLE are properly reset.

    In addition to ncsi_start_dev() use ncsi_reset_dev() in ncsi-netlink.c
    to update the channel configuration more cleanly.

    Signed-off-by: Samuel Mendoza-Jonas
    Signed-off-by: David S. Miller

    Samuel Mendoza-Jonas
     
  • The concepts of a channel being 'active' and it having link are slightly
    muddled in the NCSI driver. Tweak this slightly so that
    NCSI_CHANNEL_ACTIVE represents a channel that has been configured and
    enabled, and NCSI_CHANNEL_INACTIVE represents a de-configured channel.
    This distinction is important because a channel can be 'active' but have
    its link down; in this case the channel may still need to be configured
    so that it may receive AEN link-state-change packets.

    Signed-off-by: Samuel Mendoza-Jonas
    Signed-off-by: David S. Miller

    Samuel Mendoza-Jonas
     
  • When a package is deselected all channels of that package cease
    communication. If there are other channels active on the package of the
    suspended channel this will disable them as well, so only send a
    deselect-package command if no other channels are active.

    Signed-off-by: Samuel Mendoza-Jonas
    Signed-off-by: David S. Miller

    Samuel Mendoza-Jonas
     
  • Currently the NCSI driver sends a select-package command to all possible
    packages simultaneously to discover what packages are available. However
    at this stage in the probe process the driver does not know if
    hardware arbitration is available: if it isn't then this process could
    cause collisions on the RMII bus when packages try to respond.

    Update the probe loop to probe each package one by one, and once
    complete check if HWA is universally supported.

    Signed-off-by: Samuel Mendoza-Jonas
    Signed-off-by: David S. Miller

    Samuel Mendoza-Jonas
     
  • NCSI hardware arbitration allows multiple packages to be enabled at once
    and share the same wiring. If the NCSI driver recognises that HWA is
    available it unconditionally enables all packages and channels; but that
    is a configuration decision rather than something required by HWA.
    Additionally the current implementation will not failover on link events
    which can cause connectivity to be lost unless the interface is manually
    bounced.

    Retain basic HWA support but remove the separate configuration path to
    enable all channels, leaving this to be handled by a later
    implementation.

    Signed-off-by: Samuel Mendoza-Jonas
    Signed-off-by: David S. Miller

    Samuel Mendoza-Jonas
     

18 Oct, 2018

1 commit

  • This patch adds OEM Broadcom commands and response handling. It also
    defines OEM Get MAC Address handler to get and configure the device.

    ncsi_oem_gma_handler_bcm: This handler send NCSI broadcom command for
    getting mac address.
    ncsi_rsp_handler_oem_bcm: This handles response received for all
    broadcom OEM commands.
    ncsi_rsp_handler_oem_bcm_gma: This handles get mac address response and
    set it to device.

    Signed-off-by: Vijay Khemka
    Reviewed-by: Samuel Mendoza-Jonas
    Signed-off-by: David S. Miller

    Vijay Khemka
     

16 Oct, 2018

1 commit

  • The new command (NCSI_CMD_SEND_CMD) is added to allow user space application
    to send NC-SI command to the network card.
    Also, add a new attribute (NCSI_ATTR_DATA) for transferring request and response.

    The work flow is as below.

    Request:
    User space application
    -> Netlink interface (msg)
    -> new Netlink handler - ncsi_send_cmd_nl()
    -> ncsi_xmit_cmd()

    Response:
    Response received - ncsi_rcv_rsp()
    -> internal response handler - ncsi_rsp_handler_xxx()
    -> ncsi_rsp_handler_netlink()
    -> ncsi_send_netlink_rsp ()
    -> Netlink interface (msg)
    -> user space application

    Command timeout - ncsi_request_timeout()
    -> ncsi_send_netlink_timeout ()
    -> Netlink interface (msg with zero data length)
    -> user space application

    Error:
    Error detected
    -> ncsi_send_netlink_err ()
    -> Netlink interface (err msg)
    -> user space application

    Signed-off-by: Justin Lee
    Reviewed-by: Samuel Mendoza-Jonas
    Signed-off-by: David S. Miller

    Justin.Lee1@Dell.com
     

20 Jun, 2018

3 commits

  • This moves all of the netdev_printk(KERN_DEBUG, ...) messages over to
    netdev_dbg.

    As Joe explains:

    > netdev_dbg is not included in object code unless
    > DEBUG is defined or CONFIG_DYNAMIC_DEBUG is set.
    > And then, it is not emitted into the log unless
    > DEBUG is set or this specific netdev_dbg is enabled
    > via the dynamic debug control file.

    Which is what we're after in this case.

    Acked-by: Samuel Mendoza-Jonas
    Signed-off-by: Joel Stanley
    Signed-off-by: David S. Miller

    Joel Stanley
     
  • This does not provide useful information. As the ncsi maintainer said:

    > either we get a channel or broadcom has gone out to lunch

    Acked-by: Samuel Mendoza-Jonas
    Signed-off-by: Joel Stanley
    Signed-off-by: David S. Miller

    Joel Stanley
     
  • In normal operation we see this series of messages as the host drives
    the network device:

    ftgmac100 1e660000.ethernet eth0: NCSI: LSC AEN - channel 0 state down
    ftgmac100 1e660000.ethernet eth0: NCSI: suspending channel 0
    ftgmac100 1e660000.ethernet eth0: NCSI: configuring channel 0
    ftgmac100 1e660000.ethernet eth0: NCSI: channel 0 link down after config
    ftgmac100 1e660000.ethernet eth0: NCSI interface down
    ftgmac100 1e660000.ethernet eth0: NCSI: LSC AEN - channel 0 state up
    ftgmac100 1e660000.ethernet eth0: NCSI: configuring channel 0
    ftgmac100 1e660000.ethernet eth0: NCSI interface up
    ftgmac100 1e660000.ethernet eth0: NCSI: LSC AEN - channel 0 state down
    ftgmac100 1e660000.ethernet eth0: NCSI: suspending channel 0
    ftgmac100 1e660000.ethernet eth0: NCSI: configuring channel 0
    ftgmac100 1e660000.ethernet eth0: NCSI: channel 0 link down after config
    ftgmac100 1e660000.ethernet eth0: NCSI interface down
    ftgmac100 1e660000.ethernet eth0: NCSI: LSC AEN - channel 0 state up
    ftgmac100 1e660000.ethernet eth0: NCSI: configuring channel 0
    ftgmac100 1e660000.ethernet eth0: NCSI interface up

    This makes all of these messages netdev_dbg. They are still useful to
    debug eg. misbehaving network device firmware, but we do not need them
    filling up the kernel logs in normal operation.

    Acked-by: Samuel Mendoza-Jonas
    Signed-off-by: Joel Stanley
    Signed-off-by: David S. Miller

    Joel Stanley
     

18 Apr, 2018

1 commit

  • The NCSI driver defines a generic ncsi_channel_filter struct that can be
    used to store arbitrarily formatted filters, and several generic methods
    of accessing data stored in such a filter.
    However in both the driver and as defined in the NCSI specification
    there are only two actual filters: VLAN ID filters and MAC address
    filters. The splitting of the MAC filter into unicast, multicast, and
    mixed is also technically not necessary as these are stored in the same
    location in hardware.

    To save complexity, particularly in the set up and accessing of these
    generic filters, remove them in favour of two specific structs. These
    can be acted on directly and do not need several generic helper
    functions to use.

    This also fixes a memory error found by KASAN on ARM32 (which is not
    upstream yet), where response handlers accessing a filter's data field
    could write past allocated memory.

    [ 114.926512] ==================================================================
    [ 114.933861] BUG: KASAN: slab-out-of-bounds in ncsi_configure_channel+0x4b8/0xc58
    [ 114.941304] Read of size 2 at addr 94888558 by task kworker/0:2/546
    [ 114.947593]
    [ 114.949146] CPU: 0 PID: 546 Comm: kworker/0:2 Not tainted 4.16.0-rc6-00119-ge156398bfcad #13
    ...
    [ 115.170233] The buggy address belongs to the object at 94888540
    [ 115.170233] which belongs to the cache kmalloc-32 of size 32
    [ 115.181917] The buggy address is located 24 bytes inside of
    [ 115.181917] 32-byte region [94888540, 94888560)
    [ 115.192115] The buggy address belongs to the page:
    [ 115.196943] page:9eeac100 count:1 mapcount:0 mapping:94888000 index:0x94888fc1
    [ 115.204200] flags: 0x100(slab)
    [ 115.207330] raw: 00000100 94888000 94888fc1 0000003f 00000001 9eea2014 9eecaa74 96c003e0
    [ 115.215444] page dumped because: kasan: bad access detected
    [ 115.221036]
    [ 115.222544] Memory state around the buggy address:
    [ 115.227384] 94888400: fb fb fb fb fc fc fc fc 04 fc fc fc fc fc fc fc
    [ 115.233959] 94888480: 00 00 00 fc fc fc fc fc 00 04 fc fc fc fc fc fc
    [ 115.240529] >94888500: 00 00 04 fc fc fc fc fc 00 00 04 fc fc fc fc fc
    [ 115.247077] ^
    [ 115.252523] 94888580: 00 04 fc fc fc fc fc fc 06 fc fc fc fc fc fc fc
    [ 115.259093] 94888600: 00 00 06 fc fc fc fc fc 00 00 04 fc fc fc fc fc
    [ 115.265639] ==================================================================

    Reported-by: Joel Stanley
    Signed-off-by: Samuel Mendoza-Jonas
    Signed-off-by: David S. Miller

    Samuel Mendoza-Jonas
     

05 Mar, 2018

1 commit

  • Add a generic netlink family for NCSI. This supports three commands;
    NCSI_CMD_PKG_INFO which returns information on packages and their
    associated channels, NCSI_CMD_SET_INTERFACE which allows a specific
    package or package/channel combination to be set as the preferred
    choice, and NCSI_CMD_CLEAR_INTERFACE which clears any preferred setting.

    Signed-off-by: Samuel Mendoza-Jonas
    Signed-off-by: David S. Miller

    Samuel Mendoza-Jonas
     

22 Nov, 2017

2 commits

  • This converts all remaining setup_timer() calls that use a nested field
    to reach a struct timer_list. Coccinelle does not have an easy way to
    match multiple fields, so a new script is needed to change the matches of
    "&_E->_timer" into "&_E->_field1._timer" in all the rules.

    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-2fields.cocci

    @fix_address_of depends@
    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 _field1;
    identifier _timer;
    type _cast_data;
    @@

    (
    -setup_timer(&_E->_field1._timer, NULL, _E);
    +timer_setup(&_E->_field1._timer, NULL, 0);
    |
    -setup_timer(&_E->_field1._timer, NULL, (_cast_data)_E);
    +timer_setup(&_E->_field1._timer, NULL, 0);
    |
    -setup_timer(&_E._field1._timer, NULL, &_E);
    +timer_setup(&_E._field1._timer, NULL, 0);
    |
    -setup_timer(&_E._field1._timer, NULL, (_cast_data)&_E);
    +timer_setup(&_E._field1._timer, NULL, 0);
    )

    @change_timer_function_usage@
    expression _E;
    identifier _field1;
    identifier _timer;
    struct timer_list _stl;
    identifier _callback;
    type _cast_func, _cast_data;
    @@

    (
    -setup_timer(&_E->_field1._timer, _callback, _E);
    +timer_setup(&_E->_field1._timer, _callback, 0);
    |
    -setup_timer(&_E->_field1._timer, &_callback, _E);
    +timer_setup(&_E->_field1._timer, _callback, 0);
    |
    -setup_timer(&_E->_field1._timer, _callback, (_cast_data)_E);
    +timer_setup(&_E->_field1._timer, _callback, 0);
    |
    -setup_timer(&_E->_field1._timer, &_callback, (_cast_data)_E);
    +timer_setup(&_E->_field1._timer, _callback, 0);
    |
    -setup_timer(&_E->_field1._timer, (_cast_func)_callback, _E);
    +timer_setup(&_E->_field1._timer, _callback, 0);
    |
    -setup_timer(&_E->_field1._timer, (_cast_func)&_callback, _E);
    +timer_setup(&_E->_field1._timer, _callback, 0);
    |
    -setup_timer(&_E->_field1._timer, (_cast_func)_callback, (_cast_data)_E);
    +timer_setup(&_E->_field1._timer, _callback, 0);
    |
    -setup_timer(&_E->_field1._timer, (_cast_func)&_callback, (_cast_data)_E);
    +timer_setup(&_E->_field1._timer, _callback, 0);
    |
    -setup_timer(&_E._field1._timer, _callback, (_cast_data)_E);
    +timer_setup(&_E._field1._timer, _callback, 0);
    |
    -setup_timer(&_E._field1._timer, _callback, (_cast_data)&_E);
    +timer_setup(&_E._field1._timer, _callback, 0);
    |
    -setup_timer(&_E._field1._timer, &_callback, (_cast_data)_E);
    +timer_setup(&_E._field1._timer, _callback, 0);
    |
    -setup_timer(&_E._field1._timer, &_callback, (_cast_data)&_E);
    +timer_setup(&_E._field1._timer, _callback, 0);
    |
    -setup_timer(&_E._field1._timer, (_cast_func)_callback, (_cast_data)_E);
    +timer_setup(&_E._field1._timer, _callback, 0);
    |
    -setup_timer(&_E._field1._timer, (_cast_func)_callback, (_cast_data)&_E);
    +timer_setup(&_E._field1._timer, _callback, 0);
    |
    -setup_timer(&_E._field1._timer, (_cast_func)&_callback, (_cast_data)_E);
    +timer_setup(&_E._field1._timer, _callback, 0);
    |
    -setup_timer(&_E._field1._timer, (_cast_func)&_callback, (_cast_data)&_E);
    +timer_setup(&_E._field1._timer, _callback, 0);
    |
    _E->_field1._timer@_stl.function = _callback;
    |
    _E->_field1._timer@_stl.function = &_callback;
    |
    _E->_field1._timer@_stl.function = (_cast_func)_callback;
    |
    _E->_field1._timer@_stl.function = (_cast_func)&_callback;
    |
    _E._field1._timer@_stl.function = _callback;
    |
    _E._field1._timer@_stl.function = &_callback;
    |
    _E._field1._timer@_stl.function = (_cast_func)_callback;
    |
    _E._field1._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._field1;
    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, _field1._timer);
    ... when != _origarg
    |
    ... when != _origarg
    _handletype *_handle =
    -(void *)_origarg;
    +from_timer(_handle, t, _field1._timer);
    ... when != _origarg
    |
    ... when != _origarg
    _handletype *_handle;
    ... when != _handle
    _handle =
    -(_handletype *)_origarg;
    +from_timer(_handle, t, _field1._timer);
    ... when != _origarg
    |
    ... when != _origarg
    _handletype *_handle;
    ... when != _handle
    _handle =
    -(void *)_origarg;
    +from_timer(_handle, t, _field1._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._field1;
    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, _field1._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._field1;
    identifier change_timer_function_usage._timer;
    type _handletype;
    identifier _handle;
    @@

    void _callback(
    -_handletype *_handle
    +struct timer_list *t
    )
    {
    + _handletype *_handle = from_timer(_handle, t, _field1._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._field1;
    identifier change_timer_function_usage._timer;
    type _handletype;
    identifier _handle;
    identifier t;
    @@

    void _callback(struct timer_list *t)
    {
    - _handletype *_handle = from_timer(_handle, t, _field1._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._field1;
    identifier change_timer_function_usage._timer;
    identifier change_timer_function_usage._callback;
    type change_timer_function_usage._cast_data;
    @@

    (
    -timer_setup(&_E->_field1._timer, _callback, 0);
    +setup_timer(&_E->_field1._timer, _callback, (_cast_data)_E);
    |
    -timer_setup(&_E._field1._timer, _callback, 0);
    +setup_timer(&_E._field1._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._field1;
    identifier change_timer_function_usage._timer;
    identifier change_timer_function_usage._callback;
    type _cast_func;
    typedef TIMER_FUNC_TYPE;
    @@

    (
    _E->_field1._timer.function =
    -_callback
    +(TIMER_FUNC_TYPE)_callback
    ;
    |
    _E->_field1._timer.function =
    -&_callback
    +(TIMER_FUNC_TYPE)_callback
    ;
    |
    _E->_field1._timer.function =
    -(_cast_func)_callback;
    +(TIMER_FUNC_TYPE)_callback
    ;
    |
    _E->_field1._timer.function =
    -(_cast_func)&_callback
    +(TIMER_FUNC_TYPE)_callback
    ;
    |
    _E._field1._timer.function =
    -_callback
    +(TIMER_FUNC_TYPE)_callback
    ;
    |
    _E._field1._timer.function =
    -&_callback;
    +(TIMER_FUNC_TYPE)_callback
    ;
    |
    _E._field1._timer.function =
    -(_cast_func)_callback
    +(TIMER_FUNC_TYPE)_callback
    ;
    |
    _E._field1._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._field1;
    identifier change_timer_function_usage._timer;
    identifier change_timer_function_usage._callback;
    type _cast_data;
    @@

    _callback(
    (
    -(_cast_data)_E
    +&_E->_field1._timer
    |
    -(_cast_data)&_E
    +&_E._field1._timer
    |
    -_E
    +&_E->_field1._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 _field1;
    identifier _timer;
    identifier _callback;
    @@

    (
    -setup_timer(&_E->_field1._timer, _callback, 0);
    +timer_setup(&_E->_field1._timer, _callback, 0);
    |
    -setup_timer(&_E->_field1._timer, _callback, 0L);
    +timer_setup(&_E->_field1._timer, _callback, 0);
    |
    -setup_timer(&_E->_field1._timer, _callback, 0UL);
    +timer_setup(&_E->_field1._timer, _callback, 0);
    |
    -setup_timer(&_E._field1._timer, _callback, 0);
    +timer_setup(&_E._field1._timer, _callback, 0);
    |
    -setup_timer(&_E._field1._timer, _callback, 0L);
    +timer_setup(&_E._field1._timer, _callback, 0);
    |
    -setup_timer(&_E._field1._timer, _callback, 0UL);
    +timer_setup(&_E._field1._timer, _callback, 0);
    |
    -setup_timer(&_field1._timer, _callback, 0);
    +timer_setup(&_field1._timer, _callback, 0);
    |
    -setup_timer(&_field1._timer, _callback, 0L);
    +timer_setup(&_field1._timer, _callback, 0);
    |
    -setup_timer(&_field1._timer, _callback, 0UL);
    +timer_setup(&_field1._timer, _callback, 0);
    |
    -setup_timer(_field1._timer, _callback, 0);
    +timer_setup(_field1._timer, _callback, 0);
    |
    -setup_timer(_field1._timer, _callback, 0L);
    +timer_setup(_field1._timer, _callback, 0);
    |
    -setup_timer(_field1._timer, _callback, 0UL);
    +timer_setup(_field1._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: Kees Cook

    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: Kees Cook

    Kees Cook
     

11 Nov, 2017

1 commit

  • The NCSI driver is mostly silent which becomes a headache when trying to
    determine what has occurred on the NCSI connection. This adds additional
    logging in a few key areas such as state transitions and calling out
    certain errors more visibly.

    Signed-off-by: Samuel Mendoza-Jonas
    Signed-off-by: David S. Miller

    Samuel Mendoza-Jonas
     

03 Nov, 2017

1 commit


21 Oct, 2017

3 commits

  • The NCSI channel has been configured to provide service if its link
    monitor timer is enabled, regardless of its state (inactive or active).
    So the timeout event on the link monitor indicates the out-of-service
    on that channel, for which a failover is needed.

    This sets NCSI_DEV_RESHUFFLE flag to enforce failover on link monitor
    timeout, regardless the channel's original state (inactive or active).
    Also, the link is put into "down" state to give the failing channel
    lowest priority when selecting for the active channel. The state of
    failing channel should be set to active in order for deinitialization
    and failover to be done.

    Signed-off-by: Gavin Shan
    Signed-off-by: Samuel Mendoza-Jonas
    Signed-off-by: David S. Miller

    Gavin Shan
     
  • When there are no NCSI channels probed, HWA (Hardware Arbitration)
    mode is enabled. It's not correct because HWA depends on the fact:
    NCSI channels exist and all of them support HWA mode. This disables
    HWA when no channels are probed.

    Signed-off-by: Gavin Shan
    Signed-off-by: Samuel Mendoza-Jonas
    Signed-off-by: David S. Miller

    Gavin Shan
     
  • ncsi_channel_monitor() misses stopping the channel monitor in several
    places that it should, causing a WARN_ON_ONCE() to trigger when the
    monitor is re-started later, eg:

    [ 459.040000] WARNING: CPU: 0 PID: 1093 at net/ncsi/ncsi-manage.c:269 ncsi_start_channel_monitor+0x7c/0x90
    [ 459.040000] CPU: 0 PID: 1093 Comm: kworker/0:3 Not tainted 4.10.17-gaca2fdd #140
    [ 459.040000] Hardware name: ASpeed SoC
    [ 459.040000] Workqueue: events ncsi_dev_work
    [ 459.040000] [] (unwind_backtrace) from [] (show_stack+0x20/0x24)
    [ 459.040000] [] (show_stack) from [] (dump_stack+0x20/0x28)
    [ 459.040000] [] (dump_stack) from [] (__warn+0xe0/0x108)
    [ 459.040000] [] (__warn) from [] (warn_slowpath_null+0x30/0x38)
    [ 459.040000] [] (warn_slowpath_null) from [] (ncsi_start_channel_monitor+0x7c/0x90)
    [ 459.040000] [] (ncsi_start_channel_monitor) from [] (ncsi_configure_channel+0xdc/0x5fc)
    [ 459.040000] [] (ncsi_configure_channel) from [] (ncsi_dev_work+0xac/0x474)
    [ 459.040000] [] (ncsi_dev_work) from [] (process_one_work+0x1e0/0x450)
    [ 459.040000] [] (process_one_work) from [] (worker_thread+0x5c/0x570)
    [ 459.040000] [] (worker_thread) from [] (kthread+0x124/0x164)
    [ 459.040000] [] (kthread) from [] (ret_from_fork+0x14/0x2c)

    This also updates the monitor instead of just returning if
    ncsi_xmit_cmd() fails to send the get-link-status command so that the
    monitor properly times out.

    Fixes: e6f44ed6d04d3 "net/ncsi: Package and channel management"

    Signed-off-by: Samuel Mendoza-Jonas
    Signed-off-by: David S. Miller

    Samuel Mendoza-Jonas
     

12 Oct, 2017

1 commit

  • Currently we drop any new VLAN ids if there are more than the current
    (or last used) channel can support. Most importantly this is a problem
    if no channel has been selected yet, resulting in a segfault.

    Secondly this does not necessarily reflect the capabilities of any other
    channels. Instead only drop a new VLAN id if we are already tracking the
    maximum allowed by the NCSI specification. Per-channel limits are
    already handled by ncsi_add_filter(), but add a message to set_one_vid()
    to make it obvious that the channel can not support any more VLAN ids.

    Signed-off-by: Samuel Mendoza-Jonas
    Signed-off-by: David S. Miller

    Samuel Mendoza-Jonas
     

06 Sep, 2017

1 commit

  • We get a new link error in allmodconfig kernels after ftgmac100
    started using the ncsi helpers:

    ERROR: "ncsi_vlan_rx_kill_vid" [drivers/net/ethernet/faraday/ftgmac100.ko] undefined!
    ERROR: "ncsi_vlan_rx_add_vid" [drivers/net/ethernet/faraday/ftgmac100.ko] undefined!

    Related to that, we get another error when CONFIG_NET_NCSI is disabled:

    drivers/net/ethernet/faraday/ftgmac100.c:1626:25: error: 'ncsi_vlan_rx_add_vid' undeclared here (not in a function); did you mean 'ncsi_start_dev'?
    drivers/net/ethernet/faraday/ftgmac100.c:1627:26: error: 'ncsi_vlan_rx_kill_vid' undeclared here (not in a function); did you mean 'ncsi_vlan_rx_add_vid'?

    This fixes both problems at once, using a 'static inline' stub helper
    for the disabled case, and exporting the functions when they are present.

    Fixes: 51564585d8c6 ("ftgmac100: Support NCSI VLAN filtering when available")
    Fixes: 21acf63013ed ("net/ncsi: Configure VLAN tag filter")
    Signed-off-by: Arnd Bergmann
    Signed-off-by: David S. Miller

    Arnd Bergmann
     

29 Aug, 2017

1 commit

  • Make use of the ndo_vlan_rx_{add,kill}_vid callbacks to have the NCSI
    stack process new VLAN tags and configure the channel VLAN filter
    appropriately.
    Several VLAN tags can be set and a "Set VLAN Filter" packet must be sent
    for each one, meaning the ncsi_dev_state_config_svf state must be
    repeated. An internal list of VLAN tags is maintained, and compared
    against the current channel's ncsi_channel_filter in order to keep track
    within the state. VLAN filters are removed in a similar manner, with the
    introduction of the ncsi_dev_state_config_clear_vids state. The maximum
    number of VLAN tag filters is determined by the "Get Capabilities"
    response from the channel.

    Signed-off-by: Samuel Mendoza-Jonas
    Signed-off-by: David S. Miller

    Samuel Mendoza-Jonas
     

20 Oct, 2016

3 commits

  • The issue was found on BCM5718 which has two NCSI channels in one
    package: C0 and C1. C0 is in link-up state while C1 is in link-down
    state. C0 is chosen as active channel until unplugging and plugging
    C0's cable: On unplugging C0's cable, LSC (Link State Change) AEN
    packet received on C0 to report link-down event. After that, C1 is
    chosen as active channel. LSC AEN for link-up event is lost on C0
    when plugging C0's cable back. We lose the network even C0 is usable.

    This resolves the issue by recording the (hot) channel that was ever
    chosen as active one. The hot channel is chosen to be active one
    if none of available channels in link-up state. With this, C0 is still
    the active one after unplugging C0's cable. LSC AEN packet received
    on C0 when plugging its cable back.

    Signed-off-by: Gavin Shan
    Signed-off-by: David S. Miller

    Gavin Shan
     
  • The issue was found on BCM5718 which has two NCSI channels in one
    package: C0 and C1. Both of them are connected to different LANs,
    means they are in link-up state and C0 is chosen as the active one
    until resetting BCM5718 happens as below.

    Resetting BCM5718 results in LSC (Link State Change) AEN packet
    received on C0, meaning LSC AEN is missed on C1. When LSC AEN packet
    received on C0 to report link-down, it fails over to C1 because C1
    is in link-up state as software can see. However, C1 is in link-down
    state in hardware. It means the link state is out of synchronization
    between hardware and software, resulting in inappropriate channel (C1)
    selected as active one.

    This resolves the issue by sending separate GLS (Get Link Status)
    commands to all channels in the package before trying to do failover.
    The last link states of all channels in the package are retrieved.
    With it, C0 (not C1) is selected as active one as expected.

    Signed-off-by: Gavin Shan
    Signed-off-by: David S. Miller

    Gavin Shan
     
  • There are several if/else statements in the state machine implemented
    by switch/case in ncsi_suspend_channel() to avoid duplicated code. It
    makes the code a bit hard to be understood.

    This drops if/else statements in ncsi_suspend_channel() to improve the
    code readability as Joel Stanley suggested. Also, it becomes easy to
    add more states in the state machine without affecting current code.
    No logical changes introduced by this.

    Suggested-by: Joel Stanley
    Signed-off-by: Gavin Shan
    Signed-off-by: David S. Miller

    Gavin Shan
     

04 Oct, 2016

7 commits

  • This introduces ncsi_stop_dev(), as counterpart to ncsi_start_dev(),
    to stop the NCSI device so that it can be reenabled in future. This
    API should be called when the network device driver is going to
    shutdown the device. There are 3 things done in the function: Stop
    the channel monitoring; Reset channels to inactive state; Report
    NCSI link down.

    Signed-off-by: Gavin Shan
    Reviewed-by: Joel Stanley
    Signed-off-by: David S. Miller

    Gavin Shan
     
  • The original NCSI channel monitoring was implemented based on a
    backoff algorithm: the GLS response should be received in the
    specified interval. Otherwise, the channel is regarded as dead
    and failover should be taken if current channel is an active one.
    There are several problems in the implementation: (A) On BCM5718,
    we found when the IID (Instance ID) in the GLS command packet
    changes from 255 to 1, the response corresponding to IID#1 never
    comes in. It means we cannot make the unfair judgement that the
    channel is dead when one response is missed. (B) The code's
    readability should be improved. (C) We should do failover when
    current channel is active one and the channel monitoring should
    be marked as disabled before doing failover.

    This reworks the channel monitoring to address all above issues.
    The fields for channel monitoring is put into separate struct
    and the state of channel monitoring is predefined. The channel
    is regarded alive if the network controller responses to one of
    two GLS commands or both of them in 5 seconds.

    Signed-off-by: Gavin Shan
    Reviewed-by: Joel Stanley
    Signed-off-by: David S. Miller

    Gavin Shan
     
  • There is only one NCSI request property for now: the response for
    the sent command need drive the workqueue or not. So we had one
    field (@driven) for the purpose. We lost the flexibility to extend
    NCSI request properties.

    This replaces @driven with @flags and @req_flags in NCSI request
    and NCSI command argument struct. Each bit of the newly introduced
    field can be used for one property. No functional changes introduced.

    Signed-off-by: Gavin Shan
    Reviewed-by: Joel Stanley
    Signed-off-by: David S. Miller

    Gavin Shan
     
  • The NCSI request index (struct ncsi_request::id) is put into instance
    ID (IID) field while sending NCSI command packet. It was designed the
    available IDs are given in round-robin fashion. @ndp->request_id was
    introduced to represent the next available ID, but it has been used
    as number of successively allocated IDs. It breaks the round-robin
    design. Besides, we shouldn't put 0 to NCSI command packet's IID
    field, meaning ID#0 should be reserved according section 6.3.1.1
    in NCSI spec (v1.1.0).

    This fixes above two issues. With it applied, the available IDs will
    be assigned in round-robin fashion and ID#0 won't be assigned.

    Signed-off-by: Gavin Shan
    Reviewed-by: Joel Stanley
    Signed-off-by: David S. Miller

    Gavin Shan
     
  • We needn't send CIS (Clear Initial State) command to the NCSI
    reserved channel (0x1f) in the enumeration. We shouldn't receive
    a valid response from CIS on NCSI channel 0x1f.

    Signed-off-by: Gavin Shan
    Reviewed-by: Joel Stanley
    Signed-off-by: David S. Miller

    Gavin Shan
     
  • This defines NCSI_RESERVED_CHANNEL as the reserved NCSI channel
    ID (0x1f). No logical changes introduced.

    Signed-off-by: Gavin Shan
    Reviewed-by: Joel Stanley
    Signed-off-by: David S. Miller

    Gavin Shan
     
  • xchg() is used to set NCSI channel's state in order for consistent
    access to the state. xchg()'s return value should be used. Otherwise,
    one build warning will be raised (with -Wunused-value) as below message
    indicates. It is reported by ia64-linux-gcc (GCC) 4.9.0.

    net/ncsi/ncsi-manage.c: In function 'ncsi_channel_monitor':
    arch/ia64/include/uapi/asm/cmpxchg.h:56:2: warning: value computed is \
    not used [-Wunused-value]
    ((__typeof__(*(ptr))) __xchg((unsigned long) (x), (ptr), sizeof(*(ptr))))
    ^
    net/ncsi/ncsi-manage.c:202:3: note: in expansion of macro 'xchg'
    xchg(&nc->state, NCSI_CHANNEL_INACTIVE);

    This removes the atomic access to NCSI channel's state avoid the above
    build warning. We have to hold the channel's lock when its state is readed
    or updated. No functional changes introduced.

    Signed-off-by: Gavin Shan
    Reviewed-by: Joel Stanley
    Signed-off-by: David S. Miller

    Gavin Shan