2006-06-28 19:26:45 +08:00
|
|
|
/*
|
2005-04-17 06:20:36 +08:00
|
|
|
Copyright (C) 2002 Richard Henderson
|
|
|
|
Copyright (C) 2001 Rusty Russell, 2002 Rusty Russell IBM.
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/moduleloader.h>
|
|
|
|
#include <linux/init.h>
|
2007-05-08 15:28:38 +08:00
|
|
|
#include <linux/kallsyms.h>
|
2007-10-17 14:26:40 +08:00
|
|
|
#include <linux/sysfs.h>
|
2005-09-13 16:25:16 +08:00
|
|
|
#include <linux/kernel.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/vmalloc.h>
|
|
|
|
#include <linux/elf.h>
|
|
|
|
#include <linux/seq_file.h>
|
|
|
|
#include <linux/syscalls.h>
|
|
|
|
#include <linux/fcntl.h>
|
|
|
|
#include <linux/rcupdate.h>
|
2006-01-12 04:17:46 +08:00
|
|
|
#include <linux/capability.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/cpu.h>
|
|
|
|
#include <linux/moduleparam.h>
|
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/err.h>
|
|
|
|
#include <linux/vermagic.h>
|
|
|
|
#include <linux/notifier.h>
|
2006-10-18 13:47:25 +08:00
|
|
|
#include <linux/sched.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/stop_machine.h>
|
|
|
|
#include <linux/device.h>
|
[PATCH] modules: add version and srcversion to sysfs
This patch adds version and srcversion files to
/sys/module/${modulename} containing the version and srcversion fields
of the module's modinfo section (if present).
/sys/module/e1000
|-- srcversion
`-- version
This patch differs slightly from the version posted in January, as it
now uses the new kstrdup() call in -mm.
Why put this in sysfs?
a) Tools like DKMS, which deal with changing out individual kernel
modules without replacing the whole kernel, can behave smarter if they
can tell the version of a given module. The autoinstaller feature, for
example, which determines if your system has a "good" version of a
driver (i.e. if the one provided by DKMS has a newer verson than that
provided by the kernel package installed), and to automatically compile
and install a newer version if DKMS has it but your kernel doesn't yet
have that version.
b) Because sysadmins manually, or with tools like DKMS, can switch out
modules on the file system, you can't count on 'modinfo foo.ko', which
looks at /lib/modules/${kernelver}/... actually matching what is loaded
into the kernel already. Hence asking sysfs for this.
c) as the unbind-driver-from-device work takes shape, it will be
possible to rebind a driver that's built-in (no .ko to modinfo for the
version) to a newly loaded module. sysfs will have the
currently-built-in version info, for comparison.
d) tech support scripts can then easily grab the version info for what's
running presently - a question I get often.
There has been renewed interest in this patch on linux-scsi by driver
authors.
As the idea originated from GregKH, I leave his Signed-off-by: intact,
though the implementation is nearly completely new. Compiled and run on
x86 and x86_64.
From: Matthew Dobson <colpatch@us.ibm.com>
build fix
From: Thierry Vignaud <tvignaud@mandriva.com>
build fix
From: Matthew Dobson <colpatch@us.ibm.com>
warning fix
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
Signed-off-by: Matt Domsch <Matt_Domsch@dell.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-06-24 13:05:15 +08:00
|
|
|
#include <linux/string.h>
|
2006-03-23 19:00:24 +08:00
|
|
|
#include <linux/mutex.h>
|
2006-06-26 19:57:28 +08:00
|
|
|
#include <linux/unwind.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <asm/uaccess.h>
|
|
|
|
#include <asm/cacheflush.h>
|
2006-06-10 03:53:55 +08:00
|
|
|
#include <linux/license.h>
|
2008-02-08 20:18:42 +08:00
|
|
|
#include <asm/sections.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#if 0
|
|
|
|
#define DEBUGP printk
|
|
|
|
#else
|
|
|
|
#define DEBUGP(fmt , a...)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef ARCH_SHF_SMALL
|
|
|
|
#define ARCH_SHF_SMALL 0
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* If this is set, the section belongs in the init part of the module */
|
|
|
|
#define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1))
|
|
|
|
|
2007-07-16 14:41:46 +08:00
|
|
|
/* List of modules, protected by module_mutex or preempt_disable
|
|
|
|
* (add/delete uses stop_machine). */
|
2006-03-23 19:00:46 +08:00
|
|
|
static DEFINE_MUTEX(module_mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
static LIST_HEAD(modules);
|
|
|
|
|
2008-01-30 06:13:18 +08:00
|
|
|
/* Waiting for a module to finish initializing? */
|
|
|
|
static DECLARE_WAIT_QUEUE_HEAD(module_wq);
|
|
|
|
|
[PATCH] Notifier chain update: API changes
The kernel's implementation of notifier chains is unsafe. There is no
protection against entries being added to or removed from a chain while the
chain is in use. The issues were discussed in this thread:
http://marc.theaimsgroup.com/?l=linux-kernel&m=113018709002036&w=2
We noticed that notifier chains in the kernel fall into two basic usage
classes:
"Blocking" chains are always called from a process context
and the callout routines are allowed to sleep;
"Atomic" chains can be called from an atomic context and
the callout routines are not allowed to sleep.
We decided to codify this distinction and make it part of the API. Therefore
this set of patches introduces three new, parallel APIs: one for blocking
notifiers, one for atomic notifiers, and one for "raw" notifiers (which is
really just the old API under a new name). New kinds of data structures are
used for the heads of the chains, and new routines are defined for
registration, unregistration, and calling a chain. The three APIs are
explained in include/linux/notifier.h and their implementation is in
kernel/sys.c.
With atomic and blocking chains, the implementation guarantees that the chain
links will not be corrupted and that chain callers will not get messed up by
entries being added or removed. For raw chains the implementation provides no
guarantees at all; users of this API must provide their own protections. (The
idea was that situations may come up where the assumptions of the atomic and
blocking APIs are not appropriate, so it should be possible for users to
handle these things in their own way.)
There are some limitations, which should not be too hard to live with. For
atomic/blocking chains, registration and unregistration must always be done in
a process context since the chain is protected by a mutex/rwsem. Also, a
callout routine for a non-raw chain must not try to register or unregister
entries on its own chain. (This did happen in a couple of places and the code
had to be changed to avoid it.)
Since atomic chains may be called from within an NMI handler, they cannot use
spinlocks for synchronization. Instead we use RCU. The overhead falls almost
entirely in the unregister routine, which is okay since unregistration is much
less frequent that calling a chain.
Here is the list of chains that we adjusted and their classifications. None
of them use the raw API, so for the moment it is only a placeholder.
ATOMIC CHAINS
-------------
arch/i386/kernel/traps.c: i386die_chain
arch/ia64/kernel/traps.c: ia64die_chain
arch/powerpc/kernel/traps.c: powerpc_die_chain
arch/sparc64/kernel/traps.c: sparc64die_chain
arch/x86_64/kernel/traps.c: die_chain
drivers/char/ipmi/ipmi_si_intf.c: xaction_notifier_list
kernel/panic.c: panic_notifier_list
kernel/profile.c: task_free_notifier
net/bluetooth/hci_core.c: hci_notifier
net/ipv4/netfilter/ip_conntrack_core.c: ip_conntrack_chain
net/ipv4/netfilter/ip_conntrack_core.c: ip_conntrack_expect_chain
net/ipv6/addrconf.c: inet6addr_chain
net/netfilter/nf_conntrack_core.c: nf_conntrack_chain
net/netfilter/nf_conntrack_core.c: nf_conntrack_expect_chain
net/netlink/af_netlink.c: netlink_chain
BLOCKING CHAINS
---------------
arch/powerpc/platforms/pseries/reconfig.c: pSeries_reconfig_chain
arch/s390/kernel/process.c: idle_chain
arch/x86_64/kernel/process.c idle_notifier
drivers/base/memory.c: memory_chain
drivers/cpufreq/cpufreq.c cpufreq_policy_notifier_list
drivers/cpufreq/cpufreq.c cpufreq_transition_notifier_list
drivers/macintosh/adb.c: adb_client_list
drivers/macintosh/via-pmu.c sleep_notifier_list
drivers/macintosh/via-pmu68k.c sleep_notifier_list
drivers/macintosh/windfarm_core.c wf_client_list
drivers/usb/core/notify.c usb_notifier_list
drivers/video/fbmem.c fb_notifier_list
kernel/cpu.c cpu_chain
kernel/module.c module_notify_list
kernel/profile.c munmap_notifier
kernel/profile.c task_exit_notifier
kernel/sys.c reboot_notifier_list
net/core/dev.c netdev_chain
net/decnet/dn_dev.c: dnaddr_chain
net/ipv4/devinet.c: inetaddr_chain
It's possible that some of these classifications are wrong. If they are,
please let us know or submit a patch to fix them. Note that any chain that
gets called very frequently should be atomic, because the rwsem read-locking
used for blocking chains is very likely to incur cache misses on SMP systems.
(However, if the chain's callout routines may sleep then the chain cannot be
atomic.)
The patch set was written by Alan Stern and Chandra Seetharaman, incorporating
material written by Keith Owens and suggestions from Paul McKenney and Andrew
Morton.
[jes@sgi.com: restructure the notifier chain initialization macros]
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Chandra Seetharaman <sekharan@us.ibm.com>
Signed-off-by: Jes Sorensen <jes@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-03-27 17:16:30 +08:00
|
|
|
static BLOCKING_NOTIFIER_HEAD(module_notify_list);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-07-23 08:24:28 +08:00
|
|
|
/* Bounds of module allocation, for speeding __module_text_address */
|
|
|
|
static unsigned long module_addr_min = -1UL, module_addr_max = 0;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
int register_module_notifier(struct notifier_block * nb)
|
|
|
|
{
|
[PATCH] Notifier chain update: API changes
The kernel's implementation of notifier chains is unsafe. There is no
protection against entries being added to or removed from a chain while the
chain is in use. The issues were discussed in this thread:
http://marc.theaimsgroup.com/?l=linux-kernel&m=113018709002036&w=2
We noticed that notifier chains in the kernel fall into two basic usage
classes:
"Blocking" chains are always called from a process context
and the callout routines are allowed to sleep;
"Atomic" chains can be called from an atomic context and
the callout routines are not allowed to sleep.
We decided to codify this distinction and make it part of the API. Therefore
this set of patches introduces three new, parallel APIs: one for blocking
notifiers, one for atomic notifiers, and one for "raw" notifiers (which is
really just the old API under a new name). New kinds of data structures are
used for the heads of the chains, and new routines are defined for
registration, unregistration, and calling a chain. The three APIs are
explained in include/linux/notifier.h and their implementation is in
kernel/sys.c.
With atomic and blocking chains, the implementation guarantees that the chain
links will not be corrupted and that chain callers will not get messed up by
entries being added or removed. For raw chains the implementation provides no
guarantees at all; users of this API must provide their own protections. (The
idea was that situations may come up where the assumptions of the atomic and
blocking APIs are not appropriate, so it should be possible for users to
handle these things in their own way.)
There are some limitations, which should not be too hard to live with. For
atomic/blocking chains, registration and unregistration must always be done in
a process context since the chain is protected by a mutex/rwsem. Also, a
callout routine for a non-raw chain must not try to register or unregister
entries on its own chain. (This did happen in a couple of places and the code
had to be changed to avoid it.)
Since atomic chains may be called from within an NMI handler, they cannot use
spinlocks for synchronization. Instead we use RCU. The overhead falls almost
entirely in the unregister routine, which is okay since unregistration is much
less frequent that calling a chain.
Here is the list of chains that we adjusted and their classifications. None
of them use the raw API, so for the moment it is only a placeholder.
ATOMIC CHAINS
-------------
arch/i386/kernel/traps.c: i386die_chain
arch/ia64/kernel/traps.c: ia64die_chain
arch/powerpc/kernel/traps.c: powerpc_die_chain
arch/sparc64/kernel/traps.c: sparc64die_chain
arch/x86_64/kernel/traps.c: die_chain
drivers/char/ipmi/ipmi_si_intf.c: xaction_notifier_list
kernel/panic.c: panic_notifier_list
kernel/profile.c: task_free_notifier
net/bluetooth/hci_core.c: hci_notifier
net/ipv4/netfilter/ip_conntrack_core.c: ip_conntrack_chain
net/ipv4/netfilter/ip_conntrack_core.c: ip_conntrack_expect_chain
net/ipv6/addrconf.c: inet6addr_chain
net/netfilter/nf_conntrack_core.c: nf_conntrack_chain
net/netfilter/nf_conntrack_core.c: nf_conntrack_expect_chain
net/netlink/af_netlink.c: netlink_chain
BLOCKING CHAINS
---------------
arch/powerpc/platforms/pseries/reconfig.c: pSeries_reconfig_chain
arch/s390/kernel/process.c: idle_chain
arch/x86_64/kernel/process.c idle_notifier
drivers/base/memory.c: memory_chain
drivers/cpufreq/cpufreq.c cpufreq_policy_notifier_list
drivers/cpufreq/cpufreq.c cpufreq_transition_notifier_list
drivers/macintosh/adb.c: adb_client_list
drivers/macintosh/via-pmu.c sleep_notifier_list
drivers/macintosh/via-pmu68k.c sleep_notifier_list
drivers/macintosh/windfarm_core.c wf_client_list
drivers/usb/core/notify.c usb_notifier_list
drivers/video/fbmem.c fb_notifier_list
kernel/cpu.c cpu_chain
kernel/module.c module_notify_list
kernel/profile.c munmap_notifier
kernel/profile.c task_exit_notifier
kernel/sys.c reboot_notifier_list
net/core/dev.c netdev_chain
net/decnet/dn_dev.c: dnaddr_chain
net/ipv4/devinet.c: inetaddr_chain
It's possible that some of these classifications are wrong. If they are,
please let us know or submit a patch to fix them. Note that any chain that
gets called very frequently should be atomic, because the rwsem read-locking
used for blocking chains is very likely to incur cache misses on SMP systems.
(However, if the chain's callout routines may sleep then the chain cannot be
atomic.)
The patch set was written by Alan Stern and Chandra Seetharaman, incorporating
material written by Keith Owens and suggestions from Paul McKenney and Andrew
Morton.
[jes@sgi.com: restructure the notifier chain initialization macros]
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Chandra Seetharaman <sekharan@us.ibm.com>
Signed-off-by: Jes Sorensen <jes@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-03-27 17:16:30 +08:00
|
|
|
return blocking_notifier_chain_register(&module_notify_list, nb);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(register_module_notifier);
|
|
|
|
|
|
|
|
int unregister_module_notifier(struct notifier_block * nb)
|
|
|
|
{
|
[PATCH] Notifier chain update: API changes
The kernel's implementation of notifier chains is unsafe. There is no
protection against entries being added to or removed from a chain while the
chain is in use. The issues were discussed in this thread:
http://marc.theaimsgroup.com/?l=linux-kernel&m=113018709002036&w=2
We noticed that notifier chains in the kernel fall into two basic usage
classes:
"Blocking" chains are always called from a process context
and the callout routines are allowed to sleep;
"Atomic" chains can be called from an atomic context and
the callout routines are not allowed to sleep.
We decided to codify this distinction and make it part of the API. Therefore
this set of patches introduces three new, parallel APIs: one for blocking
notifiers, one for atomic notifiers, and one for "raw" notifiers (which is
really just the old API under a new name). New kinds of data structures are
used for the heads of the chains, and new routines are defined for
registration, unregistration, and calling a chain. The three APIs are
explained in include/linux/notifier.h and their implementation is in
kernel/sys.c.
With atomic and blocking chains, the implementation guarantees that the chain
links will not be corrupted and that chain callers will not get messed up by
entries being added or removed. For raw chains the implementation provides no
guarantees at all; users of this API must provide their own protections. (The
idea was that situations may come up where the assumptions of the atomic and
blocking APIs are not appropriate, so it should be possible for users to
handle these things in their own way.)
There are some limitations, which should not be too hard to live with. For
atomic/blocking chains, registration and unregistration must always be done in
a process context since the chain is protected by a mutex/rwsem. Also, a
callout routine for a non-raw chain must not try to register or unregister
entries on its own chain. (This did happen in a couple of places and the code
had to be changed to avoid it.)
Since atomic chains may be called from within an NMI handler, they cannot use
spinlocks for synchronization. Instead we use RCU. The overhead falls almost
entirely in the unregister routine, which is okay since unregistration is much
less frequent that calling a chain.
Here is the list of chains that we adjusted and their classifications. None
of them use the raw API, so for the moment it is only a placeholder.
ATOMIC CHAINS
-------------
arch/i386/kernel/traps.c: i386die_chain
arch/ia64/kernel/traps.c: ia64die_chain
arch/powerpc/kernel/traps.c: powerpc_die_chain
arch/sparc64/kernel/traps.c: sparc64die_chain
arch/x86_64/kernel/traps.c: die_chain
drivers/char/ipmi/ipmi_si_intf.c: xaction_notifier_list
kernel/panic.c: panic_notifier_list
kernel/profile.c: task_free_notifier
net/bluetooth/hci_core.c: hci_notifier
net/ipv4/netfilter/ip_conntrack_core.c: ip_conntrack_chain
net/ipv4/netfilter/ip_conntrack_core.c: ip_conntrack_expect_chain
net/ipv6/addrconf.c: inet6addr_chain
net/netfilter/nf_conntrack_core.c: nf_conntrack_chain
net/netfilter/nf_conntrack_core.c: nf_conntrack_expect_chain
net/netlink/af_netlink.c: netlink_chain
BLOCKING CHAINS
---------------
arch/powerpc/platforms/pseries/reconfig.c: pSeries_reconfig_chain
arch/s390/kernel/process.c: idle_chain
arch/x86_64/kernel/process.c idle_notifier
drivers/base/memory.c: memory_chain
drivers/cpufreq/cpufreq.c cpufreq_policy_notifier_list
drivers/cpufreq/cpufreq.c cpufreq_transition_notifier_list
drivers/macintosh/adb.c: adb_client_list
drivers/macintosh/via-pmu.c sleep_notifier_list
drivers/macintosh/via-pmu68k.c sleep_notifier_list
drivers/macintosh/windfarm_core.c wf_client_list
drivers/usb/core/notify.c usb_notifier_list
drivers/video/fbmem.c fb_notifier_list
kernel/cpu.c cpu_chain
kernel/module.c module_notify_list
kernel/profile.c munmap_notifier
kernel/profile.c task_exit_notifier
kernel/sys.c reboot_notifier_list
net/core/dev.c netdev_chain
net/decnet/dn_dev.c: dnaddr_chain
net/ipv4/devinet.c: inetaddr_chain
It's possible that some of these classifications are wrong. If they are,
please let us know or submit a patch to fix them. Note that any chain that
gets called very frequently should be atomic, because the rwsem read-locking
used for blocking chains is very likely to incur cache misses on SMP systems.
(However, if the chain's callout routines may sleep then the chain cannot be
atomic.)
The patch set was written by Alan Stern and Chandra Seetharaman, incorporating
material written by Keith Owens and suggestions from Paul McKenney and Andrew
Morton.
[jes@sgi.com: restructure the notifier chain initialization macros]
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Chandra Seetharaman <sekharan@us.ibm.com>
Signed-off-by: Jes Sorensen <jes@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-03-27 17:16:30 +08:00
|
|
|
return blocking_notifier_chain_unregister(&module_notify_list, nb);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(unregister_module_notifier);
|
|
|
|
|
2007-11-09 00:37:38 +08:00
|
|
|
/* We require a truly strong try_module_get(): 0 means failure due to
|
|
|
|
ongoing or failed initialization etc. */
|
2005-04-17 06:20:36 +08:00
|
|
|
static inline int strong_try_module_get(struct module *mod)
|
|
|
|
{
|
|
|
|
if (mod && mod->state == MODULE_STATE_COMING)
|
2008-01-30 06:13:18 +08:00
|
|
|
return -EBUSY;
|
|
|
|
if (try_module_get(mod))
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
2008-01-30 06:13:18 +08:00
|
|
|
else
|
|
|
|
return -ENOENT;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2006-10-11 16:21:48 +08:00
|
|
|
static inline void add_taint_module(struct module *mod, unsigned flag)
|
|
|
|
{
|
|
|
|
add_taint(flag);
|
2008-10-16 13:01:41 +08:00
|
|
|
mod->taints |= (1U << flag);
|
2006-10-11 16:21:48 +08:00
|
|
|
}
|
|
|
|
|
2007-05-09 13:26:28 +08:00
|
|
|
/*
|
|
|
|
* A thread that wants to hold a reference to a module only while it
|
|
|
|
* is running can call this to safely exit. nfsd and lockd use this.
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
void __module_put_and_exit(struct module *mod, long code)
|
|
|
|
{
|
|
|
|
module_put(mod);
|
|
|
|
do_exit(code);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(__module_put_and_exit);
|
2007-10-18 18:06:07 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Find a module section: 0 means not found. */
|
|
|
|
static unsigned int find_sec(Elf_Ehdr *hdr,
|
|
|
|
Elf_Shdr *sechdrs,
|
|
|
|
const char *secstrings,
|
|
|
|
const char *name)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
for (i = 1; i < hdr->e_shnum; i++)
|
|
|
|
/* Alloc bit cleared means "ignore it." */
|
|
|
|
if ((sechdrs[i].sh_flags & SHF_ALLOC)
|
|
|
|
&& strcmp(secstrings+sechdrs[i].sh_name, name) == 0)
|
|
|
|
return i;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Provided by the linker */
|
|
|
|
extern const struct kernel_symbol __start___ksymtab[];
|
|
|
|
extern const struct kernel_symbol __stop___ksymtab[];
|
|
|
|
extern const struct kernel_symbol __start___ksymtab_gpl[];
|
|
|
|
extern const struct kernel_symbol __stop___ksymtab_gpl[];
|
2006-03-21 05:17:13 +08:00
|
|
|
extern const struct kernel_symbol __start___ksymtab_gpl_future[];
|
|
|
|
extern const struct kernel_symbol __stop___ksymtab_gpl_future[];
|
2006-06-28 19:26:45 +08:00
|
|
|
extern const struct kernel_symbol __start___ksymtab_gpl_future[];
|
|
|
|
extern const struct kernel_symbol __stop___ksymtab_gpl_future[];
|
2005-04-17 06:20:36 +08:00
|
|
|
extern const unsigned long __start___kcrctab[];
|
|
|
|
extern const unsigned long __start___kcrctab_gpl[];
|
2006-03-21 05:17:13 +08:00
|
|
|
extern const unsigned long __start___kcrctab_gpl_future[];
|
2008-07-23 08:24:26 +08:00
|
|
|
#ifdef CONFIG_UNUSED_SYMBOLS
|
|
|
|
extern const struct kernel_symbol __start___ksymtab_unused[];
|
|
|
|
extern const struct kernel_symbol __stop___ksymtab_unused[];
|
|
|
|
extern const struct kernel_symbol __start___ksymtab_unused_gpl[];
|
|
|
|
extern const struct kernel_symbol __stop___ksymtab_unused_gpl[];
|
2006-06-28 19:26:45 +08:00
|
|
|
extern const unsigned long __start___kcrctab_unused[];
|
|
|
|
extern const unsigned long __start___kcrctab_unused_gpl[];
|
2008-07-23 08:24:26 +08:00
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#ifndef CONFIG_MODVERSIONS
|
|
|
|
#define symversion(base, idx) NULL
|
|
|
|
#else
|
2006-03-28 17:56:20 +08:00
|
|
|
#define symversion(base, idx) ((base != NULL) ? ((base) + (idx)) : NULL)
|
2005-04-17 06:20:36 +08:00
|
|
|
#endif
|
|
|
|
|
2008-05-02 10:14:59 +08:00
|
|
|
struct symsearch {
|
|
|
|
const struct kernel_symbol *start, *stop;
|
|
|
|
const unsigned long *crcs;
|
2008-07-23 08:24:25 +08:00
|
|
|
enum {
|
|
|
|
NOT_GPL_ONLY,
|
|
|
|
GPL_ONLY,
|
|
|
|
WILL_BE_GPL_ONLY,
|
|
|
|
} licence;
|
|
|
|
bool unused;
|
2008-05-02 10:14:59 +08:00
|
|
|
};
|
|
|
|
|
2008-07-23 08:24:25 +08:00
|
|
|
static bool each_symbol_in_section(const struct symsearch *arr,
|
|
|
|
unsigned int arrsize,
|
|
|
|
struct module *owner,
|
|
|
|
bool (*fn)(const struct symsearch *syms,
|
|
|
|
struct module *owner,
|
|
|
|
unsigned int symnum, void *data),
|
|
|
|
void *data)
|
2008-05-02 10:14:59 +08:00
|
|
|
{
|
2008-07-23 08:24:25 +08:00
|
|
|
unsigned int i, j;
|
2008-05-02 10:14:59 +08:00
|
|
|
|
2008-07-23 08:24:25 +08:00
|
|
|
for (j = 0; j < arrsize; j++) {
|
|
|
|
for (i = 0; i < arr[j].stop - arr[j].start; i++)
|
|
|
|
if (fn(&arr[j], owner, i, data))
|
|
|
|
return true;
|
2006-06-28 19:26:45 +08:00
|
|
|
}
|
2008-07-23 08:24:25 +08:00
|
|
|
|
|
|
|
return false;
|
2008-05-02 10:14:59 +08:00
|
|
|
}
|
|
|
|
|
2008-07-23 08:24:25 +08:00
|
|
|
/* Returns true as soon as fn returns true, otherwise false. */
|
|
|
|
static bool each_symbol(bool (*fn)(const struct symsearch *arr,
|
|
|
|
struct module *owner,
|
|
|
|
unsigned int symnum, void *data),
|
|
|
|
void *data)
|
2008-05-02 10:14:59 +08:00
|
|
|
{
|
|
|
|
struct module *mod;
|
|
|
|
const struct symsearch arr[] = {
|
|
|
|
{ __start___ksymtab, __stop___ksymtab, __start___kcrctab,
|
2008-07-23 08:24:25 +08:00
|
|
|
NOT_GPL_ONLY, false },
|
2008-05-02 10:14:59 +08:00
|
|
|
{ __start___ksymtab_gpl, __stop___ksymtab_gpl,
|
2008-07-23 08:24:25 +08:00
|
|
|
__start___kcrctab_gpl,
|
|
|
|
GPL_ONLY, false },
|
2008-05-02 10:14:59 +08:00
|
|
|
{ __start___ksymtab_gpl_future, __stop___ksymtab_gpl_future,
|
2008-07-23 08:24:25 +08:00
|
|
|
__start___kcrctab_gpl_future,
|
|
|
|
WILL_BE_GPL_ONLY, false },
|
2008-07-23 08:24:26 +08:00
|
|
|
#ifdef CONFIG_UNUSED_SYMBOLS
|
2008-05-02 10:14:59 +08:00
|
|
|
{ __start___ksymtab_unused, __stop___ksymtab_unused,
|
2008-07-23 08:24:25 +08:00
|
|
|
__start___kcrctab_unused,
|
|
|
|
NOT_GPL_ONLY, true },
|
2008-05-02 10:14:59 +08:00
|
|
|
{ __start___ksymtab_unused_gpl, __stop___ksymtab_unused_gpl,
|
2008-07-23 08:24:25 +08:00
|
|
|
__start___kcrctab_unused_gpl,
|
|
|
|
GPL_ONLY, true },
|
2008-07-23 08:24:26 +08:00
|
|
|
#endif
|
2008-05-02 10:14:59 +08:00
|
|
|
};
|
2006-06-28 19:26:45 +08:00
|
|
|
|
2008-07-23 08:24:25 +08:00
|
|
|
if (each_symbol_in_section(arr, ARRAY_SIZE(arr), NULL, fn, data))
|
|
|
|
return true;
|
2006-06-28 19:26:45 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
list_for_each_entry(mod, &modules, list) {
|
2008-05-02 10:14:59 +08:00
|
|
|
struct symsearch arr[] = {
|
|
|
|
{ mod->syms, mod->syms + mod->num_syms, mod->crcs,
|
2008-07-23 08:24:25 +08:00
|
|
|
NOT_GPL_ONLY, false },
|
2008-05-02 10:14:59 +08:00
|
|
|
{ mod->gpl_syms, mod->gpl_syms + mod->num_gpl_syms,
|
2008-07-23 08:24:25 +08:00
|
|
|
mod->gpl_crcs,
|
|
|
|
GPL_ONLY, false },
|
2008-05-02 10:14:59 +08:00
|
|
|
{ mod->gpl_future_syms,
|
|
|
|
mod->gpl_future_syms + mod->num_gpl_future_syms,
|
2008-07-23 08:24:25 +08:00
|
|
|
mod->gpl_future_crcs,
|
|
|
|
WILL_BE_GPL_ONLY, false },
|
2008-07-23 08:24:26 +08:00
|
|
|
#ifdef CONFIG_UNUSED_SYMBOLS
|
2008-05-02 10:14:59 +08:00
|
|
|
{ mod->unused_syms,
|
|
|
|
mod->unused_syms + mod->num_unused_syms,
|
2008-07-23 08:24:25 +08:00
|
|
|
mod->unused_crcs,
|
|
|
|
NOT_GPL_ONLY, true },
|
2008-05-02 10:14:59 +08:00
|
|
|
{ mod->unused_gpl_syms,
|
|
|
|
mod->unused_gpl_syms + mod->num_unused_gpl_syms,
|
2008-07-23 08:24:25 +08:00
|
|
|
mod->unused_gpl_crcs,
|
|
|
|
GPL_ONLY, true },
|
2008-07-23 08:24:26 +08:00
|
|
|
#endif
|
2008-05-02 10:14:59 +08:00
|
|
|
};
|
|
|
|
|
2008-07-23 08:24:25 +08:00
|
|
|
if (each_symbol_in_section(arr, ARRAY_SIZE(arr), mod, fn, data))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct find_symbol_arg {
|
|
|
|
/* Input */
|
|
|
|
const char *name;
|
|
|
|
bool gplok;
|
|
|
|
bool warn;
|
|
|
|
|
|
|
|
/* Output */
|
|
|
|
struct module *owner;
|
|
|
|
const unsigned long *crc;
|
|
|
|
unsigned long value;
|
|
|
|
};
|
|
|
|
|
|
|
|
static bool find_symbol_in_section(const struct symsearch *syms,
|
|
|
|
struct module *owner,
|
|
|
|
unsigned int symnum, void *data)
|
|
|
|
{
|
|
|
|
struct find_symbol_arg *fsa = data;
|
|
|
|
|
|
|
|
if (strcmp(syms->start[symnum].name, fsa->name) != 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!fsa->gplok) {
|
|
|
|
if (syms->licence == GPL_ONLY)
|
|
|
|
return false;
|
|
|
|
if (syms->licence == WILL_BE_GPL_ONLY && fsa->warn) {
|
|
|
|
printk(KERN_WARNING "Symbol %s is being used "
|
|
|
|
"by a non-GPL module, which will not "
|
|
|
|
"be allowed in the future\n", fsa->name);
|
|
|
|
printk(KERN_WARNING "Please see the file "
|
|
|
|
"Documentation/feature-removal-schedule.txt "
|
|
|
|
"in the kernel source tree for more details.\n");
|
2006-03-21 05:17:13 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2008-05-02 10:14:59 +08:00
|
|
|
|
2008-07-23 08:24:26 +08:00
|
|
|
#ifdef CONFIG_UNUSED_SYMBOLS
|
2008-07-23 08:24:25 +08:00
|
|
|
if (syms->unused && fsa->warn) {
|
|
|
|
printk(KERN_WARNING "Symbol %s is marked as UNUSED, "
|
|
|
|
"however this module is using it.\n", fsa->name);
|
|
|
|
printk(KERN_WARNING
|
|
|
|
"This symbol will go away in the future.\n");
|
|
|
|
printk(KERN_WARNING
|
|
|
|
"Please evalute if this is the right api to use and if "
|
|
|
|
"it really is, submit a report the linux kernel "
|
|
|
|
"mailinglist together with submitting your code for "
|
|
|
|
"inclusion.\n");
|
|
|
|
}
|
2008-07-23 08:24:26 +08:00
|
|
|
#endif
|
2008-07-23 08:24:25 +08:00
|
|
|
|
|
|
|
fsa->owner = owner;
|
|
|
|
fsa->crc = symversion(syms->crcs, symnum);
|
|
|
|
fsa->value = syms->start[symnum].value;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find a symbol, return value, (optional) crc and (optional) module
|
|
|
|
* which owns it */
|
|
|
|
static unsigned long find_symbol(const char *name,
|
|
|
|
struct module **owner,
|
|
|
|
const unsigned long **crc,
|
|
|
|
bool gplok,
|
|
|
|
bool warn)
|
|
|
|
{
|
|
|
|
struct find_symbol_arg fsa;
|
|
|
|
|
|
|
|
fsa.name = name;
|
|
|
|
fsa.gplok = gplok;
|
|
|
|
fsa.warn = warn;
|
|
|
|
|
|
|
|
if (each_symbol(find_symbol_in_section, &fsa)) {
|
|
|
|
if (owner)
|
|
|
|
*owner = fsa.owner;
|
|
|
|
if (crc)
|
|
|
|
*crc = fsa.crc;
|
|
|
|
return fsa.value;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
DEBUGP("Failed to find symbol %s\n", name);
|
2008-02-08 20:18:41 +08:00
|
|
|
return -ENOENT;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Search for module by name: must hold module_mutex. */
|
|
|
|
static struct module *find_module(const char *name)
|
|
|
|
{
|
|
|
|
struct module *mod;
|
|
|
|
|
|
|
|
list_for_each_entry(mod, &modules, list) {
|
|
|
|
if (strcmp(mod->name, name) == 0)
|
|
|
|
return mod;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_SMP
|
|
|
|
/* Number of blocks used and allocated. */
|
|
|
|
static unsigned int pcpu_num_used, pcpu_num_allocated;
|
|
|
|
/* Size of each block. -ve means used. */
|
|
|
|
static int *pcpu_size;
|
|
|
|
|
|
|
|
static int split_block(unsigned int i, unsigned short size)
|
|
|
|
{
|
|
|
|
/* Reallocation required? */
|
|
|
|
if (pcpu_num_used + 1 > pcpu_num_allocated) {
|
2007-05-08 15:24:58 +08:00
|
|
|
int *new;
|
|
|
|
|
|
|
|
new = krealloc(pcpu_size, sizeof(new[0])*pcpu_num_allocated*2,
|
|
|
|
GFP_KERNEL);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!new)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
pcpu_num_allocated *= 2;
|
|
|
|
pcpu_size = new;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Insert a new subblock */
|
|
|
|
memmove(&pcpu_size[i+1], &pcpu_size[i],
|
|
|
|
sizeof(pcpu_size[0]) * (pcpu_num_used - i));
|
|
|
|
pcpu_num_used++;
|
|
|
|
|
|
|
|
pcpu_size[i+1] -= size;
|
|
|
|
pcpu_size[i] = size;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline unsigned int block_size(int val)
|
|
|
|
{
|
|
|
|
if (val < 0)
|
|
|
|
return -val;
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2005-08-02 12:11:47 +08:00
|
|
|
static void *percpu_modalloc(unsigned long size, unsigned long align,
|
|
|
|
const char *name)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
unsigned long extra;
|
|
|
|
unsigned int i;
|
|
|
|
void *ptr;
|
|
|
|
|
2007-05-03 01:27:12 +08:00
|
|
|
if (align > PAGE_SIZE) {
|
|
|
|
printk(KERN_WARNING "%s: per-cpu alignment %li > %li\n",
|
|
|
|
name, align, PAGE_SIZE);
|
|
|
|
align = PAGE_SIZE;
|
2005-08-02 12:11:47 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
ptr = __per_cpu_start;
|
|
|
|
for (i = 0; i < pcpu_num_used; ptr += block_size(pcpu_size[i]), i++) {
|
|
|
|
/* Extra for alignment requirement. */
|
|
|
|
extra = ALIGN((unsigned long)ptr, align) - (unsigned long)ptr;
|
|
|
|
BUG_ON(i == 0 && extra != 0);
|
|
|
|
|
|
|
|
if (pcpu_size[i] < 0 || pcpu_size[i] < extra + size)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Transfer extra to previous block. */
|
|
|
|
if (pcpu_size[i-1] < 0)
|
|
|
|
pcpu_size[i-1] -= extra;
|
|
|
|
else
|
|
|
|
pcpu_size[i-1] += extra;
|
|
|
|
pcpu_size[i] -= extra;
|
|
|
|
ptr += extra;
|
|
|
|
|
|
|
|
/* Split block if warranted */
|
|
|
|
if (pcpu_size[i] - size > sizeof(unsigned long))
|
|
|
|
if (!split_block(i, size))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* Mark allocated */
|
|
|
|
pcpu_size[i] = -pcpu_size[i];
|
|
|
|
return ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
printk(KERN_WARNING "Could not allocate %lu bytes percpu data\n",
|
|
|
|
size);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void percpu_modfree(void *freeme)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
void *ptr = __per_cpu_start + block_size(pcpu_size[0]);
|
|
|
|
|
|
|
|
/* First entry is core kernel percpu data. */
|
|
|
|
for (i = 1; i < pcpu_num_used; ptr += block_size(pcpu_size[i]), i++) {
|
|
|
|
if (ptr == freeme) {
|
|
|
|
pcpu_size[i] = -pcpu_size[i];
|
|
|
|
goto free;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
BUG();
|
|
|
|
|
|
|
|
free:
|
|
|
|
/* Merge with previous? */
|
|
|
|
if (pcpu_size[i-1] >= 0) {
|
|
|
|
pcpu_size[i-1] += pcpu_size[i];
|
|
|
|
pcpu_num_used--;
|
|
|
|
memmove(&pcpu_size[i], &pcpu_size[i+1],
|
|
|
|
(pcpu_num_used - i) * sizeof(pcpu_size[0]));
|
|
|
|
i--;
|
|
|
|
}
|
|
|
|
/* Merge with next? */
|
|
|
|
if (i+1 < pcpu_num_used && pcpu_size[i+1] >= 0) {
|
|
|
|
pcpu_size[i] += pcpu_size[i+1];
|
|
|
|
pcpu_num_used--;
|
|
|
|
memmove(&pcpu_size[i+1], &pcpu_size[i+2],
|
|
|
|
(pcpu_num_used - (i+1)) * sizeof(pcpu_size[0]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int find_pcpusec(Elf_Ehdr *hdr,
|
|
|
|
Elf_Shdr *sechdrs,
|
|
|
|
const char *secstrings)
|
|
|
|
{
|
|
|
|
return find_sec(hdr, sechdrs, secstrings, ".data.percpu");
|
|
|
|
}
|
|
|
|
|
2008-01-30 20:33:32 +08:00
|
|
|
static void percpu_modcopy(void *pcpudest, const void *from, unsigned long size)
|
|
|
|
{
|
|
|
|
int cpu;
|
|
|
|
|
|
|
|
for_each_possible_cpu(cpu)
|
|
|
|
memcpy(pcpudest + per_cpu_offset(cpu), from, size);
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static int percpu_modinit(void)
|
|
|
|
{
|
|
|
|
pcpu_num_used = 2;
|
|
|
|
pcpu_num_allocated = 2;
|
|
|
|
pcpu_size = kmalloc(sizeof(pcpu_size[0]) * pcpu_num_allocated,
|
|
|
|
GFP_KERNEL);
|
|
|
|
/* Static in-kernel percpu data (used). */
|
2007-05-03 01:27:11 +08:00
|
|
|
pcpu_size[0] = -(__per_cpu_end-__per_cpu_start);
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Free room. */
|
|
|
|
pcpu_size[1] = PERCPU_ENOUGH_ROOM + pcpu_size[0];
|
|
|
|
if (pcpu_size[1] < 0) {
|
|
|
|
printk(KERN_ERR "No per-cpu room for modules.\n");
|
|
|
|
pcpu_num_used = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2007-10-18 18:06:07 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
__initcall(percpu_modinit);
|
|
|
|
#else /* ... !CONFIG_SMP */
|
2005-08-02 12:11:47 +08:00
|
|
|
static inline void *percpu_modalloc(unsigned long size, unsigned long align,
|
|
|
|
const char *name)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
static inline void percpu_modfree(void *pcpuptr)
|
|
|
|
{
|
|
|
|
BUG();
|
|
|
|
}
|
|
|
|
static inline unsigned int find_pcpusec(Elf_Ehdr *hdr,
|
|
|
|
Elf_Shdr *sechdrs,
|
|
|
|
const char *secstrings)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static inline void percpu_modcopy(void *pcpudst, const void *src,
|
|
|
|
unsigned long size)
|
|
|
|
{
|
|
|
|
/* pcpusec should be 0, and size of that section should be 0. */
|
|
|
|
BUG_ON(size != 0);
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_SMP */
|
|
|
|
|
[PATCH] modules: add version and srcversion to sysfs
This patch adds version and srcversion files to
/sys/module/${modulename} containing the version and srcversion fields
of the module's modinfo section (if present).
/sys/module/e1000
|-- srcversion
`-- version
This patch differs slightly from the version posted in January, as it
now uses the new kstrdup() call in -mm.
Why put this in sysfs?
a) Tools like DKMS, which deal with changing out individual kernel
modules without replacing the whole kernel, can behave smarter if they
can tell the version of a given module. The autoinstaller feature, for
example, which determines if your system has a "good" version of a
driver (i.e. if the one provided by DKMS has a newer verson than that
provided by the kernel package installed), and to automatically compile
and install a newer version if DKMS has it but your kernel doesn't yet
have that version.
b) Because sysadmins manually, or with tools like DKMS, can switch out
modules on the file system, you can't count on 'modinfo foo.ko', which
looks at /lib/modules/${kernelver}/... actually matching what is loaded
into the kernel already. Hence asking sysfs for this.
c) as the unbind-driver-from-device work takes shape, it will be
possible to rebind a driver that's built-in (no .ko to modinfo for the
version) to a newly loaded module. sysfs will have the
currently-built-in version info, for comparison.
d) tech support scripts can then easily grab the version info for what's
running presently - a question I get often.
There has been renewed interest in this patch on linux-scsi by driver
authors.
As the idea originated from GregKH, I leave his Signed-off-by: intact,
though the implementation is nearly completely new. Compiled and run on
x86 and x86_64.
From: Matthew Dobson <colpatch@us.ibm.com>
build fix
From: Thierry Vignaud <tvignaud@mandriva.com>
build fix
From: Matthew Dobson <colpatch@us.ibm.com>
warning fix
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
Signed-off-by: Matt Domsch <Matt_Domsch@dell.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-06-24 13:05:15 +08:00
|
|
|
#define MODINFO_ATTR(field) \
|
|
|
|
static void setup_modinfo_##field(struct module *mod, const char *s) \
|
|
|
|
{ \
|
|
|
|
mod->field = kstrdup(s, GFP_KERNEL); \
|
|
|
|
} \
|
|
|
|
static ssize_t show_modinfo_##field(struct module_attribute *mattr, \
|
|
|
|
struct module *mod, char *buffer) \
|
|
|
|
{ \
|
|
|
|
return sprintf(buffer, "%s\n", mod->field); \
|
|
|
|
} \
|
|
|
|
static int modinfo_##field##_exists(struct module *mod) \
|
|
|
|
{ \
|
|
|
|
return mod->field != NULL; \
|
|
|
|
} \
|
|
|
|
static void free_modinfo_##field(struct module *mod) \
|
|
|
|
{ \
|
2007-10-18 18:06:07 +08:00
|
|
|
kfree(mod->field); \
|
|
|
|
mod->field = NULL; \
|
[PATCH] modules: add version and srcversion to sysfs
This patch adds version and srcversion files to
/sys/module/${modulename} containing the version and srcversion fields
of the module's modinfo section (if present).
/sys/module/e1000
|-- srcversion
`-- version
This patch differs slightly from the version posted in January, as it
now uses the new kstrdup() call in -mm.
Why put this in sysfs?
a) Tools like DKMS, which deal with changing out individual kernel
modules without replacing the whole kernel, can behave smarter if they
can tell the version of a given module. The autoinstaller feature, for
example, which determines if your system has a "good" version of a
driver (i.e. if the one provided by DKMS has a newer verson than that
provided by the kernel package installed), and to automatically compile
and install a newer version if DKMS has it but your kernel doesn't yet
have that version.
b) Because sysadmins manually, or with tools like DKMS, can switch out
modules on the file system, you can't count on 'modinfo foo.ko', which
looks at /lib/modules/${kernelver}/... actually matching what is loaded
into the kernel already. Hence asking sysfs for this.
c) as the unbind-driver-from-device work takes shape, it will be
possible to rebind a driver that's built-in (no .ko to modinfo for the
version) to a newly loaded module. sysfs will have the
currently-built-in version info, for comparison.
d) tech support scripts can then easily grab the version info for what's
running presently - a question I get often.
There has been renewed interest in this patch on linux-scsi by driver
authors.
As the idea originated from GregKH, I leave his Signed-off-by: intact,
though the implementation is nearly completely new. Compiled and run on
x86 and x86_64.
From: Matthew Dobson <colpatch@us.ibm.com>
build fix
From: Thierry Vignaud <tvignaud@mandriva.com>
build fix
From: Matthew Dobson <colpatch@us.ibm.com>
warning fix
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
Signed-off-by: Matt Domsch <Matt_Domsch@dell.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-06-24 13:05:15 +08:00
|
|
|
} \
|
|
|
|
static struct module_attribute modinfo_##field = { \
|
2007-06-14 02:45:17 +08:00
|
|
|
.attr = { .name = __stringify(field), .mode = 0444 }, \
|
[PATCH] modules: add version and srcversion to sysfs
This patch adds version and srcversion files to
/sys/module/${modulename} containing the version and srcversion fields
of the module's modinfo section (if present).
/sys/module/e1000
|-- srcversion
`-- version
This patch differs slightly from the version posted in January, as it
now uses the new kstrdup() call in -mm.
Why put this in sysfs?
a) Tools like DKMS, which deal with changing out individual kernel
modules without replacing the whole kernel, can behave smarter if they
can tell the version of a given module. The autoinstaller feature, for
example, which determines if your system has a "good" version of a
driver (i.e. if the one provided by DKMS has a newer verson than that
provided by the kernel package installed), and to automatically compile
and install a newer version if DKMS has it but your kernel doesn't yet
have that version.
b) Because sysadmins manually, or with tools like DKMS, can switch out
modules on the file system, you can't count on 'modinfo foo.ko', which
looks at /lib/modules/${kernelver}/... actually matching what is loaded
into the kernel already. Hence asking sysfs for this.
c) as the unbind-driver-from-device work takes shape, it will be
possible to rebind a driver that's built-in (no .ko to modinfo for the
version) to a newly loaded module. sysfs will have the
currently-built-in version info, for comparison.
d) tech support scripts can then easily grab the version info for what's
running presently - a question I get often.
There has been renewed interest in this patch on linux-scsi by driver
authors.
As the idea originated from GregKH, I leave his Signed-off-by: intact,
though the implementation is nearly completely new. Compiled and run on
x86 and x86_64.
From: Matthew Dobson <colpatch@us.ibm.com>
build fix
From: Thierry Vignaud <tvignaud@mandriva.com>
build fix
From: Matthew Dobson <colpatch@us.ibm.com>
warning fix
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
Signed-off-by: Matt Domsch <Matt_Domsch@dell.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-06-24 13:05:15 +08:00
|
|
|
.show = show_modinfo_##field, \
|
|
|
|
.setup = setup_modinfo_##field, \
|
|
|
|
.test = modinfo_##field##_exists, \
|
|
|
|
.free = free_modinfo_##field, \
|
|
|
|
};
|
|
|
|
|
|
|
|
MODINFO_ATTR(version);
|
|
|
|
MODINFO_ATTR(srcversion);
|
|
|
|
|
2008-01-26 04:08:33 +08:00
|
|
|
static char last_unloaded_module[MODULE_NAME_LEN+1];
|
|
|
|
|
2006-02-17 05:50:23 +08:00
|
|
|
#ifdef CONFIG_MODULE_UNLOAD
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Init the unload section of the module. */
|
|
|
|
static void module_unload_init(struct module *mod)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
INIT_LIST_HEAD(&mod->modules_which_use_me);
|
|
|
|
for (i = 0; i < NR_CPUS; i++)
|
|
|
|
local_set(&mod->ref[i].count, 0);
|
|
|
|
/* Hold reference count during initialization. */
|
2005-06-22 08:14:34 +08:00
|
|
|
local_set(&mod->ref[raw_smp_processor_id()].count, 1);
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Backwards compatibility macros put refcount during init. */
|
|
|
|
mod->waiter = current;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* modules using other modules */
|
|
|
|
struct module_use
|
|
|
|
{
|
|
|
|
struct list_head list;
|
|
|
|
struct module *module_which_uses;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Does a already use b? */
|
|
|
|
static int already_uses(struct module *a, struct module *b)
|
|
|
|
{
|
|
|
|
struct module_use *use;
|
|
|
|
|
|
|
|
list_for_each_entry(use, &b->modules_which_use_me, list) {
|
|
|
|
if (use->module_which_uses == a) {
|
|
|
|
DEBUGP("%s uses %s!\n", a->name, b->name);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DEBUGP("%s does not use %s!\n", a->name, b->name);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Module a uses b */
|
|
|
|
static int use_module(struct module *a, struct module *b)
|
|
|
|
{
|
|
|
|
struct module_use *use;
|
2008-01-30 06:13:18 +08:00
|
|
|
int no_warn, err;
|
2007-01-18 20:26:15 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (b == NULL || already_uses(a, b)) return 1;
|
|
|
|
|
2008-01-30 06:13:18 +08:00
|
|
|
/* If we're interrupted or time out, we fail. */
|
|
|
|
if (wait_event_interruptible_timeout(
|
|
|
|
module_wq, (err = strong_try_module_get(b)) != -EBUSY,
|
|
|
|
30 * HZ) <= 0) {
|
|
|
|
printk("%s: gave up waiting for init of module %s.\n",
|
|
|
|
a->name, b->name);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If strong_try_module_get() returned a different error, we fail. */
|
|
|
|
if (err)
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
DEBUGP("Allocating new usage for %s.\n", a->name);
|
|
|
|
use = kmalloc(sizeof(*use), GFP_ATOMIC);
|
|
|
|
if (!use) {
|
|
|
|
printk("%s: out of memory loading\n", a->name);
|
|
|
|
module_put(b);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
use->module_which_uses = a;
|
|
|
|
list_add(&use->list, &b->modules_which_use_me);
|
2007-01-18 20:26:15 +08:00
|
|
|
no_warn = sysfs_create_link(b->holders_dir, &a->mkobj.kobj, a->name);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Clear the unload stuff of the module. */
|
|
|
|
static void module_unload_free(struct module *mod)
|
|
|
|
{
|
|
|
|
struct module *i;
|
|
|
|
|
|
|
|
list_for_each_entry(i, &modules, list) {
|
|
|
|
struct module_use *use;
|
|
|
|
|
|
|
|
list_for_each_entry(use, &i->modules_which_use_me, list) {
|
|
|
|
if (use->module_which_uses == mod) {
|
|
|
|
DEBUGP("%s unusing %s\n", mod->name, i->name);
|
|
|
|
module_put(i);
|
|
|
|
list_del(&use->list);
|
|
|
|
kfree(use);
|
2007-01-18 20:26:15 +08:00
|
|
|
sysfs_remove_link(i->holders_dir, mod->name);
|
2005-04-17 06:20:36 +08:00
|
|
|
/* There can be at most one match. */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_MODULE_FORCE_UNLOAD
|
2006-01-08 17:04:29 +08:00
|
|
|
static inline int try_force_unload(unsigned int flags)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int ret = (flags & O_TRUNC);
|
|
|
|
if (ret)
|
2006-01-08 17:04:29 +08:00
|
|
|
add_taint(TAINT_FORCED_RMMOD);
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
#else
|
2006-01-08 17:04:29 +08:00
|
|
|
static inline int try_force_unload(unsigned int flags)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_MODULE_FORCE_UNLOAD */
|
|
|
|
|
|
|
|
struct stopref
|
|
|
|
{
|
|
|
|
struct module *mod;
|
|
|
|
int flags;
|
|
|
|
int *forced;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Whole machine is stopped with interrupts off when this runs. */
|
|
|
|
static int __try_stop_module(void *_sref)
|
|
|
|
{
|
|
|
|
struct stopref *sref = _sref;
|
|
|
|
|
2008-07-23 08:24:25 +08:00
|
|
|
/* If it's not unused, quit unless we're forcing. */
|
|
|
|
if (module_refcount(sref->mod) != 0) {
|
2006-01-08 17:04:29 +08:00
|
|
|
if (!(*sref->forced = try_force_unload(sref->flags)))
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EWOULDBLOCK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Mark it as dying. */
|
|
|
|
sref->mod->state = MODULE_STATE_GOING;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int try_stop_module(struct module *mod, int flags, int *forced)
|
|
|
|
{
|
2008-07-23 08:24:25 +08:00
|
|
|
if (flags & O_NONBLOCK) {
|
|
|
|
struct stopref sref = { mod, flags, forced };
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-07-29 01:16:30 +08:00
|
|
|
return stop_machine(__try_stop_module, &sref, NULL);
|
2008-07-23 08:24:25 +08:00
|
|
|
} else {
|
|
|
|
/* We don't need to stop the machine for this. */
|
|
|
|
mod->state = MODULE_STATE_GOING;
|
|
|
|
synchronize_sched();
|
|
|
|
return 0;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int module_refcount(struct module *mod)
|
|
|
|
{
|
|
|
|
unsigned int i, total = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < NR_CPUS; i++)
|
|
|
|
total += local_read(&mod->ref[i].count);
|
|
|
|
return total;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(module_refcount);
|
|
|
|
|
|
|
|
/* This exists whether we can unload or not */
|
|
|
|
static void free_module(struct module *mod);
|
|
|
|
|
|
|
|
static void wait_for_zero_refcount(struct module *mod)
|
|
|
|
{
|
2008-02-26 23:47:18 +08:00
|
|
|
/* Since we might sleep for some time, release the mutex first */
|
2006-03-23 19:00:46 +08:00
|
|
|
mutex_unlock(&module_mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
for (;;) {
|
|
|
|
DEBUGP("Looking at refcount...\n");
|
|
|
|
set_current_state(TASK_UNINTERRUPTIBLE);
|
|
|
|
if (module_refcount(mod) == 0)
|
|
|
|
break;
|
|
|
|
schedule();
|
|
|
|
}
|
|
|
|
current->state = TASK_RUNNING;
|
2006-03-23 19:00:46 +08:00
|
|
|
mutex_lock(&module_mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2007-02-24 06:54:57 +08:00
|
|
|
asmlinkage long
|
|
|
|
sys_delete_module(const char __user *name_user, unsigned int flags)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct module *mod;
|
2007-02-24 06:54:57 +08:00
|
|
|
char name[MODULE_NAME_LEN];
|
2005-04-17 06:20:36 +08:00
|
|
|
int ret, forced = 0;
|
|
|
|
|
2007-02-24 06:54:57 +08:00
|
|
|
if (!capable(CAP_SYS_MODULE))
|
|
|
|
return -EPERM;
|
|
|
|
|
|
|
|
if (strncpy_from_user(name, name_user, MODULE_NAME_LEN-1) < 0)
|
|
|
|
return -EFAULT;
|
|
|
|
name[MODULE_NAME_LEN-1] = '\0';
|
|
|
|
|
2006-03-23 19:00:46 +08:00
|
|
|
if (mutex_lock_interruptible(&module_mutex) != 0)
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINTR;
|
|
|
|
|
|
|
|
mod = find_module(name);
|
|
|
|
if (!mod) {
|
|
|
|
ret = -ENOENT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!list_empty(&mod->modules_which_use_me)) {
|
|
|
|
/* Other modules depend on us: get rid of them first. */
|
|
|
|
ret = -EWOULDBLOCK;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Doing init or already dying? */
|
|
|
|
if (mod->state != MODULE_STATE_LIVE) {
|
|
|
|
/* FIXME: if (force), slam module count and wake up
|
|
|
|
waiter --RR */
|
|
|
|
DEBUGP("%s already dying\n", mod->name);
|
|
|
|
ret = -EBUSY;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If it has an init func, it must have an exit func to unload */
|
2007-10-17 14:26:27 +08:00
|
|
|
if (mod->init && !mod->exit) {
|
2006-01-08 17:04:29 +08:00
|
|
|
forced = try_force_unload(flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!forced) {
|
|
|
|
/* This module can't be removed */
|
|
|
|
ret = -EBUSY;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set this up before setting mod->state */
|
|
|
|
mod->waiter = current;
|
|
|
|
|
|
|
|
/* Stop the machine so refcounts can't move and disable module. */
|
|
|
|
ret = try_stop_module(mod, flags, &forced);
|
|
|
|
if (ret != 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/* Never wait if forced. */
|
|
|
|
if (!forced && module_refcount(mod) != 0)
|
|
|
|
wait_for_zero_refcount(mod);
|
|
|
|
|
2008-04-21 20:34:31 +08:00
|
|
|
mutex_unlock(&module_mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Final destruction now noone is using it. */
|
2008-04-21 20:34:31 +08:00
|
|
|
if (mod->exit != NULL)
|
2005-04-17 06:20:36 +08:00
|
|
|
mod->exit();
|
2008-04-21 20:34:31 +08:00
|
|
|
blocking_notifier_call_chain(&module_notify_list,
|
|
|
|
MODULE_STATE_GOING, mod);
|
|
|
|
mutex_lock(&module_mutex);
|
2008-01-26 04:08:33 +08:00
|
|
|
/* Store the name of the last unloaded module for diagnostic purposes */
|
2008-01-30 06:13:20 +08:00
|
|
|
strlcpy(last_unloaded_module, mod->name, sizeof(last_unloaded_module));
|
2005-04-17 06:20:36 +08:00
|
|
|
free_module(mod);
|
|
|
|
|
|
|
|
out:
|
2006-03-23 19:00:46 +08:00
|
|
|
mutex_unlock(&module_mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void print_unload_info(struct seq_file *m, struct module *mod)
|
|
|
|
{
|
|
|
|
struct module_use *use;
|
|
|
|
int printed_something = 0;
|
|
|
|
|
|
|
|
seq_printf(m, " %u ", module_refcount(mod));
|
|
|
|
|
|
|
|
/* Always include a trailing , so userspace can differentiate
|
|
|
|
between this and the old multi-field proc format. */
|
|
|
|
list_for_each_entry(use, &mod->modules_which_use_me, list) {
|
|
|
|
printed_something = 1;
|
|
|
|
seq_printf(m, "%s,", use->module_which_uses->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mod->init != NULL && mod->exit == NULL) {
|
|
|
|
printed_something = 1;
|
|
|
|
seq_printf(m, "[permanent],");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!printed_something)
|
|
|
|
seq_printf(m, "-");
|
|
|
|
}
|
|
|
|
|
|
|
|
void __symbol_put(const char *symbol)
|
|
|
|
{
|
|
|
|
struct module *owner;
|
|
|
|
|
2007-07-16 14:41:46 +08:00
|
|
|
preempt_disable();
|
2008-05-02 10:14:59 +08:00
|
|
|
if (IS_ERR_VALUE(find_symbol(symbol, &owner, NULL, true, false)))
|
2005-04-17 06:20:36 +08:00
|
|
|
BUG();
|
|
|
|
module_put(owner);
|
2007-07-16 14:41:46 +08:00
|
|
|
preempt_enable();
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(__symbol_put);
|
|
|
|
|
|
|
|
void symbol_put_addr(void *addr)
|
|
|
|
{
|
2006-05-16 00:44:06 +08:00
|
|
|
struct module *modaddr;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-05-16 00:44:06 +08:00
|
|
|
if (core_kernel_text((unsigned long)addr))
|
|
|
|
return;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-05-16 00:44:06 +08:00
|
|
|
if (!(modaddr = module_text_address((unsigned long)addr)))
|
|
|
|
BUG();
|
|
|
|
module_put(modaddr);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(symbol_put_addr);
|
|
|
|
|
|
|
|
static ssize_t show_refcnt(struct module_attribute *mattr,
|
|
|
|
struct module *mod, char *buffer)
|
|
|
|
{
|
2007-08-07 03:47:45 +08:00
|
|
|
return sprintf(buffer, "%u\n", module_refcount(mod));
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct module_attribute refcnt = {
|
2007-06-14 02:45:17 +08:00
|
|
|
.attr = { .name = "refcnt", .mode = 0444 },
|
2005-04-17 06:20:36 +08:00
|
|
|
.show = show_refcnt,
|
|
|
|
};
|
|
|
|
|
2006-10-18 13:47:25 +08:00
|
|
|
void module_put(struct module *module)
|
|
|
|
{
|
|
|
|
if (module) {
|
|
|
|
unsigned int cpu = get_cpu();
|
|
|
|
local_dec(&module->ref[cpu].count);
|
|
|
|
/* Maybe they're waiting for us to drop reference? */
|
|
|
|
if (unlikely(!module_is_live(module)))
|
|
|
|
wake_up_process(module->waiter);
|
|
|
|
put_cpu();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(module_put);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
#else /* !CONFIG_MODULE_UNLOAD */
|
|
|
|
static void print_unload_info(struct seq_file *m, struct module *mod)
|
|
|
|
{
|
|
|
|
/* We don't know the usage count, or what modules are using. */
|
|
|
|
seq_printf(m, " - -");
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void module_unload_free(struct module *mod)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int use_module(struct module *a, struct module *b)
|
|
|
|
{
|
2008-01-30 06:13:18 +08:00
|
|
|
return strong_try_module_get(b) == 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline void module_unload_init(struct module *mod)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_MODULE_UNLOAD */
|
|
|
|
|
2006-11-24 19:15:25 +08:00
|
|
|
static ssize_t show_initstate(struct module_attribute *mattr,
|
|
|
|
struct module *mod, char *buffer)
|
|
|
|
{
|
|
|
|
const char *state = "unknown";
|
|
|
|
|
|
|
|
switch (mod->state) {
|
|
|
|
case MODULE_STATE_LIVE:
|
|
|
|
state = "live";
|
|
|
|
break;
|
|
|
|
case MODULE_STATE_COMING:
|
|
|
|
state = "coming";
|
|
|
|
break;
|
|
|
|
case MODULE_STATE_GOING:
|
|
|
|
state = "going";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return sprintf(buffer, "%s\n", state);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct module_attribute initstate = {
|
2007-06-14 02:45:17 +08:00
|
|
|
.attr = { .name = "initstate", .mode = 0444 },
|
2006-11-24 19:15:25 +08:00
|
|
|
.show = show_initstate,
|
|
|
|
};
|
|
|
|
|
2006-02-17 05:50:23 +08:00
|
|
|
static struct module_attribute *modinfo_attrs[] = {
|
|
|
|
&modinfo_version,
|
|
|
|
&modinfo_srcversion,
|
2006-11-24 19:15:25 +08:00
|
|
|
&initstate,
|
2006-02-17 05:50:23 +08:00
|
|
|
#ifdef CONFIG_MODULE_UNLOAD
|
|
|
|
&refcnt,
|
|
|
|
#endif
|
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static const char vermagic[] = VERMAGIC_STRING;
|
|
|
|
|
2008-05-05 08:04:16 +08:00
|
|
|
static int try_to_force_load(struct module *mod, const char *symname)
|
|
|
|
{
|
|
|
|
#ifdef CONFIG_MODULE_FORCE_LOAD
|
2008-10-16 13:01:41 +08:00
|
|
|
if (!test_taint(TAINT_FORCED_MODULE))
|
2008-05-05 08:04:16 +08:00
|
|
|
printk("%s: no version for \"%s\" found: kernel tainted.\n",
|
|
|
|
mod->name, symname);
|
|
|
|
add_taint_module(mod, TAINT_FORCED_MODULE);
|
|
|
|
return 0;
|
|
|
|
#else
|
|
|
|
return -ENOEXEC;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
#ifdef CONFIG_MODVERSIONS
|
|
|
|
static int check_version(Elf_Shdr *sechdrs,
|
|
|
|
unsigned int versindex,
|
|
|
|
const char *symname,
|
|
|
|
struct module *mod,
|
|
|
|
const unsigned long *crc)
|
|
|
|
{
|
|
|
|
unsigned int i, num_versions;
|
|
|
|
struct modversion_info *versions;
|
|
|
|
|
|
|
|
/* Exporting module didn't supply crcs? OK, we're already tainted. */
|
|
|
|
if (!crc)
|
|
|
|
return 1;
|
|
|
|
|
2008-05-09 14:24:21 +08:00
|
|
|
/* No versions at all? modprobe --force does this. */
|
|
|
|
if (versindex == 0)
|
|
|
|
return try_to_force_load(mod, symname) == 0;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
versions = (void *) sechdrs[versindex].sh_addr;
|
|
|
|
num_versions = sechdrs[versindex].sh_size
|
|
|
|
/ sizeof(struct modversion_info);
|
|
|
|
|
|
|
|
for (i = 0; i < num_versions; i++) {
|
|
|
|
if (strcmp(versions[i].name, symname) != 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (versions[i].crc == *crc)
|
|
|
|
return 1;
|
|
|
|
DEBUGP("Found checksum %lX vs module %lX\n",
|
|
|
|
*crc, versions[i].crc);
|
2008-05-05 08:04:16 +08:00
|
|
|
goto bad_version;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2008-05-05 08:04:16 +08:00
|
|
|
|
2008-05-09 14:24:21 +08:00
|
|
|
printk(KERN_WARNING "%s: no symbol version for %s\n",
|
|
|
|
mod->name, symname);
|
|
|
|
return 0;
|
2008-05-05 08:04:16 +08:00
|
|
|
|
|
|
|
bad_version:
|
|
|
|
printk("%s: disagrees about version of symbol %s\n",
|
|
|
|
mod->name, symname);
|
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline int check_modstruct_version(Elf_Shdr *sechdrs,
|
|
|
|
unsigned int versindex,
|
|
|
|
struct module *mod)
|
|
|
|
{
|
|
|
|
const unsigned long *crc;
|
|
|
|
|
2008-05-02 10:14:59 +08:00
|
|
|
if (IS_ERR_VALUE(find_symbol("struct_module", NULL, &crc, true, false)))
|
2005-04-17 06:20:36 +08:00
|
|
|
BUG();
|
2008-05-02 10:14:59 +08:00
|
|
|
return check_version(sechdrs, versindex, "struct_module", mod, crc);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2008-05-09 14:25:28 +08:00
|
|
|
/* First part is kernel version, which we ignore if module has crcs. */
|
|
|
|
static inline int same_magic(const char *amagic, const char *bmagic,
|
|
|
|
bool has_crcs)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-05-09 14:25:28 +08:00
|
|
|
if (has_crcs) {
|
|
|
|
amagic += strcspn(amagic, " ");
|
|
|
|
bmagic += strcspn(bmagic, " ");
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
return strcmp(amagic, bmagic) == 0;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
static inline int check_version(Elf_Shdr *sechdrs,
|
|
|
|
unsigned int versindex,
|
|
|
|
const char *symname,
|
|
|
|
struct module *mod,
|
|
|
|
const unsigned long *crc)
|
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int check_modstruct_version(Elf_Shdr *sechdrs,
|
|
|
|
unsigned int versindex,
|
|
|
|
struct module *mod)
|
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2008-05-09 14:25:28 +08:00
|
|
|
static inline int same_magic(const char *amagic, const char *bmagic,
|
|
|
|
bool has_crcs)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
return strcmp(amagic, bmagic) == 0;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_MODVERSIONS */
|
|
|
|
|
|
|
|
/* Resolve a symbol for this module. I.e. if we find one, record usage.
|
|
|
|
Must be holding module_mutex. */
|
|
|
|
static unsigned long resolve_symbol(Elf_Shdr *sechdrs,
|
|
|
|
unsigned int versindex,
|
|
|
|
const char *name,
|
|
|
|
struct module *mod)
|
|
|
|
{
|
|
|
|
struct module *owner;
|
|
|
|
unsigned long ret;
|
|
|
|
const unsigned long *crc;
|
|
|
|
|
2008-05-02 10:14:59 +08:00
|
|
|
ret = find_symbol(name, &owner, &crc,
|
2008-10-16 13:01:41 +08:00
|
|
|
!(mod->taints & (1 << TAINT_PROPRIETARY_MODULE)), true);
|
2008-02-08 20:18:41 +08:00
|
|
|
if (!IS_ERR_VALUE(ret)) {
|
2007-11-09 00:37:38 +08:00
|
|
|
/* use_module can fail due to OOM,
|
|
|
|
or module initialization or unloading */
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!check_version(sechdrs, versindex, name, mod, crc) ||
|
|
|
|
!use_module(mod, owner))
|
2008-02-08 20:18:41 +08:00
|
|
|
ret = -EINVAL;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* /sys/module/foo/sections stuff
|
|
|
|
* J. Corbet <corbet@lwn.net>
|
|
|
|
*/
|
2008-02-21 07:33:20 +08:00
|
|
|
#if defined(CONFIG_KALLSYMS) && defined(CONFIG_SYSFS)
|
2008-03-13 17:03:44 +08:00
|
|
|
struct module_sect_attr
|
|
|
|
{
|
|
|
|
struct module_attribute mattr;
|
|
|
|
char *name;
|
|
|
|
unsigned long address;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct module_sect_attrs
|
|
|
|
{
|
|
|
|
struct attribute_group grp;
|
|
|
|
unsigned int nsections;
|
|
|
|
struct module_sect_attr attrs[0];
|
|
|
|
};
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static ssize_t module_sect_show(struct module_attribute *mattr,
|
|
|
|
struct module *mod, char *buf)
|
|
|
|
{
|
|
|
|
struct module_sect_attr *sattr =
|
|
|
|
container_of(mattr, struct module_sect_attr, mattr);
|
|
|
|
return sprintf(buf, "0x%lx\n", sattr->address);
|
|
|
|
}
|
|
|
|
|
2006-09-29 17:01:31 +08:00
|
|
|
static void free_sect_attrs(struct module_sect_attrs *sect_attrs)
|
|
|
|
{
|
2008-03-13 17:03:44 +08:00
|
|
|
unsigned int section;
|
2006-09-29 17:01:31 +08:00
|
|
|
|
|
|
|
for (section = 0; section < sect_attrs->nsections; section++)
|
|
|
|
kfree(sect_attrs->attrs[section].name);
|
|
|
|
kfree(sect_attrs);
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static void add_sect_attrs(struct module *mod, unsigned int nsect,
|
|
|
|
char *secstrings, Elf_Shdr *sechdrs)
|
|
|
|
{
|
|
|
|
unsigned int nloaded = 0, i, size[2];
|
|
|
|
struct module_sect_attrs *sect_attrs;
|
|
|
|
struct module_sect_attr *sattr;
|
|
|
|
struct attribute **gattr;
|
2007-10-18 18:06:07 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Count loaded sections and allocate structures */
|
|
|
|
for (i = 0; i < nsect; i++)
|
|
|
|
if (sechdrs[i].sh_flags & SHF_ALLOC)
|
|
|
|
nloaded++;
|
|
|
|
size[0] = ALIGN(sizeof(*sect_attrs)
|
|
|
|
+ nloaded * sizeof(sect_attrs->attrs[0]),
|
|
|
|
sizeof(sect_attrs->grp.attrs[0]));
|
|
|
|
size[1] = (nloaded + 1) * sizeof(sect_attrs->grp.attrs[0]);
|
2006-09-29 17:01:31 +08:00
|
|
|
sect_attrs = kzalloc(size[0] + size[1], GFP_KERNEL);
|
|
|
|
if (sect_attrs == NULL)
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
/* Setup section attributes. */
|
|
|
|
sect_attrs->grp.name = "sections";
|
|
|
|
sect_attrs->grp.attrs = (void *)sect_attrs + size[0];
|
|
|
|
|
2006-09-29 17:01:31 +08:00
|
|
|
sect_attrs->nsections = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
sattr = §_attrs->attrs[0];
|
|
|
|
gattr = §_attrs->grp.attrs[0];
|
|
|
|
for (i = 0; i < nsect; i++) {
|
|
|
|
if (! (sechdrs[i].sh_flags & SHF_ALLOC))
|
|
|
|
continue;
|
|
|
|
sattr->address = sechdrs[i].sh_addr;
|
2006-09-29 17:01:31 +08:00
|
|
|
sattr->name = kstrdup(secstrings + sechdrs[i].sh_name,
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (sattr->name == NULL)
|
|
|
|
goto out;
|
|
|
|
sect_attrs->nsections++;
|
2005-04-17 06:20:36 +08:00
|
|
|
sattr->mattr.show = module_sect_show;
|
|
|
|
sattr->mattr.store = NULL;
|
|
|
|
sattr->mattr.attr.name = sattr->name;
|
|
|
|
sattr->mattr.attr.mode = S_IRUGO;
|
|
|
|
*(gattr++) = &(sattr++)->mattr.attr;
|
|
|
|
}
|
|
|
|
*gattr = NULL;
|
|
|
|
|
|
|
|
if (sysfs_create_group(&mod->mkobj.kobj, §_attrs->grp))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
mod->sect_attrs = sect_attrs;
|
|
|
|
return;
|
|
|
|
out:
|
2006-09-29 17:01:31 +08:00
|
|
|
free_sect_attrs(sect_attrs);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void remove_sect_attrs(struct module *mod)
|
|
|
|
{
|
|
|
|
if (mod->sect_attrs) {
|
|
|
|
sysfs_remove_group(&mod->mkobj.kobj,
|
|
|
|
&mod->sect_attrs->grp);
|
|
|
|
/* We are positive that no one is using any sect attrs
|
|
|
|
* at this point. Deallocate immediately. */
|
2006-09-29 17:01:31 +08:00
|
|
|
free_sect_attrs(mod->sect_attrs);
|
2005-04-17 06:20:36 +08:00
|
|
|
mod->sect_attrs = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-10-17 14:26:40 +08:00
|
|
|
/*
|
|
|
|
* /sys/module/foo/notes/.section.name gives contents of SHT_NOTE sections.
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct module_notes_attrs {
|
|
|
|
struct kobject *dir;
|
|
|
|
unsigned int notes;
|
|
|
|
struct bin_attribute attrs[0];
|
|
|
|
};
|
|
|
|
|
|
|
|
static ssize_t module_notes_read(struct kobject *kobj,
|
|
|
|
struct bin_attribute *bin_attr,
|
|
|
|
char *buf, loff_t pos, size_t count)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* The caller checked the pos and count against our size.
|
|
|
|
*/
|
|
|
|
memcpy(buf, bin_attr->private + pos, count);
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void free_notes_attrs(struct module_notes_attrs *notes_attrs,
|
|
|
|
unsigned int i)
|
|
|
|
{
|
|
|
|
if (notes_attrs->dir) {
|
|
|
|
while (i-- > 0)
|
|
|
|
sysfs_remove_bin_file(notes_attrs->dir,
|
|
|
|
¬es_attrs->attrs[i]);
|
|
|
|
kobject_del(notes_attrs->dir);
|
|
|
|
}
|
|
|
|
kfree(notes_attrs);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void add_notes_attrs(struct module *mod, unsigned int nsect,
|
|
|
|
char *secstrings, Elf_Shdr *sechdrs)
|
|
|
|
{
|
|
|
|
unsigned int notes, loaded, i;
|
|
|
|
struct module_notes_attrs *notes_attrs;
|
|
|
|
struct bin_attribute *nattr;
|
|
|
|
|
|
|
|
/* Count notes sections and allocate structures. */
|
|
|
|
notes = 0;
|
|
|
|
for (i = 0; i < nsect; i++)
|
|
|
|
if ((sechdrs[i].sh_flags & SHF_ALLOC) &&
|
|
|
|
(sechdrs[i].sh_type == SHT_NOTE))
|
|
|
|
++notes;
|
|
|
|
|
|
|
|
if (notes == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
notes_attrs = kzalloc(sizeof(*notes_attrs)
|
|
|
|
+ notes * sizeof(notes_attrs->attrs[0]),
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (notes_attrs == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
notes_attrs->notes = notes;
|
|
|
|
nattr = ¬es_attrs->attrs[0];
|
|
|
|
for (loaded = i = 0; i < nsect; ++i) {
|
|
|
|
if (!(sechdrs[i].sh_flags & SHF_ALLOC))
|
|
|
|
continue;
|
|
|
|
if (sechdrs[i].sh_type == SHT_NOTE) {
|
|
|
|
nattr->attr.name = mod->sect_attrs->attrs[loaded].name;
|
|
|
|
nattr->attr.mode = S_IRUGO;
|
|
|
|
nattr->size = sechdrs[i].sh_size;
|
|
|
|
nattr->private = (void *) sechdrs[i].sh_addr;
|
|
|
|
nattr->read = module_notes_read;
|
|
|
|
++nattr;
|
|
|
|
}
|
|
|
|
++loaded;
|
|
|
|
}
|
|
|
|
|
2007-11-06 14:24:43 +08:00
|
|
|
notes_attrs->dir = kobject_create_and_add("notes", &mod->mkobj.kobj);
|
2007-10-17 14:26:40 +08:00
|
|
|
if (!notes_attrs->dir)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
for (i = 0; i < notes; ++i)
|
|
|
|
if (sysfs_create_bin_file(notes_attrs->dir,
|
|
|
|
¬es_attrs->attrs[i]))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
mod->notes_attrs = notes_attrs;
|
|
|
|
return;
|
|
|
|
|
|
|
|
out:
|
|
|
|
free_notes_attrs(notes_attrs, i);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void remove_notes_attrs(struct module *mod)
|
|
|
|
{
|
|
|
|
if (mod->notes_attrs)
|
|
|
|
free_notes_attrs(mod->notes_attrs, mod->notes_attrs->notes);
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
#else
|
2006-09-29 17:01:31 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static inline void add_sect_attrs(struct module *mod, unsigned int nsect,
|
|
|
|
char *sectstrings, Elf_Shdr *sechdrs)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void remove_sect_attrs(struct module *mod)
|
|
|
|
{
|
|
|
|
}
|
2007-10-17 14:26:40 +08:00
|
|
|
|
|
|
|
static inline void add_notes_attrs(struct module *mod, unsigned int nsect,
|
|
|
|
char *sectstrings, Elf_Shdr *sechdrs)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void remove_notes_attrs(struct module *mod)
|
|
|
|
{
|
|
|
|
}
|
2008-02-21 07:33:20 +08:00
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-02-14 07:19:06 +08:00
|
|
|
#ifdef CONFIG_SYSFS
|
|
|
|
int module_add_modinfo_attrs(struct module *mod)
|
[PATCH] modules: add version and srcversion to sysfs
This patch adds version and srcversion files to
/sys/module/${modulename} containing the version and srcversion fields
of the module's modinfo section (if present).
/sys/module/e1000
|-- srcversion
`-- version
This patch differs slightly from the version posted in January, as it
now uses the new kstrdup() call in -mm.
Why put this in sysfs?
a) Tools like DKMS, which deal with changing out individual kernel
modules without replacing the whole kernel, can behave smarter if they
can tell the version of a given module. The autoinstaller feature, for
example, which determines if your system has a "good" version of a
driver (i.e. if the one provided by DKMS has a newer verson than that
provided by the kernel package installed), and to automatically compile
and install a newer version if DKMS has it but your kernel doesn't yet
have that version.
b) Because sysadmins manually, or with tools like DKMS, can switch out
modules on the file system, you can't count on 'modinfo foo.ko', which
looks at /lib/modules/${kernelver}/... actually matching what is loaded
into the kernel already. Hence asking sysfs for this.
c) as the unbind-driver-from-device work takes shape, it will be
possible to rebind a driver that's built-in (no .ko to modinfo for the
version) to a newly loaded module. sysfs will have the
currently-built-in version info, for comparison.
d) tech support scripts can then easily grab the version info for what's
running presently - a question I get often.
There has been renewed interest in this patch on linux-scsi by driver
authors.
As the idea originated from GregKH, I leave his Signed-off-by: intact,
though the implementation is nearly completely new. Compiled and run on
x86 and x86_64.
From: Matthew Dobson <colpatch@us.ibm.com>
build fix
From: Thierry Vignaud <tvignaud@mandriva.com>
build fix
From: Matthew Dobson <colpatch@us.ibm.com>
warning fix
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
Signed-off-by: Matt Domsch <Matt_Domsch@dell.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-06-24 13:05:15 +08:00
|
|
|
{
|
|
|
|
struct module_attribute *attr;
|
2006-02-17 05:50:23 +08:00
|
|
|
struct module_attribute *temp_attr;
|
[PATCH] modules: add version and srcversion to sysfs
This patch adds version and srcversion files to
/sys/module/${modulename} containing the version and srcversion fields
of the module's modinfo section (if present).
/sys/module/e1000
|-- srcversion
`-- version
This patch differs slightly from the version posted in January, as it
now uses the new kstrdup() call in -mm.
Why put this in sysfs?
a) Tools like DKMS, which deal with changing out individual kernel
modules without replacing the whole kernel, can behave smarter if they
can tell the version of a given module. The autoinstaller feature, for
example, which determines if your system has a "good" version of a
driver (i.e. if the one provided by DKMS has a newer verson than that
provided by the kernel package installed), and to automatically compile
and install a newer version if DKMS has it but your kernel doesn't yet
have that version.
b) Because sysadmins manually, or with tools like DKMS, can switch out
modules on the file system, you can't count on 'modinfo foo.ko', which
looks at /lib/modules/${kernelver}/... actually matching what is loaded
into the kernel already. Hence asking sysfs for this.
c) as the unbind-driver-from-device work takes shape, it will be
possible to rebind a driver that's built-in (no .ko to modinfo for the
version) to a newly loaded module. sysfs will have the
currently-built-in version info, for comparison.
d) tech support scripts can then easily grab the version info for what's
running presently - a question I get often.
There has been renewed interest in this patch on linux-scsi by driver
authors.
As the idea originated from GregKH, I leave his Signed-off-by: intact,
though the implementation is nearly completely new. Compiled and run on
x86 and x86_64.
From: Matthew Dobson <colpatch@us.ibm.com>
build fix
From: Thierry Vignaud <tvignaud@mandriva.com>
build fix
From: Matthew Dobson <colpatch@us.ibm.com>
warning fix
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
Signed-off-by: Matt Domsch <Matt_Domsch@dell.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-06-24 13:05:15 +08:00
|
|
|
int error = 0;
|
|
|
|
int i;
|
|
|
|
|
2006-02-17 05:50:23 +08:00
|
|
|
mod->modinfo_attrs = kzalloc((sizeof(struct module_attribute) *
|
|
|
|
(ARRAY_SIZE(modinfo_attrs) + 1)),
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!mod->modinfo_attrs)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
temp_attr = mod->modinfo_attrs;
|
[PATCH] modules: add version and srcversion to sysfs
This patch adds version and srcversion files to
/sys/module/${modulename} containing the version and srcversion fields
of the module's modinfo section (if present).
/sys/module/e1000
|-- srcversion
`-- version
This patch differs slightly from the version posted in January, as it
now uses the new kstrdup() call in -mm.
Why put this in sysfs?
a) Tools like DKMS, which deal with changing out individual kernel
modules without replacing the whole kernel, can behave smarter if they
can tell the version of a given module. The autoinstaller feature, for
example, which determines if your system has a "good" version of a
driver (i.e. if the one provided by DKMS has a newer verson than that
provided by the kernel package installed), and to automatically compile
and install a newer version if DKMS has it but your kernel doesn't yet
have that version.
b) Because sysadmins manually, or with tools like DKMS, can switch out
modules on the file system, you can't count on 'modinfo foo.ko', which
looks at /lib/modules/${kernelver}/... actually matching what is loaded
into the kernel already. Hence asking sysfs for this.
c) as the unbind-driver-from-device work takes shape, it will be
possible to rebind a driver that's built-in (no .ko to modinfo for the
version) to a newly loaded module. sysfs will have the
currently-built-in version info, for comparison.
d) tech support scripts can then easily grab the version info for what's
running presently - a question I get often.
There has been renewed interest in this patch on linux-scsi by driver
authors.
As the idea originated from GregKH, I leave his Signed-off-by: intact,
though the implementation is nearly completely new. Compiled and run on
x86 and x86_64.
From: Matthew Dobson <colpatch@us.ibm.com>
build fix
From: Thierry Vignaud <tvignaud@mandriva.com>
build fix
From: Matthew Dobson <colpatch@us.ibm.com>
warning fix
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
Signed-off-by: Matt Domsch <Matt_Domsch@dell.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-06-24 13:05:15 +08:00
|
|
|
for (i = 0; (attr = modinfo_attrs[i]) && !error; i++) {
|
|
|
|
if (!attr->test ||
|
2006-02-17 05:50:23 +08:00
|
|
|
(attr->test && attr->test(mod))) {
|
|
|
|
memcpy(temp_attr, attr, sizeof(*temp_attr));
|
|
|
|
error = sysfs_create_file(&mod->mkobj.kobj,&temp_attr->attr);
|
|
|
|
++temp_attr;
|
|
|
|
}
|
[PATCH] modules: add version and srcversion to sysfs
This patch adds version and srcversion files to
/sys/module/${modulename} containing the version and srcversion fields
of the module's modinfo section (if present).
/sys/module/e1000
|-- srcversion
`-- version
This patch differs slightly from the version posted in January, as it
now uses the new kstrdup() call in -mm.
Why put this in sysfs?
a) Tools like DKMS, which deal with changing out individual kernel
modules without replacing the whole kernel, can behave smarter if they
can tell the version of a given module. The autoinstaller feature, for
example, which determines if your system has a "good" version of a
driver (i.e. if the one provided by DKMS has a newer verson than that
provided by the kernel package installed), and to automatically compile
and install a newer version if DKMS has it but your kernel doesn't yet
have that version.
b) Because sysadmins manually, or with tools like DKMS, can switch out
modules on the file system, you can't count on 'modinfo foo.ko', which
looks at /lib/modules/${kernelver}/... actually matching what is loaded
into the kernel already. Hence asking sysfs for this.
c) as the unbind-driver-from-device work takes shape, it will be
possible to rebind a driver that's built-in (no .ko to modinfo for the
version) to a newly loaded module. sysfs will have the
currently-built-in version info, for comparison.
d) tech support scripts can then easily grab the version info for what's
running presently - a question I get often.
There has been renewed interest in this patch on linux-scsi by driver
authors.
As the idea originated from GregKH, I leave his Signed-off-by: intact,
though the implementation is nearly completely new. Compiled and run on
x86 and x86_64.
From: Matthew Dobson <colpatch@us.ibm.com>
build fix
From: Thierry Vignaud <tvignaud@mandriva.com>
build fix
From: Matthew Dobson <colpatch@us.ibm.com>
warning fix
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
Signed-off-by: Matt Domsch <Matt_Domsch@dell.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-06-24 13:05:15 +08:00
|
|
|
}
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2007-02-14 07:19:06 +08:00
|
|
|
void module_remove_modinfo_attrs(struct module *mod)
|
[PATCH] modules: add version and srcversion to sysfs
This patch adds version and srcversion files to
/sys/module/${modulename} containing the version and srcversion fields
of the module's modinfo section (if present).
/sys/module/e1000
|-- srcversion
`-- version
This patch differs slightly from the version posted in January, as it
now uses the new kstrdup() call in -mm.
Why put this in sysfs?
a) Tools like DKMS, which deal with changing out individual kernel
modules without replacing the whole kernel, can behave smarter if they
can tell the version of a given module. The autoinstaller feature, for
example, which determines if your system has a "good" version of a
driver (i.e. if the one provided by DKMS has a newer verson than that
provided by the kernel package installed), and to automatically compile
and install a newer version if DKMS has it but your kernel doesn't yet
have that version.
b) Because sysadmins manually, or with tools like DKMS, can switch out
modules on the file system, you can't count on 'modinfo foo.ko', which
looks at /lib/modules/${kernelver}/... actually matching what is loaded
into the kernel already. Hence asking sysfs for this.
c) as the unbind-driver-from-device work takes shape, it will be
possible to rebind a driver that's built-in (no .ko to modinfo for the
version) to a newly loaded module. sysfs will have the
currently-built-in version info, for comparison.
d) tech support scripts can then easily grab the version info for what's
running presently - a question I get often.
There has been renewed interest in this patch on linux-scsi by driver
authors.
As the idea originated from GregKH, I leave his Signed-off-by: intact,
though the implementation is nearly completely new. Compiled and run on
x86 and x86_64.
From: Matthew Dobson <colpatch@us.ibm.com>
build fix
From: Thierry Vignaud <tvignaud@mandriva.com>
build fix
From: Matthew Dobson <colpatch@us.ibm.com>
warning fix
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
Signed-off-by: Matt Domsch <Matt_Domsch@dell.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-06-24 13:05:15 +08:00
|
|
|
{
|
|
|
|
struct module_attribute *attr;
|
|
|
|
int i;
|
|
|
|
|
2006-02-17 05:50:23 +08:00
|
|
|
for (i = 0; (attr = &mod->modinfo_attrs[i]); i++) {
|
|
|
|
/* pick a field to test for end of list */
|
|
|
|
if (!attr->attr.name)
|
|
|
|
break;
|
[PATCH] modules: add version and srcversion to sysfs
This patch adds version and srcversion files to
/sys/module/${modulename} containing the version and srcversion fields
of the module's modinfo section (if present).
/sys/module/e1000
|-- srcversion
`-- version
This patch differs slightly from the version posted in January, as it
now uses the new kstrdup() call in -mm.
Why put this in sysfs?
a) Tools like DKMS, which deal with changing out individual kernel
modules without replacing the whole kernel, can behave smarter if they
can tell the version of a given module. The autoinstaller feature, for
example, which determines if your system has a "good" version of a
driver (i.e. if the one provided by DKMS has a newer verson than that
provided by the kernel package installed), and to automatically compile
and install a newer version if DKMS has it but your kernel doesn't yet
have that version.
b) Because sysadmins manually, or with tools like DKMS, can switch out
modules on the file system, you can't count on 'modinfo foo.ko', which
looks at /lib/modules/${kernelver}/... actually matching what is loaded
into the kernel already. Hence asking sysfs for this.
c) as the unbind-driver-from-device work takes shape, it will be
possible to rebind a driver that's built-in (no .ko to modinfo for the
version) to a newly loaded module. sysfs will have the
currently-built-in version info, for comparison.
d) tech support scripts can then easily grab the version info for what's
running presently - a question I get often.
There has been renewed interest in this patch on linux-scsi by driver
authors.
As the idea originated from GregKH, I leave his Signed-off-by: intact,
though the implementation is nearly completely new. Compiled and run on
x86 and x86_64.
From: Matthew Dobson <colpatch@us.ibm.com>
build fix
From: Thierry Vignaud <tvignaud@mandriva.com>
build fix
From: Matthew Dobson <colpatch@us.ibm.com>
warning fix
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
Signed-off-by: Matt Domsch <Matt_Domsch@dell.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-06-24 13:05:15 +08:00
|
|
|
sysfs_remove_file(&mod->mkobj.kobj,&attr->attr);
|
2006-02-17 05:50:23 +08:00
|
|
|
if (attr->free)
|
|
|
|
attr->free(mod);
|
[PATCH] modules: add version and srcversion to sysfs
This patch adds version and srcversion files to
/sys/module/${modulename} containing the version and srcversion fields
of the module's modinfo section (if present).
/sys/module/e1000
|-- srcversion
`-- version
This patch differs slightly from the version posted in January, as it
now uses the new kstrdup() call in -mm.
Why put this in sysfs?
a) Tools like DKMS, which deal with changing out individual kernel
modules without replacing the whole kernel, can behave smarter if they
can tell the version of a given module. The autoinstaller feature, for
example, which determines if your system has a "good" version of a
driver (i.e. if the one provided by DKMS has a newer verson than that
provided by the kernel package installed), and to automatically compile
and install a newer version if DKMS has it but your kernel doesn't yet
have that version.
b) Because sysadmins manually, or with tools like DKMS, can switch out
modules on the file system, you can't count on 'modinfo foo.ko', which
looks at /lib/modules/${kernelver}/... actually matching what is loaded
into the kernel already. Hence asking sysfs for this.
c) as the unbind-driver-from-device work takes shape, it will be
possible to rebind a driver that's built-in (no .ko to modinfo for the
version) to a newly loaded module. sysfs will have the
currently-built-in version info, for comparison.
d) tech support scripts can then easily grab the version info for what's
running presently - a question I get often.
There has been renewed interest in this patch on linux-scsi by driver
authors.
As the idea originated from GregKH, I leave his Signed-off-by: intact,
though the implementation is nearly completely new. Compiled and run on
x86 and x86_64.
From: Matthew Dobson <colpatch@us.ibm.com>
build fix
From: Thierry Vignaud <tvignaud@mandriva.com>
build fix
From: Matthew Dobson <colpatch@us.ibm.com>
warning fix
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
Signed-off-by: Matt Domsch <Matt_Domsch@dell.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-06-24 13:05:15 +08:00
|
|
|
}
|
2006-02-17 05:50:23 +08:00
|
|
|
kfree(mod->modinfo_attrs);
|
[PATCH] modules: add version and srcversion to sysfs
This patch adds version and srcversion files to
/sys/module/${modulename} containing the version and srcversion fields
of the module's modinfo section (if present).
/sys/module/e1000
|-- srcversion
`-- version
This patch differs slightly from the version posted in January, as it
now uses the new kstrdup() call in -mm.
Why put this in sysfs?
a) Tools like DKMS, which deal with changing out individual kernel
modules without replacing the whole kernel, can behave smarter if they
can tell the version of a given module. The autoinstaller feature, for
example, which determines if your system has a "good" version of a
driver (i.e. if the one provided by DKMS has a newer verson than that
provided by the kernel package installed), and to automatically compile
and install a newer version if DKMS has it but your kernel doesn't yet
have that version.
b) Because sysadmins manually, or with tools like DKMS, can switch out
modules on the file system, you can't count on 'modinfo foo.ko', which
looks at /lib/modules/${kernelver}/... actually matching what is loaded
into the kernel already. Hence asking sysfs for this.
c) as the unbind-driver-from-device work takes shape, it will be
possible to rebind a driver that's built-in (no .ko to modinfo for the
version) to a newly loaded module. sysfs will have the
currently-built-in version info, for comparison.
d) tech support scripts can then easily grab the version info for what's
running presently - a question I get often.
There has been renewed interest in this patch on linux-scsi by driver
authors.
As the idea originated from GregKH, I leave his Signed-off-by: intact,
though the implementation is nearly completely new. Compiled and run on
x86 and x86_64.
From: Matthew Dobson <colpatch@us.ibm.com>
build fix
From: Thierry Vignaud <tvignaud@mandriva.com>
build fix
From: Matthew Dobson <colpatch@us.ibm.com>
warning fix
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
Signed-off-by: Matt Domsch <Matt_Domsch@dell.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-06-24 13:05:15 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-02-14 07:19:06 +08:00
|
|
|
int mod_sysfs_init(struct module *mod)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int err;
|
2008-01-28 07:38:40 +08:00
|
|
|
struct kobject *kobj;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-04-14 04:15:19 +08:00
|
|
|
if (!module_sysfs_initialized) {
|
|
|
|
printk(KERN_ERR "%s: module sysfs not initialized\n",
|
2006-09-26 07:25:36 +08:00
|
|
|
mod->name);
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
2008-01-28 07:38:40 +08:00
|
|
|
|
|
|
|
kobj = kset_find_obj(module_kset, mod->name);
|
|
|
|
if (kobj) {
|
|
|
|
printk(KERN_ERR "%s: module is already loaded\n", mod->name);
|
|
|
|
kobject_put(kobj);
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
mod->mkobj.mod = mod;
|
2006-11-24 19:15:25 +08:00
|
|
|
|
2007-12-18 14:05:35 +08:00
|
|
|
memset(&mod->mkobj.kobj, 0, sizeof(mod->mkobj.kobj));
|
|
|
|
mod->mkobj.kobj.kset = module_kset;
|
|
|
|
err = kobject_init_and_add(&mod->mkobj.kobj, &module_ktype, NULL,
|
|
|
|
"%s", mod->name);
|
|
|
|
if (err)
|
|
|
|
kobject_put(&mod->mkobj.kobj);
|
2007-01-18 20:26:15 +08:00
|
|
|
|
2007-11-30 06:46:11 +08:00
|
|
|
/* delay uevent until full sysfs population */
|
2007-01-18 20:26:15 +08:00
|
|
|
out:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2007-02-14 07:19:06 +08:00
|
|
|
int mod_sysfs_setup(struct module *mod,
|
2007-01-18 20:26:15 +08:00
|
|
|
struct kernel_param *kparam,
|
|
|
|
unsigned int num_params)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
2007-11-06 14:24:43 +08:00
|
|
|
mod->holders_dir = kobject_create_and_add("holders", &mod->mkobj.kobj);
|
2007-04-26 15:12:09 +08:00
|
|
|
if (!mod->holders_dir) {
|
|
|
|
err = -ENOMEM;
|
2007-01-18 20:26:15 +08:00
|
|
|
goto out_unreg;
|
2007-04-26 15:12:09 +08:00
|
|
|
}
|
2007-01-18 20:26:15 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
err = module_param_sysfs_setup(mod, kparam, num_params);
|
|
|
|
if (err)
|
2007-01-18 20:26:15 +08:00
|
|
|
goto out_unreg_holders;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
[PATCH] modules: add version and srcversion to sysfs
This patch adds version and srcversion files to
/sys/module/${modulename} containing the version and srcversion fields
of the module's modinfo section (if present).
/sys/module/e1000
|-- srcversion
`-- version
This patch differs slightly from the version posted in January, as it
now uses the new kstrdup() call in -mm.
Why put this in sysfs?
a) Tools like DKMS, which deal with changing out individual kernel
modules without replacing the whole kernel, can behave smarter if they
can tell the version of a given module. The autoinstaller feature, for
example, which determines if your system has a "good" version of a
driver (i.e. if the one provided by DKMS has a newer verson than that
provided by the kernel package installed), and to automatically compile
and install a newer version if DKMS has it but your kernel doesn't yet
have that version.
b) Because sysadmins manually, or with tools like DKMS, can switch out
modules on the file system, you can't count on 'modinfo foo.ko', which
looks at /lib/modules/${kernelver}/... actually matching what is loaded
into the kernel already. Hence asking sysfs for this.
c) as the unbind-driver-from-device work takes shape, it will be
possible to rebind a driver that's built-in (no .ko to modinfo for the
version) to a newly loaded module. sysfs will have the
currently-built-in version info, for comparison.
d) tech support scripts can then easily grab the version info for what's
running presently - a question I get often.
There has been renewed interest in this patch on linux-scsi by driver
authors.
As the idea originated from GregKH, I leave his Signed-off-by: intact,
though the implementation is nearly completely new. Compiled and run on
x86 and x86_64.
From: Matthew Dobson <colpatch@us.ibm.com>
build fix
From: Thierry Vignaud <tvignaud@mandriva.com>
build fix
From: Matthew Dobson <colpatch@us.ibm.com>
warning fix
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
Signed-off-by: Matt Domsch <Matt_Domsch@dell.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-06-24 13:05:15 +08:00
|
|
|
err = module_add_modinfo_attrs(mod);
|
|
|
|
if (err)
|
2006-11-24 19:15:25 +08:00
|
|
|
goto out_unreg_param;
|
[PATCH] modules: add version and srcversion to sysfs
This patch adds version and srcversion files to
/sys/module/${modulename} containing the version and srcversion fields
of the module's modinfo section (if present).
/sys/module/e1000
|-- srcversion
`-- version
This patch differs slightly from the version posted in January, as it
now uses the new kstrdup() call in -mm.
Why put this in sysfs?
a) Tools like DKMS, which deal with changing out individual kernel
modules without replacing the whole kernel, can behave smarter if they
can tell the version of a given module. The autoinstaller feature, for
example, which determines if your system has a "good" version of a
driver (i.e. if the one provided by DKMS has a newer verson than that
provided by the kernel package installed), and to automatically compile
and install a newer version if DKMS has it but your kernel doesn't yet
have that version.
b) Because sysadmins manually, or with tools like DKMS, can switch out
modules on the file system, you can't count on 'modinfo foo.ko', which
looks at /lib/modules/${kernelver}/... actually matching what is loaded
into the kernel already. Hence asking sysfs for this.
c) as the unbind-driver-from-device work takes shape, it will be
possible to rebind a driver that's built-in (no .ko to modinfo for the
version) to a newly loaded module. sysfs will have the
currently-built-in version info, for comparison.
d) tech support scripts can then easily grab the version info for what's
running presently - a question I get often.
There has been renewed interest in this patch on linux-scsi by driver
authors.
As the idea originated from GregKH, I leave his Signed-off-by: intact,
though the implementation is nearly completely new. Compiled and run on
x86 and x86_64.
From: Matthew Dobson <colpatch@us.ibm.com>
build fix
From: Thierry Vignaud <tvignaud@mandriva.com>
build fix
From: Matthew Dobson <colpatch@us.ibm.com>
warning fix
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
Signed-off-by: Matt Domsch <Matt_Domsch@dell.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-06-24 13:05:15 +08:00
|
|
|
|
2006-11-24 19:15:25 +08:00
|
|
|
kobject_uevent(&mod->mkobj.kobj, KOBJ_ADD);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
|
2006-11-24 19:15:25 +08:00
|
|
|
out_unreg_param:
|
|
|
|
module_param_sysfs_remove(mod);
|
2007-01-18 20:26:15 +08:00
|
|
|
out_unreg_holders:
|
2007-12-21 00:13:05 +08:00
|
|
|
kobject_put(mod->holders_dir);
|
2007-01-18 20:26:15 +08:00
|
|
|
out_unreg:
|
2006-11-24 19:15:25 +08:00
|
|
|
kobject_put(&mod->mkobj.kobj);
|
2005-04-17 06:20:36 +08:00
|
|
|
return err;
|
|
|
|
}
|
2008-05-20 17:59:48 +08:00
|
|
|
|
|
|
|
static void mod_sysfs_fini(struct module *mod)
|
|
|
|
{
|
|
|
|
kobject_put(&mod->mkobj.kobj);
|
|
|
|
}
|
|
|
|
|
|
|
|
#else /* CONFIG_SYSFS */
|
|
|
|
|
|
|
|
static void mod_sysfs_fini(struct module *mod)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* CONFIG_SYSFS */
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
static void mod_kobject_remove(struct module *mod)
|
|
|
|
{
|
[PATCH] modules: add version and srcversion to sysfs
This patch adds version and srcversion files to
/sys/module/${modulename} containing the version and srcversion fields
of the module's modinfo section (if present).
/sys/module/e1000
|-- srcversion
`-- version
This patch differs slightly from the version posted in January, as it
now uses the new kstrdup() call in -mm.
Why put this in sysfs?
a) Tools like DKMS, which deal with changing out individual kernel
modules without replacing the whole kernel, can behave smarter if they
can tell the version of a given module. The autoinstaller feature, for
example, which determines if your system has a "good" version of a
driver (i.e. if the one provided by DKMS has a newer verson than that
provided by the kernel package installed), and to automatically compile
and install a newer version if DKMS has it but your kernel doesn't yet
have that version.
b) Because sysadmins manually, or with tools like DKMS, can switch out
modules on the file system, you can't count on 'modinfo foo.ko', which
looks at /lib/modules/${kernelver}/... actually matching what is loaded
into the kernel already. Hence asking sysfs for this.
c) as the unbind-driver-from-device work takes shape, it will be
possible to rebind a driver that's built-in (no .ko to modinfo for the
version) to a newly loaded module. sysfs will have the
currently-built-in version info, for comparison.
d) tech support scripts can then easily grab the version info for what's
running presently - a question I get often.
There has been renewed interest in this patch on linux-scsi by driver
authors.
As the idea originated from GregKH, I leave his Signed-off-by: intact,
though the implementation is nearly completely new. Compiled and run on
x86 and x86_64.
From: Matthew Dobson <colpatch@us.ibm.com>
build fix
From: Thierry Vignaud <tvignaud@mandriva.com>
build fix
From: Matthew Dobson <colpatch@us.ibm.com>
warning fix
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
Signed-off-by: Matt Domsch <Matt_Domsch@dell.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-06-24 13:05:15 +08:00
|
|
|
module_remove_modinfo_attrs(mod);
|
2005-04-17 06:20:36 +08:00
|
|
|
module_param_sysfs_remove(mod);
|
2007-12-21 00:13:05 +08:00
|
|
|
kobject_put(mod->mkobj.drivers_dir);
|
|
|
|
kobject_put(mod->holders_dir);
|
2008-05-20 17:59:48 +08:00
|
|
|
mod_sysfs_fini(mod);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2008-01-30 06:13:21 +08:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* unlink the module with the whole machine is stopped with interrupts off
|
|
|
|
* - this defends against kallsyms not taking locks
|
|
|
|
*/
|
|
|
|
static int __unlink_module(void *_mod)
|
|
|
|
{
|
|
|
|
struct module *mod = _mod;
|
|
|
|
list_del(&mod->list);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-05-09 13:26:28 +08:00
|
|
|
/* Free a module, remove from lists, etc (must hold module_mutex). */
|
2005-04-17 06:20:36 +08:00
|
|
|
static void free_module(struct module *mod)
|
|
|
|
{
|
|
|
|
/* Delete from various lists */
|
2008-07-29 01:16:30 +08:00
|
|
|
stop_machine(__unlink_module, mod, NULL);
|
2007-10-17 14:26:40 +08:00
|
|
|
remove_notes_attrs(mod);
|
2005-04-17 06:20:36 +08:00
|
|
|
remove_sect_attrs(mod);
|
|
|
|
mod_kobject_remove(mod);
|
|
|
|
|
2006-06-26 19:57:28 +08:00
|
|
|
unwind_remove_table(mod->unwind_info, 0);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Arch-specific cleanup. */
|
|
|
|
module_arch_cleanup(mod);
|
|
|
|
|
|
|
|
/* Module unload stuff */
|
|
|
|
module_unload_free(mod);
|
|
|
|
|
|
|
|
/* This may be NULL, but that's OK */
|
|
|
|
module_free(mod, mod->module_init);
|
|
|
|
kfree(mod->args);
|
|
|
|
if (mod->percpu)
|
|
|
|
percpu_modfree(mod->percpu);
|
|
|
|
|
[PATCH] lockdep: core
Do 'make oldconfig' and accept all the defaults for new config options -
reboot into the kernel and if everything goes well it should boot up fine and
you should have /proc/lockdep and /proc/lockdep_stats files.
Typically if the lock validator finds some problem it will print out
voluminous debug output that begins with "BUG: ..." and which syslog output
can be used by kernel developers to figure out the precise locking scenario.
What does the lock validator do? It "observes" and maps all locking rules as
they occur dynamically (as triggered by the kernel's natural use of spinlocks,
rwlocks, mutexes and rwsems). Whenever the lock validator subsystem detects a
new locking scenario, it validates this new rule against the existing set of
rules. If this new rule is consistent with the existing set of rules then the
new rule is added transparently and the kernel continues as normal. If the
new rule could create a deadlock scenario then this condition is printed out.
When determining validity of locking, all possible "deadlock scenarios" are
considered: assuming arbitrary number of CPUs, arbitrary irq context and task
context constellations, running arbitrary combinations of all the existing
locking scenarios. In a typical system this means millions of separate
scenarios. This is why we call it a "locking correctness" validator - for all
rules that are observed the lock validator proves it with mathematical
certainty that a deadlock could not occur (assuming that the lock validator
implementation itself is correct and its internal data structures are not
corrupted by some other kernel subsystem). [see more details and conditionals
of this statement in include/linux/lockdep.h and
Documentation/lockdep-design.txt]
Furthermore, this "all possible scenarios" property of the validator also
enables the finding of complex, highly unlikely multi-CPU multi-context races
via single single-context rules, increasing the likelyhood of finding bugs
drastically. In practical terms: the lock validator already found a bug in
the upstream kernel that could only occur on systems with 3 or more CPUs, and
which needed 3 very unlikely code sequences to occur at once on the 3 CPUs.
That bug was found and reported on a single-CPU system (!). So in essence a
race will be found "piecemail-wise", triggering all the necessary components
for the race, without having to reproduce the race scenario itself! In its
short existence the lock validator found and reported many bugs before they
actually caused a real deadlock.
To further increase the efficiency of the validator, the mapping is not per
"lock instance", but per "lock-class". For example, all struct inode objects
in the kernel have inode->inotify_mutex. If there are 10,000 inodes cached,
then there are 10,000 lock objects. But ->inotify_mutex is a single "lock
type", and all locking activities that occur against ->inotify_mutex are
"unified" into this single lock-class. The advantage of the lock-class
approach is that all historical ->inotify_mutex uses are mapped into a single
(and as narrow as possible) set of locking rules - regardless of how many
different tasks or inode structures it took to build this set of rules. The
set of rules persist during the lifetime of the kernel.
To see the rough magnitude of checking that the lock validator does, here's a
portion of /proc/lockdep_stats, fresh after bootup:
lock-classes: 694 [max: 2048]
direct dependencies: 1598 [max: 8192]
indirect dependencies: 17896
all direct dependencies: 16206
dependency chains: 1910 [max: 8192]
in-hardirq chains: 17
in-softirq chains: 105
in-process chains: 1065
stack-trace entries: 38761 [max: 131072]
combined max dependencies: 2033928
hardirq-safe locks: 24
hardirq-unsafe locks: 176
softirq-safe locks: 53
softirq-unsafe locks: 137
irq-safe locks: 59
irq-unsafe locks: 176
The lock validator has observed 1598 actual single-thread locking patterns,
and has validated all possible 2033928 distinct locking scenarios.
More details about the design of the lock validator can be found in
Documentation/lockdep-design.txt, which can also found at:
http://redhat.com/~mingo/lockdep-patches/lockdep-design.txt
[bunk@stusta.de: cleanups]
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Signed-off-by: Adrian Bunk <bunk@stusta.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-07-03 15:24:50 +08:00
|
|
|
/* Free lock-classes: */
|
|
|
|
lockdep_free_key_range(mod->module_core, mod->core_size);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Finally, free the core (containing the module structure) */
|
|
|
|
module_free(mod, mod->module_core);
|
|
|
|
}
|
|
|
|
|
|
|
|
void *__symbol_get(const char *symbol)
|
|
|
|
{
|
|
|
|
struct module *owner;
|
2007-07-16 14:41:46 +08:00
|
|
|
unsigned long value;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-07-16 14:41:46 +08:00
|
|
|
preempt_disable();
|
2008-05-02 10:14:59 +08:00
|
|
|
value = find_symbol(symbol, &owner, NULL, true, true);
|
2008-02-08 20:18:41 +08:00
|
|
|
if (IS_ERR_VALUE(value))
|
|
|
|
value = 0;
|
|
|
|
else if (strong_try_module_get(owner))
|
2005-04-17 06:20:36 +08:00
|
|
|
value = 0;
|
2007-07-16 14:41:46 +08:00
|
|
|
preempt_enable();
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
return (void *)value;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(__symbol_get);
|
|
|
|
|
2006-01-08 17:04:25 +08:00
|
|
|
/*
|
|
|
|
* Ensure that an exported symbol [global namespace] does not already exist
|
2007-05-09 13:26:28 +08:00
|
|
|
* in the kernel or in some other module's exported symbol table.
|
2006-01-08 17:04:25 +08:00
|
|
|
*/
|
|
|
|
static int verify_export_symbols(struct module *mod)
|
|
|
|
{
|
2008-05-02 10:15:00 +08:00
|
|
|
unsigned int i;
|
2006-01-08 17:04:25 +08:00
|
|
|
struct module *owner;
|
2008-05-02 10:15:00 +08:00
|
|
|
const struct kernel_symbol *s;
|
|
|
|
struct {
|
|
|
|
const struct kernel_symbol *sym;
|
|
|
|
unsigned int num;
|
|
|
|
} arr[] = {
|
|
|
|
{ mod->syms, mod->num_syms },
|
|
|
|
{ mod->gpl_syms, mod->num_gpl_syms },
|
|
|
|
{ mod->gpl_future_syms, mod->num_gpl_future_syms },
|
2008-07-23 08:24:26 +08:00
|
|
|
#ifdef CONFIG_UNUSED_SYMBOLS
|
2008-05-02 10:15:00 +08:00
|
|
|
{ mod->unused_syms, mod->num_unused_syms },
|
|
|
|
{ mod->unused_gpl_syms, mod->num_unused_gpl_syms },
|
2008-07-23 08:24:26 +08:00
|
|
|
#endif
|
2008-05-02 10:15:00 +08:00
|
|
|
};
|
2006-01-08 17:04:25 +08:00
|
|
|
|
2008-05-02 10:15:00 +08:00
|
|
|
for (i = 0; i < ARRAY_SIZE(arr); i++) {
|
|
|
|
for (s = arr[i].sym; s < arr[i].sym + arr[i].num; s++) {
|
|
|
|
if (!IS_ERR_VALUE(find_symbol(s->name, &owner,
|
|
|
|
NULL, true, false))) {
|
|
|
|
printk(KERN_ERR
|
|
|
|
"%s: exports duplicate symbol %s"
|
|
|
|
" (owned by %s)\n",
|
|
|
|
mod->name, s->name, module_name(owner));
|
|
|
|
return -ENOEXEC;
|
|
|
|
}
|
2006-01-08 17:04:25 +08:00
|
|
|
}
|
2008-05-02 10:15:00 +08:00
|
|
|
}
|
|
|
|
return 0;
|
2006-01-08 17:04:25 +08:00
|
|
|
}
|
|
|
|
|
2007-11-09 00:37:38 +08:00
|
|
|
/* Change all symbols so that st_value encodes the pointer directly. */
|
2005-04-17 06:20:36 +08:00
|
|
|
static int simplify_symbols(Elf_Shdr *sechdrs,
|
|
|
|
unsigned int symindex,
|
|
|
|
const char *strtab,
|
|
|
|
unsigned int versindex,
|
|
|
|
unsigned int pcpuindex,
|
|
|
|
struct module *mod)
|
|
|
|
{
|
|
|
|
Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;
|
|
|
|
unsigned long secbase;
|
|
|
|
unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
for (i = 1; i < n; i++) {
|
|
|
|
switch (sym[i].st_shndx) {
|
|
|
|
case SHN_COMMON:
|
|
|
|
/* We compiled with -fno-common. These are not
|
|
|
|
supposed to happen. */
|
|
|
|
DEBUGP("Common symbol: %s\n", strtab + sym[i].st_name);
|
|
|
|
printk("%s: please compile with -fno-common\n",
|
|
|
|
mod->name);
|
|
|
|
ret = -ENOEXEC;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SHN_ABS:
|
|
|
|
/* Don't need to do anything */
|
|
|
|
DEBUGP("Absolute symbol: 0x%08lx\n",
|
|
|
|
(long)sym[i].st_value);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SHN_UNDEF:
|
|
|
|
sym[i].st_value
|
|
|
|
= resolve_symbol(sechdrs, versindex,
|
|
|
|
strtab + sym[i].st_name, mod);
|
|
|
|
|
|
|
|
/* Ok if resolved. */
|
2008-02-08 20:18:41 +08:00
|
|
|
if (!IS_ERR_VALUE(sym[i].st_value))
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
/* Ok if weak. */
|
|
|
|
if (ELF_ST_BIND(sym[i].st_info) == STB_WEAK)
|
|
|
|
break;
|
|
|
|
|
|
|
|
printk(KERN_WARNING "%s: Unknown symbol %s\n",
|
|
|
|
mod->name, strtab + sym[i].st_name);
|
|
|
|
ret = -ENOENT;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
/* Divert to percpu allocation if a percpu var. */
|
|
|
|
if (sym[i].st_shndx == pcpuindex)
|
|
|
|
secbase = (unsigned long)mod->percpu;
|
|
|
|
else
|
|
|
|
secbase = sechdrs[sym[i].st_shndx].sh_addr;
|
|
|
|
sym[i].st_value += secbase;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update size with this section: return offset. */
|
2008-07-23 08:24:27 +08:00
|
|
|
static long get_offset(unsigned int *size, Elf_Shdr *sechdr)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
long ret;
|
|
|
|
|
|
|
|
ret = ALIGN(*size, sechdr->sh_addralign ?: 1);
|
|
|
|
*size = ret + sechdr->sh_size;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Lay out the SHF_ALLOC sections in a way not dissimilar to how ld
|
|
|
|
might -- code, read-only data, read-write data, small data. Tally
|
|
|
|
sizes, and place the offsets into sh_entsize fields: high bit means it
|
|
|
|
belongs in init. */
|
|
|
|
static void layout_sections(struct module *mod,
|
|
|
|
const Elf_Ehdr *hdr,
|
|
|
|
Elf_Shdr *sechdrs,
|
|
|
|
const char *secstrings)
|
|
|
|
{
|
|
|
|
static unsigned long const masks[][2] = {
|
|
|
|
/* NOTE: all executable code must be the first section
|
|
|
|
* in this array; otherwise modify the text_size
|
|
|
|
* finder in the two loops below */
|
|
|
|
{ SHF_EXECINSTR | SHF_ALLOC, ARCH_SHF_SMALL },
|
|
|
|
{ SHF_ALLOC, SHF_WRITE | ARCH_SHF_SMALL },
|
|
|
|
{ SHF_WRITE | SHF_ALLOC, ARCH_SHF_SMALL },
|
|
|
|
{ ARCH_SHF_SMALL | SHF_ALLOC, 0 }
|
|
|
|
};
|
|
|
|
unsigned int m, i;
|
|
|
|
|
|
|
|
for (i = 0; i < hdr->e_shnum; i++)
|
|
|
|
sechdrs[i].sh_entsize = ~0UL;
|
|
|
|
|
|
|
|
DEBUGP("Core section allocation order:\n");
|
|
|
|
for (m = 0; m < ARRAY_SIZE(masks); ++m) {
|
|
|
|
for (i = 0; i < hdr->e_shnum; ++i) {
|
|
|
|
Elf_Shdr *s = &sechdrs[i];
|
|
|
|
|
|
|
|
if ((s->sh_flags & masks[m][0]) != masks[m][0]
|
|
|
|
|| (s->sh_flags & masks[m][1])
|
|
|
|
|| s->sh_entsize != ~0UL
|
|
|
|
|| strncmp(secstrings + s->sh_name,
|
|
|
|
".init", 5) == 0)
|
|
|
|
continue;
|
|
|
|
s->sh_entsize = get_offset(&mod->core_size, s);
|
|
|
|
DEBUGP("\t%s\n", secstrings + s->sh_name);
|
|
|
|
}
|
|
|
|
if (m == 0)
|
|
|
|
mod->core_text_size = mod->core_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
DEBUGP("Init section allocation order:\n");
|
|
|
|
for (m = 0; m < ARRAY_SIZE(masks); ++m) {
|
|
|
|
for (i = 0; i < hdr->e_shnum; ++i) {
|
|
|
|
Elf_Shdr *s = &sechdrs[i];
|
|
|
|
|
|
|
|
if ((s->sh_flags & masks[m][0]) != masks[m][0]
|
|
|
|
|| (s->sh_flags & masks[m][1])
|
|
|
|
|| s->sh_entsize != ~0UL
|
|
|
|
|| strncmp(secstrings + s->sh_name,
|
|
|
|
".init", 5) != 0)
|
|
|
|
continue;
|
|
|
|
s->sh_entsize = (get_offset(&mod->init_size, s)
|
|
|
|
| INIT_OFFSET_MASK);
|
|
|
|
DEBUGP("\t%s\n", secstrings + s->sh_name);
|
|
|
|
}
|
|
|
|
if (m == 0)
|
|
|
|
mod->init_text_size = mod->init_size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void set_license(struct module *mod, const char *license)
|
|
|
|
{
|
|
|
|
if (!license)
|
|
|
|
license = "unspecified";
|
|
|
|
|
2006-10-11 16:21:48 +08:00
|
|
|
if (!license_is_gpl_compatible(license)) {
|
2008-10-16 13:01:41 +08:00
|
|
|
if (!test_taint(TAINT_PROPRIETARY_MODULE))
|
2006-10-29 01:38:38 +08:00
|
|
|
printk(KERN_WARNING "%s: module license '%s' taints "
|
2006-10-11 16:21:48 +08:00
|
|
|
"kernel.\n", mod->name, license);
|
|
|
|
add_taint_module(mod, TAINT_PROPRIETARY_MODULE);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Parse tag=value strings from .modinfo section */
|
|
|
|
static char *next_string(char *string, unsigned long *secsize)
|
|
|
|
{
|
|
|
|
/* Skip non-zero chars */
|
|
|
|
while (string[0]) {
|
|
|
|
string++;
|
|
|
|
if ((*secsize)-- <= 1)
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Skip any zero padding. */
|
|
|
|
while (!string[0]) {
|
|
|
|
string++;
|
|
|
|
if ((*secsize)-- <= 1)
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return string;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *get_modinfo(Elf_Shdr *sechdrs,
|
|
|
|
unsigned int info,
|
|
|
|
const char *tag)
|
|
|
|
{
|
|
|
|
char *p;
|
|
|
|
unsigned int taglen = strlen(tag);
|
|
|
|
unsigned long size = sechdrs[info].sh_size;
|
|
|
|
|
|
|
|
for (p = (char *)sechdrs[info].sh_addr; p; p = next_string(p, &size)) {
|
|
|
|
if (strncmp(p, tag, taglen) == 0 && p[taglen] == '=')
|
|
|
|
return p + taglen + 1;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
[PATCH] modules: add version and srcversion to sysfs
This patch adds version and srcversion files to
/sys/module/${modulename} containing the version and srcversion fields
of the module's modinfo section (if present).
/sys/module/e1000
|-- srcversion
`-- version
This patch differs slightly from the version posted in January, as it
now uses the new kstrdup() call in -mm.
Why put this in sysfs?
a) Tools like DKMS, which deal with changing out individual kernel
modules without replacing the whole kernel, can behave smarter if they
can tell the version of a given module. The autoinstaller feature, for
example, which determines if your system has a "good" version of a
driver (i.e. if the one provided by DKMS has a newer verson than that
provided by the kernel package installed), and to automatically compile
and install a newer version if DKMS has it but your kernel doesn't yet
have that version.
b) Because sysadmins manually, or with tools like DKMS, can switch out
modules on the file system, you can't count on 'modinfo foo.ko', which
looks at /lib/modules/${kernelver}/... actually matching what is loaded
into the kernel already. Hence asking sysfs for this.
c) as the unbind-driver-from-device work takes shape, it will be
possible to rebind a driver that's built-in (no .ko to modinfo for the
version) to a newly loaded module. sysfs will have the
currently-built-in version info, for comparison.
d) tech support scripts can then easily grab the version info for what's
running presently - a question I get often.
There has been renewed interest in this patch on linux-scsi by driver
authors.
As the idea originated from GregKH, I leave his Signed-off-by: intact,
though the implementation is nearly completely new. Compiled and run on
x86 and x86_64.
From: Matthew Dobson <colpatch@us.ibm.com>
build fix
From: Thierry Vignaud <tvignaud@mandriva.com>
build fix
From: Matthew Dobson <colpatch@us.ibm.com>
warning fix
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
Signed-off-by: Matt Domsch <Matt_Domsch@dell.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-06-24 13:05:15 +08:00
|
|
|
static void setup_modinfo(struct module *mod, Elf_Shdr *sechdrs,
|
|
|
|
unsigned int infoindex)
|
|
|
|
{
|
|
|
|
struct module_attribute *attr;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; (attr = modinfo_attrs[i]); i++) {
|
|
|
|
if (attr->setup)
|
|
|
|
attr->setup(mod,
|
|
|
|
get_modinfo(sechdrs,
|
|
|
|
infoindex,
|
|
|
|
attr->attr.name));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
#ifdef CONFIG_KALLSYMS
|
2008-07-24 22:41:48 +08:00
|
|
|
|
|
|
|
/* lookup symbol in given range of kernel_symbols */
|
|
|
|
static const struct kernel_symbol *lookup_symbol(const char *name,
|
|
|
|
const struct kernel_symbol *start,
|
|
|
|
const struct kernel_symbol *stop)
|
|
|
|
{
|
|
|
|
const struct kernel_symbol *ks = start;
|
|
|
|
for (; ks < stop; ks++)
|
|
|
|
if (strcmp(ks->name, name) == 0)
|
|
|
|
return ks;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2007-05-08 15:28:39 +08:00
|
|
|
static int is_exported(const char *name, const struct module *mod)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2006-02-09 04:16:45 +08:00
|
|
|
if (!mod && lookup_symbol(name, __start___ksymtab, __stop___ksymtab))
|
|
|
|
return 1;
|
|
|
|
else
|
2006-06-25 20:47:09 +08:00
|
|
|
if (mod && lookup_symbol(name, mod->syms, mod->syms + mod->num_syms))
|
2005-04-17 06:20:36 +08:00
|
|
|
return 1;
|
2006-02-09 04:16:45 +08:00
|
|
|
else
|
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* As per nm */
|
|
|
|
static char elf_type(const Elf_Sym *sym,
|
|
|
|
Elf_Shdr *sechdrs,
|
|
|
|
const char *secstrings,
|
|
|
|
struct module *mod)
|
|
|
|
{
|
|
|
|
if (ELF_ST_BIND(sym->st_info) == STB_WEAK) {
|
|
|
|
if (ELF_ST_TYPE(sym->st_info) == STT_OBJECT)
|
|
|
|
return 'v';
|
|
|
|
else
|
|
|
|
return 'w';
|
|
|
|
}
|
|
|
|
if (sym->st_shndx == SHN_UNDEF)
|
|
|
|
return 'U';
|
|
|
|
if (sym->st_shndx == SHN_ABS)
|
|
|
|
return 'a';
|
|
|
|
if (sym->st_shndx >= SHN_LORESERVE)
|
|
|
|
return '?';
|
|
|
|
if (sechdrs[sym->st_shndx].sh_flags & SHF_EXECINSTR)
|
|
|
|
return 't';
|
|
|
|
if (sechdrs[sym->st_shndx].sh_flags & SHF_ALLOC
|
|
|
|
&& sechdrs[sym->st_shndx].sh_type != SHT_NOBITS) {
|
|
|
|
if (!(sechdrs[sym->st_shndx].sh_flags & SHF_WRITE))
|
|
|
|
return 'r';
|
|
|
|
else if (sechdrs[sym->st_shndx].sh_flags & ARCH_SHF_SMALL)
|
|
|
|
return 'g';
|
|
|
|
else
|
|
|
|
return 'd';
|
|
|
|
}
|
|
|
|
if (sechdrs[sym->st_shndx].sh_type == SHT_NOBITS) {
|
|
|
|
if (sechdrs[sym->st_shndx].sh_flags & ARCH_SHF_SMALL)
|
|
|
|
return 's';
|
|
|
|
else
|
|
|
|
return 'b';
|
|
|
|
}
|
|
|
|
if (strncmp(secstrings + sechdrs[sym->st_shndx].sh_name,
|
|
|
|
".debug", strlen(".debug")) == 0)
|
|
|
|
return 'n';
|
|
|
|
return '?';
|
|
|
|
}
|
|
|
|
|
|
|
|
static void add_kallsyms(struct module *mod,
|
|
|
|
Elf_Shdr *sechdrs,
|
|
|
|
unsigned int symindex,
|
|
|
|
unsigned int strindex,
|
|
|
|
const char *secstrings)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
mod->symtab = (void *)sechdrs[symindex].sh_addr;
|
|
|
|
mod->num_symtab = sechdrs[symindex].sh_size / sizeof(Elf_Sym);
|
|
|
|
mod->strtab = (void *)sechdrs[strindex].sh_addr;
|
|
|
|
|
|
|
|
/* Set types up while we still have access to sections. */
|
|
|
|
for (i = 0; i < mod->num_symtab; i++)
|
|
|
|
mod->symtab[i].st_info
|
|
|
|
= elf_type(&mod->symtab[i], sechdrs, secstrings, mod);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
static inline void add_kallsyms(struct module *mod,
|
|
|
|
Elf_Shdr *sechdrs,
|
|
|
|
unsigned int symindex,
|
|
|
|
unsigned int strindex,
|
|
|
|
const char *secstrings)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_KALLSYMS */
|
|
|
|
|
2008-07-23 08:24:28 +08:00
|
|
|
static void *module_alloc_update_bounds(unsigned long size)
|
|
|
|
{
|
|
|
|
void *ret = module_alloc(size);
|
|
|
|
|
|
|
|
if (ret) {
|
|
|
|
/* Update module bounds. */
|
|
|
|
if ((unsigned long)ret < module_addr_min)
|
|
|
|
module_addr_min = (unsigned long)ret;
|
|
|
|
if ((unsigned long)ret + size > module_addr_max)
|
|
|
|
module_addr_max = (unsigned long)ret + size;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Allocate and load the module: note that size of section 0 is always
|
|
|
|
zero, and we rely on this for optional sections. */
|
2008-08-26 02:10:26 +08:00
|
|
|
static noinline struct module *load_module(void __user *umod,
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned long len,
|
|
|
|
const char __user *uargs)
|
|
|
|
{
|
|
|
|
Elf_Ehdr *hdr;
|
|
|
|
Elf_Shdr *sechdrs;
|
|
|
|
char *secstrings, *args, *modmagic, *strtab = NULL;
|
2006-06-28 19:26:46 +08:00
|
|
|
unsigned int i;
|
|
|
|
unsigned int symindex = 0;
|
|
|
|
unsigned int strindex = 0;
|
|
|
|
unsigned int setupindex;
|
|
|
|
unsigned int exindex;
|
|
|
|
unsigned int exportindex;
|
|
|
|
unsigned int modindex;
|
|
|
|
unsigned int obsparmindex;
|
|
|
|
unsigned int infoindex;
|
|
|
|
unsigned int gplindex;
|
|
|
|
unsigned int crcindex;
|
|
|
|
unsigned int gplcrcindex;
|
|
|
|
unsigned int versindex;
|
|
|
|
unsigned int pcpuindex;
|
|
|
|
unsigned int gplfutureindex;
|
|
|
|
unsigned int gplfuturecrcindex;
|
|
|
|
unsigned int unwindex = 0;
|
2008-07-23 08:24:26 +08:00
|
|
|
#ifdef CONFIG_UNUSED_SYMBOLS
|
2006-06-28 19:26:46 +08:00
|
|
|
unsigned int unusedindex;
|
|
|
|
unsigned int unusedcrcindex;
|
|
|
|
unsigned int unusedgplindex;
|
|
|
|
unsigned int unusedgplcrcindex;
|
2008-07-23 08:24:26 +08:00
|
|
|
#endif
|
2007-10-19 14:41:06 +08:00
|
|
|
unsigned int markersindex;
|
|
|
|
unsigned int markersstringsindex;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct module *mod;
|
|
|
|
long err = 0;
|
|
|
|
void *percpu = NULL, *ptr = NULL; /* Stops spurious gcc warning */
|
|
|
|
struct exception_table_entry *extable;
|
2005-09-07 06:17:11 +08:00
|
|
|
mm_segment_t old_fs;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
DEBUGP("load_module: umod=%p, len=%lu, uargs=%p\n",
|
|
|
|
umod, len, uargs);
|
|
|
|
if (len < sizeof(*hdr))
|
|
|
|
return ERR_PTR(-ENOEXEC);
|
|
|
|
|
|
|
|
/* Suck in entire file: we'll want most of it. */
|
|
|
|
/* vmalloc barfs on "unusual" numbers. Check here */
|
|
|
|
if (len > 64 * 1024 * 1024 || (hdr = vmalloc(len)) == NULL)
|
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
if (copy_from_user(hdr, umod, len) != 0) {
|
|
|
|
err = -EFAULT;
|
|
|
|
goto free_hdr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Sanity checks against insmoding binaries or wrong arch,
|
|
|
|
weird elf version */
|
2008-05-15 07:27:29 +08:00
|
|
|
if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) != 0
|
2005-04-17 06:20:36 +08:00
|
|
|
|| hdr->e_type != ET_REL
|
|
|
|
|| !elf_check_arch(hdr)
|
|
|
|
|| hdr->e_shentsize != sizeof(*sechdrs)) {
|
|
|
|
err = -ENOEXEC;
|
|
|
|
goto free_hdr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len < hdr->e_shoff + hdr->e_shnum * sizeof(Elf_Shdr))
|
|
|
|
goto truncated;
|
|
|
|
|
|
|
|
/* Convenience variables */
|
|
|
|
sechdrs = (void *)hdr + hdr->e_shoff;
|
|
|
|
secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
|
|
|
|
sechdrs[0].sh_addr = 0;
|
|
|
|
|
|
|
|
for (i = 1; i < hdr->e_shnum; i++) {
|
|
|
|
if (sechdrs[i].sh_type != SHT_NOBITS
|
|
|
|
&& len < sechdrs[i].sh_offset + sechdrs[i].sh_size)
|
|
|
|
goto truncated;
|
|
|
|
|
|
|
|
/* Mark all sections sh_addr with their address in the
|
|
|
|
temporary image. */
|
|
|
|
sechdrs[i].sh_addr = (size_t)hdr + sechdrs[i].sh_offset;
|
|
|
|
|
|
|
|
/* Internal symbols and strings. */
|
|
|
|
if (sechdrs[i].sh_type == SHT_SYMTAB) {
|
|
|
|
symindex = i;
|
|
|
|
strindex = sechdrs[i].sh_link;
|
|
|
|
strtab = (char *)hdr + sechdrs[strindex].sh_offset;
|
|
|
|
}
|
|
|
|
#ifndef CONFIG_MODULE_UNLOAD
|
|
|
|
/* Don't load .exit sections */
|
|
|
|
if (strncmp(secstrings+sechdrs[i].sh_name, ".exit", 5) == 0)
|
|
|
|
sechdrs[i].sh_flags &= ~(unsigned long)SHF_ALLOC;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
modindex = find_sec(hdr, sechdrs, secstrings,
|
|
|
|
".gnu.linkonce.this_module");
|
|
|
|
if (!modindex) {
|
|
|
|
printk(KERN_WARNING "No module found in object\n");
|
|
|
|
err = -ENOEXEC;
|
|
|
|
goto free_hdr;
|
|
|
|
}
|
|
|
|
mod = (void *)sechdrs[modindex].sh_addr;
|
|
|
|
|
|
|
|
if (symindex == 0) {
|
|
|
|
printk(KERN_WARNING "%s: module has no symbols (stripped?)\n",
|
|
|
|
mod->name);
|
|
|
|
err = -ENOEXEC;
|
|
|
|
goto free_hdr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Optional sections */
|
|
|
|
exportindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab");
|
|
|
|
gplindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab_gpl");
|
2006-03-21 05:17:13 +08:00
|
|
|
gplfutureindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab_gpl_future");
|
2005-04-17 06:20:36 +08:00
|
|
|
crcindex = find_sec(hdr, sechdrs, secstrings, "__kcrctab");
|
|
|
|
gplcrcindex = find_sec(hdr, sechdrs, secstrings, "__kcrctab_gpl");
|
2006-03-21 05:17:13 +08:00
|
|
|
gplfuturecrcindex = find_sec(hdr, sechdrs, secstrings, "__kcrctab_gpl_future");
|
2008-07-23 08:24:26 +08:00
|
|
|
#ifdef CONFIG_UNUSED_SYMBOLS
|
|
|
|
unusedindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab_unused");
|
|
|
|
unusedgplindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab_unused_gpl");
|
2006-06-28 19:26:45 +08:00
|
|
|
unusedcrcindex = find_sec(hdr, sechdrs, secstrings, "__kcrctab_unused");
|
|
|
|
unusedgplcrcindex = find_sec(hdr, sechdrs, secstrings, "__kcrctab_unused_gpl");
|
2008-07-23 08:24:26 +08:00
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
setupindex = find_sec(hdr, sechdrs, secstrings, "__param");
|
|
|
|
exindex = find_sec(hdr, sechdrs, secstrings, "__ex_table");
|
|
|
|
obsparmindex = find_sec(hdr, sechdrs, secstrings, "__obsparm");
|
|
|
|
versindex = find_sec(hdr, sechdrs, secstrings, "__versions");
|
|
|
|
infoindex = find_sec(hdr, sechdrs, secstrings, ".modinfo");
|
|
|
|
pcpuindex = find_pcpusec(hdr, sechdrs, secstrings);
|
2006-06-26 19:57:28 +08:00
|
|
|
#ifdef ARCH_UNWIND_SECTION_NAME
|
|
|
|
unwindex = find_sec(hdr, sechdrs, secstrings, ARCH_UNWIND_SECTION_NAME);
|
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-03-13 17:02:17 +08:00
|
|
|
/* Don't keep modinfo and version sections. */
|
2005-04-17 06:20:36 +08:00
|
|
|
sechdrs[infoindex].sh_flags &= ~(unsigned long)SHF_ALLOC;
|
2008-03-13 17:02:17 +08:00
|
|
|
sechdrs[versindex].sh_flags &= ~(unsigned long)SHF_ALLOC;
|
2005-04-17 06:20:36 +08:00
|
|
|
#ifdef CONFIG_KALLSYMS
|
|
|
|
/* Keep symbol and string tables for decoding later. */
|
|
|
|
sechdrs[symindex].sh_flags |= SHF_ALLOC;
|
|
|
|
sechdrs[strindex].sh_flags |= SHF_ALLOC;
|
|
|
|
#endif
|
2006-06-26 19:57:28 +08:00
|
|
|
if (unwindex)
|
|
|
|
sechdrs[unwindex].sh_flags |= SHF_ALLOC;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* Check module struct version now, before we try to use module. */
|
|
|
|
if (!check_modstruct_version(sechdrs, versindex, mod)) {
|
|
|
|
err = -ENOEXEC;
|
|
|
|
goto free_hdr;
|
|
|
|
}
|
|
|
|
|
|
|
|
modmagic = get_modinfo(sechdrs, infoindex, "vermagic");
|
|
|
|
/* This is allowed: modprobe --force will invalidate it. */
|
|
|
|
if (!modmagic) {
|
2008-05-05 08:04:16 +08:00
|
|
|
err = try_to_force_load(mod, "magic");
|
|
|
|
if (err)
|
|
|
|
goto free_hdr;
|
2008-05-09 14:25:28 +08:00
|
|
|
} else if (!same_magic(modmagic, vermagic, versindex)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
printk(KERN_ERR "%s: version magic '%s' should be '%s'\n",
|
|
|
|
mod->name, modmagic, vermagic);
|
|
|
|
err = -ENOEXEC;
|
|
|
|
goto free_hdr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now copy in args */
|
2006-03-24 19:18:43 +08:00
|
|
|
args = strndup_user(uargs, ~0UL >> 1);
|
|
|
|
if (IS_ERR(args)) {
|
|
|
|
err = PTR_ERR(args);
|
2005-04-17 06:20:36 +08:00
|
|
|
goto free_hdr;
|
|
|
|
}
|
2006-02-08 04:58:45 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (find_module(mod->name)) {
|
|
|
|
err = -EEXIST;
|
|
|
|
goto free_mod;
|
|
|
|
}
|
|
|
|
|
|
|
|
mod->state = MODULE_STATE_COMING;
|
|
|
|
|
|
|
|
/* Allow arches to frob section contents and sizes. */
|
|
|
|
err = module_frob_arch_sections(hdr, sechdrs, secstrings, mod);
|
|
|
|
if (err < 0)
|
|
|
|
goto free_mod;
|
|
|
|
|
|
|
|
if (pcpuindex) {
|
|
|
|
/* We have a special allocation for this section. */
|
|
|
|
percpu = percpu_modalloc(sechdrs[pcpuindex].sh_size,
|
2005-08-02 12:11:47 +08:00
|
|
|
sechdrs[pcpuindex].sh_addralign,
|
|
|
|
mod->name);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!percpu) {
|
|
|
|
err = -ENOMEM;
|
|
|
|
goto free_mod;
|
|
|
|
}
|
|
|
|
sechdrs[pcpuindex].sh_flags &= ~(unsigned long)SHF_ALLOC;
|
|
|
|
mod->percpu = percpu;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Determine total sizes, and put offsets in sh_entsize. For now
|
|
|
|
this is done generically; there doesn't appear to be any
|
|
|
|
special cases for the architectures. */
|
|
|
|
layout_sections(mod, hdr, sechdrs, secstrings);
|
|
|
|
|
|
|
|
/* Do the allocs. */
|
2008-07-23 08:24:28 +08:00
|
|
|
ptr = module_alloc_update_bounds(mod->core_size);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!ptr) {
|
|
|
|
err = -ENOMEM;
|
|
|
|
goto free_percpu;
|
|
|
|
}
|
|
|
|
memset(ptr, 0, mod->core_size);
|
|
|
|
mod->module_core = ptr;
|
|
|
|
|
2008-07-23 08:24:28 +08:00
|
|
|
ptr = module_alloc_update_bounds(mod->init_size);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!ptr && mod->init_size) {
|
|
|
|
err = -ENOMEM;
|
|
|
|
goto free_core;
|
|
|
|
}
|
|
|
|
memset(ptr, 0, mod->init_size);
|
|
|
|
mod->module_init = ptr;
|
|
|
|
|
|
|
|
/* Transfer each section which specifies SHF_ALLOC */
|
|
|
|
DEBUGP("final section addresses:\n");
|
|
|
|
for (i = 0; i < hdr->e_shnum; i++) {
|
|
|
|
void *dest;
|
|
|
|
|
|
|
|
if (!(sechdrs[i].sh_flags & SHF_ALLOC))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (sechdrs[i].sh_entsize & INIT_OFFSET_MASK)
|
|
|
|
dest = mod->module_init
|
|
|
|
+ (sechdrs[i].sh_entsize & ~INIT_OFFSET_MASK);
|
|
|
|
else
|
|
|
|
dest = mod->module_core + sechdrs[i].sh_entsize;
|
|
|
|
|
|
|
|
if (sechdrs[i].sh_type != SHT_NOBITS)
|
|
|
|
memcpy(dest, (void *)sechdrs[i].sh_addr,
|
|
|
|
sechdrs[i].sh_size);
|
|
|
|
/* Update sh_addr to point to copy in image. */
|
|
|
|
sechdrs[i].sh_addr = (unsigned long)dest;
|
|
|
|
DEBUGP("\t0x%lx %s\n", sechdrs[i].sh_addr, secstrings + sechdrs[i].sh_name);
|
|
|
|
}
|
|
|
|
/* Module has been moved. */
|
|
|
|
mod = (void *)sechdrs[modindex].sh_addr;
|
|
|
|
|
|
|
|
/* Now we've moved module, initialize linked lists, etc. */
|
|
|
|
module_unload_init(mod);
|
|
|
|
|
2007-11-30 06:46:11 +08:00
|
|
|
/* add kobject, so we can reference it. */
|
2007-10-17 14:30:27 +08:00
|
|
|
err = mod_sysfs_init(mod);
|
|
|
|
if (err)
|
2007-11-30 06:46:11 +08:00
|
|
|
goto free_unload;
|
2007-01-18 20:26:15 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Set up license info based on the info section */
|
|
|
|
set_license(mod, get_modinfo(sechdrs, infoindex, "license"));
|
|
|
|
|
2008-02-29 06:11:02 +08:00
|
|
|
/*
|
|
|
|
* ndiswrapper is under GPL by itself, but loads proprietary modules.
|
|
|
|
* Don't use add_taint_module(), as it would prevent ndiswrapper from
|
|
|
|
* using GPL-only symbols it needs.
|
|
|
|
*/
|
2006-10-11 16:21:48 +08:00
|
|
|
if (strcmp(mod->name, "ndiswrapper") == 0)
|
2008-02-29 06:11:02 +08:00
|
|
|
add_taint(TAINT_PROPRIETARY_MODULE);
|
|
|
|
|
|
|
|
/* driverloader was caught wrongly pretending to be under GPL */
|
2006-10-11 16:21:48 +08:00
|
|
|
if (strcmp(mod->name, "driverloader") == 0)
|
|
|
|
add_taint_module(mod, TAINT_PROPRIETARY_MODULE);
|
2006-01-08 17:03:41 +08:00
|
|
|
|
[PATCH] modules: add version and srcversion to sysfs
This patch adds version and srcversion files to
/sys/module/${modulename} containing the version and srcversion fields
of the module's modinfo section (if present).
/sys/module/e1000
|-- srcversion
`-- version
This patch differs slightly from the version posted in January, as it
now uses the new kstrdup() call in -mm.
Why put this in sysfs?
a) Tools like DKMS, which deal with changing out individual kernel
modules without replacing the whole kernel, can behave smarter if they
can tell the version of a given module. The autoinstaller feature, for
example, which determines if your system has a "good" version of a
driver (i.e. if the one provided by DKMS has a newer verson than that
provided by the kernel package installed), and to automatically compile
and install a newer version if DKMS has it but your kernel doesn't yet
have that version.
b) Because sysadmins manually, or with tools like DKMS, can switch out
modules on the file system, you can't count on 'modinfo foo.ko', which
looks at /lib/modules/${kernelver}/... actually matching what is loaded
into the kernel already. Hence asking sysfs for this.
c) as the unbind-driver-from-device work takes shape, it will be
possible to rebind a driver that's built-in (no .ko to modinfo for the
version) to a newly loaded module. sysfs will have the
currently-built-in version info, for comparison.
d) tech support scripts can then easily grab the version info for what's
running presently - a question I get often.
There has been renewed interest in this patch on linux-scsi by driver
authors.
As the idea originated from GregKH, I leave his Signed-off-by: intact,
though the implementation is nearly completely new. Compiled and run on
x86 and x86_64.
From: Matthew Dobson <colpatch@us.ibm.com>
build fix
From: Thierry Vignaud <tvignaud@mandriva.com>
build fix
From: Matthew Dobson <colpatch@us.ibm.com>
warning fix
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
Signed-off-by: Matt Domsch <Matt_Domsch@dell.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-06-24 13:05:15 +08:00
|
|
|
/* Set up MODINFO_ATTR fields */
|
|
|
|
setup_modinfo(mod, sechdrs, infoindex);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Fix up syms, so that st_value is a pointer to location. */
|
|
|
|
err = simplify_symbols(sechdrs, symindex, strtab, versindex, pcpuindex,
|
|
|
|
mod);
|
|
|
|
if (err < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
/* Set up EXPORTed & EXPORT_GPLed symbols (section 0 is 0 length) */
|
|
|
|
mod->num_syms = sechdrs[exportindex].sh_size / sizeof(*mod->syms);
|
|
|
|
mod->syms = (void *)sechdrs[exportindex].sh_addr;
|
|
|
|
if (crcindex)
|
|
|
|
mod->crcs = (void *)sechdrs[crcindex].sh_addr;
|
|
|
|
mod->num_gpl_syms = sechdrs[gplindex].sh_size / sizeof(*mod->gpl_syms);
|
|
|
|
mod->gpl_syms = (void *)sechdrs[gplindex].sh_addr;
|
|
|
|
if (gplcrcindex)
|
|
|
|
mod->gpl_crcs = (void *)sechdrs[gplcrcindex].sh_addr;
|
2006-03-21 05:17:13 +08:00
|
|
|
mod->num_gpl_future_syms = sechdrs[gplfutureindex].sh_size /
|
|
|
|
sizeof(*mod->gpl_future_syms);
|
|
|
|
mod->gpl_future_syms = (void *)sechdrs[gplfutureindex].sh_addr;
|
|
|
|
if (gplfuturecrcindex)
|
|
|
|
mod->gpl_future_crcs = (void *)sechdrs[gplfuturecrcindex].sh_addr;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-07-23 08:24:26 +08:00
|
|
|
#ifdef CONFIG_UNUSED_SYMBOLS
|
|
|
|
mod->num_unused_syms = sechdrs[unusedindex].sh_size /
|
|
|
|
sizeof(*mod->unused_syms);
|
|
|
|
mod->num_unused_gpl_syms = sechdrs[unusedgplindex].sh_size /
|
|
|
|
sizeof(*mod->unused_gpl_syms);
|
2006-06-28 19:26:45 +08:00
|
|
|
mod->unused_syms = (void *)sechdrs[unusedindex].sh_addr;
|
|
|
|
if (unusedcrcindex)
|
|
|
|
mod->unused_crcs = (void *)sechdrs[unusedcrcindex].sh_addr;
|
|
|
|
mod->unused_gpl_syms = (void *)sechdrs[unusedgplindex].sh_addr;
|
|
|
|
if (unusedgplcrcindex)
|
2008-05-02 10:15:00 +08:00
|
|
|
mod->unused_gpl_crcs
|
|
|
|
= (void *)sechdrs[unusedgplcrcindex].sh_addr;
|
2008-07-23 08:24:26 +08:00
|
|
|
#endif
|
2006-06-28 19:26:45 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
#ifdef CONFIG_MODVERSIONS
|
2008-07-23 08:24:26 +08:00
|
|
|
if ((mod->num_syms && !crcindex)
|
|
|
|
|| (mod->num_gpl_syms && !gplcrcindex)
|
|
|
|
|| (mod->num_gpl_future_syms && !gplfuturecrcindex)
|
|
|
|
#ifdef CONFIG_UNUSED_SYMBOLS
|
|
|
|
|| (mod->num_unused_syms && !unusedcrcindex)
|
|
|
|
|| (mod->num_unused_gpl_syms && !unusedgplcrcindex)
|
|
|
|
#endif
|
|
|
|
) {
|
2008-05-05 08:04:16 +08:00
|
|
|
printk(KERN_WARNING "%s: No versions for exported symbols.\n", mod->name);
|
|
|
|
err = try_to_force_load(mod, "nocrc");
|
|
|
|
if (err)
|
|
|
|
goto cleanup;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
#endif
|
2007-10-19 14:41:06 +08:00
|
|
|
markersindex = find_sec(hdr, sechdrs, secstrings, "__markers");
|
|
|
|
markersstringsindex = find_sec(hdr, sechdrs, secstrings,
|
|
|
|
"__markers_strings");
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* Now do relocations. */
|
|
|
|
for (i = 1; i < hdr->e_shnum; i++) {
|
|
|
|
const char *strtab = (char *)sechdrs[strindex].sh_addr;
|
|
|
|
unsigned int info = sechdrs[i].sh_info;
|
|
|
|
|
|
|
|
/* Not a valid relocation section? */
|
|
|
|
if (info >= hdr->e_shnum)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Don't bother with non-allocated sections */
|
|
|
|
if (!(sechdrs[info].sh_flags & SHF_ALLOC))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (sechdrs[i].sh_type == SHT_REL)
|
|
|
|
err = apply_relocate(sechdrs, strtab, symindex, i,mod);
|
|
|
|
else if (sechdrs[i].sh_type == SHT_RELA)
|
|
|
|
err = apply_relocate_add(sechdrs, strtab, symindex, i,
|
|
|
|
mod);
|
|
|
|
if (err < 0)
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2007-10-19 14:41:06 +08:00
|
|
|
#ifdef CONFIG_MARKERS
|
|
|
|
mod->markers = (void *)sechdrs[markersindex].sh_addr;
|
|
|
|
mod->num_markers =
|
|
|
|
sechdrs[markersindex].sh_size / sizeof(*mod->markers);
|
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-01-08 17:04:25 +08:00
|
|
|
/* Find duplicate symbols */
|
|
|
|
err = verify_export_symbols(mod);
|
|
|
|
|
|
|
|
if (err < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Set up and sort exception table */
|
|
|
|
mod->num_exentries = sechdrs[exindex].sh_size / sizeof(*mod->extable);
|
|
|
|
mod->extable = extable = (void *)sechdrs[exindex].sh_addr;
|
|
|
|
sort_extable(extable, extable + mod->num_exentries);
|
|
|
|
|
|
|
|
/* Finally, copy percpu area over. */
|
|
|
|
percpu_modcopy(mod->percpu, (void *)sechdrs[pcpuindex].sh_addr,
|
|
|
|
sechdrs[pcpuindex].sh_size);
|
|
|
|
|
|
|
|
add_kallsyms(mod, sechdrs, symindex, strindex, secstrings);
|
|
|
|
|
2007-10-19 14:41:06 +08:00
|
|
|
#ifdef CONFIG_MARKERS
|
|
|
|
if (!mod->taints)
|
|
|
|
marker_update_probe_range(mod->markers,
|
Linux Kernel Markers: support multiple probes
RCU style multiple probes support for the Linux Kernel Markers. Common case
(one probe) is still fast and does not require dynamic allocation or a
supplementary pointer dereference on the fast path.
- Move preempt disable from the marker site to the callback.
Since we now have an internal callback, move the preempt disable/enable to the
callback instead of the marker site.
Since the callback change is done asynchronously (passing from a handler that
supports arguments to a handler that does not setup the arguments is no
arguments are passed), we can safely update it even if it is outside the
preempt disable section.
- Move probe arm to probe connection. Now, a connected probe is automatically
armed.
Remove MARK_MAX_FORMAT_LEN, unused.
This patch modifies the Linux Kernel Markers API : it removes the probe
"arm/disarm" and changes the probe function prototype : it now expects a
va_list * instead of a "...".
If we want to have more than one probe connected to a marker at a given
time (LTTng, or blktrace, ssytemtap) then we need this patch. Without it,
connecting a second probe handler to a marker will fail.
It allow us, for instance, to do interesting combinations :
Do standard tracing with LTTng and, eventually, to compute statistics
with SystemTAP, or to have a special trigger on an event that would call
a systemtap script which would stop flight recorder tracing.
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Mike Mason <mmlnx@us.ibm.com>
Cc: Dipankar Sarma <dipankar@in.ibm.com>
Cc: David Smith <dsmith@redhat.com>
Cc: "Paul E. McKenney" <paulmck@us.ibm.com>
Cc: "Frank Ch. Eigler" <fche@redhat.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-02-14 07:03:37 +08:00
|
|
|
mod->markers + mod->num_markers);
|
2007-10-19 14:41:06 +08:00
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
err = module_finalize(hdr, sechdrs, mod);
|
|
|
|
if (err < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2005-09-07 06:17:11 +08:00
|
|
|
/* flush the icache in correct context */
|
|
|
|
old_fs = get_fs();
|
|
|
|
set_fs(KERNEL_DS);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Flush the instruction cache, since we've played with text.
|
|
|
|
* Do it before processing of module parameters, so the module
|
|
|
|
* can provide parameter accessor functions of its own.
|
|
|
|
*/
|
|
|
|
if (mod->module_init)
|
|
|
|
flush_icache_range((unsigned long)mod->module_init,
|
|
|
|
(unsigned long)mod->module_init
|
|
|
|
+ mod->init_size);
|
|
|
|
flush_icache_range((unsigned long)mod->module_core,
|
|
|
|
(unsigned long)mod->module_core + mod->core_size);
|
|
|
|
|
|
|
|
set_fs(old_fs);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
mod->args = args;
|
2006-03-25 19:07:05 +08:00
|
|
|
if (obsparmindex)
|
|
|
|
printk(KERN_WARNING "%s: Ignoring obsolete parameters\n",
|
|
|
|
mod->name);
|
|
|
|
|
2008-01-30 06:13:21 +08:00
|
|
|
/* 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. */
|
2008-07-29 01:16:30 +08:00
|
|
|
stop_machine(__link_module, mod, NULL);
|
2008-01-30 06:13:21 +08:00
|
|
|
|
2006-03-25 19:07:05 +08:00
|
|
|
/* Size of section 0 is 0, so this works well if no params */
|
|
|
|
err = parse_args(mod->name, mod->args,
|
|
|
|
(struct kernel_param *)
|
|
|
|
sechdrs[setupindex].sh_addr,
|
|
|
|
sechdrs[setupindex].sh_size
|
|
|
|
/ sizeof(struct kernel_param),
|
|
|
|
NULL);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (err < 0)
|
2008-01-30 06:13:21 +08:00
|
|
|
goto unlink;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-10-18 18:06:07 +08:00
|
|
|
err = mod_sysfs_setup(mod,
|
2005-04-17 06:20:36 +08:00
|
|
|
(struct kernel_param *)
|
|
|
|
sechdrs[setupindex].sh_addr,
|
|
|
|
sechdrs[setupindex].sh_size
|
|
|
|
/ sizeof(struct kernel_param));
|
|
|
|
if (err < 0)
|
2008-01-30 06:13:21 +08:00
|
|
|
goto unlink;
|
2005-04-17 06:20:36 +08:00
|
|
|
add_sect_attrs(mod, hdr->e_shnum, secstrings, sechdrs);
|
2007-10-17 14:26:40 +08:00
|
|
|
add_notes_attrs(mod, hdr->e_shnum, secstrings, sechdrs);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-06-26 19:57:28 +08:00
|
|
|
/* Size of section 0 is 0, so this works well if no unwind info. */
|
|
|
|
mod->unwind_info = unwind_add_table(mod,
|
2007-10-18 18:06:07 +08:00
|
|
|
(void *)sechdrs[unwindex].sh_addr,
|
|
|
|
sechdrs[unwindex].sh_size);
|
2006-06-26 19:57:28 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Get rid of temporary copy */
|
|
|
|
vfree(hdr);
|
|
|
|
|
|
|
|
/* Done! */
|
|
|
|
return mod;
|
|
|
|
|
2008-01-30 06:13:21 +08:00
|
|
|
unlink:
|
2008-07-29 01:16:30 +08:00
|
|
|
stop_machine(__unlink_module, mod, NULL);
|
2005-04-17 06:20:36 +08:00
|
|
|
module_arch_cleanup(mod);
|
|
|
|
cleanup:
|
2007-11-30 06:46:11 +08:00
|
|
|
kobject_del(&mod->mkobj.kobj);
|
|
|
|
kobject_put(&mod->mkobj.kobj);
|
|
|
|
free_unload:
|
2005-04-17 06:20:36 +08:00
|
|
|
module_unload_free(mod);
|
|
|
|
module_free(mod, mod->module_init);
|
|
|
|
free_core:
|
|
|
|
module_free(mod, mod->module_core);
|
|
|
|
free_percpu:
|
|
|
|
if (percpu)
|
|
|
|
percpu_modfree(percpu);
|
|
|
|
free_mod:
|
|
|
|
kfree(args);
|
|
|
|
free_hdr:
|
|
|
|
vfree(hdr);
|
2006-01-06 16:19:54 +08:00
|
|
|
return ERR_PTR(err);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
truncated:
|
|
|
|
printk(KERN_ERR "Module len %lu truncated\n", len);
|
|
|
|
err = -ENOEXEC;
|
|
|
|
goto free_hdr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This is where the real work happens */
|
|
|
|
asmlinkage long
|
|
|
|
sys_init_module(void __user *umod,
|
|
|
|
unsigned long len,
|
|
|
|
const char __user *uargs)
|
|
|
|
{
|
|
|
|
struct module *mod;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
/* Must have permission */
|
|
|
|
if (!capable(CAP_SYS_MODULE))
|
|
|
|
return -EPERM;
|
|
|
|
|
|
|
|
/* Only one module load at a time, please */
|
2006-03-23 19:00:46 +08:00
|
|
|
if (mutex_lock_interruptible(&module_mutex) != 0)
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINTR;
|
|
|
|
|
|
|
|
/* Do all the hard work */
|
|
|
|
mod = load_module(umod, len, uargs);
|
|
|
|
if (IS_ERR(mod)) {
|
2006-03-23 19:00:46 +08:00
|
|
|
mutex_unlock(&module_mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
return PTR_ERR(mod);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Drop lock so they can recurse */
|
2006-03-23 19:00:46 +08:00
|
|
|
mutex_unlock(&module_mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
[PATCH] Notifier chain update: API changes
The kernel's implementation of notifier chains is unsafe. There is no
protection against entries being added to or removed from a chain while the
chain is in use. The issues were discussed in this thread:
http://marc.theaimsgroup.com/?l=linux-kernel&m=113018709002036&w=2
We noticed that notifier chains in the kernel fall into two basic usage
classes:
"Blocking" chains are always called from a process context
and the callout routines are allowed to sleep;
"Atomic" chains can be called from an atomic context and
the callout routines are not allowed to sleep.
We decided to codify this distinction and make it part of the API. Therefore
this set of patches introduces three new, parallel APIs: one for blocking
notifiers, one for atomic notifiers, and one for "raw" notifiers (which is
really just the old API under a new name). New kinds of data structures are
used for the heads of the chains, and new routines are defined for
registration, unregistration, and calling a chain. The three APIs are
explained in include/linux/notifier.h and their implementation is in
kernel/sys.c.
With atomic and blocking chains, the implementation guarantees that the chain
links will not be corrupted and that chain callers will not get messed up by
entries being added or removed. For raw chains the implementation provides no
guarantees at all; users of this API must provide their own protections. (The
idea was that situations may come up where the assumptions of the atomic and
blocking APIs are not appropriate, so it should be possible for users to
handle these things in their own way.)
There are some limitations, which should not be too hard to live with. For
atomic/blocking chains, registration and unregistration must always be done in
a process context since the chain is protected by a mutex/rwsem. Also, a
callout routine for a non-raw chain must not try to register or unregister
entries on its own chain. (This did happen in a couple of places and the code
had to be changed to avoid it.)
Since atomic chains may be called from within an NMI handler, they cannot use
spinlocks for synchronization. Instead we use RCU. The overhead falls almost
entirely in the unregister routine, which is okay since unregistration is much
less frequent that calling a chain.
Here is the list of chains that we adjusted and their classifications. None
of them use the raw API, so for the moment it is only a placeholder.
ATOMIC CHAINS
-------------
arch/i386/kernel/traps.c: i386die_chain
arch/ia64/kernel/traps.c: ia64die_chain
arch/powerpc/kernel/traps.c: powerpc_die_chain
arch/sparc64/kernel/traps.c: sparc64die_chain
arch/x86_64/kernel/traps.c: die_chain
drivers/char/ipmi/ipmi_si_intf.c: xaction_notifier_list
kernel/panic.c: panic_notifier_list
kernel/profile.c: task_free_notifier
net/bluetooth/hci_core.c: hci_notifier
net/ipv4/netfilter/ip_conntrack_core.c: ip_conntrack_chain
net/ipv4/netfilter/ip_conntrack_core.c: ip_conntrack_expect_chain
net/ipv6/addrconf.c: inet6addr_chain
net/netfilter/nf_conntrack_core.c: nf_conntrack_chain
net/netfilter/nf_conntrack_core.c: nf_conntrack_expect_chain
net/netlink/af_netlink.c: netlink_chain
BLOCKING CHAINS
---------------
arch/powerpc/platforms/pseries/reconfig.c: pSeries_reconfig_chain
arch/s390/kernel/process.c: idle_chain
arch/x86_64/kernel/process.c idle_notifier
drivers/base/memory.c: memory_chain
drivers/cpufreq/cpufreq.c cpufreq_policy_notifier_list
drivers/cpufreq/cpufreq.c cpufreq_transition_notifier_list
drivers/macintosh/adb.c: adb_client_list
drivers/macintosh/via-pmu.c sleep_notifier_list
drivers/macintosh/via-pmu68k.c sleep_notifier_list
drivers/macintosh/windfarm_core.c wf_client_list
drivers/usb/core/notify.c usb_notifier_list
drivers/video/fbmem.c fb_notifier_list
kernel/cpu.c cpu_chain
kernel/module.c module_notify_list
kernel/profile.c munmap_notifier
kernel/profile.c task_exit_notifier
kernel/sys.c reboot_notifier_list
net/core/dev.c netdev_chain
net/decnet/dn_dev.c: dnaddr_chain
net/ipv4/devinet.c: inetaddr_chain
It's possible that some of these classifications are wrong. If they are,
please let us know or submit a patch to fix them. Note that any chain that
gets called very frequently should be atomic, because the rwsem read-locking
used for blocking chains is very likely to incur cache misses on SMP systems.
(However, if the chain's callout routines may sleep then the chain cannot be
atomic.)
The patch set was written by Alan Stern and Chandra Seetharaman, incorporating
material written by Keith Owens and suggestions from Paul McKenney and Andrew
Morton.
[jes@sgi.com: restructure the notifier chain initialization macros]
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Chandra Seetharaman <sekharan@us.ibm.com>
Signed-off-by: Jes Sorensen <jes@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-03-27 17:16:30 +08:00
|
|
|
blocking_notifier_call_chain(&module_notify_list,
|
|
|
|
MODULE_STATE_COMING, mod);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* Start the module */
|
|
|
|
if (mod->init != NULL)
|
2008-07-31 03:49:02 +08:00
|
|
|
ret = do_one_initcall(mod->init);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (ret < 0) {
|
|
|
|
/* Init routine failed: abort. Try to protect us from
|
|
|
|
buggy refcounters. */
|
|
|
|
mod->state = MODULE_STATE_GOING;
|
2005-05-01 23:59:04 +08:00
|
|
|
synchronize_sched();
|
2007-10-17 14:26:27 +08:00
|
|
|
module_put(mod);
|
2008-04-21 20:34:31 +08:00
|
|
|
blocking_notifier_call_chain(&module_notify_list,
|
|
|
|
MODULE_STATE_GOING, mod);
|
2007-10-17 14:26:27 +08:00
|
|
|
mutex_lock(&module_mutex);
|
|
|
|
free_module(mod);
|
|
|
|
mutex_unlock(&module_mutex);
|
2008-01-30 06:13:18 +08:00
|
|
|
wake_up(&module_wq);
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret;
|
|
|
|
}
|
2008-03-11 02:43:53 +08:00
|
|
|
if (ret > 0) {
|
|
|
|
printk(KERN_WARNING "%s: '%s'->init suspiciously returned %d, "
|
|
|
|
"it should follow 0/-E convention\n"
|
|
|
|
KERN_WARNING "%s: loading module anyway...\n",
|
|
|
|
__func__, mod->name, ret,
|
|
|
|
__func__);
|
|
|
|
dump_stack();
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-03-11 02:43:52 +08:00
|
|
|
/* Now it's a first class citizen! Wake up anyone waiting for it. */
|
2005-04-17 06:20:36 +08:00
|
|
|
mod->state = MODULE_STATE_LIVE;
|
2008-03-11 02:43:52 +08:00
|
|
|
wake_up(&module_wq);
|
|
|
|
|
|
|
|
mutex_lock(&module_mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Drop initial reference. */
|
|
|
|
module_put(mod);
|
2006-06-26 19:57:28 +08:00
|
|
|
unwind_remove_table(mod->unwind_info, 1);
|
2005-04-17 06:20:36 +08:00
|
|
|
module_free(mod, mod->module_init);
|
|
|
|
mod->module_init = NULL;
|
|
|
|
mod->init_size = 0;
|
|
|
|
mod->init_text_size = 0;
|
2006-03-23 19:00:46 +08:00
|
|
|
mutex_unlock(&module_mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int within(unsigned long addr, void *start, unsigned long size)
|
|
|
|
{
|
|
|
|
return ((void *)addr >= start && (void *)addr < start + size);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_KALLSYMS
|
|
|
|
/*
|
|
|
|
* This ignores the intensely annoying "mapping symbols" found
|
|
|
|
* in ARM ELF files: $a, $t and $d.
|
|
|
|
*/
|
|
|
|
static inline int is_arm_mapping_symbol(const char *str)
|
|
|
|
{
|
2007-10-18 18:06:07 +08:00
|
|
|
return str[0] == '$' && strchr("atd", str[1])
|
2005-04-17 06:20:36 +08:00
|
|
|
&& (str[2] == '\0' || str[2] == '.');
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char *get_ksymbol(struct module *mod,
|
|
|
|
unsigned long addr,
|
|
|
|
unsigned long *size,
|
|
|
|
unsigned long *offset)
|
|
|
|
{
|
|
|
|
unsigned int i, best = 0;
|
|
|
|
unsigned long nextval;
|
|
|
|
|
|
|
|
/* At worse, next value is at end of module */
|
|
|
|
if (within(addr, mod->module_init, mod->init_size))
|
|
|
|
nextval = (unsigned long)mod->module_init+mod->init_text_size;
|
2007-10-18 18:06:07 +08:00
|
|
|
else
|
2005-04-17 06:20:36 +08:00
|
|
|
nextval = (unsigned long)mod->module_core+mod->core_text_size;
|
|
|
|
|
|
|
|
/* Scan for closest preceeding symbol, and next symbol. (ELF
|
2007-10-18 18:06:07 +08:00
|
|
|
starts real symbols at 1). */
|
2005-04-17 06:20:36 +08:00
|
|
|
for (i = 1; i < mod->num_symtab; i++) {
|
|
|
|
if (mod->symtab[i].st_shndx == SHN_UNDEF)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* We ignore unnamed symbols: they're uninformative
|
|
|
|
* and inserted at a whim. */
|
|
|
|
if (mod->symtab[i].st_value <= addr
|
|
|
|
&& mod->symtab[i].st_value > mod->symtab[best].st_value
|
|
|
|
&& *(mod->strtab + mod->symtab[i].st_name) != '\0'
|
|
|
|
&& !is_arm_mapping_symbol(mod->strtab + mod->symtab[i].st_name))
|
|
|
|
best = i;
|
|
|
|
if (mod->symtab[i].st_value > addr
|
|
|
|
&& mod->symtab[i].st_value < nextval
|
|
|
|
&& *(mod->strtab + mod->symtab[i].st_name) != '\0'
|
|
|
|
&& !is_arm_mapping_symbol(mod->strtab + mod->symtab[i].st_name))
|
|
|
|
nextval = mod->symtab[i].st_value;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!best)
|
|
|
|
return NULL;
|
|
|
|
|
2007-05-08 15:28:41 +08:00
|
|
|
if (size)
|
|
|
|
*size = nextval - mod->symtab[best].st_value;
|
|
|
|
if (offset)
|
|
|
|
*offset = addr - mod->symtab[best].st_value;
|
2005-04-17 06:20:36 +08:00
|
|
|
return mod->strtab + mod->symtab[best].st_name;
|
|
|
|
}
|
|
|
|
|
2008-01-30 06:13:22 +08:00
|
|
|
/* For kallsyms to ask for address resolution. NULL means not found. Careful
|
|
|
|
* not to lock to avoid deadlock on oopses, simply disable preemption. */
|
2008-02-08 20:18:43 +08:00
|
|
|
const char *module_address_lookup(unsigned long addr,
|
2008-01-30 06:13:22 +08:00
|
|
|
unsigned long *size,
|
|
|
|
unsigned long *offset,
|
|
|
|
char **modname,
|
|
|
|
char *namebuf)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct module *mod;
|
2008-01-14 16:55:03 +08:00
|
|
|
const char *ret = NULL;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-01-14 16:55:03 +08:00
|
|
|
preempt_disable();
|
2005-04-17 06:20:36 +08:00
|
|
|
list_for_each_entry(mod, &modules, list) {
|
|
|
|
if (within(addr, mod->module_init, mod->init_size)
|
|
|
|
|| within(addr, mod->module_core, mod->core_size)) {
|
2006-10-03 16:13:48 +08:00
|
|
|
if (modname)
|
|
|
|
*modname = mod->name;
|
2008-01-14 16:55:03 +08:00
|
|
|
ret = get_ksymbol(mod, addr, size, offset);
|
|
|
|
break;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
2008-01-30 06:13:22 +08:00
|
|
|
/* Make a copy in here where it's safe */
|
|
|
|
if (ret) {
|
|
|
|
strncpy(namebuf, ret, KSYM_NAME_LEN - 1);
|
|
|
|
ret = namebuf;
|
|
|
|
}
|
2008-01-14 16:55:03 +08:00
|
|
|
preempt_enable();
|
2008-02-08 20:18:43 +08:00
|
|
|
return ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2007-05-08 15:28:43 +08:00
|
|
|
int lookup_module_symbol_name(unsigned long addr, char *symname)
|
|
|
|
{
|
|
|
|
struct module *mod;
|
|
|
|
|
2008-01-14 16:55:03 +08:00
|
|
|
preempt_disable();
|
2007-05-08 15:28:43 +08:00
|
|
|
list_for_each_entry(mod, &modules, list) {
|
|
|
|
if (within(addr, mod->module_init, mod->init_size) ||
|
|
|
|
within(addr, mod->module_core, mod->core_size)) {
|
|
|
|
const char *sym;
|
|
|
|
|
|
|
|
sym = get_ksymbol(mod, addr, NULL, NULL);
|
|
|
|
if (!sym)
|
|
|
|
goto out;
|
2007-07-17 19:03:51 +08:00
|
|
|
strlcpy(symname, sym, KSYM_NAME_LEN);
|
2008-01-14 16:55:03 +08:00
|
|
|
preempt_enable();
|
2007-05-08 15:28:43 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
out:
|
2008-01-14 16:55:03 +08:00
|
|
|
preempt_enable();
|
2007-05-08 15:28:43 +08:00
|
|
|
return -ERANGE;
|
|
|
|
}
|
|
|
|
|
2007-05-08 15:28:47 +08:00
|
|
|
int lookup_module_symbol_attrs(unsigned long addr, unsigned long *size,
|
|
|
|
unsigned long *offset, char *modname, char *name)
|
|
|
|
{
|
|
|
|
struct module *mod;
|
|
|
|
|
2008-01-14 16:55:03 +08:00
|
|
|
preempt_disable();
|
2007-05-08 15:28:47 +08:00
|
|
|
list_for_each_entry(mod, &modules, list) {
|
|
|
|
if (within(addr, mod->module_init, mod->init_size) ||
|
|
|
|
within(addr, mod->module_core, mod->core_size)) {
|
|
|
|
const char *sym;
|
|
|
|
|
|
|
|
sym = get_ksymbol(mod, addr, size, offset);
|
|
|
|
if (!sym)
|
|
|
|
goto out;
|
|
|
|
if (modname)
|
2007-07-17 19:03:51 +08:00
|
|
|
strlcpy(modname, mod->name, MODULE_NAME_LEN);
|
2007-05-08 15:28:47 +08:00
|
|
|
if (name)
|
2007-07-17 19:03:51 +08:00
|
|
|
strlcpy(name, sym, KSYM_NAME_LEN);
|
2008-01-14 16:55:03 +08:00
|
|
|
preempt_enable();
|
2007-05-08 15:28:47 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
out:
|
2008-01-14 16:55:03 +08:00
|
|
|
preempt_enable();
|
2007-05-08 15:28:47 +08:00
|
|
|
return -ERANGE;
|
|
|
|
}
|
|
|
|
|
2007-05-08 15:28:39 +08:00
|
|
|
int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type,
|
|
|
|
char *name, char *module_name, int *exported)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct module *mod;
|
|
|
|
|
2008-01-14 16:55:03 +08:00
|
|
|
preempt_disable();
|
2005-04-17 06:20:36 +08:00
|
|
|
list_for_each_entry(mod, &modules, list) {
|
|
|
|
if (symnum < mod->num_symtab) {
|
|
|
|
*value = mod->symtab[symnum].st_value;
|
|
|
|
*type = mod->symtab[symnum].st_info;
|
2006-07-14 15:24:04 +08:00
|
|
|
strlcpy(name, mod->strtab + mod->symtab[symnum].st_name,
|
2007-07-17 19:03:51 +08:00
|
|
|
KSYM_NAME_LEN);
|
|
|
|
strlcpy(module_name, mod->name, MODULE_NAME_LEN);
|
2007-05-08 15:28:39 +08:00
|
|
|
*exported = is_exported(name, mod);
|
2008-01-14 16:55:03 +08:00
|
|
|
preempt_enable();
|
2007-05-08 15:28:39 +08:00
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
symnum -= mod->num_symtab;
|
|
|
|
}
|
2008-01-14 16:55:03 +08:00
|
|
|
preempt_enable();
|
2007-05-08 15:28:39 +08:00
|
|
|
return -ERANGE;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned long mod_find_symname(struct module *mod, const char *name)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
for (i = 0; i < mod->num_symtab; i++)
|
2006-02-03 19:03:53 +08:00
|
|
|
if (strcmp(name, mod->strtab+mod->symtab[i].st_name) == 0 &&
|
|
|
|
mod->symtab[i].st_info != 'U')
|
2005-04-17 06:20:36 +08:00
|
|
|
return mod->symtab[i].st_value;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Look for this name: can be of form module:name. */
|
|
|
|
unsigned long module_kallsyms_lookup_name(const char *name)
|
|
|
|
{
|
|
|
|
struct module *mod;
|
|
|
|
char *colon;
|
|
|
|
unsigned long ret = 0;
|
|
|
|
|
|
|
|
/* Don't lock: we're in enough trouble already. */
|
2008-01-14 16:55:03 +08:00
|
|
|
preempt_disable();
|
2005-04-17 06:20:36 +08:00
|
|
|
if ((colon = strchr(name, ':')) != NULL) {
|
|
|
|
*colon = '\0';
|
|
|
|
if ((mod = find_module(name)) != NULL)
|
|
|
|
ret = mod_find_symname(mod, colon+1);
|
|
|
|
*colon = ':';
|
|
|
|
} else {
|
|
|
|
list_for_each_entry(mod, &modules, list)
|
|
|
|
if ((ret = mod_find_symname(mod, name)) != 0)
|
|
|
|
break;
|
|
|
|
}
|
2008-01-14 16:55:03 +08:00
|
|
|
preempt_enable();
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_KALLSYMS */
|
|
|
|
|
|
|
|
/* Called by the /proc file system to return a list of modules. */
|
|
|
|
static void *m_start(struct seq_file *m, loff_t *pos)
|
|
|
|
{
|
2006-03-23 19:00:46 +08:00
|
|
|
mutex_lock(&module_mutex);
|
2007-07-16 14:39:54 +08:00
|
|
|
return seq_list_start(&modules, *pos);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void *m_next(struct seq_file *m, void *p, loff_t *pos)
|
|
|
|
{
|
2007-07-16 14:39:54 +08:00
|
|
|
return seq_list_next(p, &modules, pos);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void m_stop(struct seq_file *m, void *p)
|
|
|
|
{
|
2006-03-23 19:00:46 +08:00
|
|
|
mutex_unlock(&module_mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2008-01-26 04:08:33 +08:00
|
|
|
static char *module_flags(struct module *mod, char *buf)
|
2006-10-11 16:21:48 +08:00
|
|
|
{
|
|
|
|
int bx = 0;
|
|
|
|
|
2008-01-26 04:08:33 +08:00
|
|
|
if (mod->taints ||
|
|
|
|
mod->state == MODULE_STATE_GOING ||
|
|
|
|
mod->state == MODULE_STATE_COMING) {
|
2006-10-11 16:21:48 +08:00
|
|
|
buf[bx++] = '(';
|
2008-10-16 13:01:41 +08:00
|
|
|
if (mod->taints & (1 << TAINT_PROPRIETARY_MODULE))
|
2006-10-11 16:21:48 +08:00
|
|
|
buf[bx++] = 'P';
|
2008-10-16 13:01:41 +08:00
|
|
|
if (mod->taints & (1 << TAINT_FORCED_MODULE))
|
2006-10-11 16:21:48 +08:00
|
|
|
buf[bx++] = 'F';
|
|
|
|
/*
|
|
|
|
* TAINT_FORCED_RMMOD: could be added.
|
|
|
|
* TAINT_UNSAFE_SMP, TAINT_MACHINE_CHECK, TAINT_BAD_PAGE don't
|
|
|
|
* apply to modules.
|
|
|
|
*/
|
2008-01-26 04:08:33 +08:00
|
|
|
|
|
|
|
/* Show a - for module-is-being-unloaded */
|
|
|
|
if (mod->state == MODULE_STATE_GOING)
|
|
|
|
buf[bx++] = '-';
|
|
|
|
/* Show a + for module-is-being-loaded */
|
|
|
|
if (mod->state == MODULE_STATE_COMING)
|
|
|
|
buf[bx++] = '+';
|
2006-10-11 16:21:48 +08:00
|
|
|
buf[bx++] = ')';
|
|
|
|
}
|
|
|
|
buf[bx] = '\0';
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static int m_show(struct seq_file *m, void *p)
|
|
|
|
{
|
|
|
|
struct module *mod = list_entry(p, struct module, list);
|
2006-10-11 16:21:48 +08:00
|
|
|
char buf[8];
|
|
|
|
|
2008-07-23 08:24:27 +08:00
|
|
|
seq_printf(m, "%s %u",
|
2005-04-17 06:20:36 +08:00
|
|
|
mod->name, mod->init_size + mod->core_size);
|
|
|
|
print_unload_info(m, mod);
|
|
|
|
|
|
|
|
/* Informative for users. */
|
|
|
|
seq_printf(m, " %s",
|
|
|
|
mod->state == MODULE_STATE_GOING ? "Unloading":
|
|
|
|
mod->state == MODULE_STATE_COMING ? "Loading":
|
|
|
|
"Live");
|
|
|
|
/* Used by oprofile and other similar tools. */
|
|
|
|
seq_printf(m, " 0x%p", mod->module_core);
|
|
|
|
|
2006-10-11 16:21:48 +08:00
|
|
|
/* Taints info */
|
|
|
|
if (mod->taints)
|
2008-01-26 04:08:33 +08:00
|
|
|
seq_printf(m, " %s", module_flags(mod, buf));
|
2006-10-11 16:21:48 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
seq_printf(m, "\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Format: modulename size refcount deps address
|
|
|
|
|
|
|
|
Where refcount is a number or -, and deps is a comma-separated list
|
|
|
|
of depends or -.
|
|
|
|
*/
|
2006-12-07 12:40:36 +08:00
|
|
|
const struct seq_operations modules_op = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.start = m_start,
|
|
|
|
.next = m_next,
|
|
|
|
.stop = m_stop,
|
|
|
|
.show = m_show
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Given an address, look for it in the module exception tables. */
|
|
|
|
const struct exception_table_entry *search_module_extables(unsigned long addr)
|
|
|
|
{
|
|
|
|
const struct exception_table_entry *e = NULL;
|
|
|
|
struct module *mod;
|
|
|
|
|
2007-07-16 14:41:46 +08:00
|
|
|
preempt_disable();
|
2005-04-17 06:20:36 +08:00
|
|
|
list_for_each_entry(mod, &modules, list) {
|
|
|
|
if (mod->num_exentries == 0)
|
|
|
|
continue;
|
2007-10-18 18:06:07 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
e = search_extable(mod->extable,
|
|
|
|
mod->extable + mod->num_exentries - 1,
|
|
|
|
addr);
|
|
|
|
if (e)
|
|
|
|
break;
|
|
|
|
}
|
2007-07-16 14:41:46 +08:00
|
|
|
preempt_enable();
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* Now, if we found one, we are running inside it now, hence
|
2007-10-18 18:06:07 +08:00
|
|
|
we cannot unload the module, hence no refcnt needed. */
|
2005-04-17 06:20:36 +08:00
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
2006-07-03 15:24:24 +08:00
|
|
|
/*
|
|
|
|
* Is this a valid module address?
|
|
|
|
*/
|
|
|
|
int is_module_address(unsigned long addr)
|
|
|
|
{
|
|
|
|
struct module *mod;
|
|
|
|
|
2007-07-16 14:41:46 +08:00
|
|
|
preempt_disable();
|
2006-07-03 15:24:24 +08:00
|
|
|
|
|
|
|
list_for_each_entry(mod, &modules, list) {
|
|
|
|
if (within(addr, mod->module_core, mod->core_size)) {
|
2007-07-16 14:41:46 +08:00
|
|
|
preempt_enable();
|
2006-07-03 15:24:24 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-07-16 14:41:46 +08:00
|
|
|
preempt_enable();
|
2006-07-03 15:24:24 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-07-16 14:41:46 +08:00
|
|
|
/* Is this a valid kernel address? */
|
2005-04-17 06:20:36 +08:00
|
|
|
struct module *__module_text_address(unsigned long addr)
|
|
|
|
{
|
|
|
|
struct module *mod;
|
|
|
|
|
2008-07-23 08:24:28 +08:00
|
|
|
if (addr < module_addr_min || addr > module_addr_max)
|
|
|
|
return NULL;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
list_for_each_entry(mod, &modules, list)
|
|
|
|
if (within(addr, mod->module_init, mod->init_text_size)
|
|
|
|
|| within(addr, mod->module_core, mod->core_text_size))
|
|
|
|
return mod;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct module *module_text_address(unsigned long addr)
|
|
|
|
{
|
|
|
|
struct module *mod;
|
|
|
|
|
2007-07-16 14:41:46 +08:00
|
|
|
preempt_disable();
|
2005-04-17 06:20:36 +08:00
|
|
|
mod = __module_text_address(addr);
|
2007-07-16 14:41:46 +08:00
|
|
|
preempt_enable();
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
return mod;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Don't grab lock, we're oopsing. */
|
|
|
|
void print_modules(void)
|
|
|
|
{
|
|
|
|
struct module *mod;
|
2006-10-02 17:17:02 +08:00
|
|
|
char buf[8];
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
printk("Modules linked in:");
|
|
|
|
list_for_each_entry(mod, &modules, list)
|
2008-01-26 04:08:33 +08:00
|
|
|
printk(" %s%s", mod->name, module_flags(mod, buf));
|
2008-01-26 04:08:33 +08:00
|
|
|
if (last_unloaded_module[0])
|
|
|
|
printk(" [last unloaded: %s]", last_unloaded_module);
|
2005-04-17 06:20:36 +08:00
|
|
|
printk("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_MODVERSIONS
|
|
|
|
/* Generate the signature for struct module here, too, for modversions. */
|
|
|
|
void struct_module(struct module *mod) { return; }
|
|
|
|
EXPORT_SYMBOL(struct_module);
|
|
|
|
#endif
|
2007-10-19 14:41:06 +08:00
|
|
|
|
|
|
|
#ifdef CONFIG_MARKERS
|
Linux Kernel Markers: support multiple probes
RCU style multiple probes support for the Linux Kernel Markers. Common case
(one probe) is still fast and does not require dynamic allocation or a
supplementary pointer dereference on the fast path.
- Move preempt disable from the marker site to the callback.
Since we now have an internal callback, move the preempt disable/enable to the
callback instead of the marker site.
Since the callback change is done asynchronously (passing from a handler that
supports arguments to a handler that does not setup the arguments is no
arguments are passed), we can safely update it even if it is outside the
preempt disable section.
- Move probe arm to probe connection. Now, a connected probe is automatically
armed.
Remove MARK_MAX_FORMAT_LEN, unused.
This patch modifies the Linux Kernel Markers API : it removes the probe
"arm/disarm" and changes the probe function prototype : it now expects a
va_list * instead of a "...".
If we want to have more than one probe connected to a marker at a given
time (LTTng, or blktrace, ssytemtap) then we need this patch. Without it,
connecting a second probe handler to a marker will fail.
It allow us, for instance, to do interesting combinations :
Do standard tracing with LTTng and, eventually, to compute statistics
with SystemTAP, or to have a special trigger on an event that would call
a systemtap script which would stop flight recorder tracing.
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Mike Mason <mmlnx@us.ibm.com>
Cc: Dipankar Sarma <dipankar@in.ibm.com>
Cc: David Smith <dsmith@redhat.com>
Cc: "Paul E. McKenney" <paulmck@us.ibm.com>
Cc: "Frank Ch. Eigler" <fche@redhat.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-02-14 07:03:37 +08:00
|
|
|
void module_update_markers(void)
|
2007-10-19 14:41:06 +08:00
|
|
|
{
|
|
|
|
struct module *mod;
|
|
|
|
|
|
|
|
mutex_lock(&module_mutex);
|
|
|
|
list_for_each_entry(mod, &modules, list)
|
|
|
|
if (!mod->taints)
|
|
|
|
marker_update_probe_range(mod->markers,
|
Linux Kernel Markers: support multiple probes
RCU style multiple probes support for the Linux Kernel Markers. Common case
(one probe) is still fast and does not require dynamic allocation or a
supplementary pointer dereference on the fast path.
- Move preempt disable from the marker site to the callback.
Since we now have an internal callback, move the preempt disable/enable to the
callback instead of the marker site.
Since the callback change is done asynchronously (passing from a handler that
supports arguments to a handler that does not setup the arguments is no
arguments are passed), we can safely update it even if it is outside the
preempt disable section.
- Move probe arm to probe connection. Now, a connected probe is automatically
armed.
Remove MARK_MAX_FORMAT_LEN, unused.
This patch modifies the Linux Kernel Markers API : it removes the probe
"arm/disarm" and changes the probe function prototype : it now expects a
va_list * instead of a "...".
If we want to have more than one probe connected to a marker at a given
time (LTTng, or blktrace, ssytemtap) then we need this patch. Without it,
connecting a second probe handler to a marker will fail.
It allow us, for instance, to do interesting combinations :
Do standard tracing with LTTng and, eventually, to compute statistics
with SystemTAP, or to have a special trigger on an event that would call
a systemtap script which would stop flight recorder tracing.
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Mike Mason <mmlnx@us.ibm.com>
Cc: Dipankar Sarma <dipankar@in.ibm.com>
Cc: David Smith <dsmith@redhat.com>
Cc: "Paul E. McKenney" <paulmck@us.ibm.com>
Cc: "Frank Ch. Eigler" <fche@redhat.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-02-14 07:03:37 +08:00
|
|
|
mod->markers + mod->num_markers);
|
2007-10-19 14:41:06 +08:00
|
|
|
mutex_unlock(&module_mutex);
|
|
|
|
}
|
|
|
|
#endif
|