if (WARN(ret >= MODULE_NAME_LEN, "truncated: '%s' (len %d)", module_name, ret))
                return;
 
-       nfnl_unlock(NFNL_SUBSYS_NFTABLES);
+       mutex_unlock(&net->nft.commit_mutex);
        request_module("%s", module_name);
-       nfnl_lock(NFNL_SUBSYS_NFTABLES);
+       mutex_lock(&net->nft.commit_mutex);
 }
 #endif
 
+static void lockdep_nfnl_nft_mutex_not_held(void)
+{
+#ifdef CONFIG_PROVE_LOCKING
+       WARN_ON_ONCE(lockdep_nfnl_is_held(NFNL_SUBSYS_NFTABLES));
+#endif
+}
+
 static const struct nft_chain_type *
 nf_tables_chain_type_lookup(struct net *net, const struct nlattr *nla,
                            u8 family, bool autoload)
        type = __nf_tables_chain_type_lookup(nla, family);
        if (type != NULL)
                return type;
+
+       lockdep_nfnl_nft_mutex_not_held();
 #ifdef CONFIG_MODULES
        if (autoload) {
                nft_request_module(net, "nft-chain-%u-%.*s", family,
        struct nft_ctx ctx;
        int err;
 
+       lockdep_assert_held(&net->nft.commit_mutex);
        attr = nla[NFTA_TABLE_NAME];
        table = nft_table_lookup(net, attr, family, genmask);
        if (IS_ERR(table)) {
        return ERR_PTR(-ENOENT);
 }
 
-static struct nft_chain *nft_chain_lookup(struct nft_table *table,
+static bool lockdep_commit_lock_is_held(struct net *net)
+{
+#ifdef CONFIG_PROVE_LOCKING
+       return lockdep_is_held(&net->nft.commit_mutex);
+#else
+       return true;
+#endif
+}
+
+static struct nft_chain *nft_chain_lookup(struct net *net,
+                                         struct nft_table *table,
                                          const struct nlattr *nla, u8 genmask)
 {
        char search[NFT_CHAIN_MAXNAMELEN + 1];
        nla_strlcpy(search, nla, sizeof(search));
 
        WARN_ON(!rcu_read_lock_held() &&
-               !lockdep_nfnl_is_held(NFNL_SUBSYS_NFTABLES));
+               !lockdep_commit_lock_is_held(net));
 
        chain = ERR_PTR(-ENOENT);
        rcu_read_lock();
                return PTR_ERR(table);
        }
 
-       chain = nft_chain_lookup(table, nla[NFTA_CHAIN_NAME], genmask);
+       chain = nft_chain_lookup(net, table, nla[NFTA_CHAIN_NAME], genmask);
        if (IS_ERR(chain)) {
                NL_SET_BAD_ATTR(extack, nla[NFTA_CHAIN_NAME]);
                return PTR_ERR(chain);
        struct net_device *dev;
        int err;
 
+       lockdep_assert_held(&net->nft.commit_mutex);
+       lockdep_nfnl_nft_mutex_not_held();
+
        err = nla_parse_nested(ha, NFTA_HOOK_MAX, nla[NFTA_CHAIN_HOOK],
                               nft_hook_policy, NULL);
        if (err < 0)
            nla[NFTA_CHAIN_NAME]) {
                struct nft_chain *chain2;
 
-               chain2 = nft_chain_lookup(table, nla[NFTA_CHAIN_NAME], genmask);
+               chain2 = nft_chain_lookup(ctx->net, table,
+                                         nla[NFTA_CHAIN_NAME], genmask);
                if (!IS_ERR(chain2))
                        return -EEXIST;
        }
 
        create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false;
 
+       lockdep_assert_held(&net->nft.commit_mutex);
+
        table = nft_table_lookup(net, nla[NFTA_CHAIN_TABLE], family, genmask);
        if (IS_ERR(table)) {
                NL_SET_BAD_ATTR(extack, nla[NFTA_CHAIN_TABLE]);
                }
                attr = nla[NFTA_CHAIN_HANDLE];
        } else {
-               chain = nft_chain_lookup(table, attr, genmask);
+               chain = nft_chain_lookup(net, table, attr, genmask);
                if (IS_ERR(chain)) {
                        if (PTR_ERR(chain) != -ENOENT) {
                                NL_SET_BAD_ATTR(extack, attr);
                chain = nft_chain_lookup_byhandle(table, handle, genmask);
        } else {
                attr = nla[NFTA_CHAIN_NAME];
-               chain = nft_chain_lookup(table, attr, genmask);
+               chain = nft_chain_lookup(net, table, attr, genmask);
        }
        if (IS_ERR(chain)) {
                NL_SET_BAD_ATTR(extack, attr);
        if (type != NULL && try_module_get(type->owner))
                return type;
 
+       lockdep_nfnl_nft_mutex_not_held();
 #ifdef CONFIG_MODULES
        if (type == NULL) {
                nft_request_module(net, "nft-expr-%u-%.*s", family,
                return PTR_ERR(table);
        }
 
-       chain = nft_chain_lookup(table, nla[NFTA_RULE_CHAIN], genmask);
+       chain = nft_chain_lookup(net, table, nla[NFTA_RULE_CHAIN], genmask);
        if (IS_ERR(chain)) {
                NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_CHAIN]);
                return PTR_ERR(chain);
 {
        struct nft_expr *expr;
 
+       lockdep_assert_held(&ctx->net->nft.commit_mutex);
        /*
         * Careful: some expressions might not be initialized in case this
         * is called on error from nf_tables_newrule().
        bool create;
        u64 handle, pos_handle;
 
+       lockdep_assert_held(&net->nft.commit_mutex);
+
        create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false;
 
        table = nft_table_lookup(net, nla[NFTA_RULE_TABLE], family, genmask);
                return PTR_ERR(table);
        }
 
-       chain = nft_chain_lookup(table, nla[NFTA_RULE_CHAIN], genmask);
+       chain = nft_chain_lookup(net, table, nla[NFTA_RULE_CHAIN], genmask);
        if (IS_ERR(chain)) {
                NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_CHAIN]);
                return PTR_ERR(chain);
        }
 
        if (nla[NFTA_RULE_CHAIN]) {
-               chain = nft_chain_lookup(table, nla[NFTA_RULE_CHAIN], genmask);
+               chain = nft_chain_lookup(net, table, nla[NFTA_RULE_CHAIN],
+                                        genmask);
                if (IS_ERR(chain)) {
                        NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_CHAIN]);
                        return PTR_ERR(chain);
        const struct nft_set_type *type;
        u32 flags = 0;
 
+       lockdep_assert_held(&ctx->net->nft.commit_mutex);
+       lockdep_nfnl_nft_mutex_not_held();
 #ifdef CONFIG_MODULES
        if (list_empty(&nf_tables_set_types)) {
                nft_request_module(ctx->net, "nft-set");
        if (type != NULL && try_module_get(type->owner))
                return type;
 
+       lockdep_nfnl_nft_mutex_not_held();
 #ifdef CONFIG_MODULES
        if (type == NULL) {
                nft_request_module(net, "nft-obj-%u", objtype);
        if (type != NULL && try_module_get(type->owner))
                return type;
 
+       lockdep_nfnl_nft_mutex_not_held();
 #ifdef CONFIG_MODULES
        if (type == NULL) {
                nft_request_module(net, "nf-flowtable-%u", family);
        next_genbit = nft_gencursor_next(net);
 
        g0 = rcu_dereference_protected(chain->rules_gen_0,
-                                      lockdep_nfnl_is_held(NFNL_SUBSYS_NFTABLES));
+                                      lockdep_commit_lock_is_held(net));
        g1 = rcu_dereference_protected(chain->rules_gen_1,
-                                      lockdep_nfnl_is_held(NFNL_SUBSYS_NFTABLES));
+                                      lockdep_commit_lock_is_held(net));
 
        /* No changes to this chain? */
        if (chain->rules_next == NULL) {
 
        nf_tables_commit_release(net);
        nf_tables_gen_notify(net, skb, NFT_MSG_NEWGEN);
+       mutex_unlock(&net->nft.commit_mutex);
 
        return 0;
 }
 
 static int nf_tables_abort(struct net *net, struct sk_buff *skb)
 {
-       return __nf_tables_abort(net);
+       int ret = __nf_tables_abort(net);
+
+       mutex_unlock(&net->nft.commit_mutex);
+
+       return ret;
 }
 
 static bool nf_tables_valid_genid(struct net *net, u32 genid)
 {
-       return genid == 0 || net->nft.base_seq == genid;
+       bool genid_ok;
+
+       mutex_lock(&net->nft.commit_mutex);
+
+       genid_ok = genid == 0 || net->nft.base_seq == genid;
+       if (!genid_ok)
+               mutex_unlock(&net->nft.commit_mutex);
+
+       /* else, commit mutex has to be released by commit or abort function */
+       return genid_ok;
 }
 
 static const struct nfnetlink_subsystem nf_tables_subsys = {
        case NFT_GOTO:
                if (!tb[NFTA_VERDICT_CHAIN])
                        return -EINVAL;
-               chain = nft_chain_lookup(ctx->table, tb[NFTA_VERDICT_CHAIN],
-                                        genmask);
+               chain = nft_chain_lookup(ctx->net, ctx->table,
+                                        tb[NFTA_VERDICT_CHAIN], genmask);
                if (IS_ERR(chain))
                        return PTR_ERR(chain);
                if (nft_is_base_chain(chain))
 {
        INIT_LIST_HEAD(&net->nft.tables);
        INIT_LIST_HEAD(&net->nft.commit_list);
+       mutex_init(&net->nft.commit_mutex);
        net->nft.base_seq = 1;
        net->nft.validate_state = NFT_VALIDATE_SKIP;
 
 
 static void __net_exit nf_tables_exit_net(struct net *net)
 {
-       nfnl_lock(NFNL_SUBSYS_NFTABLES);
+       mutex_lock(&net->nft.commit_mutex);
        if (!list_empty(&net->nft.commit_list))
                __nf_tables_abort(net);
        __nft_release_tables(net);
-       nfnl_unlock(NFNL_SUBSYS_NFTABLES);
+       mutex_unlock(&net->nft.commit_mutex);
        WARN_ON_ONCE(!list_empty(&net->nft.tables));
 }