23 Aug, 2018

1 commit


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
     

03 Jun, 2018

2 commits

  • ncsi_rsp_handler_gc() allocates the filter arrays using GFP_KERNEL in
    softirq context, causing the below backtrace. This allocation is only a
    few dozen bytes during probing so allocate with GFP_ATOMIC instead.

    [ 42.813372] BUG: sleeping function called from invalid context at mm/slab.h:416
    [ 42.820900] in_atomic(): 1, irqs_disabled(): 0, pid: 213, name: kworker/0:1
    [ 42.827893] INFO: lockdep is turned off.
    [ 42.832023] CPU: 0 PID: 213 Comm: kworker/0:1 Tainted: G W 4.13.16-01441-gad99b38 #65
    [ 42.841007] Hardware name: Generic DT based system
    [ 42.845966] Workqueue: events ncsi_dev_work
    [ 42.850251] [] (unwind_backtrace) from [] (show_stack+0x20/0x24)
    [ 42.858046] [] (show_stack) from [] (dump_stack+0x20/0x28)
    [ 42.865309] [] (dump_stack) from [] (___might_sleep+0x230/0x2b0)
    [ 42.873241] [] (___might_sleep) from [] (__might_sleep+0x6c/0xac)
    [ 42.881129] [] (__might_sleep) from [] (__kmalloc+0x210/0x2fc)
    [ 42.888737] [] (__kmalloc) from [] (ncsi_rsp_handler_gc+0xd0/0x170)
    [ 42.896770] [] (ncsi_rsp_handler_gc) from [] (ncsi_rcv_rsp+0x16c/0x1d4)
    [ 42.905314] [] (ncsi_rcv_rsp) from [] (__netif_receive_skb_core+0x3c8/0xb50)
    [ 42.914158] [] (__netif_receive_skb_core) from [] (__netif_receive_skb+0x20/0x7c)
    [ 42.923420] [] (__netif_receive_skb) from [] (netif_receive_skb_internal+0x78/0x6a4)
    [ 42.932931] [] (netif_receive_skb_internal) from [] (netif_receive_skb+0x78/0x158)
    [ 42.942292] [] (netif_receive_skb) from [] (ftgmac100_poll+0x43c/0x4e8)
    [ 42.950855] [] (ftgmac100_poll) from [] (net_rx_action+0x278/0x4c4)
    [ 42.958918] [] (net_rx_action) from [] (__do_softirq+0xe0/0x4c4)
    [ 42.966716] [] (__do_softirq) from [] (do_softirq.part.4+0x50/0x78)
    [ 42.974756] [] (do_softirq.part.4) from [] (__local_bh_enable_ip+0xf8/0x11c)
    [ 42.983579] [] (__local_bh_enable_ip) from [] (__dev_queue_xmit+0x260/0x890)
    [ 42.992392] [] (__dev_queue_xmit) from [] (dev_queue_xmit+0x1c/0x20)
    [ 43.000689] [] (dev_queue_xmit) from [] (ncsi_xmit_cmd+0x1c0/0x244)
    [ 43.008763] [] (ncsi_xmit_cmd) from [] (ncsi_dev_work+0x2e0/0x4c8)
    [ 43.016725] [] (ncsi_dev_work) from [] (process_one_work+0x214/0x6f8)
    [ 43.024940] [] (process_one_work) from [] (worker_thread+0x48/0x558)
    [ 43.033070] [] (worker_thread) from [] (kthread+0x130/0x174)
    [ 43.040506] [] (kthread) from [] (ret_from_fork+0x14/0x24)

    Fixes: 062b3e1b6d4f ("net/ncsi: Refactor MAC, VLAN filters")
    Signed-off-by: Samuel Mendoza-Jonas
    Signed-off-by: David S. Miller

    Samuel Mendoza-Jonas
     
  • Filling in the padding slot in the bpf structure as a bug fix in 'ne'
    overlapped with actually using that padding area for something in
    'net-next'.

    Signed-off-by: David S. Miller

    David S. Miller
     

01 Jun, 2018

1 commit

  • With CONFIG_CC_STACKPROTECTOR enabled the kernel panics as below when
    parsing a NCSI_CMD_PKG_INFO command:

    [ 150.149711] Kernel panic - not syncing: stack-protector: Kernel stack is corrupted in: 805cff08
    [ 150.149711]
    [ 150.159919] CPU: 0 PID: 1301 Comm: ncsi-netlink Not tainted 4.13.16-468cbec6d2c91239332cb91b1f0a73aafcb6f0c6 #1
    [ 150.170004] Hardware name: Generic DT based system
    [ 150.174852] [] (unwind_backtrace) from [] (show_stack+0x20/0x24)
    [ 150.182641] [] (show_stack) from [] (dump_stack+0x20/0x28)
    [ 150.189888] [] (dump_stack) from [] (panic+0xdc/0x278)
    [ 150.196780] [] (panic) from [] (__stack_chk_fail+0x20/0x24)
    [ 150.204111] [] (__stack_chk_fail) from [] (ncsi_pkg_info_all_nl+0x244/0x258)
    [ 150.212912] [] (ncsi_pkg_info_all_nl) from [] (genl_lock_dumpit+0x3c/0x54)
    [ 150.221535] [] (genl_lock_dumpit) from [] (netlink_dump+0xf8/0x284)
    [ 150.229550] [] (netlink_dump) from [] (__netlink_dump_start+0x124/0x17c)
    [ 150.237992] [] (__netlink_dump_start) from [] (genl_rcv_msg+0x1c8/0x3d4)
    [ 150.246440] [] (genl_rcv_msg) from [] (netlink_rcv_skb+0xd8/0x134)
    [ 150.254361] [] (netlink_rcv_skb) from [] (genl_rcv+0x30/0x44)
    [ 150.261850] [] (genl_rcv) from [] (netlink_unicast+0x198/0x234)
    [ 150.269511] [] (netlink_unicast) from [] (netlink_sendmsg+0x368/0x3b0)
    [ 150.277783] [] (netlink_sendmsg) from [] (sock_sendmsg+0x24/0x34)
    [ 150.285625] [] (sock_sendmsg) from [] (___sys_sendmsg+0x244/0x260)
    [ 150.293556] [] (___sys_sendmsg) from [] (__sys_sendmsg+0x5c/0x9c)
    [ 150.301400] [] (__sys_sendmsg) from [] (SyS_sendmsg+0x18/0x1c)
    [ 150.308984] [] (SyS_sendmsg) from [] (ret_fast_syscall+0x0/0x3c)
    [ 150.316743] ---[ end Kernel panic - not syncing: stack-protector: Kernel stack is corrupted in: 805cff08

    This turns out to be because the attrs array in ncsi_pkg_info_all_nl()
    is initialised to a length of NCSI_ATTR_MAX which is the maximum
    attribute number, not the number of attributes.

    Fixes: 955dc68cb9b2 ("net/ncsi: Add generic netlink family")
    Signed-off-by: Samuel Mendoza-Jonas
    Signed-off-by: David S. Miller

    Samuel Mendoza-Jonas
     

29 May, 2018

1 commit


18 May, 2018

1 commit

  • We recently refactored this code and introduced a static checker
    warning. Smatch complains that if cmd->index is zero then we would
    underflow the arrays. That's obviously true.

    The question is whether we prevent cmd->index from being zero at a
    different level. I've looked at the code and I don't immediately see
    a check for that.

    Fixes: 062b3e1b6d4f ("net/ncsi: Refactor MAC, VLAN filters")
    Signed-off-by: Dan Carpenter
    Signed-off-by: David S. Miller

    Dan Carpenter
     

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
     

27 Mar, 2018

1 commit

  • The call to nla_nest_start calls nla_put which can lead to a NULL
    return so it's possible for attr to become NULL and we can potentially
    get a NULL pointer dereference on attr. Fix this by checking for
    a NULL return.

    Detected by CoverityScan, CID#1466125 ("Dereference null return")

    Fixes: 955dc68cb9b2 ("net/ncsi: Add generic netlink family")
    Signed-off-by: Colin Ian King
    Signed-off-by: David S. Miller

    Colin Ian King
     

09 Mar, 2018

2 commits


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
     

19 Dec, 2017

1 commit

  • The current HNCDSC handler takes the status flag from the AEN packet and
    will update or change the current channel based on this flag and the
    current channel status.

    However the flag from the HNCDSC packet merely represents the host link
    state. While the state of the host interface is potentially interesting
    information it should not affect the state of the NCSI link. Indeed the
    NCSI specification makes no mention of any recommended action related to
    the host network controller driver state.

    Update the HNCDSC handler to record the host network driver status but
    take no other action.

    Signed-off-by: Samuel Mendoza-Jonas
    Acked-by: Jeremy Kerr
    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

2 commits

  • Several response handlers return EBUSY if the data corresponding to the
    command/response pair is already set. There is no reason to return an
    error here; the channel is advertising something as enabled because we
    told it to enable it, and it's possible that the feature has been
    enabled previously.

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

    Samuel Mendoza-Jonas
     
  • 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

5 commits

  • The length of GVI (GetVersionInfo) response packet should be 40 instead
    of 36. This issue was found from /sys/kernel/debug/ncsi/eth0/stats.

    # ethtool --ncsi eth0 swstats
    :
    RESPONSE OK TIMEOUT ERROR
    =======================================
    GVI 0 0 2

    With this applied, no error reported on GVI response packets:

    # ethtool --ncsi eth0 swstats
    :
    RESPONSE OK TIMEOUT ERROR
    =======================================
    GVI 2 0 0

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

    Gavin Shan
     
  • 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
     
  • Correct the value of the HNCDSC AEN packet.
    Fixes: 7a82ecf4cfb85 "net/ncsi: NCSI AEN packet handler"

    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

2 commits

  • 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
     
  • Signed-off-by: Samuel Mendoza-Jonas
    Signed-off-by: David S. Miller

    Samuel Mendoza-Jonas
     

16 Jun, 2017

2 commits

  • It seems like a historic accident that these return unsigned char *,
    and in many places that means casts are required, more often than not.

    Make these functions return void * and remove all the casts across
    the tree, adding a (u8 *) cast only where the unsigned char pointer
    was used directly, all done with the following spatch:

    @@
    expression SKB, LEN;
    typedef u8;
    identifier fn = { skb_push, __skb_push, skb_push_rcsum };
    @@
    - *(fn(SKB, LEN))
    + *(u8 *)fn(SKB, LEN)

    @@
    expression E, SKB, LEN;
    identifier fn = { skb_push, __skb_push, skb_push_rcsum };
    type T;
    @@
    - E = ((T *)(fn(SKB, LEN)))
    + E = fn(SKB, LEN)

    @@
    expression SKB, LEN;
    identifier fn = { skb_push, __skb_push, skb_push_rcsum };
    @@
    - fn(SKB, LEN)[0]
    + *(u8 *)fn(SKB, LEN)

    Note that the last part there converts from push(...)[0] to the
    more idiomatic *(u8 *)push(...).

    Signed-off-by: Johannes Berg
    Signed-off-by: David S. Miller

    Johannes Berg
     
  • There were many places that my previous spatch didn't find,
    as pointed out by yuan linyu in various patches.

    The following spatch found many more and also removes the
    now unnecessary casts:

    @@
    identifier p, p2;
    expression len;
    expression skb;
    type t, t2;
    @@
    (
    -p = skb_put(skb, len);
    +p = skb_put_zero(skb, len);
    |
    -p = (t)skb_put(skb, len);
    +p = skb_put_zero(skb, len);
    )
    ... when != p
    (
    p2 = (t2)p;
    -memset(p2, 0, len);
    |
    -memset(p, 0, len);
    )

    @@
    type t, t2;
    identifier p, p2;
    expression skb;
    @@
    t *p;
    ...
    (
    -p = skb_put(skb, sizeof(t));
    +p = skb_put_zero(skb, sizeof(t));
    |
    -p = (t *)skb_put(skb, sizeof(t));
    +p = skb_put_zero(skb, sizeof(t));
    )
    ... when != p
    (
    p2 = (t2)p;
    -memset(p2, 0, sizeof(*p));
    |
    -memset(p, 0, sizeof(*p));
    )

    @@
    expression skb, len;
    @@
    -memset(skb_put(skb, len), 0, len);
    +skb_put_zero(skb, len);

    Apply it to the tree (with one manual fixup to keep the
    comment in vxlan.c, which spatch removed.)

    Signed-off-by: Johannes Berg
    Signed-off-by: David S. Miller

    Johannes Berg
     

20 Oct, 2016

4 commits

  • This improves AEN handler for Host Network Controller Driver Status
    Change (HNCDSC):

    * The channel's lock should be hold when accessing its state.
    * Do failover when host driver isn't ready.
    * Configure channel when host driver becomes ready.

    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. 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

5 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