Commit a93e3b17 authored by Petr Machata's avatar Petr Machata Committed by David S. Miller

switchdev: Add a blocking notifier chain

In general one can't assume that a switchdev notifier is called in a
non-atomic context, and correspondingly, the switchdev notifier chain is
an atomic one.

However, port object addition and deletion messages are delivered from a
process context. Even the MDB addition messages, whose delivery is
scheduled from atomic context, are queued and the delivery itself takes
place in blocking context. For VLAN messages in particular, keeping the
blocking nature is important for error reporting.

Therefore introduce a blocking notifier chain and related service
functions to distribute the notifications for which a blocking context
can be assumed.
Signed-off-by: default avatarPetr Machata <petrm@mellanox.com>
Reviewed-by: default avatarJiri Pirko <jiri@mellanox.com>
Reviewed-by: default avatarIdo Schimmel <idosch@mellanox.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent ec394af5
...@@ -182,10 +182,17 @@ int switchdev_port_obj_add(struct net_device *dev, ...@@ -182,10 +182,17 @@ int switchdev_port_obj_add(struct net_device *dev,
const struct switchdev_obj *obj); const struct switchdev_obj *obj);
int switchdev_port_obj_del(struct net_device *dev, int switchdev_port_obj_del(struct net_device *dev,
const struct switchdev_obj *obj); const struct switchdev_obj *obj);
int register_switchdev_notifier(struct notifier_block *nb); int register_switchdev_notifier(struct notifier_block *nb);
int unregister_switchdev_notifier(struct notifier_block *nb); int unregister_switchdev_notifier(struct notifier_block *nb);
int call_switchdev_notifiers(unsigned long val, struct net_device *dev, int call_switchdev_notifiers(unsigned long val, struct net_device *dev,
struct switchdev_notifier_info *info); struct switchdev_notifier_info *info);
int register_switchdev_blocking_notifier(struct notifier_block *nb);
int unregister_switchdev_blocking_notifier(struct notifier_block *nb);
int call_switchdev_blocking_notifiers(unsigned long val, struct net_device *dev,
struct switchdev_notifier_info *info);
void switchdev_port_fwd_mark_set(struct net_device *dev, void switchdev_port_fwd_mark_set(struct net_device *dev,
struct net_device *group_dev, struct net_device *group_dev,
bool joining); bool joining);
...@@ -241,6 +248,26 @@ static inline int call_switchdev_notifiers(unsigned long val, ...@@ -241,6 +248,26 @@ static inline int call_switchdev_notifiers(unsigned long val,
return NOTIFY_DONE; return NOTIFY_DONE;
} }
static inline int
register_switchdev_blocking_notifier(struct notifier_block *nb)
{
return 0;
}
static inline int
unregister_switchdev_blocking_notifier(struct notifier_block *nb)
{
return 0;
}
static inline int
call_switchdev_blocking_notifiers(unsigned long val,
struct net_device *dev,
struct switchdev_notifier_info *info)
{
return NOTIFY_DONE;
}
static inline bool switchdev_port_same_parent_id(struct net_device *a, static inline bool switchdev_port_same_parent_id(struct net_device *a,
struct net_device *b) struct net_device *b)
{ {
......
...@@ -535,6 +535,7 @@ int switchdev_port_obj_del(struct net_device *dev, ...@@ -535,6 +535,7 @@ int switchdev_port_obj_del(struct net_device *dev,
EXPORT_SYMBOL_GPL(switchdev_port_obj_del); EXPORT_SYMBOL_GPL(switchdev_port_obj_del);
static ATOMIC_NOTIFIER_HEAD(switchdev_notif_chain); static ATOMIC_NOTIFIER_HEAD(switchdev_notif_chain);
static BLOCKING_NOTIFIER_HEAD(switchdev_blocking_notif_chain);
/** /**
* register_switchdev_notifier - Register notifier * register_switchdev_notifier - Register notifier
...@@ -576,6 +577,31 @@ int call_switchdev_notifiers(unsigned long val, struct net_device *dev, ...@@ -576,6 +577,31 @@ int call_switchdev_notifiers(unsigned long val, struct net_device *dev,
} }
EXPORT_SYMBOL_GPL(call_switchdev_notifiers); EXPORT_SYMBOL_GPL(call_switchdev_notifiers);
int register_switchdev_blocking_notifier(struct notifier_block *nb)
{
struct blocking_notifier_head *chain = &switchdev_blocking_notif_chain;
return blocking_notifier_chain_register(chain, nb);
}
EXPORT_SYMBOL_GPL(register_switchdev_blocking_notifier);
int unregister_switchdev_blocking_notifier(struct notifier_block *nb)
{
struct blocking_notifier_head *chain = &switchdev_blocking_notif_chain;
return blocking_notifier_chain_unregister(chain, nb);
}
EXPORT_SYMBOL_GPL(unregister_switchdev_blocking_notifier);
int call_switchdev_blocking_notifiers(unsigned long val, struct net_device *dev,
struct switchdev_notifier_info *info)
{
info->dev = dev;
return blocking_notifier_call_chain(&switchdev_blocking_notif_chain,
val, info);
}
EXPORT_SYMBOL_GPL(call_switchdev_blocking_notifiers);
bool switchdev_port_same_parent_id(struct net_device *a, bool switchdev_port_same_parent_id(struct net_device *a,
struct net_device *b) struct net_device *b)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment