All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Paul E. McKenney" <paulmck@linux.vnet.ibm.com>
To: Andi Kleen <andi@firstfloor.org>
Cc: rusty@rustcorp.com.au, linux-kernel@vger.kernel.org, akpm@osdl.org
Subject: Re: [PATCH] Remove stop_machine during module load
Date: Fri, 29 Aug 2008 13:44:57 -0700	[thread overview]
Message-ID: <20080829204457.GF6725@linux.vnet.ibm.com> (raw)
In-Reply-To: <20080829191734.GA28329@basil.nowhere.org>

On Fri, Aug 29, 2008 at 09:17:34PM +0200, Andi Kleen wrote:
> Remove stop_machine during module load
> 
> module loading currently does a stop_machine on each module load to insert
> the module into the global module lists.  Especially on larger systems this 
> can be quite expensive.
> 
> It does that to handle concurrent lock lessmodule list readers
> like kallsyms.
> 
> I don't think stop_machine() is actually needed to insert something
> into a list though. There are no concurrent writers because the
> module mutex is taken. And the RCU list functions know how to insert
> a node into a list with the right memory ordering so that concurrent
> readers don't go off into the wood.
> 
> So remove the stop_machine for the module list insert and just
> do a list_add_rcu() instead. 
> 
> Module removal will still do a stop_machine of course, it needs
> that for other reasons.
> 
> [cc Paul McKenney for review. It's not RCU, but quite similar.]

Seems plausible, and faster module loading on big machines would be very
nice.  But aren't adjustments also needed on the removal side?

Ah, no they do not, because __unlink_module() is only called from the
context of stop_machine

OK, what about the read side?  Not so good for __unlink_module() to yank
the module out from under a reader.  Therefore, all readers must either
disable interrupts to block stop_machine() or must hold some sort of
mutex that prevents modules from being unloaded.

First, where the heck -is- the read side...

o	each_symbol() needs its list_for_each_entry() to become
	list_for_each_entry_rcu() and needs local_irq_disable()
	across the loop.  (I think -- looks to me like irqs are enabled,
	anyway...)

o	module_address_lookup() -- ditto.  Also lookup_module_symbol_name().
	And lookup_module_symbol_attrs().  And module_get_kallsym().
	As well as module_kallsyms_lookup_name().  And it looks like
	search_module_extables() also.  Ditto is_module_address().
	Plus __module_text_address().  Perhaps module_update_markers().

o	print_modules() -- needs the same treatment, but not sure
	how wise it is to invoke a potentially very large number of
	printk()s with interrupts disabled.

	An alternative would be to have free_module() do a
	synchronize_rcu() after the stop_machine().  Heck, if you are
	incurring a stop_machine(), what is an additional
	synchronize_rcu() among friends?  ;-)

	Yet another approach would be to use call_rcu() to defer the
	various kfree() &c calls later in free_module.

	Both the synchronize_rcu() and the call_rcu() approaches of
	course require that the list traversals all be done under either
	rcu_read_lock() or local_irq_disable().  This works with
	preemptable RCU -- rcu_read_lock() blocks either the
	synchronize_rcu() or the call_rcu(), which ever is chosen, while
	the local_irq_disable() blocks the stop_machine().

	But gack, there do appear to be lots of module_free()
	definitions out there!  More definitions than uses, it appears.
	So maybe not so bad.

	So maybe acquire module_mutex() or whatever to exclude module
	load and unload?  Unless of course print_modules() is sometimes
	invoked with module_mutex() already held!

	Too much fun!!!  ;-)

o	find_module() -- ditto.  Unless the comment about callers
	holding module_mutex really is honored, and unless module_mutex
	really prevents modules from being loaded and unloaded.

o	My guess is that already_uses() is an innocent bystander and
	thus need not change, but I cannot claim to be an expert on the
	modules code.  Ditto for print_unload_info ().

o	The caller of module_unload_free() presumably holds whatever
	mutex prevents other modules from being loaded and unloaded,
	so should not need to change.

Anyway, the general idea looks good, and getting rid of stop_machine()
for module load would be very cool on big machines, but the removal and
read sides need some help as noted above.

							Thanx, Paul

> Cc: rusty@rustcorp.com.au
> Cc: paulmck@us.ibm.com
> 
> Signed-off-by: Andi Kleen <ak@linux.intel.com>
> 
> Index: linux-2.6.27-rc4-misc/kernel/module.c
> ===================================================================
> --- linux-2.6.27-rc4-misc.orig/kernel/module.c
> +++ linux-2.6.27-rc4-misc/kernel/module.c
> @@ -42,6 +42,7 @@
>  #include <linux/string.h>
>  #include <linux/mutex.h>
>  #include <linux/unwind.h>
> +#include <linux/rculist.h>
>  #include <asm/uaccess.h>
>  #include <asm/cacheflush.h>
>  #include <linux/license.h>
> @@ -61,7 +62,7 @@
>  #define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1))
> 
>  /* List of modules, protected by module_mutex or preempt_disable
> - * (add/delete uses stop_machine). */
> + * (delete uses stop_machine/add uses RCU list operations). */
>  static DEFINE_MUTEX(module_mutex);
>  static LIST_HEAD(modules);
> 
> @@ -1391,17 +1392,6 @@ static void mod_kobject_remove(struct mo
>  }
> 
>  /*
> - * link the module with the whole machine is stopped with interrupts off
> - * - this defends against kallsyms not taking locks
> - */
> -static int __link_module(void *_mod)
> -{
> -	struct module *mod = _mod;
> -	list_add(&mod->list, &modules);
> -	return 0;
> -}
> -
> -/*
>   * unlink the module with the whole machine is stopped with interrupts off
>   * - this defends against kallsyms not taking locks
>   */
> @@ -2196,8 +2186,12 @@ static struct module *load_module(void _
> 
>  	/* Now sew it into the lists so we can get lockdep and oops
>           * info during argument parsing.  Noone should access us, since
> -         * strong_try_module_get() will fail. */
> -	stop_machine(__link_module, mod, NULL);
> +         * strong_try_module_get() will fail.
> +	 * lockdep/oops can run asynchronous, so use the RCU list insertion
> +	 * function to insert in a way safe to concurrent readers.
> +	 * The mutex protects against concurrent writers.
> +	 */
> +	list_add_rcu(&mod->list, &modules);
> 
>  	/* Size of section 0 is 0, so this works well if no params */
>  	err = parse_args(mod->name, mod->args,

  reply	other threads:[~2008-08-29 20:45 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-08-29 19:17 [PATCH] Remove stop_machine during module load Andi Kleen
2008-08-29 20:44 ` Paul E. McKenney [this message]
2008-08-29 21:23   ` Andi Kleen
2008-08-29 22:07     ` Paul E. McKenney
2008-09-01  7:02 ` Rusty Russell
2008-09-01  8:52   ` Andi Kleen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20080829204457.GF6725@linux.vnet.ibm.com \
    --to=paulmck@linux.vnet.ibm.com \
    --cc=akpm@osdl.org \
    --cc=andi@firstfloor.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=rusty@rustcorp.com.au \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.