@@ -78,7 +78,7 @@ struct net {
struct user_namespace *user_ns; /* Owning user namespace */
struct ucounts *ucounts;
spinlock_t nsid_lock;
- struct idr netns_ids;
+ struct xarray netns_ids;
struct ns_common ns;
@@ -188,27 +188,18 @@ static void ops_free_list(const struct pernet_operations *ops,
/* should be called with nsid_lock held */
static int alloc_netid(struct net *net, struct net *peer, int reqid)
{
- int min = 0, max = 0;
+ int ret;
if (reqid >= 0) {
- min = reqid;
- max = reqid + 1;
+ ret = xa_insert(&net->netns_ids, reqid, peer, GFP_ATOMIC);
+ } else {
+ ret = xa_alloc(&net->netns_ids, &reqid, peer, xa_limit_31b,
+ GFP_ATOMIC);
}
- return idr_alloc(&net->netns_ids, peer, min, max, GFP_ATOMIC);
-}
-
-/* This function is used by idr_for_each(). If net is equal to peer, the
- * function returns the id so that idr_for_each() stops. Because we cannot
- * returns the id 0 (idr_for_each() will not stop), we return the magic value
- * NET_ID_ZERO (-1) for it.
- */
-#define NET_ID_ZERO -1
-static int net_eq_idr(int id, void *net, void *peer)
-{
- if (net_eq(net, peer))
- return id ? : NET_ID_ZERO;
- return 0;
+ if (ret)
+ return ret;
+ return reqid;
}
/* Should be called with nsid_lock held. If a new id is assigned, the bool alloc
@@ -217,16 +208,17 @@ static int net_eq_idr(int id, void *net, void *peer)
*/
static int __peernet2id_alloc(struct net *net, struct net *peer, bool *alloc)
{
- int id = idr_for_each(&net->netns_ids, net_eq_idr, peer);
+ int id;
+ struct net *tmp;
+ unsigned long index;
bool alloc_it = *alloc;
- *alloc = false;
-
- /* Magic value for id 0. */
- if (id == NET_ID_ZERO)
- return 0;
- if (id > 0)
- return id;
+ xa_for_each(&net->netns_ids, index, tmp) {
+ if (net_eq(tmp, peer)) {
+ *alloc = false;
+ return index;
+ }
+ }
if (alloc_it) {
id = alloc_netid(net, peer, -1);
@@ -261,7 +253,7 @@ int peernet2id_alloc(struct net *net, struct net *peer)
* When peer is obtained from RCU lists, we may race with
* its cleanup. Check whether it's alive, and this guarantees
* we never hash a peer back to net->netns_ids, after it has
- * just been idr_remove()'d from there in cleanup_net().
+ * just been removed from there in cleanup_net().
*/
if (maybe_get_net(peer))
alive = alloc = true;
@@ -303,7 +295,7 @@ struct net *get_net_ns_by_id(struct net *net, int id)
return NULL;
rcu_read_lock();
- peer = idr_find(&net->netns_ids, id);
+ peer = xa_load(&net->netns_ids, id);
if (peer)
peer = maybe_get_net(peer);
rcu_read_unlock();
@@ -326,7 +318,7 @@ static __net_init int setup_net(struct net *net, struct user_namespace *user_ns)
get_random_bytes(&net->hash_mix, sizeof(u32));
net->dev_base_seq = 1;
net->user_ns = user_ns;
- idr_init(&net->netns_ids);
+ xa_init_flags(&net->netns_ids, XA_FLAGS_ALLOC);
spin_lock_init(&net->nsid_lock);
mutex_init(&net->ipv4.ra_mutex);
@@ -529,16 +521,14 @@ static void unhash_nsid(struct net *net, struct net *last)
spin_lock_bh(&tmp->nsid_lock);
id = __peernet2id(tmp, net);
if (id >= 0)
- idr_remove(&tmp->netns_ids, id);
+ xa_erase(&tmp->netns_ids, id);
spin_unlock_bh(&tmp->nsid_lock);
if (id >= 0)
rtnl_net_notifyid(tmp, RTM_DELNSID, id);
if (tmp == last)
break;
}
- spin_lock_bh(&net->nsid_lock);
- idr_destroy(&net->netns_ids);
- spin_unlock_bh(&net->nsid_lock);
+ BUG_ON(!xa_empty(&net->netns_ids));
}
static LLIST_HEAD(cleanup_list);
@@ -766,7 +756,7 @@ static int rtnl_net_newid(struct sk_buff *skb, struct nlmsghdr *nlh,
if (err >= 0) {
rtnl_net_notifyid(net, RTM_NEWNSID, err);
err = 0;
- } else if (err == -ENOSPC && nsid >= 0) {
+ } else if (err == -EBUSY && nsid >= 0) {
err = -EEXIST;
NL_SET_BAD_ATTR(extack, tb[NETNSA_NSID]);
NL_SET_ERR_MSG(extack, "The specified nsid is already used");
@@ -946,9 +936,9 @@ struct rtnl_net_dump_cb {
int s_idx;
};
-static int rtnl_net_dumpid_one(int id, void *peer, void *data)
+static int rtnl_net_dumpid_one(int id, struct net *peer,
+ struct rtnl_net_dump_cb *net_cb)
{
- struct rtnl_net_dump_cb *net_cb = (struct rtnl_net_dump_cb *)data;
int ret;
if (net_cb->idx < net_cb->s_idx)
@@ -1022,6 +1012,8 @@ static int rtnl_net_dumpid(struct sk_buff *skb, struct netlink_callback *cb)
.idx = 0,
.s_idx = cb->args[0],
};
+ struct net *peer;
+ unsigned long index;
int err = 0;
if (cb->strict_check) {
@@ -1038,7 +1030,8 @@ static int rtnl_net_dumpid(struct sk_buff *skb, struct netlink_callback *cb)
err = -EAGAIN;
goto end;
}
- idr_for_each(&net_cb.tgt_net->netns_ids, rtnl_net_dumpid_one, &net_cb);
+ xa_for_each(&net_cb.tgt_net->netns_ids, index, peer)
+ rtnl_net_dumpid_one(index, peer, &net_cb);
if (net_cb.fillargs.add_ref &&
!net_eq(net_cb.ref_net, net_cb.tgt_net))
spin_unlock_bh(&net_cb.ref_net->nsid_lock);