* [patch 0/2] Sort module list @ 2007-08-20 20:26 Mathieu Desnoyers 2007-08-20 20:26 ` [patch 1/2] Seq_file add support for sorted list Mathieu Desnoyers 2007-08-20 20:26 ` [patch 2/2] Sort module list by pointer address to get coherent sleepable seq_file iterators Mathieu Desnoyers 0 siblings, 2 replies; 8+ messages in thread From: Mathieu Desnoyers @ 2007-08-20 20:26 UTC (permalink / raw) To: akpm, linux-kernel Hi Andrew, Here is a repost of the "sort module list patch", needed by the Linux Kernel Markers. Applies to 2.6.23-rc2-mm2, in the following order: seq_file_sorted.patch module.c-sort-module-list.patch Mathieu -- Mathieu Desnoyers Computer Engineering Ph.D. Student, Ecole Polytechnique de Montreal OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68 ^ permalink raw reply [flat|nested] 8+ messages in thread
* [patch 1/2] Seq_file add support for sorted list 2007-08-20 20:26 [patch 0/2] Sort module list Mathieu Desnoyers @ 2007-08-20 20:26 ` Mathieu Desnoyers 2007-08-20 20:26 ` [patch 2/2] Sort module list by pointer address to get coherent sleepable seq_file iterators Mathieu Desnoyers 1 sibling, 0 replies; 8+ messages in thread From: Mathieu Desnoyers @ 2007-08-20 20:26 UTC (permalink / raw) To: akpm, linux-kernel; +Cc: Mathieu Desnoyers [-- Attachment #1: seq_file_sorted.patch --] [-- Type: text/plain, Size: 3172 bytes --] Add support for sorted list in seq_file. It aims at changing the way /proc/modules and kallsyms iterates on the module list to remove a race between module unload and module/symbol listing. The list is sorted by ascending list_head pointer address. Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca> --- fs/seq_file.c | 40 ++++++++++++++++++++++++++++++++++++++++ include/linux/seq_file.h | 20 ++++++++++++++++++++ 2 files changed, 60 insertions(+) Index: linux-2.6-lttng/fs/seq_file.c =================================================================== --- linux-2.6-lttng.orig/fs/seq_file.c 2007-07-25 01:40:13.000000000 -0400 +++ linux-2.6-lttng/fs/seq_file.c 2007-07-25 02:47:55.000000000 -0400 @@ -485,3 +485,43 @@ struct list_head *seq_list_next(void *v, } EXPORT_SYMBOL(seq_list_next); + +struct list_head *seq_sorted_list_start(struct list_head *head, void *pos) +{ + struct list_head *lh; + + list_for_each(lh, head) + if ((void*)lh >= pos) + return lh; + return NULL; +} + +EXPORT_SYMBOL(seq_sorted_list_start); + +struct list_head *seq_sorted_list_start_head(struct list_head *head, void *pos) +{ + struct list_head *lh; + + if (!pos) + return head; + list_for_each(lh, head) + if ((void*)lh >= pos) + return lh->prev; + return NULL; +} + +EXPORT_SYMBOL(seq_sorted_list_start_head); + +struct list_head *seq_sorted_list_next(void *p, struct list_head *head, + void **ppos) +{ + struct list_head *lh; + void *next; + + lh = ((struct list_head *)p)->next; + next = (lh == head) ? NULL : lh; + *ppos = next ? next : (void*)-1UL; + return next; +} + +EXPORT_SYMBOL(seq_sorted_list_next); Index: linux-2.6-lttng/include/linux/seq_file.h =================================================================== --- linux-2.6-lttng.orig/include/linux/seq_file.h 2007-07-25 01:40:13.000000000 -0400 +++ linux-2.6-lttng/include/linux/seq_file.h 2007-07-25 02:41:22.000000000 -0400 @@ -61,5 +61,25 @@ extern struct list_head *seq_list_start_ extern struct list_head *seq_list_next(void *v, struct list_head *head, loff_t *ppos); +/* + * Helpers for iteration over a list sorted by ascending head pointer address. + * To be used in contexts where preemption cannot be disabled to insure to + * continue iteration on a modified list starting at the same location where it + * stopped, or at a following location. It insures that the lost information + * will only be in elements added/removed from the list between iterations. + * void *pos is only used to get the next list element and may not be a valid + * list_head anymore when given to seq_sorted_list_start() or + * seq_sorted_list_start_head(). + */ +extern struct list_head *seq_sorted_list_start(struct list_head *head, + void *pos); +extern struct list_head *seq_sorted_list_start_head(struct list_head *head, + void *pos); +/* + * next must be called with an existing p node + */ +extern struct list_head *seq_sorted_list_next(void *p, struct list_head *head, + void **ppos); + #endif #endif -- Mathieu Desnoyers Computer Engineering Ph.D. Student, Ecole Polytechnique de Montreal OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68 ^ permalink raw reply [flat|nested] 8+ messages in thread
* [patch 2/2] Sort module list by pointer address to get coherent sleepable seq_file iterators 2007-08-20 20:26 [patch 0/2] Sort module list Mathieu Desnoyers 2007-08-20 20:26 ` [patch 1/2] Seq_file add support for sorted list Mathieu Desnoyers @ 2007-08-20 20:26 ` Mathieu Desnoyers 2007-08-21 0:08 ` Rusty Russell 1 sibling, 1 reply; 8+ messages in thread From: Mathieu Desnoyers @ 2007-08-20 20:26 UTC (permalink / raw) To: akpm, linux-kernel; +Cc: Mathieu Desnoyers [-- Attachment #1: module.c-sort-module-list.patch --] [-- Type: text/plain, Size: 3510 bytes --] A race that appears both in /proc/modules and in kallsyms: if, between the seq file reads, the process is put to sleep and at this moment a module is or removed from the module list, the listing will skip an amount of modules/symbols corresponding to the amount of elements present in the unloaded module, but at the current position in the list if the iteration is located after the removed module. The cleanest way I found to deal with this problem is to sort the module list. We can then keep the old struct module * as the old iterator, knowing the it may be removed between the seq file reads, but we only use it as "get next". If it is not present in the module list, the next pointer will be used. By doing this, removing a given module will now only fuzz the output related to this specific module, not any random module anymore. Since modprobe uses /proc/modules, it might be important to make sure multiple concurrent running modprobes won't interfere with each other. Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca> --- kernel/module.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) Index: linux-2.6-lttng/kernel/module.c =================================================================== --- linux-2.6-lttng.orig/kernel/module.c 2007-08-12 10:05:39.000000000 -0400 +++ linux-2.6-lttng/kernel/module.c 2007-08-12 10:22:05.000000000 -0400 @@ -63,8 +63,10 @@ extern int module_sysfs_initialized; /* If this is set, the section belongs in the init part of the module */ #define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1)) -/* List of modules, protected by module_mutex or preempt_disable - * (add/delete uses stop_machine). */ +/* + * List of modules, protected by module_mutex or preempt_disable + * (add/delete uses stop_machine). Sorted by ascending list node address. + */ DEFINE_MUTEX(module_mutex); LIST_HEAD(modules); static DECLARE_MUTEX(notify_mutex); @@ -2134,10 +2136,24 @@ nomodsectinfo: /* * link the module with the whole machine is stopped with interrupts off * - this defends against kallsyms not taking locks + * We sort the modules by struct module pointer address to permit correct + * iteration over modules of, at least, kallsyms for preemptible operations, + * such as read(). Sorting by struct module pointer address is equivalent to + * sort by list node address. */ static int __link_module(void *_mod) { - struct module *mod = _mod; + struct module *mod = _mod, *iter; + + list_for_each_entry_reverse(iter, &modules, list) { + BUG_ON(iter == mod); /* Should never be in the list twice */ + if (iter < mod) { + /* We belong to the location right after iter. */ + list_add(&mod->list, &iter->list); + return 0; + } + } + /* We should be added at the head of the list */ list_add(&mod->list, &modules); return 0; } @@ -2402,12 +2418,14 @@ unsigned long module_kallsyms_lookup_nam static void *m_start(struct seq_file *m, loff_t *pos) { mutex_lock(&module_mutex); - return seq_list_start(&modules, *pos); + if (!*pos) + m->private = NULL; + return seq_sorted_list_start(&modules, m->private); } static void *m_next(struct seq_file *m, void *p, loff_t *pos) { - return seq_list_next(p, &modules, pos); + return seq_sorted_list_next(p, &modules, &m->private); } static void m_stop(struct seq_file *m, void *p) -- Mathieu Desnoyers Computer Engineering Ph.D. Student, Ecole Polytechnique de Montreal OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68 ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [patch 2/2] Sort module list by pointer address to get coherent sleepable seq_file iterators 2007-08-20 20:26 ` [patch 2/2] Sort module list by pointer address to get coherent sleepable seq_file iterators Mathieu Desnoyers @ 2007-08-21 0:08 ` Rusty Russell 2007-08-24 15:45 ` Mathieu Desnoyers 0 siblings, 1 reply; 8+ messages in thread From: Rusty Russell @ 2007-08-21 0:08 UTC (permalink / raw) To: Mathieu Desnoyers; +Cc: akpm, linux-kernel On Mon, 2007-08-20 at 16:26 -0400, Mathieu Desnoyers wrote: > plain text document attachment (module.c-sort-module-list.patch) > A race that appears both in /proc/modules and in kallsyms: if, between the > seq file reads, the process is put to sleep and at this moment a module is > or removed from the module list, the listing will skip an amount of > modules/symbols corresponding to the amount of elements present in the unloaded > module, but at the current position in the list if the iteration is located > after the removed module. > > The cleanest way I found to deal with this problem is to sort the module list. > We can then keep the old struct module * as the old iterator, knowing the it may > be removed between the seq file reads, but we only use it as "get next". If it > is not present in the module list, the next pointer will be used. > > By doing this, removing a given module will now only fuzz the output related to > this specific module, not any random module anymore. Since modprobe uses > /proc/modules, it might be important to make sure multiple concurrent running > modprobes won't interfere with each other. You've reduced, but not eliminated, the problem. A new module inserted is quite likely to reuse the same address. I don't have a real problem with this patch, but I'm wondering if the problem is theoretical or demonstrated. Rusty. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [patch 2/2] Sort module list by pointer address to get coherent sleepable seq_file iterators 2007-08-21 0:08 ` Rusty Russell @ 2007-08-24 15:45 ` Mathieu Desnoyers 2007-08-25 21:44 ` Rusty Russell 0 siblings, 1 reply; 8+ messages in thread From: Mathieu Desnoyers @ 2007-08-24 15:45 UTC (permalink / raw) To: Rusty Russell; +Cc: akpm, linux-kernel * Rusty Russell (rusty@rustcorp.com.au) wrote: > On Mon, 2007-08-20 at 16:26 -0400, Mathieu Desnoyers wrote: > > plain text document attachment (module.c-sort-module-list.patch) > > A race that appears both in /proc/modules and in kallsyms: if, between the > > seq file reads, the process is put to sleep and at this moment a module is > > or removed from the module list, the listing will skip an amount of > > modules/symbols corresponding to the amount of elements present in the unloaded > > module, but at the current position in the list if the iteration is located > > after the removed module. > > > > The cleanest way I found to deal with this problem is to sort the module list. > > We can then keep the old struct module * as the old iterator, knowing the it may > > be removed between the seq file reads, but we only use it as "get next". If it > > is not present in the module list, the next pointer will be used. > > > > By doing this, removing a given module will now only fuzz the output related to > > this specific module, not any random module anymore. Since modprobe uses > > /proc/modules, it might be important to make sure multiple concurrent running > > modprobes won't interfere with each other. > > You've reduced, but not eliminated, the problem. A new module inserted > is quite likely to reuse the same address. > Hi Rusty, Please tell me if I'm wrong, but I think it would not be a problem: - seq_read() makes sure that a buffer large enough is available so that m_show() can fully extract and print the information relative to 1 module. - m_start() and m_stop() takes the module_mutex, therefore within one seq_read(), once m_start has returned, the struct module * that we have is valid and will be consistent during the whole seq_read operation. - If a module is removed, and then a different one is inserted at the same address, while we are between two seq_reads for this given struct module address, the seq_reads will copy to user-space the information that is still in the buffer for the _first_ struct module encountered, not the new one. - After that, iteration will continue to the new struct module address, effectively skipping the newly inserted module. > I don't have a real problem with this patch, but I'm wondering if the > problem is theoretical or demonstrated. > Small test module for this: #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #define BUFSIZE 1024 int main() { int fd = open("/proc/modules", O_RDONLY); char buf[BUFSIZE]; ssize_t size; do { size = read(fd, buf, 1); printf("%c", buf[0]); usleep(100000); } while(size > 0); close(fd); return 0; } (compiled as ./module) Actual test (kernel 2.6.23-rc3): dijkstra:~# lsmod Module Size Used by pl2303 18564 0 usbserial 29032 1 pl2303 ppdev 7844 0 sky2 37476 0 skge 36368 0 rtc 10104 0 snd_hda_intel 265628 0 (here, while we are printing the 2nd line, I rmmod pl2303) compudj@dijkstra:~/test$ ./module pl2303 18564 0 - Live 0xf886e000 usbserial 29032 1 pl2303, Live 0xf8865000 sky2 37476 0 - Live 0xf884f000 skge 36368 0 - Live 0xf8838000 rtc 10104 0 - Live 0xf8825000 We see the the 2nd line is garbage. Now, with my patch applied: (here, while we are printing the rtc module, I rmmod rtc) nd_hda_intel 268708 0 - Live 0xf8820000 ltt_control 2372 0 - Live 0xf8866000 rtc 10392 0 - Live 0xf886d000 skge 36768 0 - Live 0xf8871000 ltt_statedump 8516 0 - Live 0xf887b000 We see that since the rtc line was already in the buffer, it has been printed completely. (here, while we are printing the skge module, I rmmod rtc) snd_hda_intel 268708 0 - Live 0xf8820000 ltt_control 2372 0 - Live 0xf8866000 rtc 10392 0 - Live 0xf886d000 skge 36768 0 - Live 0xf8871000 ltt_statedump 8516 0 - Live 0xf887b000 sky2 38420 0 - Live 0xf88cd000 We see that the iteration continued at the same position even though the rtc module, located in earlier addresses, was removed. Note that this test is done with the "Sort modules list - use ppos instead of m->private" patch applied. Thanks for the review, Mathieu > Rusty. > > -- Mathieu Desnoyers Computer Engineering Ph.D. Student, Ecole Polytechnique de Montreal OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68 ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [patch 2/2] Sort module list by pointer address to get coherent sleepable seq_file iterators 2007-08-24 15:45 ` Mathieu Desnoyers @ 2007-08-25 21:44 ` Rusty Russell 2007-08-25 21:53 ` Mathieu Desnoyers 0 siblings, 1 reply; 8+ messages in thread From: Rusty Russell @ 2007-08-25 21:44 UTC (permalink / raw) To: Mathieu Desnoyers; +Cc: akpm, linux-kernel On Fri, 2007-08-24 at 11:45 -0400, Mathieu Desnoyers wrote: > * Rusty Russell (rusty@rustcorp.com.au) wrote: > > On Mon, 2007-08-20 at 16:26 -0400, Mathieu Desnoyers wrote: > > > plain text document attachment (module.c-sort-module-list.patch) > > > A race that appears both in /proc/modules and in kallsyms: if, between the > > > seq file reads, the process is put to sleep and at this moment a module is > > > or removed from the module list, the listing will skip an amount of > > > modules/symbols corresponding to the amount of elements present in the unloaded > > > module, but at the current position in the list if the iteration is located > > > after the removed module. > > > > > > The cleanest way I found to deal with this problem is to sort the module list. > > > We can then keep the old struct module * as the old iterator, knowing the it may > > > be removed between the seq file reads, but we only use it as "get next". If it > > > is not present in the module list, the next pointer will be used. > > > > > > By doing this, removing a given module will now only fuzz the output related to > > > this specific module, not any random module anymore. Since modprobe uses > > > /proc/modules, it might be important to make sure multiple concurrent running > > > modprobes won't interfere with each other. > > > > You've reduced, but not eliminated, the problem. A new module inserted > > is quite likely to reuse the same address. > > > > Hi Rusty, > > Please tell me if I'm wrong, but I think it would not be a problem: > > - seq_read() makes sure that a buffer large enough is available so that > m_show() can fully extract and print the information relative to 1 > module. > - m_start() and m_stop() takes the module_mutex, therefore within one > seq_read(), once m_start has returned, the struct module * that we > have is valid and will be consistent during the whole seq_read > operation. > - If a module is removed, and then a different one is inserted at the > same address, while we are between two seq_reads for this given struct > module address, the seq_reads will copy to user-space the information > that is still in the buffer for the _first_ struct module encountered, > not the new one. > - After that, iteration will continue to the new struct module address, > effectively skipping the newly inserted module. Indeed, I thought that this was a general problem: the seq_list code was never intended to work on modifiable lists unless you get them in one big read. If we accept this problem, what do we do about all the other users? Rusty. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [patch 2/2] Sort module list by pointer address to get coherent sleepable seq_file iterators 2007-08-25 21:44 ` Rusty Russell @ 2007-08-25 21:53 ` Mathieu Desnoyers 0 siblings, 0 replies; 8+ messages in thread From: Mathieu Desnoyers @ 2007-08-25 21:53 UTC (permalink / raw) To: Rusty Russell; +Cc: akpm, linux-kernel * Rusty Russell (rusty@rustcorp.com.au) wrote: > On Fri, 2007-08-24 at 11:45 -0400, Mathieu Desnoyers wrote: > > * Rusty Russell (rusty@rustcorp.com.au) wrote: > > > On Mon, 2007-08-20 at 16:26 -0400, Mathieu Desnoyers wrote: > > > > plain text document attachment (module.c-sort-module-list.patch) > > > > A race that appears both in /proc/modules and in kallsyms: if, between the > > > > seq file reads, the process is put to sleep and at this moment a module is > > > > or removed from the module list, the listing will skip an amount of > > > > modules/symbols corresponding to the amount of elements present in the unloaded > > > > module, but at the current position in the list if the iteration is located > > > > after the removed module. > > > > > > > > The cleanest way I found to deal with this problem is to sort the module list. > > > > We can then keep the old struct module * as the old iterator, knowing the it may > > > > be removed between the seq file reads, but we only use it as "get next". If it > > > > is not present in the module list, the next pointer will be used. > > > > > > > > By doing this, removing a given module will now only fuzz the output related to > > > > this specific module, not any random module anymore. Since modprobe uses > > > > /proc/modules, it might be important to make sure multiple concurrent running > > > > modprobes won't interfere with each other. > > > > > > You've reduced, but not eliminated, the problem. A new module inserted > > > is quite likely to reuse the same address. > > > > > > > Hi Rusty, > > > > Please tell me if I'm wrong, but I think it would not be a problem: > > > > - seq_read() makes sure that a buffer large enough is available so that > > m_show() can fully extract and print the information relative to 1 > > module. > > - m_start() and m_stop() takes the module_mutex, therefore within one > > seq_read(), once m_start has returned, the struct module * that we > > have is valid and will be consistent during the whole seq_read > > operation. > > - If a module is removed, and then a different one is inserted at the > > same address, while we are between two seq_reads for this given struct > > module address, the seq_reads will copy to user-space the information > > that is still in the buffer for the _first_ struct module encountered, > > not the new one. > > - After that, iteration will continue to the new struct module address, > > effectively skipping the newly inserted module. > > Indeed, I thought that this was a general problem: the seq_list code was > never intended to work on modifiable lists unless you get them in one > big read. > > If we accept this problem, what do we do about all the other users? > Hum, I guess it would be best for them to switch to the proposed seq sorted list too. I think that having one example (module.c) that shows well how this works will be an incentive for other developers to port their seq_file code to the sorted list (I am thinking, among others, about kallsyms). Mathieu > Rusty. > -- Mathieu Desnoyers Computer Engineering Ph.D. Student, Ecole Polytechnique de Montreal OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68 ^ permalink raw reply [flat|nested] 8+ messages in thread
* [patch 0/2] Sort module list @ 2007-09-17 18:43 Mathieu Desnoyers 0 siblings, 0 replies; 8+ messages in thread From: Mathieu Desnoyers @ 2007-09-17 18:43 UTC (permalink / raw) To: akpm, linux-kernel Hi Andrew, Here is the updated sorted module list, required by the linux kernel markers. It applies to 2.6.23-rc4-mm1 in this order: seq_file_sorted.patch module.c-sort-module-list.patch Mathieu -- Mathieu Desnoyers Computer Engineering Ph.D. Student, Ecole Polytechnique de Montreal OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68 ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2007-09-17 18:46 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2007-08-20 20:26 [patch 0/2] Sort module list Mathieu Desnoyers 2007-08-20 20:26 ` [patch 1/2] Seq_file add support for sorted list Mathieu Desnoyers 2007-08-20 20:26 ` [patch 2/2] Sort module list by pointer address to get coherent sleepable seq_file iterators Mathieu Desnoyers 2007-08-21 0:08 ` Rusty Russell 2007-08-24 15:45 ` Mathieu Desnoyers 2007-08-25 21:44 ` Rusty Russell 2007-08-25 21:53 ` Mathieu Desnoyers -- strict thread matches above, loose matches on Subject: below -- 2007-09-17 18:43 [patch 0/2] Sort module list Mathieu Desnoyers
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox