Commit 1cc523271ef0b6305c565a143e3d48f6fff826dd
Committed by
David S. Miller
1 parent
35e2da46d2
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>
Showing 3 changed files with 87 additions and 4 deletions Side-by-side Diff
fs/seq_file.c
... | ... | @@ -750,4 +750,75 @@ |
750 | 750 | return node->next; |
751 | 751 | } |
752 | 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); |
include/linux/rculist.h
... | ... | @@ -406,6 +406,11 @@ |
406 | 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 | 415 | * hlist_for_each_entry_rcu - iterate over rcu list of given type |
411 | 416 | * @tpos: the type * to use as a loop cursor. |
include/linux/seq_file.h
... | ... | @@ -140,11 +140,18 @@ |
140 | 140 | */ |
141 | 141 | |
142 | 142 | extern struct hlist_node *seq_hlist_start(struct hlist_head *head, |
143 | - loff_t pos); | |
143 | + loff_t pos); | |
144 | 144 | extern struct hlist_node *seq_hlist_start_head(struct hlist_head *head, |
145 | - loff_t pos); | |
145 | + loff_t pos); | |
146 | 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 | 156 | #endif |