diff options
author | stephen hemminger <shemminger@vyatta.com> | 2010-02-22 02:57:17 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-02-22 18:45:54 -0500 |
commit | 1cc523271ef0b6305c565a143e3d48f6fff826dd (patch) | |
tree | 8acfcce42d5d81e4ce993c078985bfc3789bf2c5 | |
parent | 35e2da46d25a53e0e19956f533cc29272a6cceb2 (diff) |
seq_file: add RCU versions of new hlist/list iterators (v3)
Many usages of seq_file use RCU protected lists, so non RCU
iterators will not work safely.
Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | fs/seq_file.c | 71 | ||||
-rw-r--r-- | include/linux/rculist.h | 5 | ||||
-rw-r--r-- | include/linux/seq_file.h | 15 |
3 files changed, 87 insertions, 4 deletions
diff --git a/fs/seq_file.c b/fs/seq_file.c index f65b16f02da3..5afd554efad3 100644 --- a/fs/seq_file.c +++ b/fs/seq_file.c | |||
@@ -750,3 +750,74 @@ struct hlist_node *seq_hlist_next(void *v, struct hlist_head *head, | |||
750 | return node->next; | 750 | return node->next; |
751 | } | 751 | } |
752 | EXPORT_SYMBOL(seq_hlist_next); | 752 | EXPORT_SYMBOL(seq_hlist_next); |
753 | |||
754 | /** | ||
755 | * seq_hlist_start_rcu - start an iteration of a hlist protected by RCU | ||
756 | * @head: the head of the hlist | ||
757 | * @pos: the start position of the sequence | ||
758 | * | ||
759 | * Called at seq_file->op->start(). | ||
760 | * | ||
761 | * This list-traversal primitive may safely run concurrently with | ||
762 | * the _rcu list-mutation primitives such as hlist_add_head_rcu() | ||
763 | * as long as the traversal is guarded by rcu_read_lock(). | ||
764 | */ | ||
765 | struct hlist_node *seq_hlist_start_rcu(struct hlist_head *head, | ||
766 | loff_t pos) | ||
767 | { | ||
768 | struct hlist_node *node; | ||
769 | |||
770 | __hlist_for_each_rcu(node, head) | ||
771 | if (pos-- == 0) | ||
772 | return node; | ||
773 | return NULL; | ||
774 | } | ||
775 | EXPORT_SYMBOL(seq_hlist_start_rcu); | ||
776 | |||
777 | /** | ||
778 | * seq_hlist_start_head_rcu - start an iteration of a hlist protected by RCU | ||
779 | * @head: the head of the hlist | ||
780 | * @pos: the start position of the sequence | ||
781 | * | ||
782 | * Called at seq_file->op->start(). Call this function if you want to | ||
783 | * print a header at the top of the output. | ||
784 | * | ||
785 | * This list-traversal primitive may safely run concurrently with | ||
786 | * the _rcu list-mutation primitives such as hlist_add_head_rcu() | ||
787 | * as long as the traversal is guarded by rcu_read_lock(). | ||
788 | */ | ||
789 | struct hlist_node *seq_hlist_start_head_rcu(struct hlist_head *head, | ||
790 | loff_t pos) | ||
791 | { | ||
792 | if (!pos) | ||
793 | return SEQ_START_TOKEN; | ||
794 | |||
795 | return seq_hlist_start_rcu(head, pos - 1); | ||
796 | } | ||
797 | EXPORT_SYMBOL(seq_hlist_start_head_rcu); | ||
798 | |||
799 | /** | ||
800 | * seq_hlist_next_rcu - move to the next position of the hlist protected by RCU | ||
801 | * @v: the current iterator | ||
802 | * @head: the head of the hlist | ||
803 | * @pos: the current posision | ||
804 | * | ||
805 | * Called at seq_file->op->next(). | ||
806 | * | ||
807 | * This list-traversal primitive may safely run concurrently with | ||
808 | * the _rcu list-mutation primitives such as hlist_add_head_rcu() | ||
809 | * as long as the traversal is guarded by rcu_read_lock(). | ||
810 | */ | ||
811 | struct hlist_node *seq_hlist_next_rcu(void *v, | ||
812 | struct hlist_head *head, | ||
813 | loff_t *ppos) | ||
814 | { | ||
815 | struct hlist_node *node = v; | ||
816 | |||
817 | ++*ppos; | ||
818 | if (v == SEQ_START_TOKEN) | ||
819 | return rcu_dereference(head->first); | ||
820 | else | ||
821 | return rcu_dereference(node->next); | ||
822 | } | ||
823 | EXPORT_SYMBOL(seq_hlist_next_rcu); | ||
diff --git a/include/linux/rculist.h b/include/linux/rculist.h index 1bf0f708c4fc..701fe9cb552a 100644 --- a/include/linux/rculist.h +++ b/include/linux/rculist.h | |||
@@ -406,6 +406,11 @@ static inline void hlist_add_after_rcu(struct hlist_node *prev, | |||
406 | n->next->pprev = &n->next; | 406 | n->next->pprev = &n->next; |
407 | } | 407 | } |
408 | 408 | ||
409 | #define __hlist_for_each_rcu(pos, head) \ | ||
410 | for (pos = rcu_dereference((head)->first); \ | ||
411 | pos && ({ prefetch(pos->next); 1; }); \ | ||
412 | pos = rcu_dereference(pos->next)) | ||
413 | |||
409 | /** | 414 | /** |
410 | * hlist_for_each_entry_rcu - iterate over rcu list of given type | 415 | * hlist_for_each_entry_rcu - iterate over rcu list of given type |
411 | * @tpos: the type * to use as a loop cursor. | 416 | * @tpos: the type * to use as a loop cursor. |
diff --git a/include/linux/seq_file.h b/include/linux/seq_file.h index c95bcdc18f4c..03c0232b4169 100644 --- a/include/linux/seq_file.h +++ b/include/linux/seq_file.h | |||
@@ -140,10 +140,17 @@ extern struct list_head *seq_list_next(void *v, struct list_head *head, | |||
140 | */ | 140 | */ |
141 | 141 | ||
142 | extern struct hlist_node *seq_hlist_start(struct hlist_head *head, | 142 | extern struct hlist_node *seq_hlist_start(struct hlist_head *head, |
143 | loff_t pos); | 143 | loff_t pos); |
144 | extern struct hlist_node *seq_hlist_start_head(struct hlist_head *head, | 144 | extern struct hlist_node *seq_hlist_start_head(struct hlist_head *head, |
145 | loff_t pos); | 145 | loff_t pos); |
146 | extern struct hlist_node *seq_hlist_next(void *v, struct hlist_head *head, | 146 | extern struct hlist_node *seq_hlist_next(void *v, struct hlist_head *head, |
147 | loff_t *ppos); | 147 | loff_t *ppos); |
148 | 148 | ||
149 | extern struct hlist_node *seq_hlist_start_rcu(struct hlist_head *head, | ||
150 | loff_t pos); | ||
151 | extern struct hlist_node *seq_hlist_start_head_rcu(struct hlist_head *head, | ||
152 | loff_t pos); | ||
153 | extern struct hlist_node *seq_hlist_next_rcu(void *v, | ||
154 | struct hlist_head *head, | ||
155 | loff_t *ppos); | ||
149 | #endif | 156 | #endif |