const u_int16_t attr_count;             /* number of nlattr's */
 };
 
+enum nfnl_abort_action {
+       NFNL_ABORT_NONE         = 0,
+       NFNL_ABORT_AUTOLOAD,
+       NFNL_ABORT_VALIDATE,
+};
+
 struct nfnetlink_subsystem {
        const char *name;
        __u8 subsys_id;                 /* nfnetlink subsystem ID */
        const struct nfnl_callback *cb; /* callback for individual types */
        struct module *owner;
        int (*commit)(struct net *net, struct sk_buff *skb);
-       int (*abort)(struct net *net, struct sk_buff *skb, bool autoload);
+       int (*abort)(struct net *net, struct sk_buff *skb,
+                    enum nfnl_abort_action action);
        void (*cleanup)(struct net *net);
        bool (*valid_genid)(struct net *net, u32 genid);
 };
 
        kfree(trans);
 }
 
-static int __nf_tables_abort(struct net *net, bool autoload)
+static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
 {
        struct nft_trans *trans, *next;
        struct nft_trans_elem *te;
        struct nft_hook *hook;
 
+       if (action == NFNL_ABORT_VALIDATE &&
+           nf_tables_validate(net) < 0)
+               return -EAGAIN;
+
        list_for_each_entry_safe_reverse(trans, next, &net->nft.commit_list,
                                         list) {
                switch (trans->msg_type) {
                nf_tables_abort_release(trans);
        }
 
-       if (autoload)
+       if (action == NFNL_ABORT_AUTOLOAD)
                nf_tables_module_autoload(net);
        else
                nf_tables_module_autoload_cleanup(net);
        nft_validate_state_update(net, NFT_VALIDATE_SKIP);
 }
 
-static int nf_tables_abort(struct net *net, struct sk_buff *skb, bool autoload)
+static int nf_tables_abort(struct net *net, struct sk_buff *skb,
+                          enum nfnl_abort_action action)
 {
-       int ret = __nf_tables_abort(net, autoload);
+       int ret = __nf_tables_abort(net, action);
 
        mutex_unlock(&net->nft.commit_mutex);
 
 {
        mutex_lock(&net->nft.commit_mutex);
        if (!list_empty(&net->nft.commit_list))
-               __nf_tables_abort(net, false);
+               __nf_tables_abort(net, NFNL_ABORT_NONE);
        __nft_release_tables(net);
        mutex_unlock(&net->nft.commit_mutex);
        WARN_ON_ONCE(!list_empty(&net->nft.tables));
 
                return netlink_ack(skb, nlh, -EINVAL, NULL);
 replay:
        status = 0;
-
+replay_abort:
        skb = netlink_skb_clone(oskb, GFP_KERNEL);
        if (!skb)
                return netlink_ack(oskb, nlh, -ENOMEM, NULL);
        }
 done:
        if (status & NFNL_BATCH_REPLAY) {
-               ss->abort(net, oskb, true);
+               ss->abort(net, oskb, NFNL_ABORT_AUTOLOAD);
                nfnl_err_reset(&err_list);
                kfree_skb(skb);
                module_put(ss->owner);
                        status |= NFNL_BATCH_REPLAY;
                        goto done;
                } else if (err) {
-                       ss->abort(net, oskb, false);
+                       ss->abort(net, oskb, NFNL_ABORT_NONE);
                        netlink_ack(oskb, nlmsg_hdr(oskb), err, NULL);
                }
        } else {
-               ss->abort(net, oskb, false);
+               enum nfnl_abort_action abort_action;
+
+               if (status & NFNL_BATCH_FAILURE)
+                       abort_action = NFNL_ABORT_NONE;
+               else
+                       abort_action = NFNL_ABORT_VALIDATE;
+
+               err = ss->abort(net, oskb, abort_action);
+               if (err == -EAGAIN) {
+                       nfnl_err_reset(&err_list);
+                       kfree_skb(skb);
+                       module_put(ss->owner);
+                       status |= NFNL_BATCH_FAILURE;
+                       goto replay_abort;
+               }
        }
        if (ss->cleanup)
                ss->cleanup(net);