net/ncsi: Resource management
NCSI spec (DSP0222) defines several objects: package, channel, mode,
filter, version and statistics etc. This introduces the data structs
to represent those objects and implement functions to manage them.
Also, this introduces CONFIG_NET_NCSI for the newly implemented NCSI
stack.
* The user (e.g. netdev driver) dereference NCSI device by
"struct ncsi_dev", which is embedded to "struct ncsi_dev_priv".
The later one is used by NCSI stack internally.
* Every NCSI device can have multiple packages simultaneously, up
to 8 packages. It's represented by "struct ncsi_package" and
identified by 3-bits ID.
* Every NCSI package can have multiple channels, up to 32. It's
represented by "struct ncsi_channel" and identified by 5-bits ID.
* Every NCSI channel has version, statistics, various modes and
filters. They are represented by "struct ncsi_channel_version",
"struct ncsi_channel_stats", "struct ncsi_channel_mode" and
"struct ncsi_channel_filter" separately.
* Apart from AEN (Asynchronous Event Notification), the NCSI stack
works in terms of command and response. This introduces "struct
ncsi_req" to represent a complete NCSI transaction made of NCSI
request and response.
link: https://www.dmtf.org/sites/default/files/standards/documents/DSP0222_1.1.0.pdf
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Acked-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-07-19 09:54:16 +08:00
|
|
|
/*
|
|
|
|
* Copyright Gavin Shan, IBM Corporation 2016.
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/netdevice.h>
|
|
|
|
#include <linux/skbuff.h>
|
|
|
|
|
|
|
|
#include <net/ncsi.h>
|
|
|
|
#include <net/net_namespace.h>
|
|
|
|
#include <net/sock.h>
|
2016-07-19 09:54:19 +08:00
|
|
|
#include <net/addrconf.h>
|
|
|
|
#include <net/ipv6.h>
|
|
|
|
#include <net/if_inet6.h>
|
net/ncsi: Resource management
NCSI spec (DSP0222) defines several objects: package, channel, mode,
filter, version and statistics etc. This introduces the data structs
to represent those objects and implement functions to manage them.
Also, this introduces CONFIG_NET_NCSI for the newly implemented NCSI
stack.
* The user (e.g. netdev driver) dereference NCSI device by
"struct ncsi_dev", which is embedded to "struct ncsi_dev_priv".
The later one is used by NCSI stack internally.
* Every NCSI device can have multiple packages simultaneously, up
to 8 packages. It's represented by "struct ncsi_package" and
identified by 3-bits ID.
* Every NCSI package can have multiple channels, up to 32. It's
represented by "struct ncsi_channel" and identified by 5-bits ID.
* Every NCSI channel has version, statistics, various modes and
filters. They are represented by "struct ncsi_channel_version",
"struct ncsi_channel_stats", "struct ncsi_channel_mode" and
"struct ncsi_channel_filter" separately.
* Apart from AEN (Asynchronous Event Notification), the NCSI stack
works in terms of command and response. This introduces "struct
ncsi_req" to represent a complete NCSI transaction made of NCSI
request and response.
link: https://www.dmtf.org/sites/default/files/standards/documents/DSP0222_1.1.0.pdf
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Acked-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-07-19 09:54:16 +08:00
|
|
|
|
|
|
|
#include "internal.h"
|
2016-07-19 09:54:19 +08:00
|
|
|
#include "ncsi-pkt.h"
|
2018-03-05 08:39:05 +08:00
|
|
|
#include "ncsi-netlink.h"
|
net/ncsi: Resource management
NCSI spec (DSP0222) defines several objects: package, channel, mode,
filter, version and statistics etc. This introduces the data structs
to represent those objects and implement functions to manage them.
Also, this introduces CONFIG_NET_NCSI for the newly implemented NCSI
stack.
* The user (e.g. netdev driver) dereference NCSI device by
"struct ncsi_dev", which is embedded to "struct ncsi_dev_priv".
The later one is used by NCSI stack internally.
* Every NCSI device can have multiple packages simultaneously, up
to 8 packages. It's represented by "struct ncsi_package" and
identified by 3-bits ID.
* Every NCSI package can have multiple channels, up to 32. It's
represented by "struct ncsi_channel" and identified by 5-bits ID.
* Every NCSI channel has version, statistics, various modes and
filters. They are represented by "struct ncsi_channel_version",
"struct ncsi_channel_stats", "struct ncsi_channel_mode" and
"struct ncsi_channel_filter" separately.
* Apart from AEN (Asynchronous Event Notification), the NCSI stack
works in terms of command and response. This introduces "struct
ncsi_req" to represent a complete NCSI transaction made of NCSI
request and response.
link: https://www.dmtf.org/sites/default/files/standards/documents/DSP0222_1.1.0.pdf
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Acked-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-07-19 09:54:16 +08:00
|
|
|
|
|
|
|
LIST_HEAD(ncsi_dev_list);
|
|
|
|
DEFINE_SPINLOCK(ncsi_dev_lock);
|
|
|
|
|
2016-07-19 09:54:19 +08:00
|
|
|
static void ncsi_report_link(struct ncsi_dev_priv *ndp, bool force_down)
|
|
|
|
{
|
|
|
|
struct ncsi_dev *nd = &ndp->ndev;
|
|
|
|
struct ncsi_package *np;
|
|
|
|
struct ncsi_channel *nc;
|
2016-10-04 08:25:47 +08:00
|
|
|
unsigned long flags;
|
2016-07-19 09:54:19 +08:00
|
|
|
|
|
|
|
nd->state = ncsi_dev_state_functional;
|
|
|
|
if (force_down) {
|
|
|
|
nd->link_up = 0;
|
|
|
|
goto report;
|
|
|
|
}
|
|
|
|
|
|
|
|
nd->link_up = 0;
|
|
|
|
NCSI_FOR_EACH_PACKAGE(ndp, np) {
|
|
|
|
NCSI_FOR_EACH_CHANNEL(np, nc) {
|
2016-10-04 08:25:47 +08:00
|
|
|
spin_lock_irqsave(&nc->lock, flags);
|
|
|
|
|
2016-07-19 09:54:19 +08:00
|
|
|
if (!list_empty(&nc->link) ||
|
2016-10-04 08:25:47 +08:00
|
|
|
nc->state != NCSI_CHANNEL_ACTIVE) {
|
|
|
|
spin_unlock_irqrestore(&nc->lock, flags);
|
2016-07-19 09:54:19 +08:00
|
|
|
continue;
|
2016-10-04 08:25:47 +08:00
|
|
|
}
|
2016-07-19 09:54:19 +08:00
|
|
|
|
|
|
|
if (nc->modes[NCSI_MODE_LINK].data[2] & 0x1) {
|
2016-10-04 08:25:47 +08:00
|
|
|
spin_unlock_irqrestore(&nc->lock, flags);
|
2016-07-19 09:54:19 +08:00
|
|
|
nd->link_up = 1;
|
|
|
|
goto report;
|
|
|
|
}
|
2016-10-04 08:25:47 +08:00
|
|
|
|
|
|
|
spin_unlock_irqrestore(&nc->lock, flags);
|
2016-07-19 09:54:19 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
report:
|
|
|
|
nd->handler(nd);
|
|
|
|
}
|
|
|
|
|
treewide: setup_timer() -> timer_setup() (2 field)
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 <keescook@chromium.org>
2017-10-18 11:21:24 +08:00
|
|
|
static void ncsi_channel_monitor(struct timer_list *t)
|
2016-07-19 09:54:19 +08:00
|
|
|
{
|
treewide: setup_timer() -> timer_setup() (2 field)
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 <keescook@chromium.org>
2017-10-18 11:21:24 +08:00
|
|
|
struct ncsi_channel *nc = from_timer(nc, t, monitor.timer);
|
2016-07-19 09:54:19 +08:00
|
|
|
struct ncsi_package *np = nc->package;
|
|
|
|
struct ncsi_dev_priv *ndp = np->ndp;
|
2017-10-19 10:43:08 +08:00
|
|
|
struct ncsi_channel_mode *ncm;
|
2016-07-19 09:54:19 +08:00
|
|
|
struct ncsi_cmd_arg nca;
|
2016-10-04 08:25:47 +08:00
|
|
|
bool enabled, chained;
|
2016-10-04 08:25:52 +08:00
|
|
|
unsigned int monitor_state;
|
2016-07-19 09:54:19 +08:00
|
|
|
unsigned long flags;
|
2016-10-04 08:25:47 +08:00
|
|
|
int state, ret;
|
2016-07-19 09:54:19 +08:00
|
|
|
|
|
|
|
spin_lock_irqsave(&nc->lock, flags);
|
2016-10-04 08:25:47 +08:00
|
|
|
state = nc->state;
|
|
|
|
chained = !list_empty(&nc->link);
|
2016-10-04 08:25:52 +08:00
|
|
|
enabled = nc->monitor.enabled;
|
|
|
|
monitor_state = nc->monitor.state;
|
2016-07-19 09:54:19 +08:00
|
|
|
spin_unlock_irqrestore(&nc->lock, flags);
|
|
|
|
|
2017-10-19 10:43:06 +08:00
|
|
|
if (!enabled || chained) {
|
|
|
|
ncsi_stop_channel_monitor(nc);
|
2016-07-19 09:54:19 +08:00
|
|
|
return;
|
2017-10-19 10:43:06 +08:00
|
|
|
}
|
2016-10-04 08:25:47 +08:00
|
|
|
if (state != NCSI_CHANNEL_INACTIVE &&
|
2017-10-19 10:43:06 +08:00
|
|
|
state != NCSI_CHANNEL_ACTIVE) {
|
|
|
|
ncsi_stop_channel_monitor(nc);
|
2016-07-19 09:54:19 +08:00
|
|
|
return;
|
2017-10-19 10:43:06 +08:00
|
|
|
}
|
2016-07-19 09:54:19 +08:00
|
|
|
|
2016-10-04 08:25:52 +08:00
|
|
|
switch (monitor_state) {
|
|
|
|
case NCSI_CHANNEL_MONITOR_START:
|
|
|
|
case NCSI_CHANNEL_MONITOR_RETRY:
|
2016-07-19 09:54:19 +08:00
|
|
|
nca.ndp = ndp;
|
|
|
|
nca.package = np->id;
|
|
|
|
nca.channel = nc->id;
|
|
|
|
nca.type = NCSI_PKT_CMD_GLS;
|
2016-10-04 08:25:51 +08:00
|
|
|
nca.req_flags = 0;
|
2016-07-19 09:54:19 +08:00
|
|
|
ret = ncsi_xmit_cmd(&nca);
|
2017-10-19 10:43:06 +08:00
|
|
|
if (ret)
|
2016-07-19 09:54:19 +08:00
|
|
|
netdev_err(ndp->ndev.dev, "Error %d sending GLS\n",
|
|
|
|
ret);
|
2016-10-04 08:25:52 +08:00
|
|
|
break;
|
|
|
|
case NCSI_CHANNEL_MONITOR_WAIT ... NCSI_CHANNEL_MONITOR_WAIT_MAX:
|
|
|
|
break;
|
|
|
|
default:
|
2017-11-08 13:30:44 +08:00
|
|
|
netdev_err(ndp->ndev.dev, "NCSI Channel %d timed out!\n",
|
|
|
|
nc->id);
|
2017-10-19 10:43:08 +08:00
|
|
|
if (!(ndp->flags & NCSI_DEV_HWA)) {
|
2016-07-19 09:54:19 +08:00
|
|
|
ncsi_report_link(ndp, true);
|
2016-10-04 08:25:52 +08:00
|
|
|
ndp->flags |= NCSI_DEV_RESHUFFLE;
|
|
|
|
}
|
2016-07-19 09:54:19 +08:00
|
|
|
|
2017-10-19 10:43:06 +08:00
|
|
|
ncsi_stop_channel_monitor(nc);
|
|
|
|
|
2017-10-19 10:43:08 +08:00
|
|
|
ncm = &nc->modes[NCSI_MODE_LINK];
|
2016-10-04 08:25:47 +08:00
|
|
|
spin_lock_irqsave(&nc->lock, flags);
|
|
|
|
nc->state = NCSI_CHANNEL_INVISIBLE;
|
2017-10-19 10:43:08 +08:00
|
|
|
ncm->data[2] &= ~0x1;
|
2016-10-04 08:25:47 +08:00
|
|
|
spin_unlock_irqrestore(&nc->lock, flags);
|
|
|
|
|
2016-07-19 09:54:19 +08:00
|
|
|
spin_lock_irqsave(&ndp->lock, flags);
|
2017-10-19 10:43:08 +08:00
|
|
|
nc->state = NCSI_CHANNEL_ACTIVE;
|
2016-07-19 09:54:19 +08:00
|
|
|
list_add_tail_rcu(&nc->link, &ndp->channel_queue);
|
|
|
|
spin_unlock_irqrestore(&ndp->lock, flags);
|
|
|
|
ncsi_process_next_channel(ndp);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_lock_irqsave(&nc->lock, flags);
|
2016-10-04 08:25:52 +08:00
|
|
|
nc->monitor.state++;
|
2016-07-19 09:54:19 +08:00
|
|
|
spin_unlock_irqrestore(&nc->lock, flags);
|
2016-10-04 08:25:52 +08:00
|
|
|
mod_timer(&nc->monitor.timer, jiffies + HZ);
|
2016-07-19 09:54:19 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void ncsi_start_channel_monitor(struct ncsi_channel *nc)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&nc->lock, flags);
|
2016-10-04 08:25:52 +08:00
|
|
|
WARN_ON_ONCE(nc->monitor.enabled);
|
|
|
|
nc->monitor.enabled = true;
|
|
|
|
nc->monitor.state = NCSI_CHANNEL_MONITOR_START;
|
2016-07-19 09:54:19 +08:00
|
|
|
spin_unlock_irqrestore(&nc->lock, flags);
|
|
|
|
|
2016-10-04 08:25:52 +08:00
|
|
|
mod_timer(&nc->monitor.timer, jiffies + HZ);
|
2016-07-19 09:54:19 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void ncsi_stop_channel_monitor(struct ncsi_channel *nc)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&nc->lock, flags);
|
2016-10-04 08:25:52 +08:00
|
|
|
if (!nc->monitor.enabled) {
|
2016-07-19 09:54:19 +08:00
|
|
|
spin_unlock_irqrestore(&nc->lock, flags);
|
|
|
|
return;
|
|
|
|
}
|
2016-10-04 08:25:52 +08:00
|
|
|
nc->monitor.enabled = false;
|
2016-07-19 09:54:19 +08:00
|
|
|
spin_unlock_irqrestore(&nc->lock, flags);
|
|
|
|
|
2016-10-04 08:25:52 +08:00
|
|
|
del_timer_sync(&nc->monitor.timer);
|
2016-07-19 09:54:19 +08:00
|
|
|
}
|
|
|
|
|
net/ncsi: Resource management
NCSI spec (DSP0222) defines several objects: package, channel, mode,
filter, version and statistics etc. This introduces the data structs
to represent those objects and implement functions to manage them.
Also, this introduces CONFIG_NET_NCSI for the newly implemented NCSI
stack.
* The user (e.g. netdev driver) dereference NCSI device by
"struct ncsi_dev", which is embedded to "struct ncsi_dev_priv".
The later one is used by NCSI stack internally.
* Every NCSI device can have multiple packages simultaneously, up
to 8 packages. It's represented by "struct ncsi_package" and
identified by 3-bits ID.
* Every NCSI package can have multiple channels, up to 32. It's
represented by "struct ncsi_channel" and identified by 5-bits ID.
* Every NCSI channel has version, statistics, various modes and
filters. They are represented by "struct ncsi_channel_version",
"struct ncsi_channel_stats", "struct ncsi_channel_mode" and
"struct ncsi_channel_filter" separately.
* Apart from AEN (Asynchronous Event Notification), the NCSI stack
works in terms of command and response. This introduces "struct
ncsi_req" to represent a complete NCSI transaction made of NCSI
request and response.
link: https://www.dmtf.org/sites/default/files/standards/documents/DSP0222_1.1.0.pdf
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Acked-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-07-19 09:54:16 +08:00
|
|
|
struct ncsi_channel *ncsi_find_channel(struct ncsi_package *np,
|
|
|
|
unsigned char id)
|
|
|
|
{
|
|
|
|
struct ncsi_channel *nc;
|
|
|
|
|
|
|
|
NCSI_FOR_EACH_CHANNEL(np, nc) {
|
|
|
|
if (nc->id == id)
|
|
|
|
return nc;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ncsi_channel *ncsi_add_channel(struct ncsi_package *np, unsigned char id)
|
|
|
|
{
|
|
|
|
struct ncsi_channel *nc, *tmp;
|
|
|
|
int index;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
nc = kzalloc(sizeof(*nc), GFP_ATOMIC);
|
|
|
|
if (!nc)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
nc->id = id;
|
|
|
|
nc->package = np;
|
|
|
|
nc->state = NCSI_CHANNEL_INACTIVE;
|
2016-10-04 08:25:52 +08:00
|
|
|
nc->monitor.enabled = false;
|
treewide: setup_timer() -> timer_setup() (2 field)
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 <keescook@chromium.org>
2017-10-18 11:21:24 +08:00
|
|
|
timer_setup(&nc->monitor.timer, ncsi_channel_monitor, 0);
|
net/ncsi: Resource management
NCSI spec (DSP0222) defines several objects: package, channel, mode,
filter, version and statistics etc. This introduces the data structs
to represent those objects and implement functions to manage them.
Also, this introduces CONFIG_NET_NCSI for the newly implemented NCSI
stack.
* The user (e.g. netdev driver) dereference NCSI device by
"struct ncsi_dev", which is embedded to "struct ncsi_dev_priv".
The later one is used by NCSI stack internally.
* Every NCSI device can have multiple packages simultaneously, up
to 8 packages. It's represented by "struct ncsi_package" and
identified by 3-bits ID.
* Every NCSI package can have multiple channels, up to 32. It's
represented by "struct ncsi_channel" and identified by 5-bits ID.
* Every NCSI channel has version, statistics, various modes and
filters. They are represented by "struct ncsi_channel_version",
"struct ncsi_channel_stats", "struct ncsi_channel_mode" and
"struct ncsi_channel_filter" separately.
* Apart from AEN (Asynchronous Event Notification), the NCSI stack
works in terms of command and response. This introduces "struct
ncsi_req" to represent a complete NCSI transaction made of NCSI
request and response.
link: https://www.dmtf.org/sites/default/files/standards/documents/DSP0222_1.1.0.pdf
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Acked-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-07-19 09:54:16 +08:00
|
|
|
spin_lock_init(&nc->lock);
|
2016-07-19 09:54:19 +08:00
|
|
|
INIT_LIST_HEAD(&nc->link);
|
net/ncsi: Resource management
NCSI spec (DSP0222) defines several objects: package, channel, mode,
filter, version and statistics etc. This introduces the data structs
to represent those objects and implement functions to manage them.
Also, this introduces CONFIG_NET_NCSI for the newly implemented NCSI
stack.
* The user (e.g. netdev driver) dereference NCSI device by
"struct ncsi_dev", which is embedded to "struct ncsi_dev_priv".
The later one is used by NCSI stack internally.
* Every NCSI device can have multiple packages simultaneously, up
to 8 packages. It's represented by "struct ncsi_package" and
identified by 3-bits ID.
* Every NCSI package can have multiple channels, up to 32. It's
represented by "struct ncsi_channel" and identified by 5-bits ID.
* Every NCSI channel has version, statistics, various modes and
filters. They are represented by "struct ncsi_channel_version",
"struct ncsi_channel_stats", "struct ncsi_channel_mode" and
"struct ncsi_channel_filter" separately.
* Apart from AEN (Asynchronous Event Notification), the NCSI stack
works in terms of command and response. This introduces "struct
ncsi_req" to represent a complete NCSI transaction made of NCSI
request and response.
link: https://www.dmtf.org/sites/default/files/standards/documents/DSP0222_1.1.0.pdf
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Acked-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-07-19 09:54:16 +08:00
|
|
|
for (index = 0; index < NCSI_CAP_MAX; index++)
|
|
|
|
nc->caps[index].index = index;
|
|
|
|
for (index = 0; index < NCSI_MODE_MAX; index++)
|
|
|
|
nc->modes[index].index = index;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&np->lock, flags);
|
|
|
|
tmp = ncsi_find_channel(np, id);
|
|
|
|
if (tmp) {
|
|
|
|
spin_unlock_irqrestore(&np->lock, flags);
|
|
|
|
kfree(nc);
|
|
|
|
return tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
list_add_tail_rcu(&nc->node, &np->channels);
|
|
|
|
np->channel_num++;
|
|
|
|
spin_unlock_irqrestore(&np->lock, flags);
|
|
|
|
|
|
|
|
return nc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ncsi_remove_channel(struct ncsi_channel *nc)
|
|
|
|
{
|
|
|
|
struct ncsi_package *np = nc->package;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&nc->lock, flags);
|
|
|
|
|
2018-04-17 12:23:23 +08:00
|
|
|
/* Release filters */
|
|
|
|
kfree(nc->mac_filter.addrs);
|
|
|
|
kfree(nc->vlan_filter.vids);
|
net/ncsi: Resource management
NCSI spec (DSP0222) defines several objects: package, channel, mode,
filter, version and statistics etc. This introduces the data structs
to represent those objects and implement functions to manage them.
Also, this introduces CONFIG_NET_NCSI for the newly implemented NCSI
stack.
* The user (e.g. netdev driver) dereference NCSI device by
"struct ncsi_dev", which is embedded to "struct ncsi_dev_priv".
The later one is used by NCSI stack internally.
* Every NCSI device can have multiple packages simultaneously, up
to 8 packages. It's represented by "struct ncsi_package" and
identified by 3-bits ID.
* Every NCSI package can have multiple channels, up to 32. It's
represented by "struct ncsi_channel" and identified by 5-bits ID.
* Every NCSI channel has version, statistics, various modes and
filters. They are represented by "struct ncsi_channel_version",
"struct ncsi_channel_stats", "struct ncsi_channel_mode" and
"struct ncsi_channel_filter" separately.
* Apart from AEN (Asynchronous Event Notification), the NCSI stack
works in terms of command and response. This introduces "struct
ncsi_req" to represent a complete NCSI transaction made of NCSI
request and response.
link: https://www.dmtf.org/sites/default/files/standards/documents/DSP0222_1.1.0.pdf
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Acked-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-07-19 09:54:16 +08:00
|
|
|
|
|
|
|
nc->state = NCSI_CHANNEL_INACTIVE;
|
|
|
|
spin_unlock_irqrestore(&nc->lock, flags);
|
2016-07-19 09:54:19 +08:00
|
|
|
ncsi_stop_channel_monitor(nc);
|
net/ncsi: Resource management
NCSI spec (DSP0222) defines several objects: package, channel, mode,
filter, version and statistics etc. This introduces the data structs
to represent those objects and implement functions to manage them.
Also, this introduces CONFIG_NET_NCSI for the newly implemented NCSI
stack.
* The user (e.g. netdev driver) dereference NCSI device by
"struct ncsi_dev", which is embedded to "struct ncsi_dev_priv".
The later one is used by NCSI stack internally.
* Every NCSI device can have multiple packages simultaneously, up
to 8 packages. It's represented by "struct ncsi_package" and
identified by 3-bits ID.
* Every NCSI package can have multiple channels, up to 32. It's
represented by "struct ncsi_channel" and identified by 5-bits ID.
* Every NCSI channel has version, statistics, various modes and
filters. They are represented by "struct ncsi_channel_version",
"struct ncsi_channel_stats", "struct ncsi_channel_mode" and
"struct ncsi_channel_filter" separately.
* Apart from AEN (Asynchronous Event Notification), the NCSI stack
works in terms of command and response. This introduces "struct
ncsi_req" to represent a complete NCSI transaction made of NCSI
request and response.
link: https://www.dmtf.org/sites/default/files/standards/documents/DSP0222_1.1.0.pdf
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Acked-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-07-19 09:54:16 +08:00
|
|
|
|
|
|
|
/* Remove and free channel */
|
|
|
|
spin_lock_irqsave(&np->lock, flags);
|
|
|
|
list_del_rcu(&nc->node);
|
|
|
|
np->channel_num--;
|
|
|
|
spin_unlock_irqrestore(&np->lock, flags);
|
|
|
|
|
|
|
|
kfree(nc);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ncsi_package *ncsi_find_package(struct ncsi_dev_priv *ndp,
|
|
|
|
unsigned char id)
|
|
|
|
{
|
|
|
|
struct ncsi_package *np;
|
|
|
|
|
|
|
|
NCSI_FOR_EACH_PACKAGE(ndp, np) {
|
|
|
|
if (np->id == id)
|
|
|
|
return np;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ncsi_package *ncsi_add_package(struct ncsi_dev_priv *ndp,
|
|
|
|
unsigned char id)
|
|
|
|
{
|
|
|
|
struct ncsi_package *np, *tmp;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
np = kzalloc(sizeof(*np), GFP_ATOMIC);
|
|
|
|
if (!np)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
np->id = id;
|
|
|
|
np->ndp = ndp;
|
|
|
|
spin_lock_init(&np->lock);
|
|
|
|
INIT_LIST_HEAD(&np->channels);
|
|
|
|
|
|
|
|
spin_lock_irqsave(&ndp->lock, flags);
|
|
|
|
tmp = ncsi_find_package(ndp, id);
|
|
|
|
if (tmp) {
|
|
|
|
spin_unlock_irqrestore(&ndp->lock, flags);
|
|
|
|
kfree(np);
|
|
|
|
return tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
list_add_tail_rcu(&np->node, &ndp->packages);
|
|
|
|
ndp->package_num++;
|
|
|
|
spin_unlock_irqrestore(&ndp->lock, flags);
|
|
|
|
|
|
|
|
return np;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ncsi_remove_package(struct ncsi_package *np)
|
|
|
|
{
|
|
|
|
struct ncsi_dev_priv *ndp = np->ndp;
|
|
|
|
struct ncsi_channel *nc, *tmp;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
/* Release all child channels */
|
|
|
|
list_for_each_entry_safe(nc, tmp, &np->channels, node)
|
|
|
|
ncsi_remove_channel(nc);
|
|
|
|
|
|
|
|
/* Remove and free package */
|
|
|
|
spin_lock_irqsave(&ndp->lock, flags);
|
|
|
|
list_del_rcu(&np->node);
|
|
|
|
ndp->package_num--;
|
|
|
|
spin_unlock_irqrestore(&ndp->lock, flags);
|
|
|
|
|
|
|
|
kfree(np);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ncsi_find_package_and_channel(struct ncsi_dev_priv *ndp,
|
|
|
|
unsigned char id,
|
|
|
|
struct ncsi_package **np,
|
|
|
|
struct ncsi_channel **nc)
|
|
|
|
{
|
|
|
|
struct ncsi_package *p;
|
|
|
|
struct ncsi_channel *c;
|
|
|
|
|
|
|
|
p = ncsi_find_package(ndp, NCSI_PACKAGE_INDEX(id));
|
|
|
|
c = p ? ncsi_find_channel(p, NCSI_CHANNEL_INDEX(id)) : NULL;
|
|
|
|
|
|
|
|
if (np)
|
|
|
|
*np = p;
|
|
|
|
if (nc)
|
|
|
|
*nc = c;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* For two consecutive NCSI commands, the packet IDs shouldn't
|
|
|
|
* be same. Otherwise, the bogus response might be replied. So
|
|
|
|
* the available IDs are allocated in round-robin fashion.
|
|
|
|
*/
|
2016-10-04 08:25:51 +08:00
|
|
|
struct ncsi_request *ncsi_alloc_request(struct ncsi_dev_priv *ndp,
|
|
|
|
unsigned int req_flags)
|
net/ncsi: Resource management
NCSI spec (DSP0222) defines several objects: package, channel, mode,
filter, version and statistics etc. This introduces the data structs
to represent those objects and implement functions to manage them.
Also, this introduces CONFIG_NET_NCSI for the newly implemented NCSI
stack.
* The user (e.g. netdev driver) dereference NCSI device by
"struct ncsi_dev", which is embedded to "struct ncsi_dev_priv".
The later one is used by NCSI stack internally.
* Every NCSI device can have multiple packages simultaneously, up
to 8 packages. It's represented by "struct ncsi_package" and
identified by 3-bits ID.
* Every NCSI package can have multiple channels, up to 32. It's
represented by "struct ncsi_channel" and identified by 5-bits ID.
* Every NCSI channel has version, statistics, various modes and
filters. They are represented by "struct ncsi_channel_version",
"struct ncsi_channel_stats", "struct ncsi_channel_mode" and
"struct ncsi_channel_filter" separately.
* Apart from AEN (Asynchronous Event Notification), the NCSI stack
works in terms of command and response. This introduces "struct
ncsi_req" to represent a complete NCSI transaction made of NCSI
request and response.
link: https://www.dmtf.org/sites/default/files/standards/documents/DSP0222_1.1.0.pdf
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Acked-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-07-19 09:54:16 +08:00
|
|
|
{
|
|
|
|
struct ncsi_request *nr = NULL;
|
|
|
|
int i, limit = ARRAY_SIZE(ndp->requests);
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
/* Check if there is one available request until the ceiling */
|
|
|
|
spin_lock_irqsave(&ndp->lock, flags);
|
2016-10-04 08:25:50 +08:00
|
|
|
for (i = ndp->request_id; i < limit; i++) {
|
net/ncsi: Resource management
NCSI spec (DSP0222) defines several objects: package, channel, mode,
filter, version and statistics etc. This introduces the data structs
to represent those objects and implement functions to manage them.
Also, this introduces CONFIG_NET_NCSI for the newly implemented NCSI
stack.
* The user (e.g. netdev driver) dereference NCSI device by
"struct ncsi_dev", which is embedded to "struct ncsi_dev_priv".
The later one is used by NCSI stack internally.
* Every NCSI device can have multiple packages simultaneously, up
to 8 packages. It's represented by "struct ncsi_package" and
identified by 3-bits ID.
* Every NCSI package can have multiple channels, up to 32. It's
represented by "struct ncsi_channel" and identified by 5-bits ID.
* Every NCSI channel has version, statistics, various modes and
filters. They are represented by "struct ncsi_channel_version",
"struct ncsi_channel_stats", "struct ncsi_channel_mode" and
"struct ncsi_channel_filter" separately.
* Apart from AEN (Asynchronous Event Notification), the NCSI stack
works in terms of command and response. This introduces "struct
ncsi_req" to represent a complete NCSI transaction made of NCSI
request and response.
link: https://www.dmtf.org/sites/default/files/standards/documents/DSP0222_1.1.0.pdf
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Acked-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-07-19 09:54:16 +08:00
|
|
|
if (ndp->requests[i].used)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
nr = &ndp->requests[i];
|
|
|
|
nr->used = true;
|
2016-10-04 08:25:51 +08:00
|
|
|
nr->flags = req_flags;
|
2016-10-04 08:25:50 +08:00
|
|
|
ndp->request_id = i + 1;
|
|
|
|
goto found;
|
net/ncsi: Resource management
NCSI spec (DSP0222) defines several objects: package, channel, mode,
filter, version and statistics etc. This introduces the data structs
to represent those objects and implement functions to manage them.
Also, this introduces CONFIG_NET_NCSI for the newly implemented NCSI
stack.
* The user (e.g. netdev driver) dereference NCSI device by
"struct ncsi_dev", which is embedded to "struct ncsi_dev_priv".
The later one is used by NCSI stack internally.
* Every NCSI device can have multiple packages simultaneously, up
to 8 packages. It's represented by "struct ncsi_package" and
identified by 3-bits ID.
* Every NCSI package can have multiple channels, up to 32. It's
represented by "struct ncsi_channel" and identified by 5-bits ID.
* Every NCSI channel has version, statistics, various modes and
filters. They are represented by "struct ncsi_channel_version",
"struct ncsi_channel_stats", "struct ncsi_channel_mode" and
"struct ncsi_channel_filter" separately.
* Apart from AEN (Asynchronous Event Notification), the NCSI stack
works in terms of command and response. This introduces "struct
ncsi_req" to represent a complete NCSI transaction made of NCSI
request and response.
link: https://www.dmtf.org/sites/default/files/standards/documents/DSP0222_1.1.0.pdf
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Acked-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-07-19 09:54:16 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Fail back to check from the starting cursor */
|
2016-10-04 08:25:50 +08:00
|
|
|
for (i = NCSI_REQ_START_IDX; i < ndp->request_id; i++) {
|
net/ncsi: Resource management
NCSI spec (DSP0222) defines several objects: package, channel, mode,
filter, version and statistics etc. This introduces the data structs
to represent those objects and implement functions to manage them.
Also, this introduces CONFIG_NET_NCSI for the newly implemented NCSI
stack.
* The user (e.g. netdev driver) dereference NCSI device by
"struct ncsi_dev", which is embedded to "struct ncsi_dev_priv".
The later one is used by NCSI stack internally.
* Every NCSI device can have multiple packages simultaneously, up
to 8 packages. It's represented by "struct ncsi_package" and
identified by 3-bits ID.
* Every NCSI package can have multiple channels, up to 32. It's
represented by "struct ncsi_channel" and identified by 5-bits ID.
* Every NCSI channel has version, statistics, various modes and
filters. They are represented by "struct ncsi_channel_version",
"struct ncsi_channel_stats", "struct ncsi_channel_mode" and
"struct ncsi_channel_filter" separately.
* Apart from AEN (Asynchronous Event Notification), the NCSI stack
works in terms of command and response. This introduces "struct
ncsi_req" to represent a complete NCSI transaction made of NCSI
request and response.
link: https://www.dmtf.org/sites/default/files/standards/documents/DSP0222_1.1.0.pdf
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Acked-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-07-19 09:54:16 +08:00
|
|
|
if (ndp->requests[i].used)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
nr = &ndp->requests[i];
|
|
|
|
nr->used = true;
|
2016-10-04 08:25:51 +08:00
|
|
|
nr->flags = req_flags;
|
2016-10-04 08:25:50 +08:00
|
|
|
ndp->request_id = i + 1;
|
|
|
|
goto found;
|
net/ncsi: Resource management
NCSI spec (DSP0222) defines several objects: package, channel, mode,
filter, version and statistics etc. This introduces the data structs
to represent those objects and implement functions to manage them.
Also, this introduces CONFIG_NET_NCSI for the newly implemented NCSI
stack.
* The user (e.g. netdev driver) dereference NCSI device by
"struct ncsi_dev", which is embedded to "struct ncsi_dev_priv".
The later one is used by NCSI stack internally.
* Every NCSI device can have multiple packages simultaneously, up
to 8 packages. It's represented by "struct ncsi_package" and
identified by 3-bits ID.
* Every NCSI package can have multiple channels, up to 32. It's
represented by "struct ncsi_channel" and identified by 5-bits ID.
* Every NCSI channel has version, statistics, various modes and
filters. They are represented by "struct ncsi_channel_version",
"struct ncsi_channel_stats", "struct ncsi_channel_mode" and
"struct ncsi_channel_filter" separately.
* Apart from AEN (Asynchronous Event Notification), the NCSI stack
works in terms of command and response. This introduces "struct
ncsi_req" to represent a complete NCSI transaction made of NCSI
request and response.
link: https://www.dmtf.org/sites/default/files/standards/documents/DSP0222_1.1.0.pdf
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Acked-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-07-19 09:54:16 +08:00
|
|
|
}
|
|
|
|
|
2016-10-04 08:25:50 +08:00
|
|
|
found:
|
|
|
|
spin_unlock_irqrestore(&ndp->lock, flags);
|
net/ncsi: Resource management
NCSI spec (DSP0222) defines several objects: package, channel, mode,
filter, version and statistics etc. This introduces the data structs
to represent those objects and implement functions to manage them.
Also, this introduces CONFIG_NET_NCSI for the newly implemented NCSI
stack.
* The user (e.g. netdev driver) dereference NCSI device by
"struct ncsi_dev", which is embedded to "struct ncsi_dev_priv".
The later one is used by NCSI stack internally.
* Every NCSI device can have multiple packages simultaneously, up
to 8 packages. It's represented by "struct ncsi_package" and
identified by 3-bits ID.
* Every NCSI package can have multiple channels, up to 32. It's
represented by "struct ncsi_channel" and identified by 5-bits ID.
* Every NCSI channel has version, statistics, various modes and
filters. They are represented by "struct ncsi_channel_version",
"struct ncsi_channel_stats", "struct ncsi_channel_mode" and
"struct ncsi_channel_filter" separately.
* Apart from AEN (Asynchronous Event Notification), the NCSI stack
works in terms of command and response. This introduces "struct
ncsi_req" to represent a complete NCSI transaction made of NCSI
request and response.
link: https://www.dmtf.org/sites/default/files/standards/documents/DSP0222_1.1.0.pdf
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Acked-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-07-19 09:54:16 +08:00
|
|
|
return nr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ncsi_free_request(struct ncsi_request *nr)
|
|
|
|
{
|
|
|
|
struct ncsi_dev_priv *ndp = nr->ndp;
|
|
|
|
struct sk_buff *cmd, *rsp;
|
|
|
|
unsigned long flags;
|
2016-07-19 09:54:19 +08:00
|
|
|
bool driven;
|
net/ncsi: Resource management
NCSI spec (DSP0222) defines several objects: package, channel, mode,
filter, version and statistics etc. This introduces the data structs
to represent those objects and implement functions to manage them.
Also, this introduces CONFIG_NET_NCSI for the newly implemented NCSI
stack.
* The user (e.g. netdev driver) dereference NCSI device by
"struct ncsi_dev", which is embedded to "struct ncsi_dev_priv".
The later one is used by NCSI stack internally.
* Every NCSI device can have multiple packages simultaneously, up
to 8 packages. It's represented by "struct ncsi_package" and
identified by 3-bits ID.
* Every NCSI package can have multiple channels, up to 32. It's
represented by "struct ncsi_channel" and identified by 5-bits ID.
* Every NCSI channel has version, statistics, various modes and
filters. They are represented by "struct ncsi_channel_version",
"struct ncsi_channel_stats", "struct ncsi_channel_mode" and
"struct ncsi_channel_filter" separately.
* Apart from AEN (Asynchronous Event Notification), the NCSI stack
works in terms of command and response. This introduces "struct
ncsi_req" to represent a complete NCSI transaction made of NCSI
request and response.
link: https://www.dmtf.org/sites/default/files/standards/documents/DSP0222_1.1.0.pdf
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Acked-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-07-19 09:54:16 +08:00
|
|
|
|
|
|
|
if (nr->enabled) {
|
|
|
|
nr->enabled = false;
|
|
|
|
del_timer_sync(&nr->timer);
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_lock_irqsave(&ndp->lock, flags);
|
|
|
|
cmd = nr->cmd;
|
|
|
|
rsp = nr->rsp;
|
|
|
|
nr->cmd = NULL;
|
|
|
|
nr->rsp = NULL;
|
|
|
|
nr->used = false;
|
2016-10-04 08:25:51 +08:00
|
|
|
driven = !!(nr->flags & NCSI_REQ_FLAG_EVENT_DRIVEN);
|
net/ncsi: Resource management
NCSI spec (DSP0222) defines several objects: package, channel, mode,
filter, version and statistics etc. This introduces the data structs
to represent those objects and implement functions to manage them.
Also, this introduces CONFIG_NET_NCSI for the newly implemented NCSI
stack.
* The user (e.g. netdev driver) dereference NCSI device by
"struct ncsi_dev", which is embedded to "struct ncsi_dev_priv".
The later one is used by NCSI stack internally.
* Every NCSI device can have multiple packages simultaneously, up
to 8 packages. It's represented by "struct ncsi_package" and
identified by 3-bits ID.
* Every NCSI package can have multiple channels, up to 32. It's
represented by "struct ncsi_channel" and identified by 5-bits ID.
* Every NCSI channel has version, statistics, various modes and
filters. They are represented by "struct ncsi_channel_version",
"struct ncsi_channel_stats", "struct ncsi_channel_mode" and
"struct ncsi_channel_filter" separately.
* Apart from AEN (Asynchronous Event Notification), the NCSI stack
works in terms of command and response. This introduces "struct
ncsi_req" to represent a complete NCSI transaction made of NCSI
request and response.
link: https://www.dmtf.org/sites/default/files/standards/documents/DSP0222_1.1.0.pdf
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Acked-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-07-19 09:54:16 +08:00
|
|
|
spin_unlock_irqrestore(&ndp->lock, flags);
|
|
|
|
|
2016-07-19 09:54:19 +08:00
|
|
|
if (driven && cmd && --ndp->pending_req_num == 0)
|
|
|
|
schedule_work(&ndp->work);
|
|
|
|
|
net/ncsi: Resource management
NCSI spec (DSP0222) defines several objects: package, channel, mode,
filter, version and statistics etc. This introduces the data structs
to represent those objects and implement functions to manage them.
Also, this introduces CONFIG_NET_NCSI for the newly implemented NCSI
stack.
* The user (e.g. netdev driver) dereference NCSI device by
"struct ncsi_dev", which is embedded to "struct ncsi_dev_priv".
The later one is used by NCSI stack internally.
* Every NCSI device can have multiple packages simultaneously, up
to 8 packages. It's represented by "struct ncsi_package" and
identified by 3-bits ID.
* Every NCSI package can have multiple channels, up to 32. It's
represented by "struct ncsi_channel" and identified by 5-bits ID.
* Every NCSI channel has version, statistics, various modes and
filters. They are represented by "struct ncsi_channel_version",
"struct ncsi_channel_stats", "struct ncsi_channel_mode" and
"struct ncsi_channel_filter" separately.
* Apart from AEN (Asynchronous Event Notification), the NCSI stack
works in terms of command and response. This introduces "struct
ncsi_req" to represent a complete NCSI transaction made of NCSI
request and response.
link: https://www.dmtf.org/sites/default/files/standards/documents/DSP0222_1.1.0.pdf
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Acked-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-07-19 09:54:16 +08:00
|
|
|
/* Release command and response */
|
|
|
|
consume_skb(cmd);
|
|
|
|
consume_skb(rsp);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ncsi_dev *ncsi_find_dev(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct ncsi_dev_priv *ndp;
|
|
|
|
|
|
|
|
NCSI_FOR_EACH_DEV(ndp) {
|
|
|
|
if (ndp->ndev.dev == dev)
|
|
|
|
return &ndp->ndev;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
treewide: setup_timer() -> timer_setup()
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 <keescook@chromium.org>
2017-10-17 05:43:17 +08:00
|
|
|
static void ncsi_request_timeout(struct timer_list *t)
|
net/ncsi: Resource management
NCSI spec (DSP0222) defines several objects: package, channel, mode,
filter, version and statistics etc. This introduces the data structs
to represent those objects and implement functions to manage them.
Also, this introduces CONFIG_NET_NCSI for the newly implemented NCSI
stack.
* The user (e.g. netdev driver) dereference NCSI device by
"struct ncsi_dev", which is embedded to "struct ncsi_dev_priv".
The later one is used by NCSI stack internally.
* Every NCSI device can have multiple packages simultaneously, up
to 8 packages. It's represented by "struct ncsi_package" and
identified by 3-bits ID.
* Every NCSI package can have multiple channels, up to 32. It's
represented by "struct ncsi_channel" and identified by 5-bits ID.
* Every NCSI channel has version, statistics, various modes and
filters. They are represented by "struct ncsi_channel_version",
"struct ncsi_channel_stats", "struct ncsi_channel_mode" and
"struct ncsi_channel_filter" separately.
* Apart from AEN (Asynchronous Event Notification), the NCSI stack
works in terms of command and response. This introduces "struct
ncsi_req" to represent a complete NCSI transaction made of NCSI
request and response.
link: https://www.dmtf.org/sites/default/files/standards/documents/DSP0222_1.1.0.pdf
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Acked-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-07-19 09:54:16 +08:00
|
|
|
{
|
treewide: setup_timer() -> timer_setup()
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 <keescook@chromium.org>
2017-10-17 05:43:17 +08:00
|
|
|
struct ncsi_request *nr = from_timer(nr, t, timer);
|
net/ncsi: Resource management
NCSI spec (DSP0222) defines several objects: package, channel, mode,
filter, version and statistics etc. This introduces the data structs
to represent those objects and implement functions to manage them.
Also, this introduces CONFIG_NET_NCSI for the newly implemented NCSI
stack.
* The user (e.g. netdev driver) dereference NCSI device by
"struct ncsi_dev", which is embedded to "struct ncsi_dev_priv".
The later one is used by NCSI stack internally.
* Every NCSI device can have multiple packages simultaneously, up
to 8 packages. It's represented by "struct ncsi_package" and
identified by 3-bits ID.
* Every NCSI package can have multiple channels, up to 32. It's
represented by "struct ncsi_channel" and identified by 5-bits ID.
* Every NCSI channel has version, statistics, various modes and
filters. They are represented by "struct ncsi_channel_version",
"struct ncsi_channel_stats", "struct ncsi_channel_mode" and
"struct ncsi_channel_filter" separately.
* Apart from AEN (Asynchronous Event Notification), the NCSI stack
works in terms of command and response. This introduces "struct
ncsi_req" to represent a complete NCSI transaction made of NCSI
request and response.
link: https://www.dmtf.org/sites/default/files/standards/documents/DSP0222_1.1.0.pdf
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Acked-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-07-19 09:54:16 +08:00
|
|
|
struct ncsi_dev_priv *ndp = nr->ndp;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
/* If the request already had associated response,
|
|
|
|
* let the response handler to release it.
|
|
|
|
*/
|
|
|
|
spin_lock_irqsave(&ndp->lock, flags);
|
|
|
|
nr->enabled = false;
|
|
|
|
if (nr->rsp || !nr->cmd) {
|
|
|
|
spin_unlock_irqrestore(&ndp->lock, flags);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&ndp->lock, flags);
|
|
|
|
|
|
|
|
/* Release the request */
|
|
|
|
ncsi_free_request(nr);
|
|
|
|
}
|
|
|
|
|
2016-07-19 09:54:19 +08:00
|
|
|
static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp)
|
|
|
|
{
|
|
|
|
struct ncsi_dev *nd = &ndp->ndev;
|
|
|
|
struct ncsi_package *np = ndp->active_package;
|
|
|
|
struct ncsi_channel *nc = ndp->active_channel;
|
|
|
|
struct ncsi_cmd_arg nca;
|
2016-10-04 08:25:47 +08:00
|
|
|
unsigned long flags;
|
2016-07-19 09:54:19 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
nca.ndp = ndp;
|
2016-10-04 08:25:51 +08:00
|
|
|
nca.req_flags = NCSI_REQ_FLAG_EVENT_DRIVEN;
|
2016-07-19 09:54:19 +08:00
|
|
|
switch (nd->state) {
|
|
|
|
case ncsi_dev_state_suspend:
|
|
|
|
nd->state = ncsi_dev_state_suspend_select;
|
|
|
|
/* Fall through */
|
|
|
|
case ncsi_dev_state_suspend_select:
|
2016-10-20 08:45:49 +08:00
|
|
|
ndp->pending_req_num = 1;
|
|
|
|
|
|
|
|
nca.type = NCSI_PKT_CMD_SP;
|
|
|
|
nca.package = np->id;
|
|
|
|
nca.channel = NCSI_RESERVED_CHANNEL;
|
|
|
|
if (ndp->flags & NCSI_DEV_HWA)
|
|
|
|
nca.bytes[0] = 0;
|
|
|
|
else
|
|
|
|
nca.bytes[0] = 1;
|
|
|
|
|
2016-10-20 08:45:50 +08:00
|
|
|
/* To retrieve the last link states of channels in current
|
|
|
|
* package when current active channel needs fail over to
|
|
|
|
* another one. It means we will possibly select another
|
|
|
|
* channel as next active one. The link states of channels
|
|
|
|
* are most important factor of the selection. So we need
|
|
|
|
* accurate link states. Unfortunately, the link states on
|
|
|
|
* inactive channels can't be updated with LSC AEN in time.
|
|
|
|
*/
|
|
|
|
if (ndp->flags & NCSI_DEV_RESHUFFLE)
|
|
|
|
nd->state = ncsi_dev_state_suspend_gls;
|
|
|
|
else
|
|
|
|
nd->state = ncsi_dev_state_suspend_dcnt;
|
2016-10-20 08:45:49 +08:00
|
|
|
ret = ncsi_xmit_cmd(&nca);
|
|
|
|
if (ret)
|
|
|
|
goto error;
|
|
|
|
|
2016-10-20 08:45:50 +08:00
|
|
|
break;
|
|
|
|
case ncsi_dev_state_suspend_gls:
|
|
|
|
ndp->pending_req_num = np->channel_num;
|
|
|
|
|
|
|
|
nca.type = NCSI_PKT_CMD_GLS;
|
|
|
|
nca.package = np->id;
|
|
|
|
|
|
|
|
nd->state = ncsi_dev_state_suspend_dcnt;
|
|
|
|
NCSI_FOR_EACH_CHANNEL(np, nc) {
|
|
|
|
nca.channel = nc->id;
|
|
|
|
ret = ncsi_xmit_cmd(&nca);
|
|
|
|
if (ret)
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2016-10-20 08:45:49 +08:00
|
|
|
break;
|
2016-07-19 09:54:19 +08:00
|
|
|
case ncsi_dev_state_suspend_dcnt:
|
2016-10-20 08:45:49 +08:00
|
|
|
ndp->pending_req_num = 1;
|
|
|
|
|
|
|
|
nca.type = NCSI_PKT_CMD_DCNT;
|
|
|
|
nca.package = np->id;
|
|
|
|
nca.channel = nc->id;
|
|
|
|
|
|
|
|
nd->state = ncsi_dev_state_suspend_dc;
|
|
|
|
ret = ncsi_xmit_cmd(&nca);
|
|
|
|
if (ret)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
break;
|
2016-07-19 09:54:19 +08:00
|
|
|
case ncsi_dev_state_suspend_dc:
|
2016-10-20 08:45:49 +08:00
|
|
|
ndp->pending_req_num = 1;
|
|
|
|
|
|
|
|
nca.type = NCSI_PKT_CMD_DC;
|
|
|
|
nca.package = np->id;
|
|
|
|
nca.channel = nc->id;
|
|
|
|
nca.bytes[0] = 1;
|
|
|
|
|
|
|
|
nd->state = ncsi_dev_state_suspend_deselect;
|
|
|
|
ret = ncsi_xmit_cmd(&nca);
|
|
|
|
if (ret)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
break;
|
2016-07-19 09:54:19 +08:00
|
|
|
case ncsi_dev_state_suspend_deselect:
|
|
|
|
ndp->pending_req_num = 1;
|
|
|
|
|
2016-10-20 08:45:49 +08:00
|
|
|
nca.type = NCSI_PKT_CMD_DP;
|
2016-07-19 09:54:19 +08:00
|
|
|
nca.package = np->id;
|
2016-10-20 08:45:49 +08:00
|
|
|
nca.channel = NCSI_RESERVED_CHANNEL;
|
2016-07-19 09:54:19 +08:00
|
|
|
|
2016-10-20 08:45:49 +08:00
|
|
|
nd->state = ncsi_dev_state_suspend_done;
|
2016-07-19 09:54:19 +08:00
|
|
|
ret = ncsi_xmit_cmd(&nca);
|
2016-10-20 08:45:49 +08:00
|
|
|
if (ret)
|
|
|
|
goto error;
|
2016-07-19 09:54:19 +08:00
|
|
|
|
|
|
|
break;
|
|
|
|
case ncsi_dev_state_suspend_done:
|
2016-10-04 08:25:47 +08:00
|
|
|
spin_lock_irqsave(&nc->lock, flags);
|
|
|
|
nc->state = NCSI_CHANNEL_INACTIVE;
|
|
|
|
spin_unlock_irqrestore(&nc->lock, flags);
|
2016-07-19 09:54:19 +08:00
|
|
|
ncsi_process_next_channel(ndp);
|
|
|
|
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
netdev_warn(nd->dev, "Wrong NCSI state 0x%x in suspend\n",
|
|
|
|
nd->state);
|
|
|
|
}
|
2016-10-20 08:45:49 +08:00
|
|
|
|
|
|
|
return;
|
|
|
|
error:
|
|
|
|
nd->state = ncsi_dev_state_functional;
|
2016-07-19 09:54:19 +08:00
|
|
|
}
|
|
|
|
|
2017-08-28 14:18:42 +08:00
|
|
|
/* Check the VLAN filter bitmap for a set filter, and construct a
|
|
|
|
* "Set VLAN Filter - Disable" packet if found.
|
|
|
|
*/
|
|
|
|
static int clear_one_vid(struct ncsi_dev_priv *ndp, struct ncsi_channel *nc,
|
|
|
|
struct ncsi_cmd_arg *nca)
|
|
|
|
{
|
2018-04-17 12:23:23 +08:00
|
|
|
struct ncsi_channel_vlan_filter *ncf;
|
|
|
|
unsigned long flags;
|
|
|
|
void *bitmap;
|
2017-08-28 14:18:42 +08:00
|
|
|
int index;
|
|
|
|
u16 vid;
|
|
|
|
|
2018-04-17 12:23:23 +08:00
|
|
|
ncf = &nc->vlan_filter;
|
|
|
|
bitmap = &ncf->bitmap;
|
2017-08-28 14:18:42 +08:00
|
|
|
|
2018-04-17 12:23:23 +08:00
|
|
|
spin_lock_irqsave(&nc->lock, flags);
|
|
|
|
index = find_next_bit(bitmap, ncf->n_vids, 0);
|
|
|
|
if (index >= ncf->n_vids) {
|
|
|
|
spin_unlock_irqrestore(&nc->lock, flags);
|
|
|
|
return -1;
|
2017-08-28 14:18:42 +08:00
|
|
|
}
|
2018-04-17 12:23:23 +08:00
|
|
|
vid = ncf->vids[index];
|
2017-08-28 14:18:42 +08:00
|
|
|
|
2018-04-17 12:23:23 +08:00
|
|
|
clear_bit(index, bitmap);
|
|
|
|
ncf->vids[index] = 0;
|
|
|
|
spin_unlock_irqrestore(&nc->lock, flags);
|
2017-08-28 14:18:42 +08:00
|
|
|
|
|
|
|
nca->type = NCSI_PKT_CMD_SVF;
|
|
|
|
nca->words[1] = vid;
|
|
|
|
/* HW filter index starts at 1 */
|
|
|
|
nca->bytes[6] = index + 1;
|
|
|
|
nca->bytes[7] = 0x00;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find an outstanding VLAN tag and constuct a "Set VLAN Filter - Enable"
|
|
|
|
* packet.
|
|
|
|
*/
|
|
|
|
static int set_one_vid(struct ncsi_dev_priv *ndp, struct ncsi_channel *nc,
|
|
|
|
struct ncsi_cmd_arg *nca)
|
|
|
|
{
|
2018-04-17 12:23:23 +08:00
|
|
|
struct ncsi_channel_vlan_filter *ncf;
|
2017-08-28 14:18:42 +08:00
|
|
|
struct vlan_vid *vlan = NULL;
|
2018-04-17 12:23:23 +08:00
|
|
|
unsigned long flags;
|
|
|
|
int i, index;
|
|
|
|
void *bitmap;
|
|
|
|
u16 vid;
|
|
|
|
|
|
|
|
if (list_empty(&ndp->vlan_vids))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ncf = &nc->vlan_filter;
|
|
|
|
bitmap = &ncf->bitmap;
|
2017-08-28 14:18:42 +08:00
|
|
|
|
2018-04-17 12:23:23 +08:00
|
|
|
spin_lock_irqsave(&nc->lock, flags);
|
|
|
|
|
|
|
|
rcu_read_lock();
|
2017-08-28 14:18:42 +08:00
|
|
|
list_for_each_entry_rcu(vlan, &ndp->vlan_vids, list) {
|
2018-04-17 12:23:23 +08:00
|
|
|
vid = vlan->vid;
|
|
|
|
for (i = 0; i < ncf->n_vids; i++)
|
|
|
|
if (ncf->vids[i] == vid) {
|
|
|
|
vid = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (vid)
|
2017-08-28 14:18:42 +08:00
|
|
|
break;
|
|
|
|
}
|
2018-04-17 12:23:23 +08:00
|
|
|
rcu_read_unlock();
|
2017-08-28 14:18:42 +08:00
|
|
|
|
2018-04-17 12:23:23 +08:00
|
|
|
if (!vid) {
|
|
|
|
/* No VLAN ID is not set */
|
|
|
|
spin_unlock_irqrestore(&nc->lock, flags);
|
2017-08-28 14:18:42 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2018-04-17 12:23:23 +08:00
|
|
|
index = find_next_zero_bit(bitmap, ncf->n_vids, 0);
|
|
|
|
if (index < 0 || index >= ncf->n_vids) {
|
2017-08-28 14:18:42 +08:00
|
|
|
netdev_err(ndp->ndev.dev,
|
2018-04-17 12:23:23 +08:00
|
|
|
"Channel %u already has all VLAN filters set\n",
|
|
|
|
nc->id);
|
|
|
|
spin_unlock_irqrestore(&nc->lock, flags);
|
2017-08-28 14:18:42 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2018-04-17 12:23:23 +08:00
|
|
|
ncf->vids[index] = vid;
|
|
|
|
set_bit(index, bitmap);
|
|
|
|
spin_unlock_irqrestore(&nc->lock, flags);
|
|
|
|
|
2017-08-28 14:18:42 +08:00
|
|
|
nca->type = NCSI_PKT_CMD_SVF;
|
2018-04-17 12:23:23 +08:00
|
|
|
nca->words[1] = vid;
|
2017-08-28 14:18:42 +08:00
|
|
|
/* HW filter index starts at 1 */
|
|
|
|
nca->bytes[6] = index + 1;
|
|
|
|
nca->bytes[7] = 0x01;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-07-19 09:54:19 +08:00
|
|
|
static void ncsi_configure_channel(struct ncsi_dev_priv *ndp)
|
|
|
|
{
|
|
|
|
struct ncsi_dev *nd = &ndp->ndev;
|
|
|
|
struct net_device *dev = nd->dev;
|
|
|
|
struct ncsi_package *np = ndp->active_package;
|
|
|
|
struct ncsi_channel *nc = ndp->active_channel;
|
2016-10-20 08:45:51 +08:00
|
|
|
struct ncsi_channel *hot_nc = NULL;
|
2016-07-19 09:54:19 +08:00
|
|
|
struct ncsi_cmd_arg nca;
|
|
|
|
unsigned char index;
|
2016-10-04 08:25:47 +08:00
|
|
|
unsigned long flags;
|
2016-07-19 09:54:19 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
nca.ndp = ndp;
|
2016-10-04 08:25:51 +08:00
|
|
|
nca.req_flags = NCSI_REQ_FLAG_EVENT_DRIVEN;
|
2016-07-19 09:54:19 +08:00
|
|
|
switch (nd->state) {
|
|
|
|
case ncsi_dev_state_config:
|
|
|
|
case ncsi_dev_state_config_sp:
|
|
|
|
ndp->pending_req_num = 1;
|
|
|
|
|
|
|
|
/* Select the specific package */
|
|
|
|
nca.type = NCSI_PKT_CMD_SP;
|
|
|
|
if (ndp->flags & NCSI_DEV_HWA)
|
|
|
|
nca.bytes[0] = 0;
|
|
|
|
else
|
|
|
|
nca.bytes[0] = 1;
|
|
|
|
nca.package = np->id;
|
2016-10-04 08:25:48 +08:00
|
|
|
nca.channel = NCSI_RESERVED_CHANNEL;
|
2016-07-19 09:54:19 +08:00
|
|
|
ret = ncsi_xmit_cmd(&nca);
|
2017-11-08 13:30:44 +08:00
|
|
|
if (ret) {
|
|
|
|
netdev_err(ndp->ndev.dev,
|
|
|
|
"NCSI: Failed to transmit CMD_SP\n");
|
2016-07-19 09:54:19 +08:00
|
|
|
goto error;
|
2017-11-08 13:30:44 +08:00
|
|
|
}
|
2016-07-19 09:54:19 +08:00
|
|
|
|
|
|
|
nd->state = ncsi_dev_state_config_cis;
|
|
|
|
break;
|
|
|
|
case ncsi_dev_state_config_cis:
|
|
|
|
ndp->pending_req_num = 1;
|
|
|
|
|
|
|
|
/* Clear initial state */
|
|
|
|
nca.type = NCSI_PKT_CMD_CIS;
|
|
|
|
nca.package = np->id;
|
|
|
|
nca.channel = nc->id;
|
|
|
|
ret = ncsi_xmit_cmd(&nca);
|
2017-11-08 13:30:44 +08:00
|
|
|
if (ret) {
|
|
|
|
netdev_err(ndp->ndev.dev,
|
|
|
|
"NCSI: Failed to transmit CMD_CIS\n");
|
2016-07-19 09:54:19 +08:00
|
|
|
goto error;
|
2017-11-08 13:30:44 +08:00
|
|
|
}
|
2016-07-19 09:54:19 +08:00
|
|
|
|
2017-08-28 14:18:42 +08:00
|
|
|
nd->state = ncsi_dev_state_config_clear_vids;
|
2016-07-19 09:54:19 +08:00
|
|
|
break;
|
2017-08-28 14:18:42 +08:00
|
|
|
case ncsi_dev_state_config_clear_vids:
|
|
|
|
case ncsi_dev_state_config_svf:
|
|
|
|
case ncsi_dev_state_config_ev:
|
2016-07-19 09:54:19 +08:00
|
|
|
case ncsi_dev_state_config_sma:
|
|
|
|
case ncsi_dev_state_config_ebf:
|
|
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
|
|
case ncsi_dev_state_config_egmf:
|
|
|
|
#endif
|
|
|
|
case ncsi_dev_state_config_ecnt:
|
|
|
|
case ncsi_dev_state_config_ec:
|
|
|
|
case ncsi_dev_state_config_ae:
|
|
|
|
case ncsi_dev_state_config_gls:
|
|
|
|
ndp->pending_req_num = 1;
|
|
|
|
|
|
|
|
nca.package = np->id;
|
|
|
|
nca.channel = nc->id;
|
|
|
|
|
2017-08-28 14:18:42 +08:00
|
|
|
/* Clear any active filters on the channel before setting */
|
|
|
|
if (nd->state == ncsi_dev_state_config_clear_vids) {
|
|
|
|
ret = clear_one_vid(ndp, nc, &nca);
|
|
|
|
if (ret) {
|
|
|
|
nd->state = ncsi_dev_state_config_svf;
|
|
|
|
schedule_work(&ndp->work);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* Repeat */
|
|
|
|
nd->state = ncsi_dev_state_config_clear_vids;
|
|
|
|
/* Add known VLAN tags to the filter */
|
|
|
|
} else if (nd->state == ncsi_dev_state_config_svf) {
|
|
|
|
ret = set_one_vid(ndp, nc, &nca);
|
|
|
|
if (ret) {
|
|
|
|
nd->state = ncsi_dev_state_config_ev;
|
|
|
|
schedule_work(&ndp->work);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* Repeat */
|
|
|
|
nd->state = ncsi_dev_state_config_svf;
|
|
|
|
/* Enable/Disable the VLAN filter */
|
|
|
|
} else if (nd->state == ncsi_dev_state_config_ev) {
|
|
|
|
if (list_empty(&ndp->vlan_vids)) {
|
|
|
|
nca.type = NCSI_PKT_CMD_DV;
|
|
|
|
} else {
|
|
|
|
nca.type = NCSI_PKT_CMD_EV;
|
|
|
|
nca.bytes[3] = NCSI_CAP_VLAN_NO;
|
|
|
|
}
|
|
|
|
nd->state = ncsi_dev_state_config_sma;
|
|
|
|
} else if (nd->state == ncsi_dev_state_config_sma) {
|
2016-07-19 09:54:19 +08:00
|
|
|
/* Use first entry in unicast filter table. Note that
|
|
|
|
* the MAC filter table starts from entry 1 instead of
|
|
|
|
* 0.
|
|
|
|
*/
|
|
|
|
nca.type = NCSI_PKT_CMD_SMA;
|
|
|
|
for (index = 0; index < 6; index++)
|
|
|
|
nca.bytes[index] = dev->dev_addr[index];
|
|
|
|
nca.bytes[6] = 0x1;
|
|
|
|
nca.bytes[7] = 0x1;
|
|
|
|
nd->state = ncsi_dev_state_config_ebf;
|
|
|
|
} else if (nd->state == ncsi_dev_state_config_ebf) {
|
|
|
|
nca.type = NCSI_PKT_CMD_EBF;
|
|
|
|
nca.dwords[0] = nc->caps[NCSI_CAP_BC].cap;
|
|
|
|
nd->state = ncsi_dev_state_config_ecnt;
|
|
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
|
|
if (ndp->inet6_addr_num > 0 &&
|
|
|
|
(nc->caps[NCSI_CAP_GENERIC].cap &
|
|
|
|
NCSI_CAP_GENERIC_MC))
|
|
|
|
nd->state = ncsi_dev_state_config_egmf;
|
|
|
|
else
|
|
|
|
nd->state = ncsi_dev_state_config_ecnt;
|
|
|
|
} else if (nd->state == ncsi_dev_state_config_egmf) {
|
|
|
|
nca.type = NCSI_PKT_CMD_EGMF;
|
|
|
|
nca.dwords[0] = nc->caps[NCSI_CAP_MC].cap;
|
|
|
|
nd->state = ncsi_dev_state_config_ecnt;
|
|
|
|
#endif /* CONFIG_IPV6 */
|
|
|
|
} else if (nd->state == ncsi_dev_state_config_ecnt) {
|
|
|
|
nca.type = NCSI_PKT_CMD_ECNT;
|
|
|
|
nd->state = ncsi_dev_state_config_ec;
|
|
|
|
} else if (nd->state == ncsi_dev_state_config_ec) {
|
|
|
|
/* Enable AEN if it's supported */
|
|
|
|
nca.type = NCSI_PKT_CMD_EC;
|
|
|
|
nd->state = ncsi_dev_state_config_ae;
|
|
|
|
if (!(nc->caps[NCSI_CAP_AEN].cap & NCSI_CAP_AEN_MASK))
|
|
|
|
nd->state = ncsi_dev_state_config_gls;
|
|
|
|
} else if (nd->state == ncsi_dev_state_config_ae) {
|
|
|
|
nca.type = NCSI_PKT_CMD_AE;
|
|
|
|
nca.bytes[0] = 0;
|
|
|
|
nca.dwords[1] = nc->caps[NCSI_CAP_AEN].cap;
|
|
|
|
nd->state = ncsi_dev_state_config_gls;
|
|
|
|
} else if (nd->state == ncsi_dev_state_config_gls) {
|
|
|
|
nca.type = NCSI_PKT_CMD_GLS;
|
|
|
|
nd->state = ncsi_dev_state_config_done;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = ncsi_xmit_cmd(&nca);
|
2017-11-08 13:30:44 +08:00
|
|
|
if (ret) {
|
|
|
|
netdev_err(ndp->ndev.dev,
|
|
|
|
"NCSI: Failed to transmit CMD %x\n",
|
|
|
|
nca.type);
|
2016-07-19 09:54:19 +08:00
|
|
|
goto error;
|
2017-11-08 13:30:44 +08:00
|
|
|
}
|
2016-07-19 09:54:19 +08:00
|
|
|
break;
|
|
|
|
case ncsi_dev_state_config_done:
|
2017-11-08 13:30:44 +08:00
|
|
|
netdev_printk(KERN_DEBUG, ndp->ndev.dev,
|
|
|
|
"NCSI: channel %u config done\n", nc->id);
|
2016-10-04 08:25:47 +08:00
|
|
|
spin_lock_irqsave(&nc->lock, flags);
|
2017-08-28 14:18:42 +08:00
|
|
|
if (nc->reconfigure_needed) {
|
|
|
|
/* This channel's configuration has been updated
|
|
|
|
* part-way during the config state - start the
|
|
|
|
* channel configuration over
|
|
|
|
*/
|
|
|
|
nc->reconfigure_needed = false;
|
|
|
|
nc->state = NCSI_CHANNEL_INACTIVE;
|
|
|
|
spin_unlock_irqrestore(&nc->lock, flags);
|
|
|
|
|
|
|
|
spin_lock_irqsave(&ndp->lock, flags);
|
|
|
|
list_add_tail_rcu(&nc->link, &ndp->channel_queue);
|
|
|
|
spin_unlock_irqrestore(&ndp->lock, flags);
|
|
|
|
|
|
|
|
netdev_printk(KERN_DEBUG, dev,
|
|
|
|
"Dirty NCSI channel state reset\n");
|
|
|
|
ncsi_process_next_channel(ndp);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-10-20 08:45:51 +08:00
|
|
|
if (nc->modes[NCSI_MODE_LINK].data[2] & 0x1) {
|
|
|
|
hot_nc = nc;
|
2016-10-04 08:25:47 +08:00
|
|
|
nc->state = NCSI_CHANNEL_ACTIVE;
|
2016-10-20 08:45:51 +08:00
|
|
|
} else {
|
|
|
|
hot_nc = NULL;
|
2016-10-04 08:25:47 +08:00
|
|
|
nc->state = NCSI_CHANNEL_INACTIVE;
|
2017-11-08 13:30:44 +08:00
|
|
|
netdev_warn(ndp->ndev.dev,
|
|
|
|
"NCSI: channel %u link down after config\n",
|
|
|
|
nc->id);
|
2016-10-20 08:45:51 +08:00
|
|
|
}
|
2016-10-04 08:25:47 +08:00
|
|
|
spin_unlock_irqrestore(&nc->lock, flags);
|
2016-07-19 09:54:19 +08:00
|
|
|
|
2016-10-20 08:45:51 +08:00
|
|
|
/* Update the hot channel */
|
|
|
|
spin_lock_irqsave(&ndp->lock, flags);
|
|
|
|
ndp->hot_channel = hot_nc;
|
|
|
|
spin_unlock_irqrestore(&ndp->lock, flags);
|
|
|
|
|
2016-07-19 09:54:19 +08:00
|
|
|
ncsi_start_channel_monitor(nc);
|
|
|
|
ncsi_process_next_channel(ndp);
|
|
|
|
break;
|
|
|
|
default:
|
2017-11-08 13:30:44 +08:00
|
|
|
netdev_alert(dev, "Wrong NCSI state 0x%x in config\n",
|
|
|
|
nd->state);
|
2016-07-19 09:54:19 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
error:
|
|
|
|
ncsi_report_link(ndp, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ncsi_choose_active_channel(struct ncsi_dev_priv *ndp)
|
|
|
|
{
|
2018-03-05 08:39:05 +08:00
|
|
|
struct ncsi_package *np, *force_package;
|
|
|
|
struct ncsi_channel *nc, *found, *hot_nc, *force_channel;
|
2016-07-19 09:54:19 +08:00
|
|
|
struct ncsi_channel_mode *ncm;
|
|
|
|
unsigned long flags;
|
|
|
|
|
2016-10-20 08:45:51 +08:00
|
|
|
spin_lock_irqsave(&ndp->lock, flags);
|
|
|
|
hot_nc = ndp->hot_channel;
|
2018-03-05 08:39:05 +08:00
|
|
|
force_channel = ndp->force_channel;
|
|
|
|
force_package = ndp->force_package;
|
2016-10-20 08:45:51 +08:00
|
|
|
spin_unlock_irqrestore(&ndp->lock, flags);
|
|
|
|
|
2018-03-05 08:39:05 +08:00
|
|
|
/* Force a specific channel whether or not it has link if we have been
|
|
|
|
* configured to do so
|
|
|
|
*/
|
|
|
|
if (force_package && force_channel) {
|
|
|
|
found = force_channel;
|
|
|
|
ncm = &found->modes[NCSI_MODE_LINK];
|
|
|
|
if (!(ncm->data[2] & 0x1))
|
|
|
|
netdev_info(ndp->ndev.dev,
|
|
|
|
"NCSI: Channel %u forced, but it is link down\n",
|
|
|
|
found->id);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2016-07-19 09:54:19 +08:00
|
|
|
/* The search is done once an inactive channel with up
|
|
|
|
* link is found.
|
|
|
|
*/
|
|
|
|
found = NULL;
|
|
|
|
NCSI_FOR_EACH_PACKAGE(ndp, np) {
|
2018-03-05 08:39:05 +08:00
|
|
|
if (ndp->force_package && np != ndp->force_package)
|
|
|
|
continue;
|
2016-07-19 09:54:19 +08:00
|
|
|
NCSI_FOR_EACH_CHANNEL(np, nc) {
|
2016-10-04 08:25:47 +08:00
|
|
|
spin_lock_irqsave(&nc->lock, flags);
|
|
|
|
|
2016-07-19 09:54:19 +08:00
|
|
|
if (!list_empty(&nc->link) ||
|
2016-10-04 08:25:47 +08:00
|
|
|
nc->state != NCSI_CHANNEL_INACTIVE) {
|
|
|
|
spin_unlock_irqrestore(&nc->lock, flags);
|
2016-07-19 09:54:19 +08:00
|
|
|
continue;
|
2016-10-04 08:25:47 +08:00
|
|
|
}
|
2016-07-19 09:54:19 +08:00
|
|
|
|
|
|
|
if (!found)
|
|
|
|
found = nc;
|
|
|
|
|
2016-10-20 08:45:51 +08:00
|
|
|
if (nc == hot_nc)
|
|
|
|
found = nc;
|
|
|
|
|
2016-07-19 09:54:19 +08:00
|
|
|
ncm = &nc->modes[NCSI_MODE_LINK];
|
|
|
|
if (ncm->data[2] & 0x1) {
|
2016-10-04 08:25:47 +08:00
|
|
|
spin_unlock_irqrestore(&nc->lock, flags);
|
2016-07-19 09:54:19 +08:00
|
|
|
found = nc;
|
|
|
|
goto out;
|
|
|
|
}
|
2016-10-04 08:25:47 +08:00
|
|
|
|
|
|
|
spin_unlock_irqrestore(&nc->lock, flags);
|
2016-07-19 09:54:19 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found) {
|
2017-11-08 13:30:44 +08:00
|
|
|
netdev_warn(ndp->ndev.dev,
|
|
|
|
"NCSI: No channel found with link\n");
|
2016-07-19 09:54:19 +08:00
|
|
|
ncsi_report_link(ndp, true);
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
2017-11-08 13:30:44 +08:00
|
|
|
ncm = &found->modes[NCSI_MODE_LINK];
|
|
|
|
netdev_printk(KERN_DEBUG, ndp->ndev.dev,
|
|
|
|
"NCSI: Channel %u added to queue (link %s)\n",
|
|
|
|
found->id, ncm->data[2] & 0x1 ? "up" : "down");
|
|
|
|
|
2016-07-19 09:54:19 +08:00
|
|
|
out:
|
|
|
|
spin_lock_irqsave(&ndp->lock, flags);
|
|
|
|
list_add_tail_rcu(&found->link, &ndp->channel_queue);
|
|
|
|
spin_unlock_irqrestore(&ndp->lock, flags);
|
|
|
|
|
|
|
|
return ncsi_process_next_channel(ndp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool ncsi_check_hwa(struct ncsi_dev_priv *ndp)
|
|
|
|
{
|
|
|
|
struct ncsi_package *np;
|
|
|
|
struct ncsi_channel *nc;
|
|
|
|
unsigned int cap;
|
2017-10-19 10:43:07 +08:00
|
|
|
bool has_channel = false;
|
2016-07-19 09:54:19 +08:00
|
|
|
|
|
|
|
/* The hardware arbitration is disabled if any one channel
|
|
|
|
* doesn't support explicitly.
|
|
|
|
*/
|
|
|
|
NCSI_FOR_EACH_PACKAGE(ndp, np) {
|
|
|
|
NCSI_FOR_EACH_CHANNEL(np, nc) {
|
2017-10-19 10:43:07 +08:00
|
|
|
has_channel = true;
|
|
|
|
|
2016-07-19 09:54:19 +08:00
|
|
|
cap = nc->caps[NCSI_CAP_GENERIC].cap;
|
|
|
|
if (!(cap & NCSI_CAP_GENERIC_HWA) ||
|
|
|
|
(cap & NCSI_CAP_GENERIC_HWA_MASK) !=
|
|
|
|
NCSI_CAP_GENERIC_HWA_SUPPORT) {
|
|
|
|
ndp->flags &= ~NCSI_DEV_HWA;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-19 10:43:07 +08:00
|
|
|
if (has_channel) {
|
|
|
|
ndp->flags |= NCSI_DEV_HWA;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
ndp->flags &= ~NCSI_DEV_HWA;
|
|
|
|
return false;
|
2016-07-19 09:54:19 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ncsi_enable_hwa(struct ncsi_dev_priv *ndp)
|
|
|
|
{
|
|
|
|
struct ncsi_package *np;
|
|
|
|
struct ncsi_channel *nc;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
/* Move all available channels to processing queue */
|
|
|
|
spin_lock_irqsave(&ndp->lock, flags);
|
|
|
|
NCSI_FOR_EACH_PACKAGE(ndp, np) {
|
|
|
|
NCSI_FOR_EACH_CHANNEL(np, nc) {
|
|
|
|
WARN_ON_ONCE(nc->state != NCSI_CHANNEL_INACTIVE ||
|
|
|
|
!list_empty(&nc->link));
|
|
|
|
ncsi_stop_channel_monitor(nc);
|
|
|
|
list_add_tail_rcu(&nc->link, &ndp->channel_queue);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&ndp->lock, flags);
|
|
|
|
|
|
|
|
/* We can have no channels in extremely case */
|
|
|
|
if (list_empty(&ndp->channel_queue)) {
|
2017-11-08 13:30:44 +08:00
|
|
|
netdev_err(ndp->ndev.dev,
|
|
|
|
"NCSI: No available channels for HWA\n");
|
2016-07-19 09:54:19 +08:00
|
|
|
ncsi_report_link(ndp, false);
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ncsi_process_next_channel(ndp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ncsi_probe_channel(struct ncsi_dev_priv *ndp)
|
|
|
|
{
|
|
|
|
struct ncsi_dev *nd = &ndp->ndev;
|
|
|
|
struct ncsi_package *np;
|
|
|
|
struct ncsi_channel *nc;
|
|
|
|
struct ncsi_cmd_arg nca;
|
|
|
|
unsigned char index;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
nca.ndp = ndp;
|
2016-10-04 08:25:51 +08:00
|
|
|
nca.req_flags = NCSI_REQ_FLAG_EVENT_DRIVEN;
|
2016-07-19 09:54:19 +08:00
|
|
|
switch (nd->state) {
|
|
|
|
case ncsi_dev_state_probe:
|
|
|
|
nd->state = ncsi_dev_state_probe_deselect;
|
|
|
|
/* Fall through */
|
|
|
|
case ncsi_dev_state_probe_deselect:
|
|
|
|
ndp->pending_req_num = 8;
|
|
|
|
|
|
|
|
/* Deselect all possible packages */
|
|
|
|
nca.type = NCSI_PKT_CMD_DP;
|
2016-10-04 08:25:48 +08:00
|
|
|
nca.channel = NCSI_RESERVED_CHANNEL;
|
2016-07-19 09:54:19 +08:00
|
|
|
for (index = 0; index < 8; index++) {
|
|
|
|
nca.package = index;
|
|
|
|
ret = ncsi_xmit_cmd(&nca);
|
|
|
|
if (ret)
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
nd->state = ncsi_dev_state_probe_package;
|
|
|
|
break;
|
|
|
|
case ncsi_dev_state_probe_package:
|
|
|
|
ndp->pending_req_num = 16;
|
|
|
|
|
|
|
|
/* Select all possible packages */
|
|
|
|
nca.type = NCSI_PKT_CMD_SP;
|
|
|
|
nca.bytes[0] = 1;
|
2016-10-04 08:25:48 +08:00
|
|
|
nca.channel = NCSI_RESERVED_CHANNEL;
|
2016-07-19 09:54:19 +08:00
|
|
|
for (index = 0; index < 8; index++) {
|
|
|
|
nca.package = index;
|
|
|
|
ret = ncsi_xmit_cmd(&nca);
|
|
|
|
if (ret)
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Disable all possible packages */
|
|
|
|
nca.type = NCSI_PKT_CMD_DP;
|
|
|
|
for (index = 0; index < 8; index++) {
|
|
|
|
nca.package = index;
|
|
|
|
ret = ncsi_xmit_cmd(&nca);
|
|
|
|
if (ret)
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
nd->state = ncsi_dev_state_probe_channel;
|
|
|
|
break;
|
|
|
|
case ncsi_dev_state_probe_channel:
|
|
|
|
if (!ndp->active_package)
|
|
|
|
ndp->active_package = list_first_or_null_rcu(
|
|
|
|
&ndp->packages, struct ncsi_package, node);
|
|
|
|
else if (list_is_last(&ndp->active_package->node,
|
|
|
|
&ndp->packages))
|
|
|
|
ndp->active_package = NULL;
|
|
|
|
else
|
|
|
|
ndp->active_package = list_next_entry(
|
|
|
|
ndp->active_package, node);
|
|
|
|
|
|
|
|
/* All available packages and channels are enumerated. The
|
|
|
|
* enumeration happens for once when the NCSI interface is
|
|
|
|
* started. So we need continue to start the interface after
|
|
|
|
* the enumeration.
|
|
|
|
*
|
|
|
|
* We have to choose an active channel before configuring it.
|
|
|
|
* Note that we possibly don't have active channel in extreme
|
|
|
|
* situation.
|
|
|
|
*/
|
|
|
|
if (!ndp->active_package) {
|
|
|
|
ndp->flags |= NCSI_DEV_PROBED;
|
|
|
|
if (ncsi_check_hwa(ndp))
|
|
|
|
ncsi_enable_hwa(ndp);
|
|
|
|
else
|
|
|
|
ncsi_choose_active_channel(ndp);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Select the active package */
|
|
|
|
ndp->pending_req_num = 1;
|
|
|
|
nca.type = NCSI_PKT_CMD_SP;
|
|
|
|
nca.bytes[0] = 1;
|
|
|
|
nca.package = ndp->active_package->id;
|
2016-10-04 08:25:48 +08:00
|
|
|
nca.channel = NCSI_RESERVED_CHANNEL;
|
2016-07-19 09:54:19 +08:00
|
|
|
ret = ncsi_xmit_cmd(&nca);
|
|
|
|
if (ret)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
nd->state = ncsi_dev_state_probe_cis;
|
|
|
|
break;
|
|
|
|
case ncsi_dev_state_probe_cis:
|
2016-10-04 08:25:49 +08:00
|
|
|
ndp->pending_req_num = NCSI_RESERVED_CHANNEL;
|
2016-07-19 09:54:19 +08:00
|
|
|
|
|
|
|
/* Clear initial state */
|
|
|
|
nca.type = NCSI_PKT_CMD_CIS;
|
|
|
|
nca.package = ndp->active_package->id;
|
2016-10-04 08:25:49 +08:00
|
|
|
for (index = 0; index < NCSI_RESERVED_CHANNEL; index++) {
|
2016-07-19 09:54:19 +08:00
|
|
|
nca.channel = index;
|
|
|
|
ret = ncsi_xmit_cmd(&nca);
|
|
|
|
if (ret)
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
nd->state = ncsi_dev_state_probe_gvi;
|
|
|
|
break;
|
|
|
|
case ncsi_dev_state_probe_gvi:
|
|
|
|
case ncsi_dev_state_probe_gc:
|
|
|
|
case ncsi_dev_state_probe_gls:
|
|
|
|
np = ndp->active_package;
|
|
|
|
ndp->pending_req_num = np->channel_num;
|
|
|
|
|
|
|
|
/* Retrieve version, capability or link status */
|
|
|
|
if (nd->state == ncsi_dev_state_probe_gvi)
|
|
|
|
nca.type = NCSI_PKT_CMD_GVI;
|
|
|
|
else if (nd->state == ncsi_dev_state_probe_gc)
|
|
|
|
nca.type = NCSI_PKT_CMD_GC;
|
|
|
|
else
|
|
|
|
nca.type = NCSI_PKT_CMD_GLS;
|
|
|
|
|
|
|
|
nca.package = np->id;
|
|
|
|
NCSI_FOR_EACH_CHANNEL(np, nc) {
|
|
|
|
nca.channel = nc->id;
|
|
|
|
ret = ncsi_xmit_cmd(&nca);
|
|
|
|
if (ret)
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nd->state == ncsi_dev_state_probe_gvi)
|
|
|
|
nd->state = ncsi_dev_state_probe_gc;
|
|
|
|
else if (nd->state == ncsi_dev_state_probe_gc)
|
|
|
|
nd->state = ncsi_dev_state_probe_gls;
|
|
|
|
else
|
|
|
|
nd->state = ncsi_dev_state_probe_dp;
|
|
|
|
break;
|
|
|
|
case ncsi_dev_state_probe_dp:
|
|
|
|
ndp->pending_req_num = 1;
|
|
|
|
|
|
|
|
/* Deselect the active package */
|
|
|
|
nca.type = NCSI_PKT_CMD_DP;
|
|
|
|
nca.package = ndp->active_package->id;
|
2016-10-04 08:25:48 +08:00
|
|
|
nca.channel = NCSI_RESERVED_CHANNEL;
|
2016-07-19 09:54:19 +08:00
|
|
|
ret = ncsi_xmit_cmd(&nca);
|
|
|
|
if (ret)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
/* Scan channels in next package */
|
|
|
|
nd->state = ncsi_dev_state_probe_channel;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
netdev_warn(nd->dev, "Wrong NCSI state 0x%0x in enumeration\n",
|
|
|
|
nd->state);
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
error:
|
2017-11-08 13:30:44 +08:00
|
|
|
netdev_err(ndp->ndev.dev,
|
|
|
|
"NCSI: Failed to transmit cmd 0x%x during probe\n",
|
|
|
|
nca.type);
|
2016-07-19 09:54:19 +08:00
|
|
|
ncsi_report_link(ndp, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ncsi_dev_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct ncsi_dev_priv *ndp = container_of(work,
|
|
|
|
struct ncsi_dev_priv, work);
|
|
|
|
struct ncsi_dev *nd = &ndp->ndev;
|
|
|
|
|
|
|
|
switch (nd->state & ncsi_dev_state_major) {
|
|
|
|
case ncsi_dev_state_probe:
|
|
|
|
ncsi_probe_channel(ndp);
|
|
|
|
break;
|
|
|
|
case ncsi_dev_state_suspend:
|
|
|
|
ncsi_suspend_channel(ndp);
|
|
|
|
break;
|
|
|
|
case ncsi_dev_state_config:
|
|
|
|
ncsi_configure_channel(ndp);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
netdev_warn(nd->dev, "Wrong NCSI state 0x%x in workqueue\n",
|
|
|
|
nd->state);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int ncsi_process_next_channel(struct ncsi_dev_priv *ndp)
|
|
|
|
{
|
|
|
|
struct ncsi_channel *nc;
|
|
|
|
int old_state;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&ndp->lock, flags);
|
|
|
|
nc = list_first_or_null_rcu(&ndp->channel_queue,
|
|
|
|
struct ncsi_channel, link);
|
2016-07-22 03:28:34 +08:00
|
|
|
if (!nc) {
|
|
|
|
spin_unlock_irqrestore(&ndp->lock, flags);
|
|
|
|
goto out;
|
2016-07-19 09:54:19 +08:00
|
|
|
}
|
2016-07-22 03:28:34 +08:00
|
|
|
|
|
|
|
list_del_init(&nc->link);
|
2016-07-19 09:54:19 +08:00
|
|
|
spin_unlock_irqrestore(&ndp->lock, flags);
|
|
|
|
|
2016-10-04 08:25:47 +08:00
|
|
|
spin_lock_irqsave(&nc->lock, flags);
|
|
|
|
old_state = nc->state;
|
|
|
|
nc->state = NCSI_CHANNEL_INVISIBLE;
|
|
|
|
spin_unlock_irqrestore(&nc->lock, flags);
|
|
|
|
|
2016-07-19 09:54:19 +08:00
|
|
|
ndp->active_channel = nc;
|
2016-07-22 03:28:34 +08:00
|
|
|
ndp->active_package = nc->package;
|
2016-07-19 09:54:19 +08:00
|
|
|
|
|
|
|
switch (old_state) {
|
|
|
|
case NCSI_CHANNEL_INACTIVE:
|
|
|
|
ndp->ndev.state = ncsi_dev_state_config;
|
2017-11-08 13:30:44 +08:00
|
|
|
netdev_info(ndp->ndev.dev, "NCSI: configuring channel %u\n",
|
|
|
|
nc->id);
|
2016-07-19 09:54:19 +08:00
|
|
|
ncsi_configure_channel(ndp);
|
|
|
|
break;
|
|
|
|
case NCSI_CHANNEL_ACTIVE:
|
|
|
|
ndp->ndev.state = ncsi_dev_state_suspend;
|
2017-11-08 13:30:44 +08:00
|
|
|
netdev_info(ndp->ndev.dev, "NCSI: suspending channel %u\n",
|
|
|
|
nc->id);
|
2016-07-19 09:54:19 +08:00
|
|
|
ncsi_suspend_channel(ndp);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
netdev_err(ndp->ndev.dev, "Invalid state 0x%x on %d:%d\n",
|
2016-10-04 08:25:47 +08:00
|
|
|
old_state, nc->package->id, nc->id);
|
2016-07-19 09:54:19 +08:00
|
|
|
ncsi_report_link(ndp, false);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2016-07-22 03:28:34 +08:00
|
|
|
|
|
|
|
out:
|
|
|
|
ndp->active_channel = NULL;
|
|
|
|
ndp->active_package = NULL;
|
|
|
|
if (ndp->flags & NCSI_DEV_RESHUFFLE) {
|
|
|
|
ndp->flags &= ~NCSI_DEV_RESHUFFLE;
|
|
|
|
return ncsi_choose_active_channel(ndp);
|
|
|
|
}
|
|
|
|
|
2017-11-08 13:30:44 +08:00
|
|
|
netdev_printk(KERN_DEBUG, ndp->ndev.dev,
|
|
|
|
"NCSI: No more channels to process\n");
|
2016-07-22 03:28:34 +08:00
|
|
|
ncsi_report_link(ndp, false);
|
|
|
|
return -ENODEV;
|
2016-07-19 09:54:19 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
|
|
static int ncsi_inet6addr_event(struct notifier_block *this,
|
|
|
|
unsigned long event, void *data)
|
|
|
|
{
|
|
|
|
struct inet6_ifaddr *ifa = data;
|
|
|
|
struct net_device *dev = ifa->idev->dev;
|
|
|
|
struct ncsi_dev *nd = ncsi_find_dev(dev);
|
|
|
|
struct ncsi_dev_priv *ndp = nd ? TO_NCSI_DEV_PRIV(nd) : NULL;
|
|
|
|
struct ncsi_package *np;
|
|
|
|
struct ncsi_channel *nc;
|
|
|
|
struct ncsi_cmd_arg nca;
|
|
|
|
bool action;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!ndp || (ipv6_addr_type(&ifa->addr) &
|
|
|
|
(IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK)))
|
|
|
|
return NOTIFY_OK;
|
|
|
|
|
|
|
|
switch (event) {
|
|
|
|
case NETDEV_UP:
|
|
|
|
action = (++ndp->inet6_addr_num) == 1;
|
|
|
|
nca.type = NCSI_PKT_CMD_EGMF;
|
|
|
|
break;
|
|
|
|
case NETDEV_DOWN:
|
|
|
|
action = (--ndp->inet6_addr_num == 0);
|
|
|
|
nca.type = NCSI_PKT_CMD_DGMF;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return NOTIFY_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We might not have active channel or packages. The IPv6
|
|
|
|
* required multicast will be enabled when active channel
|
|
|
|
* or packages are chosen.
|
|
|
|
*/
|
|
|
|
np = ndp->active_package;
|
|
|
|
nc = ndp->active_channel;
|
|
|
|
if (!action || !np || !nc)
|
|
|
|
return NOTIFY_OK;
|
|
|
|
|
|
|
|
/* We needn't enable or disable it if the function isn't supported */
|
|
|
|
if (!(nc->caps[NCSI_CAP_GENERIC].cap & NCSI_CAP_GENERIC_MC))
|
|
|
|
return NOTIFY_OK;
|
|
|
|
|
|
|
|
nca.ndp = ndp;
|
2016-10-04 08:25:51 +08:00
|
|
|
nca.req_flags = 0;
|
2016-07-19 09:54:19 +08:00
|
|
|
nca.package = np->id;
|
|
|
|
nca.channel = nc->id;
|
|
|
|
nca.dwords[0] = nc->caps[NCSI_CAP_MC].cap;
|
|
|
|
ret = ncsi_xmit_cmd(&nca);
|
|
|
|
if (ret) {
|
|
|
|
netdev_warn(dev, "Fail to %s global multicast filter (%d)\n",
|
|
|
|
(event == NETDEV_UP) ? "enable" : "disable", ret);
|
|
|
|
return NOTIFY_DONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NOTIFY_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct notifier_block ncsi_inet6addr_notifier = {
|
|
|
|
.notifier_call = ncsi_inet6addr_event,
|
|
|
|
};
|
|
|
|
#endif /* CONFIG_IPV6 */
|
|
|
|
|
2017-08-28 14:18:42 +08:00
|
|
|
static int ncsi_kick_channels(struct ncsi_dev_priv *ndp)
|
|
|
|
{
|
|
|
|
struct ncsi_dev *nd = &ndp->ndev;
|
|
|
|
struct ncsi_channel *nc;
|
|
|
|
struct ncsi_package *np;
|
|
|
|
unsigned long flags;
|
|
|
|
unsigned int n = 0;
|
|
|
|
|
|
|
|
NCSI_FOR_EACH_PACKAGE(ndp, np) {
|
|
|
|
NCSI_FOR_EACH_CHANNEL(np, nc) {
|
|
|
|
spin_lock_irqsave(&nc->lock, flags);
|
|
|
|
|
|
|
|
/* Channels may be busy, mark dirty instead of
|
|
|
|
* kicking if;
|
|
|
|
* a) not ACTIVE (configured)
|
|
|
|
* b) in the channel_queue (to be configured)
|
|
|
|
* c) it's ndev is in the config state
|
|
|
|
*/
|
|
|
|
if (nc->state != NCSI_CHANNEL_ACTIVE) {
|
|
|
|
if ((ndp->ndev.state & 0xff00) ==
|
|
|
|
ncsi_dev_state_config ||
|
|
|
|
!list_empty(&nc->link)) {
|
|
|
|
netdev_printk(KERN_DEBUG, nd->dev,
|
2017-11-08 13:30:44 +08:00
|
|
|
"NCSI: channel %p marked dirty\n",
|
2017-08-28 14:18:42 +08:00
|
|
|
nc);
|
|
|
|
nc->reconfigure_needed = true;
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&nc->lock, flags);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&nc->lock, flags);
|
|
|
|
|
|
|
|
ncsi_stop_channel_monitor(nc);
|
|
|
|
spin_lock_irqsave(&nc->lock, flags);
|
|
|
|
nc->state = NCSI_CHANNEL_INACTIVE;
|
|
|
|
spin_unlock_irqrestore(&nc->lock, flags);
|
|
|
|
|
|
|
|
spin_lock_irqsave(&ndp->lock, flags);
|
|
|
|
list_add_tail_rcu(&nc->link, &ndp->channel_queue);
|
|
|
|
spin_unlock_irqrestore(&ndp->lock, flags);
|
|
|
|
|
|
|
|
netdev_printk(KERN_DEBUG, nd->dev,
|
2017-11-08 13:30:44 +08:00
|
|
|
"NCSI: kicked channel %p\n", nc);
|
2017-08-28 14:18:42 +08:00
|
|
|
n++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ncsi_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
|
|
|
|
{
|
|
|
|
struct ncsi_dev_priv *ndp;
|
|
|
|
unsigned int n_vids = 0;
|
|
|
|
struct vlan_vid *vlan;
|
|
|
|
struct ncsi_dev *nd;
|
|
|
|
bool found = false;
|
|
|
|
|
|
|
|
if (vid == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
nd = ncsi_find_dev(dev);
|
|
|
|
if (!nd) {
|
2017-11-08 13:30:44 +08:00
|
|
|
netdev_warn(dev, "NCSI: No net_device?\n");
|
2017-08-28 14:18:42 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ndp = TO_NCSI_DEV_PRIV(nd);
|
|
|
|
|
|
|
|
/* Add the VLAN id to our internal list */
|
|
|
|
list_for_each_entry_rcu(vlan, &ndp->vlan_vids, list) {
|
|
|
|
n_vids++;
|
|
|
|
if (vlan->vid == vid) {
|
|
|
|
netdev_printk(KERN_DEBUG, dev,
|
2017-11-08 13:30:44 +08:00
|
|
|
"NCSI: vid %u already registered\n", vid);
|
2017-08-28 14:18:42 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
2017-10-11 13:54:27 +08:00
|
|
|
if (n_vids >= NCSI_MAX_VLAN_VIDS) {
|
|
|
|
netdev_warn(dev,
|
|
|
|
"tried to add vlan id %u but NCSI max already registered (%u)\n",
|
|
|
|
vid, NCSI_MAX_VLAN_VIDS);
|
|
|
|
return -ENOSPC;
|
2017-08-28 14:18:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
vlan = kzalloc(sizeof(*vlan), GFP_KERNEL);
|
|
|
|
if (!vlan)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
vlan->proto = proto;
|
|
|
|
vlan->vid = vid;
|
|
|
|
list_add_rcu(&vlan->list, &ndp->vlan_vids);
|
|
|
|
|
2017-11-08 13:30:44 +08:00
|
|
|
netdev_printk(KERN_DEBUG, dev, "NCSI: Added new vid %u\n", vid);
|
2017-08-28 14:18:42 +08:00
|
|
|
|
|
|
|
found = ncsi_kick_channels(ndp) != 0;
|
|
|
|
|
|
|
|
return found ? ncsi_process_next_channel(ndp) : 0;
|
|
|
|
}
|
2017-09-05 16:05:47 +08:00
|
|
|
EXPORT_SYMBOL_GPL(ncsi_vlan_rx_add_vid);
|
2017-08-28 14:18:42 +08:00
|
|
|
|
|
|
|
int ncsi_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid)
|
|
|
|
{
|
|
|
|
struct vlan_vid *vlan, *tmp;
|
|
|
|
struct ncsi_dev_priv *ndp;
|
|
|
|
struct ncsi_dev *nd;
|
|
|
|
bool found = false;
|
|
|
|
|
|
|
|
if (vid == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
nd = ncsi_find_dev(dev);
|
|
|
|
if (!nd) {
|
2017-11-08 13:30:44 +08:00
|
|
|
netdev_warn(dev, "NCSI: no net_device?\n");
|
2017-08-28 14:18:42 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ndp = TO_NCSI_DEV_PRIV(nd);
|
|
|
|
|
|
|
|
/* Remove the VLAN id from our internal list */
|
|
|
|
list_for_each_entry_safe(vlan, tmp, &ndp->vlan_vids, list)
|
|
|
|
if (vlan->vid == vid) {
|
|
|
|
netdev_printk(KERN_DEBUG, dev,
|
2017-11-08 13:30:44 +08:00
|
|
|
"NCSI: vid %u found, removing\n", vid);
|
2017-08-28 14:18:42 +08:00
|
|
|
list_del_rcu(&vlan->list);
|
|
|
|
found = true;
|
|
|
|
kfree(vlan);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found) {
|
2017-11-08 13:30:44 +08:00
|
|
|
netdev_err(dev, "NCSI: vid %u wasn't registered!\n", vid);
|
2017-08-28 14:18:42 +08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
found = ncsi_kick_channels(ndp) != 0;
|
|
|
|
|
|
|
|
return found ? ncsi_process_next_channel(ndp) : 0;
|
|
|
|
}
|
2017-09-05 16:05:47 +08:00
|
|
|
EXPORT_SYMBOL_GPL(ncsi_vlan_rx_kill_vid);
|
2017-08-28 14:18:42 +08:00
|
|
|
|
net/ncsi: Resource management
NCSI spec (DSP0222) defines several objects: package, channel, mode,
filter, version and statistics etc. This introduces the data structs
to represent those objects and implement functions to manage them.
Also, this introduces CONFIG_NET_NCSI for the newly implemented NCSI
stack.
* The user (e.g. netdev driver) dereference NCSI device by
"struct ncsi_dev", which is embedded to "struct ncsi_dev_priv".
The later one is used by NCSI stack internally.
* Every NCSI device can have multiple packages simultaneously, up
to 8 packages. It's represented by "struct ncsi_package" and
identified by 3-bits ID.
* Every NCSI package can have multiple channels, up to 32. It's
represented by "struct ncsi_channel" and identified by 5-bits ID.
* Every NCSI channel has version, statistics, various modes and
filters. They are represented by "struct ncsi_channel_version",
"struct ncsi_channel_stats", "struct ncsi_channel_mode" and
"struct ncsi_channel_filter" separately.
* Apart from AEN (Asynchronous Event Notification), the NCSI stack
works in terms of command and response. This introduces "struct
ncsi_req" to represent a complete NCSI transaction made of NCSI
request and response.
link: https://www.dmtf.org/sites/default/files/standards/documents/DSP0222_1.1.0.pdf
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Acked-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-07-19 09:54:16 +08:00
|
|
|
struct ncsi_dev *ncsi_register_dev(struct net_device *dev,
|
|
|
|
void (*handler)(struct ncsi_dev *ndev))
|
|
|
|
{
|
|
|
|
struct ncsi_dev_priv *ndp;
|
|
|
|
struct ncsi_dev *nd;
|
|
|
|
unsigned long flags;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Check if the device has been registered or not */
|
|
|
|
nd = ncsi_find_dev(dev);
|
|
|
|
if (nd)
|
|
|
|
return nd;
|
|
|
|
|
|
|
|
/* Create NCSI device */
|
|
|
|
ndp = kzalloc(sizeof(*ndp), GFP_ATOMIC);
|
|
|
|
if (!ndp)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
nd = &ndp->ndev;
|
|
|
|
nd->state = ncsi_dev_state_registered;
|
|
|
|
nd->dev = dev;
|
|
|
|
nd->handler = handler;
|
2016-07-19 09:54:19 +08:00
|
|
|
ndp->pending_req_num = 0;
|
|
|
|
INIT_LIST_HEAD(&ndp->channel_queue);
|
2017-08-28 14:18:42 +08:00
|
|
|
INIT_LIST_HEAD(&ndp->vlan_vids);
|
2016-07-19 09:54:19 +08:00
|
|
|
INIT_WORK(&ndp->work, ncsi_dev_work);
|
net/ncsi: Resource management
NCSI spec (DSP0222) defines several objects: package, channel, mode,
filter, version and statistics etc. This introduces the data structs
to represent those objects and implement functions to manage them.
Also, this introduces CONFIG_NET_NCSI for the newly implemented NCSI
stack.
* The user (e.g. netdev driver) dereference NCSI device by
"struct ncsi_dev", which is embedded to "struct ncsi_dev_priv".
The later one is used by NCSI stack internally.
* Every NCSI device can have multiple packages simultaneously, up
to 8 packages. It's represented by "struct ncsi_package" and
identified by 3-bits ID.
* Every NCSI package can have multiple channels, up to 32. It's
represented by "struct ncsi_channel" and identified by 5-bits ID.
* Every NCSI channel has version, statistics, various modes and
filters. They are represented by "struct ncsi_channel_version",
"struct ncsi_channel_stats", "struct ncsi_channel_mode" and
"struct ncsi_channel_filter" separately.
* Apart from AEN (Asynchronous Event Notification), the NCSI stack
works in terms of command and response. This introduces "struct
ncsi_req" to represent a complete NCSI transaction made of NCSI
request and response.
link: https://www.dmtf.org/sites/default/files/standards/documents/DSP0222_1.1.0.pdf
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Acked-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-07-19 09:54:16 +08:00
|
|
|
|
|
|
|
/* Initialize private NCSI device */
|
|
|
|
spin_lock_init(&ndp->lock);
|
|
|
|
INIT_LIST_HEAD(&ndp->packages);
|
2016-10-04 08:25:50 +08:00
|
|
|
ndp->request_id = NCSI_REQ_START_IDX;
|
net/ncsi: Resource management
NCSI spec (DSP0222) defines several objects: package, channel, mode,
filter, version and statistics etc. This introduces the data structs
to represent those objects and implement functions to manage them.
Also, this introduces CONFIG_NET_NCSI for the newly implemented NCSI
stack.
* The user (e.g. netdev driver) dereference NCSI device by
"struct ncsi_dev", which is embedded to "struct ncsi_dev_priv".
The later one is used by NCSI stack internally.
* Every NCSI device can have multiple packages simultaneously, up
to 8 packages. It's represented by "struct ncsi_package" and
identified by 3-bits ID.
* Every NCSI package can have multiple channels, up to 32. It's
represented by "struct ncsi_channel" and identified by 5-bits ID.
* Every NCSI channel has version, statistics, various modes and
filters. They are represented by "struct ncsi_channel_version",
"struct ncsi_channel_stats", "struct ncsi_channel_mode" and
"struct ncsi_channel_filter" separately.
* Apart from AEN (Asynchronous Event Notification), the NCSI stack
works in terms of command and response. This introduces "struct
ncsi_req" to represent a complete NCSI transaction made of NCSI
request and response.
link: https://www.dmtf.org/sites/default/files/standards/documents/DSP0222_1.1.0.pdf
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Acked-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-07-19 09:54:16 +08:00
|
|
|
for (i = 0; i < ARRAY_SIZE(ndp->requests); i++) {
|
|
|
|
ndp->requests[i].id = i;
|
|
|
|
ndp->requests[i].ndp = ndp;
|
treewide: setup_timer() -> timer_setup()
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 <keescook@chromium.org>
2017-10-17 05:43:17 +08:00
|
|
|
timer_setup(&ndp->requests[i].timer, ncsi_request_timeout, 0);
|
net/ncsi: Resource management
NCSI spec (DSP0222) defines several objects: package, channel, mode,
filter, version and statistics etc. This introduces the data structs
to represent those objects and implement functions to manage them.
Also, this introduces CONFIG_NET_NCSI for the newly implemented NCSI
stack.
* The user (e.g. netdev driver) dereference NCSI device by
"struct ncsi_dev", which is embedded to "struct ncsi_dev_priv".
The later one is used by NCSI stack internally.
* Every NCSI device can have multiple packages simultaneously, up
to 8 packages. It's represented by "struct ncsi_package" and
identified by 3-bits ID.
* Every NCSI package can have multiple channels, up to 32. It's
represented by "struct ncsi_channel" and identified by 5-bits ID.
* Every NCSI channel has version, statistics, various modes and
filters. They are represented by "struct ncsi_channel_version",
"struct ncsi_channel_stats", "struct ncsi_channel_mode" and
"struct ncsi_channel_filter" separately.
* Apart from AEN (Asynchronous Event Notification), the NCSI stack
works in terms of command and response. This introduces "struct
ncsi_req" to represent a complete NCSI transaction made of NCSI
request and response.
link: https://www.dmtf.org/sites/default/files/standards/documents/DSP0222_1.1.0.pdf
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Acked-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-07-19 09:54:16 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
spin_lock_irqsave(&ncsi_dev_lock, flags);
|
2016-07-19 09:54:19 +08:00
|
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
|
|
ndp->inet6_addr_num = 0;
|
|
|
|
if (list_empty(&ncsi_dev_list))
|
|
|
|
register_inet6addr_notifier(&ncsi_inet6addr_notifier);
|
|
|
|
#endif
|
net/ncsi: Resource management
NCSI spec (DSP0222) defines several objects: package, channel, mode,
filter, version and statistics etc. This introduces the data structs
to represent those objects and implement functions to manage them.
Also, this introduces CONFIG_NET_NCSI for the newly implemented NCSI
stack.
* The user (e.g. netdev driver) dereference NCSI device by
"struct ncsi_dev", which is embedded to "struct ncsi_dev_priv".
The later one is used by NCSI stack internally.
* Every NCSI device can have multiple packages simultaneously, up
to 8 packages. It's represented by "struct ncsi_package" and
identified by 3-bits ID.
* Every NCSI package can have multiple channels, up to 32. It's
represented by "struct ncsi_channel" and identified by 5-bits ID.
* Every NCSI channel has version, statistics, various modes and
filters. They are represented by "struct ncsi_channel_version",
"struct ncsi_channel_stats", "struct ncsi_channel_mode" and
"struct ncsi_channel_filter" separately.
* Apart from AEN (Asynchronous Event Notification), the NCSI stack
works in terms of command and response. This introduces "struct
ncsi_req" to represent a complete NCSI transaction made of NCSI
request and response.
link: https://www.dmtf.org/sites/default/files/standards/documents/DSP0222_1.1.0.pdf
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Acked-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-07-19 09:54:16 +08:00
|
|
|
list_add_tail_rcu(&ndp->node, &ncsi_dev_list);
|
|
|
|
spin_unlock_irqrestore(&ncsi_dev_lock, flags);
|
|
|
|
|
2016-07-19 09:54:19 +08:00
|
|
|
/* Register NCSI packet Rx handler */
|
|
|
|
ndp->ptype.type = cpu_to_be16(ETH_P_NCSI);
|
|
|
|
ndp->ptype.func = ncsi_rcv_rsp;
|
|
|
|
ndp->ptype.dev = dev;
|
|
|
|
dev_add_pack(&ndp->ptype);
|
|
|
|
|
2018-03-05 08:39:05 +08:00
|
|
|
/* Set up generic netlink interface */
|
|
|
|
ncsi_init_netlink(dev);
|
|
|
|
|
net/ncsi: Resource management
NCSI spec (DSP0222) defines several objects: package, channel, mode,
filter, version and statistics etc. This introduces the data structs
to represent those objects and implement functions to manage them.
Also, this introduces CONFIG_NET_NCSI for the newly implemented NCSI
stack.
* The user (e.g. netdev driver) dereference NCSI device by
"struct ncsi_dev", which is embedded to "struct ncsi_dev_priv".
The later one is used by NCSI stack internally.
* Every NCSI device can have multiple packages simultaneously, up
to 8 packages. It's represented by "struct ncsi_package" and
identified by 3-bits ID.
* Every NCSI package can have multiple channels, up to 32. It's
represented by "struct ncsi_channel" and identified by 5-bits ID.
* Every NCSI channel has version, statistics, various modes and
filters. They are represented by "struct ncsi_channel_version",
"struct ncsi_channel_stats", "struct ncsi_channel_mode" and
"struct ncsi_channel_filter" separately.
* Apart from AEN (Asynchronous Event Notification), the NCSI stack
works in terms of command and response. This introduces "struct
ncsi_req" to represent a complete NCSI transaction made of NCSI
request and response.
link: https://www.dmtf.org/sites/default/files/standards/documents/DSP0222_1.1.0.pdf
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Acked-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-07-19 09:54:16 +08:00
|
|
|
return nd;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(ncsi_register_dev);
|
|
|
|
|
2016-07-19 09:54:19 +08:00
|
|
|
int ncsi_start_dev(struct ncsi_dev *nd)
|
|
|
|
{
|
|
|
|
struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
|
2016-10-04 08:25:53 +08:00
|
|
|
int ret;
|
2016-07-19 09:54:19 +08:00
|
|
|
|
|
|
|
if (nd->state != ncsi_dev_state_registered &&
|
|
|
|
nd->state != ncsi_dev_state_functional)
|
|
|
|
return -ENOTTY;
|
|
|
|
|
|
|
|
if (!(ndp->flags & NCSI_DEV_PROBED)) {
|
|
|
|
nd->state = ncsi_dev_state_probe;
|
|
|
|
schedule_work(&ndp->work);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-11-08 13:30:44 +08:00
|
|
|
if (ndp->flags & NCSI_DEV_HWA) {
|
|
|
|
netdev_info(ndp->ndev.dev, "NCSI: Enabling HWA mode\n");
|
2016-10-04 08:25:53 +08:00
|
|
|
ret = ncsi_enable_hwa(ndp);
|
2017-11-08 13:30:44 +08:00
|
|
|
} else {
|
2016-10-04 08:25:53 +08:00
|
|
|
ret = ncsi_choose_active_channel(ndp);
|
2017-11-08 13:30:44 +08:00
|
|
|
}
|
2016-10-04 08:25:53 +08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(ncsi_start_dev);
|
|
|
|
|
|
|
|
void ncsi_stop_dev(struct ncsi_dev *nd)
|
|
|
|
{
|
|
|
|
struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
|
|
|
|
struct ncsi_package *np;
|
|
|
|
struct ncsi_channel *nc;
|
|
|
|
bool chained;
|
|
|
|
int old_state;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
/* Stop the channel monitor and reset channel's state */
|
2016-07-19 09:54:19 +08:00
|
|
|
NCSI_FOR_EACH_PACKAGE(ndp, np) {
|
|
|
|
NCSI_FOR_EACH_CHANNEL(np, nc) {
|
2016-10-04 08:25:53 +08:00
|
|
|
ncsi_stop_channel_monitor(nc);
|
|
|
|
|
2016-10-04 08:25:47 +08:00
|
|
|
spin_lock_irqsave(&nc->lock, flags);
|
|
|
|
chained = !list_empty(&nc->link);
|
|
|
|
old_state = nc->state;
|
|
|
|
nc->state = NCSI_CHANNEL_INACTIVE;
|
|
|
|
spin_unlock_irqrestore(&nc->lock, flags);
|
|
|
|
|
|
|
|
WARN_ON_ONCE(chained ||
|
2016-07-19 09:54:19 +08:00
|
|
|
old_state == NCSI_CHANNEL_INVISIBLE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-08 13:30:44 +08:00
|
|
|
netdev_printk(KERN_DEBUG, ndp->ndev.dev, "NCSI: Stopping device\n");
|
2016-10-04 08:25:53 +08:00
|
|
|
ncsi_report_link(ndp, true);
|
2016-07-19 09:54:19 +08:00
|
|
|
}
|
2016-10-04 08:25:53 +08:00
|
|
|
EXPORT_SYMBOL_GPL(ncsi_stop_dev);
|
2016-07-19 09:54:19 +08:00
|
|
|
|
net/ncsi: Resource management
NCSI spec (DSP0222) defines several objects: package, channel, mode,
filter, version and statistics etc. This introduces the data structs
to represent those objects and implement functions to manage them.
Also, this introduces CONFIG_NET_NCSI for the newly implemented NCSI
stack.
* The user (e.g. netdev driver) dereference NCSI device by
"struct ncsi_dev", which is embedded to "struct ncsi_dev_priv".
The later one is used by NCSI stack internally.
* Every NCSI device can have multiple packages simultaneously, up
to 8 packages. It's represented by "struct ncsi_package" and
identified by 3-bits ID.
* Every NCSI package can have multiple channels, up to 32. It's
represented by "struct ncsi_channel" and identified by 5-bits ID.
* Every NCSI channel has version, statistics, various modes and
filters. They are represented by "struct ncsi_channel_version",
"struct ncsi_channel_stats", "struct ncsi_channel_mode" and
"struct ncsi_channel_filter" separately.
* Apart from AEN (Asynchronous Event Notification), the NCSI stack
works in terms of command and response. This introduces "struct
ncsi_req" to represent a complete NCSI transaction made of NCSI
request and response.
link: https://www.dmtf.org/sites/default/files/standards/documents/DSP0222_1.1.0.pdf
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Acked-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-07-19 09:54:16 +08:00
|
|
|
void ncsi_unregister_dev(struct ncsi_dev *nd)
|
|
|
|
{
|
|
|
|
struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
|
|
|
|
struct ncsi_package *np, *tmp;
|
|
|
|
unsigned long flags;
|
|
|
|
|
2016-07-19 09:54:19 +08:00
|
|
|
dev_remove_pack(&ndp->ptype);
|
|
|
|
|
net/ncsi: Resource management
NCSI spec (DSP0222) defines several objects: package, channel, mode,
filter, version and statistics etc. This introduces the data structs
to represent those objects and implement functions to manage them.
Also, this introduces CONFIG_NET_NCSI for the newly implemented NCSI
stack.
* The user (e.g. netdev driver) dereference NCSI device by
"struct ncsi_dev", which is embedded to "struct ncsi_dev_priv".
The later one is used by NCSI stack internally.
* Every NCSI device can have multiple packages simultaneously, up
to 8 packages. It's represented by "struct ncsi_package" and
identified by 3-bits ID.
* Every NCSI package can have multiple channels, up to 32. It's
represented by "struct ncsi_channel" and identified by 5-bits ID.
* Every NCSI channel has version, statistics, various modes and
filters. They are represented by "struct ncsi_channel_version",
"struct ncsi_channel_stats", "struct ncsi_channel_mode" and
"struct ncsi_channel_filter" separately.
* Apart from AEN (Asynchronous Event Notification), the NCSI stack
works in terms of command and response. This introduces "struct
ncsi_req" to represent a complete NCSI transaction made of NCSI
request and response.
link: https://www.dmtf.org/sites/default/files/standards/documents/DSP0222_1.1.0.pdf
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Acked-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-07-19 09:54:16 +08:00
|
|
|
list_for_each_entry_safe(np, tmp, &ndp->packages, node)
|
|
|
|
ncsi_remove_package(np);
|
|
|
|
|
|
|
|
spin_lock_irqsave(&ncsi_dev_lock, flags);
|
|
|
|
list_del_rcu(&ndp->node);
|
2016-07-19 09:54:19 +08:00
|
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
|
|
if (list_empty(&ncsi_dev_list))
|
|
|
|
unregister_inet6addr_notifier(&ncsi_inet6addr_notifier);
|
|
|
|
#endif
|
net/ncsi: Resource management
NCSI spec (DSP0222) defines several objects: package, channel, mode,
filter, version and statistics etc. This introduces the data structs
to represent those objects and implement functions to manage them.
Also, this introduces CONFIG_NET_NCSI for the newly implemented NCSI
stack.
* The user (e.g. netdev driver) dereference NCSI device by
"struct ncsi_dev", which is embedded to "struct ncsi_dev_priv".
The later one is used by NCSI stack internally.
* Every NCSI device can have multiple packages simultaneously, up
to 8 packages. It's represented by "struct ncsi_package" and
identified by 3-bits ID.
* Every NCSI package can have multiple channels, up to 32. It's
represented by "struct ncsi_channel" and identified by 5-bits ID.
* Every NCSI channel has version, statistics, various modes and
filters. They are represented by "struct ncsi_channel_version",
"struct ncsi_channel_stats", "struct ncsi_channel_mode" and
"struct ncsi_channel_filter" separately.
* Apart from AEN (Asynchronous Event Notification), the NCSI stack
works in terms of command and response. This introduces "struct
ncsi_req" to represent a complete NCSI transaction made of NCSI
request and response.
link: https://www.dmtf.org/sites/default/files/standards/documents/DSP0222_1.1.0.pdf
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Acked-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-07-19 09:54:16 +08:00
|
|
|
spin_unlock_irqrestore(&ncsi_dev_lock, flags);
|
|
|
|
|
2018-03-05 08:39:05 +08:00
|
|
|
ncsi_unregister_netlink(nd->dev);
|
|
|
|
|
net/ncsi: Resource management
NCSI spec (DSP0222) defines several objects: package, channel, mode,
filter, version and statistics etc. This introduces the data structs
to represent those objects and implement functions to manage them.
Also, this introduces CONFIG_NET_NCSI for the newly implemented NCSI
stack.
* The user (e.g. netdev driver) dereference NCSI device by
"struct ncsi_dev", which is embedded to "struct ncsi_dev_priv".
The later one is used by NCSI stack internally.
* Every NCSI device can have multiple packages simultaneously, up
to 8 packages. It's represented by "struct ncsi_package" and
identified by 3-bits ID.
* Every NCSI package can have multiple channels, up to 32. It's
represented by "struct ncsi_channel" and identified by 5-bits ID.
* Every NCSI channel has version, statistics, various modes and
filters. They are represented by "struct ncsi_channel_version",
"struct ncsi_channel_stats", "struct ncsi_channel_mode" and
"struct ncsi_channel_filter" separately.
* Apart from AEN (Asynchronous Event Notification), the NCSI stack
works in terms of command and response. This introduces "struct
ncsi_req" to represent a complete NCSI transaction made of NCSI
request and response.
link: https://www.dmtf.org/sites/default/files/standards/documents/DSP0222_1.1.0.pdf
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Acked-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-07-19 09:54:16 +08:00
|
|
|
kfree(ndp);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(ncsi_unregister_dev);
|