devlink: Add per devlink instance lock

This is a preparation before introducing resources and hot reload support.
Currently there are two global lock where one protects all devlink access,
and the second one protects devlink port access. This patch adds per devlink
instance lock which protects the internal members which are the sb/dpipe/
resource/ports. By introducing this lock the global devlink port lock can
be discarded.

Signed-off-by: Arkadi Sharshevsky <arkadis@mellanox.com>
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Arkadi Sharshevsky 2018-01-15 08:59:02 +01:00 committed by David S. Miller
parent d98c8ccdeb
commit 2406e7e546
2 changed files with 78 additions and 59 deletions

View File

@ -30,6 +30,7 @@ struct devlink {
const struct devlink_ops *ops; const struct devlink_ops *ops;
struct device *dev; struct device *dev;
possible_net_t _net; possible_net_t _net;
struct mutex lock;
char priv[0] __aligned(NETDEV_ALIGN); char priv[0] __aligned(NETDEV_ALIGN);
}; };

View File

@ -92,12 +92,6 @@ static LIST_HEAD(devlink_list);
*/ */
static DEFINE_MUTEX(devlink_mutex); static DEFINE_MUTEX(devlink_mutex);
/* devlink_port_mutex
*
* Shared lock to guard lists of ports in all devlink devices.
*/
static DEFINE_MUTEX(devlink_port_mutex);
static struct net *devlink_net(const struct devlink *devlink) static struct net *devlink_net(const struct devlink *devlink)
{ {
return read_pnet(&devlink->_net); return read_pnet(&devlink->_net);
@ -335,15 +329,18 @@ devlink_sb_tc_index_get_from_info(struct devlink_sb *devlink_sb,
#define DEVLINK_NL_FLAG_NEED_DEVLINK BIT(0) #define DEVLINK_NL_FLAG_NEED_DEVLINK BIT(0)
#define DEVLINK_NL_FLAG_NEED_PORT BIT(1) #define DEVLINK_NL_FLAG_NEED_PORT BIT(1)
#define DEVLINK_NL_FLAG_NEED_SB BIT(2) #define DEVLINK_NL_FLAG_NEED_SB BIT(2)
#define DEVLINK_NL_FLAG_LOCK_PORTS BIT(3)
/* port is not needed but we need to ensure they don't /* The per devlink instance lock is taken by default in the pre-doit
* change in the middle of command * operation, yet several commands do not require this. The global
*/ * devlink lock is taken and protects from disruption by user-calls.
*/
#define DEVLINK_NL_FLAG_NO_LOCK BIT(3)
static int devlink_nl_pre_doit(const struct genl_ops *ops, static int devlink_nl_pre_doit(const struct genl_ops *ops,
struct sk_buff *skb, struct genl_info *info) struct sk_buff *skb, struct genl_info *info)
{ {
struct devlink *devlink; struct devlink *devlink;
int err;
mutex_lock(&devlink_mutex); mutex_lock(&devlink_mutex);
devlink = devlink_get_from_info(info); devlink = devlink_get_from_info(info);
@ -351,44 +348,47 @@ static int devlink_nl_pre_doit(const struct genl_ops *ops,
mutex_unlock(&devlink_mutex); mutex_unlock(&devlink_mutex);
return PTR_ERR(devlink); return PTR_ERR(devlink);
} }
if (~ops->internal_flags & DEVLINK_NL_FLAG_NO_LOCK)
mutex_lock(&devlink->lock);
if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_DEVLINK) { if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_DEVLINK) {
info->user_ptr[0] = devlink; info->user_ptr[0] = devlink;
} else if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_PORT) { } else if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_PORT) {
struct devlink_port *devlink_port; struct devlink_port *devlink_port;
mutex_lock(&devlink_port_mutex);
devlink_port = devlink_port_get_from_info(devlink, info); devlink_port = devlink_port_get_from_info(devlink, info);
if (IS_ERR(devlink_port)) { if (IS_ERR(devlink_port)) {
mutex_unlock(&devlink_port_mutex); err = PTR_ERR(devlink_port);
mutex_unlock(&devlink_mutex); goto unlock;
return PTR_ERR(devlink_port);
} }
info->user_ptr[0] = devlink_port; info->user_ptr[0] = devlink_port;
} }
if (ops->internal_flags & DEVLINK_NL_FLAG_LOCK_PORTS) {
mutex_lock(&devlink_port_mutex);
}
if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_SB) { if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_SB) {
struct devlink_sb *devlink_sb; struct devlink_sb *devlink_sb;
devlink_sb = devlink_sb_get_from_info(devlink, info); devlink_sb = devlink_sb_get_from_info(devlink, info);
if (IS_ERR(devlink_sb)) { if (IS_ERR(devlink_sb)) {
if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_PORT) err = PTR_ERR(devlink_sb);
mutex_unlock(&devlink_port_mutex); goto unlock;
mutex_unlock(&devlink_mutex);
return PTR_ERR(devlink_sb);
} }
info->user_ptr[1] = devlink_sb; info->user_ptr[1] = devlink_sb;
} }
return 0; return 0;
unlock:
if (~ops->internal_flags & DEVLINK_NL_FLAG_NO_LOCK)
mutex_unlock(&devlink->lock);
mutex_unlock(&devlink_mutex);
return err;
} }
static void devlink_nl_post_doit(const struct genl_ops *ops, static void devlink_nl_post_doit(const struct genl_ops *ops,
struct sk_buff *skb, struct genl_info *info) struct sk_buff *skb, struct genl_info *info)
{ {
if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_PORT || struct devlink *devlink;
ops->internal_flags & DEVLINK_NL_FLAG_LOCK_PORTS)
mutex_unlock(&devlink_port_mutex); devlink = devlink_get_from_info(info);
if (~ops->internal_flags & DEVLINK_NL_FLAG_NO_LOCK)
mutex_unlock(&devlink->lock);
mutex_unlock(&devlink_mutex); mutex_unlock(&devlink_mutex);
} }
@ -614,10 +614,10 @@ static int devlink_nl_cmd_port_get_dumpit(struct sk_buff *msg,
int err; int err;
mutex_lock(&devlink_mutex); mutex_lock(&devlink_mutex);
mutex_lock(&devlink_port_mutex);
list_for_each_entry(devlink, &devlink_list, list) { list_for_each_entry(devlink, &devlink_list, list) {
if (!net_eq(devlink_net(devlink), sock_net(msg->sk))) if (!net_eq(devlink_net(devlink), sock_net(msg->sk)))
continue; continue;
mutex_lock(&devlink->lock);
list_for_each_entry(devlink_port, &devlink->port_list, list) { list_for_each_entry(devlink_port, &devlink->port_list, list) {
if (idx < start) { if (idx < start) {
idx++; idx++;
@ -628,13 +628,15 @@ static int devlink_nl_cmd_port_get_dumpit(struct sk_buff *msg,
NETLINK_CB(cb->skb).portid, NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, cb->nlh->nlmsg_seq,
NLM_F_MULTI); NLM_F_MULTI);
if (err) if (err) {
mutex_unlock(&devlink->lock);
goto out; goto out;
}
idx++; idx++;
} }
mutex_unlock(&devlink->lock);
} }
out: out:
mutex_unlock(&devlink_port_mutex);
mutex_unlock(&devlink_mutex); mutex_unlock(&devlink_mutex);
cb->args[0] = idx; cb->args[0] = idx;
@ -801,6 +803,7 @@ static int devlink_nl_cmd_sb_get_dumpit(struct sk_buff *msg,
list_for_each_entry(devlink, &devlink_list, list) { list_for_each_entry(devlink, &devlink_list, list) {
if (!net_eq(devlink_net(devlink), sock_net(msg->sk))) if (!net_eq(devlink_net(devlink), sock_net(msg->sk)))
continue; continue;
mutex_lock(&devlink->lock);
list_for_each_entry(devlink_sb, &devlink->sb_list, list) { list_for_each_entry(devlink_sb, &devlink->sb_list, list) {
if (idx < start) { if (idx < start) {
idx++; idx++;
@ -811,10 +814,13 @@ static int devlink_nl_cmd_sb_get_dumpit(struct sk_buff *msg,
NETLINK_CB(cb->skb).portid, NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, cb->nlh->nlmsg_seq,
NLM_F_MULTI); NLM_F_MULTI);
if (err) if (err) {
mutex_unlock(&devlink->lock);
goto out; goto out;
}
idx++; idx++;
} }
mutex_unlock(&devlink->lock);
} }
out: out:
mutex_unlock(&devlink_mutex); mutex_unlock(&devlink_mutex);
@ -935,14 +941,18 @@ static int devlink_nl_cmd_sb_pool_get_dumpit(struct sk_buff *msg,
if (!net_eq(devlink_net(devlink), sock_net(msg->sk)) || if (!net_eq(devlink_net(devlink), sock_net(msg->sk)) ||
!devlink->ops || !devlink->ops->sb_pool_get) !devlink->ops || !devlink->ops->sb_pool_get)
continue; continue;
mutex_lock(&devlink->lock);
list_for_each_entry(devlink_sb, &devlink->sb_list, list) { list_for_each_entry(devlink_sb, &devlink->sb_list, list) {
err = __sb_pool_get_dumpit(msg, start, &idx, devlink, err = __sb_pool_get_dumpit(msg, start, &idx, devlink,
devlink_sb, devlink_sb,
NETLINK_CB(cb->skb).portid, NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq); cb->nlh->nlmsg_seq);
if (err && err != -EOPNOTSUPP) if (err && err != -EOPNOTSUPP) {
mutex_unlock(&devlink->lock);
goto out; goto out;
}
} }
mutex_unlock(&devlink->lock);
} }
out: out:
mutex_unlock(&devlink_mutex); mutex_unlock(&devlink_mutex);
@ -1123,22 +1133,24 @@ static int devlink_nl_cmd_sb_port_pool_get_dumpit(struct sk_buff *msg,
int err; int err;
mutex_lock(&devlink_mutex); mutex_lock(&devlink_mutex);
mutex_lock(&devlink_port_mutex);
list_for_each_entry(devlink, &devlink_list, list) { list_for_each_entry(devlink, &devlink_list, list) {
if (!net_eq(devlink_net(devlink), sock_net(msg->sk)) || if (!net_eq(devlink_net(devlink), sock_net(msg->sk)) ||
!devlink->ops || !devlink->ops->sb_port_pool_get) !devlink->ops || !devlink->ops->sb_port_pool_get)
continue; continue;
mutex_lock(&devlink->lock);
list_for_each_entry(devlink_sb, &devlink->sb_list, list) { list_for_each_entry(devlink_sb, &devlink->sb_list, list) {
err = __sb_port_pool_get_dumpit(msg, start, &idx, err = __sb_port_pool_get_dumpit(msg, start, &idx,
devlink, devlink_sb, devlink, devlink_sb,
NETLINK_CB(cb->skb).portid, NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq); cb->nlh->nlmsg_seq);
if (err && err != -EOPNOTSUPP) if (err && err != -EOPNOTSUPP) {
mutex_unlock(&devlink->lock);
goto out; goto out;
}
} }
mutex_unlock(&devlink->lock);
} }
out: out:
mutex_unlock(&devlink_port_mutex);
mutex_unlock(&devlink_mutex); mutex_unlock(&devlink_mutex);
cb->args[0] = idx; cb->args[0] = idx;
@ -1347,23 +1359,26 @@ devlink_nl_cmd_sb_tc_pool_bind_get_dumpit(struct sk_buff *msg,
int err; int err;
mutex_lock(&devlink_mutex); mutex_lock(&devlink_mutex);
mutex_lock(&devlink_port_mutex);
list_for_each_entry(devlink, &devlink_list, list) { list_for_each_entry(devlink, &devlink_list, list) {
if (!net_eq(devlink_net(devlink), sock_net(msg->sk)) || if (!net_eq(devlink_net(devlink), sock_net(msg->sk)) ||
!devlink->ops || !devlink->ops->sb_tc_pool_bind_get) !devlink->ops || !devlink->ops->sb_tc_pool_bind_get)
continue; continue;
mutex_lock(&devlink->lock);
list_for_each_entry(devlink_sb, &devlink->sb_list, list) { list_for_each_entry(devlink_sb, &devlink->sb_list, list) {
err = __sb_tc_pool_bind_get_dumpit(msg, start, &idx, err = __sb_tc_pool_bind_get_dumpit(msg, start, &idx,
devlink, devlink,
devlink_sb, devlink_sb,
NETLINK_CB(cb->skb).portid, NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq); cb->nlh->nlmsg_seq);
if (err && err != -EOPNOTSUPP) if (err && err != -EOPNOTSUPP) {
mutex_unlock(&devlink->lock);
goto out; goto out;
}
} }
mutex_unlock(&devlink->lock);
} }
out: out:
mutex_unlock(&devlink_port_mutex);
mutex_unlock(&devlink_mutex); mutex_unlock(&devlink_mutex);
cb->args[0] = idx; cb->args[0] = idx;
@ -2322,14 +2337,16 @@ static const struct genl_ops devlink_nl_ops[] = {
.doit = devlink_nl_cmd_port_split_doit, .doit = devlink_nl_cmd_port_split_doit,
.policy = devlink_nl_policy, .policy = devlink_nl_policy,
.flags = GENL_ADMIN_PERM, .flags = GENL_ADMIN_PERM,
.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK, .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
DEVLINK_NL_FLAG_NO_LOCK,
}, },
{ {
.cmd = DEVLINK_CMD_PORT_UNSPLIT, .cmd = DEVLINK_CMD_PORT_UNSPLIT,
.doit = devlink_nl_cmd_port_unsplit_doit, .doit = devlink_nl_cmd_port_unsplit_doit,
.policy = devlink_nl_policy, .policy = devlink_nl_policy,
.flags = GENL_ADMIN_PERM, .flags = GENL_ADMIN_PERM,
.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK, .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
DEVLINK_NL_FLAG_NO_LOCK,
}, },
{ {
.cmd = DEVLINK_CMD_SB_GET, .cmd = DEVLINK_CMD_SB_GET,
@ -2397,8 +2414,7 @@ static const struct genl_ops devlink_nl_ops[] = {
.policy = devlink_nl_policy, .policy = devlink_nl_policy,
.flags = GENL_ADMIN_PERM, .flags = GENL_ADMIN_PERM,
.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK | .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
DEVLINK_NL_FLAG_NEED_SB | DEVLINK_NL_FLAG_NEED_SB,
DEVLINK_NL_FLAG_LOCK_PORTS,
}, },
{ {
.cmd = DEVLINK_CMD_SB_OCC_MAX_CLEAR, .cmd = DEVLINK_CMD_SB_OCC_MAX_CLEAR,
@ -2406,8 +2422,7 @@ static const struct genl_ops devlink_nl_ops[] = {
.policy = devlink_nl_policy, .policy = devlink_nl_policy,
.flags = GENL_ADMIN_PERM, .flags = GENL_ADMIN_PERM,
.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK | .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
DEVLINK_NL_FLAG_NEED_SB | DEVLINK_NL_FLAG_NEED_SB,
DEVLINK_NL_FLAG_LOCK_PORTS,
}, },
{ {
.cmd = DEVLINK_CMD_ESWITCH_GET, .cmd = DEVLINK_CMD_ESWITCH_GET,
@ -2488,6 +2503,7 @@ struct devlink *devlink_alloc(const struct devlink_ops *ops, size_t priv_size)
INIT_LIST_HEAD(&devlink->port_list); INIT_LIST_HEAD(&devlink->port_list);
INIT_LIST_HEAD(&devlink->sb_list); INIT_LIST_HEAD(&devlink->sb_list);
INIT_LIST_HEAD_RCU(&devlink->dpipe_table_list); INIT_LIST_HEAD_RCU(&devlink->dpipe_table_list);
mutex_init(&devlink->lock);
return devlink; return devlink;
} }
EXPORT_SYMBOL_GPL(devlink_alloc); EXPORT_SYMBOL_GPL(devlink_alloc);
@ -2550,16 +2566,16 @@ int devlink_port_register(struct devlink *devlink,
struct devlink_port *devlink_port, struct devlink_port *devlink_port,
unsigned int port_index) unsigned int port_index)
{ {
mutex_lock(&devlink_port_mutex); mutex_lock(&devlink->lock);
if (devlink_port_index_exists(devlink, port_index)) { if (devlink_port_index_exists(devlink, port_index)) {
mutex_unlock(&devlink_port_mutex); mutex_unlock(&devlink->lock);
return -EEXIST; return -EEXIST;
} }
devlink_port->devlink = devlink; devlink_port->devlink = devlink;
devlink_port->index = port_index; devlink_port->index = port_index;
devlink_port->registered = true; devlink_port->registered = true;
list_add_tail(&devlink_port->list, &devlink->port_list); list_add_tail(&devlink_port->list, &devlink->port_list);
mutex_unlock(&devlink_port_mutex); mutex_unlock(&devlink->lock);
devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_NEW); devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_NEW);
return 0; return 0;
} }
@ -2572,10 +2588,12 @@ EXPORT_SYMBOL_GPL(devlink_port_register);
*/ */
void devlink_port_unregister(struct devlink_port *devlink_port) void devlink_port_unregister(struct devlink_port *devlink_port)
{ {
struct devlink *devlink = devlink_port->devlink;
devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_DEL); devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_DEL);
mutex_lock(&devlink_port_mutex); mutex_lock(&devlink->lock);
list_del(&devlink_port->list); list_del(&devlink_port->list);
mutex_unlock(&devlink_port_mutex); mutex_unlock(&devlink->lock);
} }
EXPORT_SYMBOL_GPL(devlink_port_unregister); EXPORT_SYMBOL_GPL(devlink_port_unregister);
@ -2651,7 +2669,7 @@ int devlink_sb_register(struct devlink *devlink, unsigned int sb_index,
struct devlink_sb *devlink_sb; struct devlink_sb *devlink_sb;
int err = 0; int err = 0;
mutex_lock(&devlink_mutex); mutex_lock(&devlink->lock);
if (devlink_sb_index_exists(devlink, sb_index)) { if (devlink_sb_index_exists(devlink, sb_index)) {
err = -EEXIST; err = -EEXIST;
goto unlock; goto unlock;
@ -2670,7 +2688,7 @@ int devlink_sb_register(struct devlink *devlink, unsigned int sb_index,
devlink_sb->egress_tc_count = egress_tc_count; devlink_sb->egress_tc_count = egress_tc_count;
list_add_tail(&devlink_sb->list, &devlink->sb_list); list_add_tail(&devlink_sb->list, &devlink->sb_list);
unlock: unlock:
mutex_unlock(&devlink_mutex); mutex_unlock(&devlink->lock);
return err; return err;
} }
EXPORT_SYMBOL_GPL(devlink_sb_register); EXPORT_SYMBOL_GPL(devlink_sb_register);
@ -2679,11 +2697,11 @@ void devlink_sb_unregister(struct devlink *devlink, unsigned int sb_index)
{ {
struct devlink_sb *devlink_sb; struct devlink_sb *devlink_sb;
mutex_lock(&devlink_mutex); mutex_lock(&devlink->lock);
devlink_sb = devlink_sb_get_by_index(devlink, sb_index); devlink_sb = devlink_sb_get_by_index(devlink, sb_index);
WARN_ON(!devlink_sb); WARN_ON(!devlink_sb);
list_del(&devlink_sb->list); list_del(&devlink_sb->list);
mutex_unlock(&devlink_mutex); mutex_unlock(&devlink->lock);
kfree(devlink_sb); kfree(devlink_sb);
} }
EXPORT_SYMBOL_GPL(devlink_sb_unregister); EXPORT_SYMBOL_GPL(devlink_sb_unregister);
@ -2699,9 +2717,9 @@ EXPORT_SYMBOL_GPL(devlink_sb_unregister);
int devlink_dpipe_headers_register(struct devlink *devlink, int devlink_dpipe_headers_register(struct devlink *devlink,
struct devlink_dpipe_headers *dpipe_headers) struct devlink_dpipe_headers *dpipe_headers)
{ {
mutex_lock(&devlink_mutex); mutex_lock(&devlink->lock);
devlink->dpipe_headers = dpipe_headers; devlink->dpipe_headers = dpipe_headers;
mutex_unlock(&devlink_mutex); mutex_unlock(&devlink->lock);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(devlink_dpipe_headers_register); EXPORT_SYMBOL_GPL(devlink_dpipe_headers_register);
@ -2715,9 +2733,9 @@ EXPORT_SYMBOL_GPL(devlink_dpipe_headers_register);
*/ */
void devlink_dpipe_headers_unregister(struct devlink *devlink) void devlink_dpipe_headers_unregister(struct devlink *devlink)
{ {
mutex_lock(&devlink_mutex); mutex_lock(&devlink->lock);
devlink->dpipe_headers = NULL; devlink->dpipe_headers = NULL;
mutex_unlock(&devlink_mutex); mutex_unlock(&devlink->lock);
} }
EXPORT_SYMBOL_GPL(devlink_dpipe_headers_unregister); EXPORT_SYMBOL_GPL(devlink_dpipe_headers_unregister);
@ -2783,9 +2801,9 @@ int devlink_dpipe_table_register(struct devlink *devlink,
table->priv = priv; table->priv = priv;
table->counter_control_extern = counter_control_extern; table->counter_control_extern = counter_control_extern;
mutex_lock(&devlink_mutex); mutex_lock(&devlink->lock);
list_add_tail_rcu(&table->list, &devlink->dpipe_table_list); list_add_tail_rcu(&table->list, &devlink->dpipe_table_list);
mutex_unlock(&devlink_mutex); mutex_unlock(&devlink->lock);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(devlink_dpipe_table_register); EXPORT_SYMBOL_GPL(devlink_dpipe_table_register);
@ -2801,17 +2819,17 @@ void devlink_dpipe_table_unregister(struct devlink *devlink,
{ {
struct devlink_dpipe_table *table; struct devlink_dpipe_table *table;
mutex_lock(&devlink_mutex); mutex_lock(&devlink->lock);
table = devlink_dpipe_table_find(&devlink->dpipe_table_list, table = devlink_dpipe_table_find(&devlink->dpipe_table_list,
table_name); table_name);
if (!table) if (!table)
goto unlock; goto unlock;
list_del_rcu(&table->list); list_del_rcu(&table->list);
mutex_unlock(&devlink_mutex); mutex_unlock(&devlink->lock);
kfree_rcu(table, rcu); kfree_rcu(table, rcu);
return; return;
unlock: unlock:
mutex_unlock(&devlink_mutex); mutex_unlock(&devlink->lock);
} }
EXPORT_SYMBOL_GPL(devlink_dpipe_table_unregister); EXPORT_SYMBOL_GPL(devlink_dpipe_table_unregister);