diff options
| -rw-r--r-- | net/netlink/af_netlink.c | 130 |
1 files changed, 64 insertions, 66 deletions
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index a36777b7cfb6..155854802d44 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c | |||
| @@ -2886,99 +2886,97 @@ EXPORT_SYMBOL(nlmsg_notify); | |||
| 2886 | #ifdef CONFIG_PROC_FS | 2886 | #ifdef CONFIG_PROC_FS |
| 2887 | struct nl_seq_iter { | 2887 | struct nl_seq_iter { |
| 2888 | struct seq_net_private p; | 2888 | struct seq_net_private p; |
| 2889 | struct rhashtable_iter hti; | ||
| 2889 | int link; | 2890 | int link; |
| 2890 | int hash_idx; | ||
| 2891 | }; | 2891 | }; |
| 2892 | 2892 | ||
| 2893 | static struct sock *netlink_seq_socket_idx(struct seq_file *seq, loff_t pos) | 2893 | static int netlink_walk_start(struct nl_seq_iter *iter) |
| 2894 | { | 2894 | { |
| 2895 | struct nl_seq_iter *iter = seq->private; | 2895 | int err; |
| 2896 | int i, j; | ||
| 2897 | struct netlink_sock *nlk; | ||
| 2898 | struct sock *s; | ||
| 2899 | loff_t off = 0; | ||
| 2900 | |||
| 2901 | for (i = 0; i < MAX_LINKS; i++) { | ||
| 2902 | struct rhashtable *ht = &nl_table[i].hash; | ||
| 2903 | const struct bucket_table *tbl = rht_dereference_rcu(ht->tbl, ht); | ||
| 2904 | |||
| 2905 | for (j = 0; j < tbl->size; j++) { | ||
| 2906 | struct rhash_head *node; | ||
| 2907 | |||
| 2908 | rht_for_each_entry_rcu(nlk, node, tbl, j, node) { | ||
| 2909 | s = (struct sock *)nlk; | ||
| 2910 | 2896 | ||
| 2911 | if (sock_net(s) != seq_file_net(seq)) | 2897 | err = rhashtable_walk_init(&nl_table[iter->link].hash, &iter->hti); |
| 2912 | continue; | 2898 | if (err) { |
| 2913 | if (off == pos) { | 2899 | iter->link = MAX_LINKS; |
| 2914 | iter->link = i; | 2900 | return err; |
| 2915 | iter->hash_idx = j; | ||
| 2916 | return s; | ||
| 2917 | } | ||
| 2918 | ++off; | ||
| 2919 | } | ||
| 2920 | } | ||
| 2921 | } | 2901 | } |
| 2922 | return NULL; | 2902 | |
| 2903 | err = rhashtable_walk_start(&iter->hti); | ||
| 2904 | return err == -EAGAIN ? 0 : err; | ||
| 2923 | } | 2905 | } |
| 2924 | 2906 | ||
| 2925 | static void *netlink_seq_start(struct seq_file *seq, loff_t *pos) | 2907 | static void netlink_walk_stop(struct nl_seq_iter *iter) |
| 2926 | __acquires(RCU) | ||
| 2927 | { | 2908 | { |
| 2928 | rcu_read_lock(); | 2909 | rhashtable_walk_stop(&iter->hti); |
| 2929 | return *pos ? netlink_seq_socket_idx(seq, *pos - 1) : SEQ_START_TOKEN; | 2910 | rhashtable_walk_exit(&iter->hti); |
| 2930 | } | 2911 | } |
| 2931 | 2912 | ||
| 2932 | static void *netlink_seq_next(struct seq_file *seq, void *v, loff_t *pos) | 2913 | static void *__netlink_seq_next(struct seq_file *seq) |
| 2933 | { | 2914 | { |
| 2934 | struct rhashtable *ht; | 2915 | struct nl_seq_iter *iter = seq->private; |
| 2935 | const struct bucket_table *tbl; | ||
| 2936 | struct rhash_head *node; | ||
| 2937 | struct netlink_sock *nlk; | 2916 | struct netlink_sock *nlk; |
| 2938 | struct nl_seq_iter *iter; | ||
| 2939 | struct net *net; | ||
| 2940 | int i, j; | ||
| 2941 | 2917 | ||
| 2942 | ++*pos; | 2918 | do { |
| 2919 | for (;;) { | ||
| 2920 | int err; | ||
| 2943 | 2921 | ||
| 2944 | if (v == SEQ_START_TOKEN) | 2922 | nlk = rhashtable_walk_next(&iter->hti); |
| 2945 | return netlink_seq_socket_idx(seq, 0); | ||
| 2946 | 2923 | ||
| 2947 | net = seq_file_net(seq); | 2924 | if (IS_ERR(nlk)) { |
| 2948 | iter = seq->private; | 2925 | if (PTR_ERR(nlk) == -EAGAIN) |
| 2949 | nlk = v; | 2926 | continue; |
| 2950 | 2927 | ||
| 2951 | i = iter->link; | 2928 | return nlk; |
| 2952 | ht = &nl_table[i].hash; | 2929 | } |
| 2953 | tbl = rht_dereference_rcu(ht->tbl, ht); | ||
| 2954 | rht_for_each_entry_rcu_continue(nlk, node, nlk->node.next, tbl, iter->hash_idx, node) | ||
| 2955 | if (net_eq(sock_net((struct sock *)nlk), net)) | ||
| 2956 | return nlk; | ||
| 2957 | 2930 | ||
| 2958 | j = iter->hash_idx + 1; | 2931 | if (nlk) |
| 2932 | break; | ||
| 2959 | 2933 | ||
| 2960 | do { | 2934 | netlink_walk_stop(iter); |
| 2935 | if (++iter->link >= MAX_LINKS) | ||
| 2936 | return NULL; | ||
| 2961 | 2937 | ||
| 2962 | for (; j < tbl->size; j++) { | 2938 | err = netlink_walk_start(iter); |
| 2963 | rht_for_each_entry_rcu(nlk, node, tbl, j, node) { | 2939 | if (err) |
| 2964 | if (net_eq(sock_net((struct sock *)nlk), net)) { | 2940 | return ERR_PTR(err); |
| 2965 | iter->link = i; | ||
| 2966 | iter->hash_idx = j; | ||
| 2967 | return nlk; | ||
| 2968 | } | ||
| 2969 | } | ||
| 2970 | } | 2941 | } |
| 2942 | } while (sock_net(&nlk->sk) != seq_file_net(seq)); | ||
| 2971 | 2943 | ||
| 2972 | j = 0; | 2944 | return nlk; |
| 2973 | } while (++i < MAX_LINKS); | 2945 | } |
| 2974 | 2946 | ||
| 2975 | return NULL; | 2947 | static void *netlink_seq_start(struct seq_file *seq, loff_t *posp) |
| 2948 | { | ||
| 2949 | struct nl_seq_iter *iter = seq->private; | ||
| 2950 | void *obj = SEQ_START_TOKEN; | ||
| 2951 | loff_t pos; | ||
| 2952 | int err; | ||
| 2953 | |||
| 2954 | iter->link = 0; | ||
| 2955 | |||
| 2956 | err = netlink_walk_start(iter); | ||
| 2957 | if (err) | ||
| 2958 | return ERR_PTR(err); | ||
| 2959 | |||
| 2960 | for (pos = *posp; pos && obj && !IS_ERR(obj); pos--) | ||
| 2961 | obj = __netlink_seq_next(seq); | ||
| 2962 | |||
| 2963 | return obj; | ||
| 2964 | } | ||
| 2965 | |||
| 2966 | static void *netlink_seq_next(struct seq_file *seq, void *v, loff_t *pos) | ||
| 2967 | { | ||
| 2968 | ++*pos; | ||
| 2969 | return __netlink_seq_next(seq); | ||
| 2976 | } | 2970 | } |
| 2977 | 2971 | ||
| 2978 | static void netlink_seq_stop(struct seq_file *seq, void *v) | 2972 | static void netlink_seq_stop(struct seq_file *seq, void *v) |
| 2979 | __releases(RCU) | ||
| 2980 | { | 2973 | { |
| 2981 | rcu_read_unlock(); | 2974 | struct nl_seq_iter *iter = seq->private; |
| 2975 | |||
| 2976 | if (iter->link >= MAX_LINKS) | ||
| 2977 | return; | ||
| 2978 | |||
| 2979 | netlink_walk_stop(iter); | ||
| 2982 | } | 2980 | } |
| 2983 | 2981 | ||
| 2984 | 2982 | ||
