Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

tipc: support in-order name publication events

It is observed that TIPC service binding order will not be kept in the
publication event report to user if the service is subscribed after the
bindings.

For example, services are bound by application in the following order:

Server: bound port A to {18888,66,66} scope 2
Server: bound port A to {18888,33,33} scope 2

Now, if a client subscribes to the service range (e.g. {18888, 0-100}),
it will get the 'TIPC_PUBLISHED' events in that binding order only when
the subscription is started before the bindings.
Otherwise, if started after the bindings, the events will arrive in the
opposite order:

Client: received event for published {18888,33,33}
Client: received event for published {18888,66,66}

For the latter case, it is clear that the bindings have existed in the
name table already, so when reported, the events' order will follow the
order of the rbtree binding nodes (- a node with lesser 'lower'/'upper'
range value will be first).

This is correct as we provide the tracking on a specific service status
(available or not), not the relationship between multiple services.
However, some users expect to see the same order of arriving events
irrespective of when the subscription is issued. This turns out to be
easy to fix. We now add functionality to ensure that publication events
always are issued in the same temporal order as the corresponding
bindings were performed.

v2: replace the unnecessary macro - 'publication_after()' with inline
function.
v3: reuse 'time_after32()' instead of reinventing the same exact code.

Acked-by: Jon Maloy <jon.maloy@ericsson.com>
Signed-off-by: Tuong Lien <tuong.t.lien@dektech.com.au>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Tuong Lien and committed by
David S. Miller
41b416f1 ba5f6a86

+47 -8
+43 -8
net/tipc/name_table.c
··· 35 35 */ 36 36 37 37 #include <net/sock.h> 38 + #include <linux/list_sort.h> 38 39 #include "core.h" 39 40 #include "netlink.h" 40 41 #include "name_table.h" ··· 67 66 /** 68 67 * struct tipc_service - container for all published instances of a service type 69 68 * @type: 32 bit 'type' value for service 69 + * @publ_cnt: increasing counter for publications in this service 70 70 * @ranges: rb tree containing all service ranges for this service 71 71 * @service_list: links to adjacent name ranges in hash chain 72 72 * @subscriptions: list of subscriptions for this service type ··· 76 74 */ 77 75 struct tipc_service { 78 76 u32 type; 77 + u32 publ_cnt; 79 78 struct rb_root ranges; 80 79 struct hlist_node service_list; 81 80 struct list_head subscriptions; ··· 112 109 INIT_LIST_HEAD(&publ->binding_node); 113 110 INIT_LIST_HEAD(&publ->local_publ); 114 111 INIT_LIST_HEAD(&publ->all_publ); 112 + INIT_LIST_HEAD(&publ->list); 115 113 return publ; 116 114 } 117 115 ··· 248 244 p = tipc_publ_create(type, lower, upper, scope, node, port, key); 249 245 if (!p) 250 246 goto err; 247 + /* Suppose there shouldn't be a huge gap btw publs i.e. >INT_MAX */ 248 + p->id = sc->publ_cnt++; 251 249 if (in_own_node(net, node)) 252 250 list_add(&p->local_publ, &sr->local_publ); 253 251 list_add(&p->all_publ, &sr->all_publ); ··· 284 278 } 285 279 286 280 /** 281 + * Code reused: time_after32() for the same purpose 282 + */ 283 + #define publication_after(pa, pb) time_after32((pa)->id, (pb)->id) 284 + static int tipc_publ_sort(void *priv, struct list_head *a, 285 + struct list_head *b) 286 + { 287 + struct publication *pa, *pb; 288 + 289 + pa = container_of(a, struct publication, list); 290 + pb = container_of(b, struct publication, list); 291 + return publication_after(pa, pb); 292 + } 293 + 294 + /** 287 295 * tipc_service_subscribe - attach a subscription, and optionally 288 296 * issue the prescribed number of events if there is any service 289 297 * range overlapping with the requested range ··· 306 286 struct tipc_subscription *sub) 307 287 { 308 288 struct tipc_subscr *sb = &sub->evt.s; 289 + struct publication *p, *first, *tmp; 290 + struct list_head publ_list; 309 291 struct service_range *sr; 310 292 struct tipc_name_seq ns; 311 - struct publication *p; 312 293 struct rb_node *n; 313 - bool first; 294 + u32 filter; 314 295 315 296 ns.type = tipc_sub_read(sb, seq.type); 316 297 ns.lower = tipc_sub_read(sb, seq.lower); 317 298 ns.upper = tipc_sub_read(sb, seq.upper); 299 + filter = tipc_sub_read(sb, filter); 318 300 319 301 tipc_sub_get(sub); 320 302 list_add(&sub->service_list, &service->subscriptions); 321 303 322 - if (tipc_sub_read(sb, filter) & TIPC_SUB_NO_STATUS) 304 + if (filter & TIPC_SUB_NO_STATUS) 323 305 return; 324 306 307 + INIT_LIST_HEAD(&publ_list); 325 308 for (n = rb_first(&service->ranges); n; n = rb_next(n)) { 326 309 sr = container_of(n, struct service_range, tree_node); 327 310 if (sr->lower > ns.upper) 328 311 break; 329 312 if (!tipc_sub_check_overlap(&ns, sr->lower, sr->upper)) 330 313 continue; 331 - first = true; 332 314 315 + first = NULL; 333 316 list_for_each_entry(p, &sr->all_publ, all_publ) { 334 - tipc_sub_report_overlap(sub, sr->lower, sr->upper, 335 - TIPC_PUBLISHED, p->port, 336 - p->node, p->scope, first); 337 - first = false; 317 + if (filter & TIPC_SUB_PORTS) 318 + list_add_tail(&p->list, &publ_list); 319 + else if (!first || publication_after(first, p)) 320 + /* Pick this range's *first* publication */ 321 + first = p; 338 322 } 323 + if (first) 324 + list_add_tail(&first->list, &publ_list); 325 + } 326 + 327 + /* Sort the publications before reporting */ 328 + list_sort(NULL, &publ_list, tipc_publ_sort); 329 + list_for_each_entry_safe(p, tmp, &publ_list, list) { 330 + tipc_sub_report_overlap(sub, p->lower, p->upper, 331 + TIPC_PUBLISHED, p->port, p->node, 332 + p->scope, true); 333 + list_del_init(&p->list); 339 334 } 340 335 } 341 336
+4
net/tipc/name_table.h
··· 58 58 * @node: network address of publishing socket's node 59 59 * @port: publishing port 60 60 * @key: publication key, unique across the cluster 61 + * @id: publication id 61 62 * @binding_node: all publications from the same node which bound this one 62 63 * - Remote publications: in node->publ_list 63 64 * Used by node/name distr to withdraw publications when node is lost ··· 70 69 * Used by closest_first and multicast receive lookup algorithms 71 70 * @all_publ: all publications identical to this one, whatever node and scope 72 71 * Used by round-robin lookup algorithm 72 + * @list: to form a list of publications in temporal order 73 73 * @rcu: RCU callback head used for deferred freeing 74 74 */ 75 75 struct publication { ··· 81 79 u32 node; 82 80 u32 port; 83 81 u32 key; 82 + u32 id; 84 83 struct list_head binding_node; 85 84 struct list_head binding_sock; 86 85 struct list_head local_publ; 87 86 struct list_head all_publ; 87 + struct list_head list; 88 88 struct rcu_head rcu; 89 89 }; 90 90