@@ -578,35 +578,45 @@ __nf_tables_chain_type_lookup(const struct nlattr *nla, u8 family)
578578 return NULL ;
579579}
580580
581- /*
582- * Loading a module requires dropping mutex that guards the transaction.
583- * A different client might race to start a new transaction meanwhile. Zap the
584- * list of pending transaction and then restore it once the mutex is grabbed
585- * again. Users of this function return EAGAIN which implicitly triggers the
586- * transaction abort path to clean up the list of pending transactions.
587- */
581+ struct nft_module_request {
582+ struct list_head list ;
583+ char module [MODULE_NAME_LEN ];
584+ bool done ;
585+ };
586+
588587#ifdef CONFIG_MODULES
589- static void nft_request_module (struct net * net , const char * fmt , ...)
588+ static int nft_request_module (struct net * net , const char * fmt , ...)
590589{
591590 char module_name [MODULE_NAME_LEN ];
592- LIST_HEAD ( commit_list ) ;
591+ struct nft_module_request * req ;
593592 va_list args ;
594593 int ret ;
595594
596- list_splice_init (& net -> nft .commit_list , & commit_list );
597-
598595 va_start (args , fmt );
599596 ret = vsnprintf (module_name , MODULE_NAME_LEN , fmt , args );
600597 va_end (args );
601598 if (ret >= MODULE_NAME_LEN )
602- return ;
599+ return 0 ;
603600
604- mutex_unlock (& net -> nft .commit_mutex );
605- request_module ("%s" , module_name );
606- mutex_lock (& net -> nft .commit_mutex );
601+ list_for_each_entry (req , & net -> nft .module_list , list ) {
602+ if (!strcmp (req -> module , module_name )) {
603+ if (req -> done )
604+ return 0 ;
607605
608- WARN_ON_ONCE (!list_empty (& net -> nft .commit_list ));
609- list_splice (& commit_list , & net -> nft .commit_list );
606+ /* A request to load this module already exists. */
607+ return - EAGAIN ;
608+ }
609+ }
610+
611+ req = kmalloc (sizeof (* req ), GFP_KERNEL );
612+ if (!req )
613+ return - ENOMEM ;
614+
615+ req -> done = false;
616+ strlcpy (req -> module , module_name , MODULE_NAME_LEN );
617+ list_add_tail (& req -> list , & net -> nft .module_list );
618+
619+ return - EAGAIN ;
610620}
611621#endif
612622
@@ -630,10 +640,9 @@ nf_tables_chain_type_lookup(struct net *net, const struct nlattr *nla,
630640 lockdep_nfnl_nft_mutex_not_held ();
631641#ifdef CONFIG_MODULES
632642 if (autoload ) {
633- nft_request_module (net , "nft-chain-%u-%.*s" , family ,
634- nla_len (nla ), (const char * )nla_data (nla ));
635- type = __nf_tables_chain_type_lookup (nla , family );
636- if (type != NULL )
643+ if (nft_request_module (net , "nft-chain-%u-%.*s" , family ,
644+ nla_len (nla ),
645+ (const char * )nla_data (nla )) == - EAGAIN )
637646 return ERR_PTR (- EAGAIN );
638647 }
639648#endif
@@ -2341,9 +2350,8 @@ static const struct nft_expr_type *__nft_expr_type_get(u8 family,
23412350static int nft_expr_type_request_module (struct net * net , u8 family ,
23422351 struct nlattr * nla )
23432352{
2344- nft_request_module (net , "nft-expr-%u-%.*s" , family ,
2345- nla_len (nla ), (char * )nla_data (nla ));
2346- if (__nft_expr_type_get (family , nla ))
2353+ if (nft_request_module (net , "nft-expr-%u-%.*s" , family ,
2354+ nla_len (nla ), (char * )nla_data (nla )) == - EAGAIN )
23472355 return - EAGAIN ;
23482356
23492357 return 0 ;
@@ -2369,9 +2377,9 @@ static const struct nft_expr_type *nft_expr_type_get(struct net *net,
23692377 if (nft_expr_type_request_module (net , family , nla ) == - EAGAIN )
23702378 return ERR_PTR (- EAGAIN );
23712379
2372- nft_request_module (net , "nft-expr-%.*s" ,
2373- nla_len ( nla ), ( char * ) nla_data (nla ));
2374- if ( __nft_expr_type_get ( family , nla ))
2380+ if ( nft_request_module (net , "nft-expr-%.*s" ,
2381+ nla_len (nla ),
2382+ ( char * ) nla_data ( nla )) == - EAGAIN )
23752383 return ERR_PTR (- EAGAIN );
23762384 }
23772385#endif
@@ -2462,9 +2470,10 @@ static int nf_tables_expr_parse(const struct nft_ctx *ctx,
24622470 err = PTR_ERR (ops );
24632471#ifdef CONFIG_MODULES
24642472 if (err == - EAGAIN )
2465- nft_expr_type_request_module (ctx -> net ,
2466- ctx -> family ,
2467- tb [NFTA_EXPR_NAME ]);
2473+ if (nft_expr_type_request_module (ctx -> net ,
2474+ ctx -> family ,
2475+ tb [NFTA_EXPR_NAME ]) != - EAGAIN )
2476+ err = - ENOENT ;
24682477#endif
24692478 goto err1 ;
24702479 }
@@ -3301,8 +3310,7 @@ nft_select_set_ops(const struct nft_ctx *ctx,
33013310 lockdep_nfnl_nft_mutex_not_held ();
33023311#ifdef CONFIG_MODULES
33033312 if (list_empty (& nf_tables_set_types )) {
3304- nft_request_module (ctx -> net , "nft-set" );
3305- if (!list_empty (& nf_tables_set_types ))
3313+ if (nft_request_module (ctx -> net , "nft-set" ) == - EAGAIN )
33063314 return ERR_PTR (- EAGAIN );
33073315 }
33083316#endif
@@ -5428,8 +5436,7 @@ nft_obj_type_get(struct net *net, u32 objtype)
54285436 lockdep_nfnl_nft_mutex_not_held ();
54295437#ifdef CONFIG_MODULES
54305438 if (type == NULL ) {
5431- nft_request_module (net , "nft-obj-%u" , objtype );
5432- if (__nft_obj_type_get (objtype ))
5439+ if (nft_request_module (net , "nft-obj-%u" , objtype ) == - EAGAIN )
54335440 return ERR_PTR (- EAGAIN );
54345441 }
54355442#endif
@@ -6002,8 +6009,7 @@ nft_flowtable_type_get(struct net *net, u8 family)
60026009 lockdep_nfnl_nft_mutex_not_held ();
60036010#ifdef CONFIG_MODULES
60046011 if (type == NULL ) {
6005- nft_request_module (net , "nf-flowtable-%u" , family );
6006- if (__nft_flowtable_type_get (family ))
6012+ if (nft_request_module (net , "nf-flowtable-%u" , family ) == - EAGAIN )
60076013 return ERR_PTR (- EAGAIN );
60086014 }
60096015#endif
@@ -7005,6 +7011,18 @@ static void nft_chain_del(struct nft_chain *chain)
70057011 list_del_rcu (& chain -> list );
70067012}
70077013
7014+ static void nf_tables_module_autoload_cleanup (struct net * net )
7015+ {
7016+ struct nft_module_request * req , * next ;
7017+
7018+ WARN_ON_ONCE (!list_empty (& net -> nft .commit_list ));
7019+ list_for_each_entry_safe (req , next , & net -> nft .module_list , list ) {
7020+ WARN_ON_ONCE (!req -> done );
7021+ list_del (& req -> list );
7022+ kfree (req );
7023+ }
7024+ }
7025+
70087026static void nf_tables_commit_release (struct net * net )
70097027{
70107028 struct nft_trans * trans ;
@@ -7017,6 +7035,7 @@ static void nf_tables_commit_release(struct net *net)
70177035 * to prevent expensive synchronize_rcu() in commit phase.
70187036 */
70197037 if (list_empty (& net -> nft .commit_list )) {
7038+ nf_tables_module_autoload_cleanup (net );
70207039 mutex_unlock (& net -> nft .commit_mutex );
70217040 return ;
70227041 }
@@ -7031,6 +7050,7 @@ static void nf_tables_commit_release(struct net *net)
70317050 list_splice_tail_init (& net -> nft .commit_list , & nf_tables_destroy_list );
70327051 spin_unlock (& nf_tables_destroy_list_lock );
70337052
7053+ nf_tables_module_autoload_cleanup (net );
70347054 mutex_unlock (& net -> nft .commit_mutex );
70357055
70367056 schedule_work (& trans_destroy_work );
@@ -7222,6 +7242,26 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
72227242 return 0 ;
72237243}
72247244
7245+ static void nf_tables_module_autoload (struct net * net )
7246+ {
7247+ struct nft_module_request * req , * next ;
7248+ LIST_HEAD (module_list );
7249+
7250+ list_splice_init (& net -> nft .module_list , & module_list );
7251+ mutex_unlock (& net -> nft .commit_mutex );
7252+ list_for_each_entry_safe (req , next , & module_list , list ) {
7253+ if (req -> done ) {
7254+ list_del (& req -> list );
7255+ kfree (req );
7256+ } else {
7257+ request_module ("%s" , req -> module );
7258+ req -> done = true;
7259+ }
7260+ }
7261+ mutex_lock (& net -> nft .commit_mutex );
7262+ list_splice (& module_list , & net -> nft .module_list );
7263+ }
7264+
72257265static void nf_tables_abort_release (struct nft_trans * trans )
72267266{
72277267 switch (trans -> msg_type ) {
@@ -7251,7 +7291,7 @@ static void nf_tables_abort_release(struct nft_trans *trans)
72517291 kfree (trans );
72527292}
72537293
7254- static int __nf_tables_abort (struct net * net )
7294+ static int __nf_tables_abort (struct net * net , bool autoload )
72557295{
72567296 struct nft_trans * trans , * next ;
72577297 struct nft_trans_elem * te ;
@@ -7373,6 +7413,11 @@ static int __nf_tables_abort(struct net *net)
73737413 nf_tables_abort_release (trans );
73747414 }
73757415
7416+ if (autoload )
7417+ nf_tables_module_autoload (net );
7418+ else
7419+ nf_tables_module_autoload_cleanup (net );
7420+
73767421 return 0 ;
73777422}
73787423
@@ -7381,9 +7426,9 @@ static void nf_tables_cleanup(struct net *net)
73817426 nft_validate_state_update (net , NFT_VALIDATE_SKIP );
73827427}
73837428
7384- static int nf_tables_abort (struct net * net , struct sk_buff * skb )
7429+ static int nf_tables_abort (struct net * net , struct sk_buff * skb , bool autoload )
73857430{
7386- int ret = __nf_tables_abort (net );
7431+ int ret = __nf_tables_abort (net , autoload );
73877432
73887433 mutex_unlock (& net -> nft .commit_mutex );
73897434
@@ -7978,6 +8023,7 @@ static int __net_init nf_tables_init_net(struct net *net)
79788023{
79798024 INIT_LIST_HEAD (& net -> nft .tables );
79808025 INIT_LIST_HEAD (& net -> nft .commit_list );
8026+ INIT_LIST_HEAD (& net -> nft .module_list );
79818027 mutex_init (& net -> nft .commit_mutex );
79828028 net -> nft .base_seq = 1 ;
79838029 net -> nft .validate_state = NFT_VALIDATE_SKIP ;
@@ -7989,7 +8035,7 @@ static void __net_exit nf_tables_exit_net(struct net *net)
79898035{
79908036 mutex_lock (& net -> nft .commit_mutex );
79918037 if (!list_empty (& net -> nft .commit_list ))
7992- __nf_tables_abort (net );
8038+ __nf_tables_abort (net , false );
79938039 __nft_release_tables (net );
79948040 mutex_unlock (& net -> nft .commit_mutex );
79958041 WARN_ON_ONCE (!list_empty (& net -> nft .tables ));
0 commit comments