diff options
author | Johannes Berg <johannes.berg@intel.com> | 2011-06-20 07:40:46 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-06-22 16:09:45 -0400 |
commit | 670dc2833d144375eac36ad74111495a825a9288 (patch) | |
tree | 12d5d3a925da562a6ba0497c47f4b55c71ff545b | |
parent | c1c3daee97498f5d74bfd23531cfe23c8e14a619 (diff) |
netlink: advertise incomplete dumps
Consider the following situation:
* a dump that would show 8 entries, four in the first
round, and four in the second
* between the first and second rounds, 6 entries are
removed
* now the second round will not show any entry, and
even if there is a sequence/generation counter the
application will not know
To solve this problem, add a new flag NLM_F_DUMP_INTR
to the netlink header that indicates the dump wasn't
consistent, this flag can also be set on the MSG_DONE
message that terminates the dump, and as such above
situation can be detected.
To achieve this, add a sequence counter to the netlink
callback struct. Of course, netlink code still needs
to use this new functionality. The correct way to do
that is to always set cb->seq when a dumpit callback
is invoked and call nl_dump_check_consistent() for
each new message. The core code will also call this
function for the final MSG_DONE message.
To make it usable with generic netlink, a new function
genlmsg_nlhdr() is needed to obtain the netlink header
from the genetlink user header.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Acked-by: David S. Miller <davem@davemloft.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r-- | include/linux/netlink.h | 2 | ||||
-rw-r--r-- | include/net/genetlink.h | 32 | ||||
-rw-r--r-- | include/net/netlink.h | 24 | ||||
-rw-r--r-- | net/netlink/af_netlink.c | 2 |
4 files changed, 60 insertions, 0 deletions
diff --git a/include/linux/netlink.h b/include/linux/netlink.h index 4c4ac3f3ce5a..8d1bcec5cc06 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h | |||
@@ -48,6 +48,7 @@ struct nlmsghdr { | |||
48 | #define NLM_F_MULTI 2 /* Multipart message, terminated by NLMSG_DONE */ | 48 | #define NLM_F_MULTI 2 /* Multipart message, terminated by NLMSG_DONE */ |
49 | #define NLM_F_ACK 4 /* Reply with ack, with zero or error code */ | 49 | #define NLM_F_ACK 4 /* Reply with ack, with zero or error code */ |
50 | #define NLM_F_ECHO 8 /* Echo this request */ | 50 | #define NLM_F_ECHO 8 /* Echo this request */ |
51 | #define NLM_F_DUMP_INTR 16 /* Dump was inconsistent due to sequence change */ | ||
51 | 52 | ||
52 | /* Modifiers to GET request */ | 53 | /* Modifiers to GET request */ |
53 | #define NLM_F_ROOT 0x100 /* specify tree root */ | 54 | #define NLM_F_ROOT 0x100 /* specify tree root */ |
@@ -221,6 +222,7 @@ struct netlink_callback { | |||
221 | struct netlink_callback *cb); | 222 | struct netlink_callback *cb); |
222 | int (*done)(struct netlink_callback *cb); | 223 | int (*done)(struct netlink_callback *cb); |
223 | int family; | 224 | int family; |
225 | unsigned int prev_seq, seq; | ||
224 | long args[6]; | 226 | long args[6]; |
225 | }; | 227 | }; |
226 | 228 | ||
diff --git a/include/net/genetlink.h b/include/net/genetlink.h index d420f28b6d60..82d8d09faa44 100644 --- a/include/net/genetlink.h +++ b/include/net/genetlink.h | |||
@@ -160,6 +160,38 @@ static inline void *genlmsg_put(struct sk_buff *skb, u32 pid, u32 seq, | |||
160 | } | 160 | } |
161 | 161 | ||
162 | /** | 162 | /** |
163 | * genlmsg_nlhdr - Obtain netlink header from user specified header | ||
164 | * @user_hdr: user header as returned from genlmsg_put() | ||
165 | * @family: generic netlink family | ||
166 | * | ||
167 | * Returns pointer to netlink header. | ||
168 | */ | ||
169 | static inline struct nlmsghdr *genlmsg_nlhdr(void *user_hdr, | ||
170 | struct genl_family *family) | ||
171 | { | ||
172 | return (struct nlmsghdr *)((char *)user_hdr - | ||
173 | family->hdrsize - | ||
174 | GENL_HDRLEN - | ||
175 | NLMSG_HDRLEN); | ||
176 | } | ||
177 | |||
178 | /** | ||
179 | * genl_dump_check_consistent - check if sequence is consistent and advertise if not | ||
180 | * @cb: netlink callback structure that stores the sequence number | ||
181 | * @user_hdr: user header as returned from genlmsg_put() | ||
182 | * @family: generic netlink family | ||
183 | * | ||
184 | * Cf. nl_dump_check_consistent(), this just provides a wrapper to make it | ||
185 | * simpler to use with generic netlink. | ||
186 | */ | ||
187 | static inline void genl_dump_check_consistent(struct netlink_callback *cb, | ||
188 | void *user_hdr, | ||
189 | struct genl_family *family) | ||
190 | { | ||
191 | nl_dump_check_consistent(cb, genlmsg_nlhdr(user_hdr, family)); | ||
192 | } | ||
193 | |||
194 | /** | ||
163 | * genlmsg_put_reply - Add generic netlink header to a reply message | 195 | * genlmsg_put_reply - Add generic netlink header to a reply message |
164 | * @skb: socket buffer holding the message | 196 | * @skb: socket buffer holding the message |
165 | * @info: receiver info | 197 | * @info: receiver info |
diff --git a/include/net/netlink.h b/include/net/netlink.h index 02740a94f108..98c185441bee 100644 --- a/include/net/netlink.h +++ b/include/net/netlink.h | |||
@@ -638,6 +638,30 @@ static inline int nlmsg_unicast(struct sock *sk, struct sk_buff *skb, u32 pid) | |||
638 | nlmsg_ok(pos, rem); \ | 638 | nlmsg_ok(pos, rem); \ |
639 | pos = nlmsg_next(pos, &(rem))) | 639 | pos = nlmsg_next(pos, &(rem))) |
640 | 640 | ||
641 | /** | ||
642 | * nl_dump_check_consistent - check if sequence is consistent and advertise if not | ||
643 | * @cb: netlink callback structure that stores the sequence number | ||
644 | * @nlh: netlink message header to write the flag to | ||
645 | * | ||
646 | * This function checks if the sequence (generation) number changed during dump | ||
647 | * and if it did, advertises it in the netlink message header. | ||
648 | * | ||
649 | * The correct way to use it is to set cb->seq to the generation counter when | ||
650 | * all locks for dumping have been acquired, and then call this function for | ||
651 | * each message that is generated. | ||
652 | * | ||
653 | * Note that due to initialisation concerns, 0 is an invalid sequence number | ||
654 | * and must not be used by code that uses this functionality. | ||
655 | */ | ||
656 | static inline void | ||
657 | nl_dump_check_consistent(struct netlink_callback *cb, | ||
658 | struct nlmsghdr *nlh) | ||
659 | { | ||
660 | if (cb->prev_seq && cb->seq != cb->prev_seq) | ||
661 | nlh->nlmsg_flags |= NLM_F_DUMP_INTR; | ||
662 | cb->prev_seq = cb->seq; | ||
663 | } | ||
664 | |||
641 | /************************************************************************** | 665 | /************************************************************************** |
642 | * Netlink Attributes | 666 | * Netlink Attributes |
643 | **************************************************************************/ | 667 | **************************************************************************/ |
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 6ef64adf7362..a7ec8512f552 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c | |||
@@ -1693,6 +1693,8 @@ static int netlink_dump(struct sock *sk) | |||
1693 | if (!nlh) | 1693 | if (!nlh) |
1694 | goto errout_skb; | 1694 | goto errout_skb; |
1695 | 1695 | ||
1696 | nl_dump_check_consistent(cb, nlh); | ||
1697 | |||
1696 | memcpy(nlmsg_data(nlh), &len, sizeof(len)); | 1698 | memcpy(nlmsg_data(nlh), &len, sizeof(len)); |
1697 | 1699 | ||
1698 | if (sk_filter(sk, skb)) | 1700 | if (sk_filter(sk, skb)) |