list.h provides generic Linux-like linked list API which also supports RCU list operations.
Also additionally was removed the spinlock which is not needed for RCU-list operations, for the list_del_rcu(...) case it is needed additionally call synchronize_rcu(...) before free the flow entry. Because of full RCU support now flows are freed after grace-period via calling synchronize_rcu() and these removed-and-ready-to-free entries are kept in separate struct list_head. Signed-off-by: Vadim Kochan <vadi...@gmail.com> --- flowtop.c | 121 +++++++++++++++++++++++--------------------------------------- 1 file changed, 44 insertions(+), 77 deletions(-) diff --git a/flowtop.c b/flowtop.c index f48f5c8..a5ac2bb 100644 --- a/flowtop.c +++ b/flowtop.c @@ -51,6 +51,9 @@ #endif struct flow_entry { + struct list_head entry; + struct list_head free; + uint32_t flow_id, use, status; uint8_t l3_proto, l4_proto; uint32_t ip4_src_addr, ip4_dst_addr; @@ -65,7 +68,6 @@ struct flow_entry { char city_src[128], city_dst[128]; char rev_dns_src[256], rev_dns_dst[256]; char procname[256]; - struct flow_entry *next; int inode; unsigned int procnum; bool is_visible; @@ -78,8 +80,8 @@ struct flow_entry { }; struct flow_list { - struct flow_entry *head; - struct spinlock lock; + struct list_head head; + struct list_head free; }; enum flow_direction { @@ -355,15 +357,15 @@ static inline struct flow_entry *flow_entry_xalloc(void) static inline void flow_entry_xfree(struct flow_entry *n) { if (n->ct) - nfct_destroy(n->ct); + xfree(n->ct); xfree(n); } static inline void flow_list_init(struct flow_list *fl) { - fl->head = NULL; - spinlock_init(&fl->lock); + INIT_LIST_HEAD(&fl->head); + INIT_LIST_HEAD(&fl->free); } static inline bool nfct_is_dns(const struct nf_conntrack *ct) @@ -392,84 +394,60 @@ static void flow_list_new_entry(struct flow_list *fl, const struct nf_conntrack flow_entry_from_ct(n, ct); flow_entry_get_extended(n); - rcu_assign_pointer(n->next, fl->head); - rcu_assign_pointer(fl->head, n); + list_add_rcu(&n->entry, &fl->head); n->is_visible = true; } -static struct flow_entry *flow_list_find_id(struct flow_list *fl, - uint32_t id) +static struct flow_entry *flow_list_find_id(struct flow_list *fl, uint32_t id) { - struct flow_entry *n = rcu_dereference(fl->head); + struct flow_entry *n; - while (n != NULL) { + list_for_each_entry_rcu(n, &fl->head, entry) { if (n->flow_id == id) return n; - - n = rcu_dereference(n->next); } return NULL; } -static struct flow_entry *flow_list_find_prev_id(const struct flow_list *fl, - uint32_t id) +static void flow_list_del_entry(struct flow_list *fl, const struct nf_conntrack *ct) { - struct flow_entry *prev = rcu_dereference(fl->head), *next; - - if (prev->flow_id == id) - return NULL; - - while ((next = rcu_dereference(prev->next)) != NULL) { - if (next->flow_id == id) - return prev; + struct flow_entry *n; - prev = next; + n = flow_list_find_id(fl, nfct_get_attr_u32(ct, ATTR_ID)); + if (n) { + list_del_rcu(&n->entry); + list_add_rcu(&n->free, &fl->free); } - - return NULL; } -static void flow_list_destroy_entry(struct flow_list *fl, - const struct nf_conntrack *ct) +static void flow_list_clear(struct flow_list *fl) { - struct flow_entry *n1, *n2; - uint32_t id = nfct_get_attr_u32(ct, ATTR_ID); - - n1 = flow_list_find_id(fl, id); - if (n1) { - n2 = flow_list_find_prev_id(fl, id); - if (n2) { - rcu_assign_pointer(n2->next, n1->next); - n1->next = NULL; - - flow_entry_xfree(n1); - } else { - struct flow_entry *next = fl->head->next; + struct flow_entry *n, *tmp; - flow_entry_xfree(fl->head); - fl->head = next; - } + list_for_each_entry_safe(n, tmp, &fl->head, entry) { + list_del_rcu(&n->entry); + list_add_rcu(&n->free, &fl->free); } } -static void flow_list_destroy(struct flow_list *fl) +static void flow_list_free(struct flow_list *fl) { - struct flow_entry *n; + struct flow_entry *n, *tmp; synchronize_rcu(); - spinlock_lock(&flow_list.lock); - while (fl->head != NULL) { - n = rcu_dereference(fl->head->next); - fl->head->next = NULL; - - flow_entry_xfree(fl->head); - rcu_assign_pointer(fl->head, n); + list_for_each_entry_safe(n, tmp, &fl->free, free) { + list_del(&n->free); + flow_entry_xfree(n); } +} - spinlock_unlock(&flow_list.lock); +static void flow_list_destroy(struct flow_list *fl) +{ + flow_list_clear(fl); + flow_list_free(fl); } static void flow_entry_find_process(struct flow_entry *n) @@ -1044,15 +1022,14 @@ static void draw_flows(WINDOW *screen, struct flow_list *fl, rcu_read_lock(); - n = rcu_dereference(fl->head); - if (!n) + if (list_empty(&fl->head)) mvwprintw(screen, line, 2, "(No sessions! " "Is netfilter running?)"); ui_table_clear(&flows_tbl); ui_table_header_print(&flows_tbl); - for (; n; n = rcu_dereference(n->next)) { + list_for_each_entry_rcu(n, &fl->head, entry) { if (!n->is_visible) continue; if (presenter_flow_wrong_state(n)) @@ -1415,9 +1392,6 @@ static int flow_event_cb(enum nf_conntrack_msg_type type, if (sigint) return NFCT_CB_STOP; - synchronize_rcu(); - spinlock_lock(&flow_list.lock); - switch (type) { case NFCT_T_NEW: flow_list_new_entry(&flow_list, ct); @@ -1426,14 +1400,12 @@ static int flow_event_cb(enum nf_conntrack_msg_type type, flow_list_update_entry(&flow_list, ct); break; case NFCT_T_DESTROY: - flow_list_destroy_entry(&flow_list, ct); + flow_list_del_entry(&flow_list, ct); break; default: break; } - spinlock_unlock(&flow_list.lock); - if (sigint) return NFCT_CB_STOP; @@ -1444,8 +1416,7 @@ static void collector_refresh_flows(struct nfct_handle *handle) { struct flow_entry *n; - n = rcu_dereference(flow_list.head); - for (; n; n = rcu_dereference(n->next)) + list_for_each_entry_rcu(n, &flow_list.head, entry) nfct_query(handle, NFCT_Q_GET, n->ct); } @@ -1500,9 +1471,6 @@ static int flow_dump_cb(enum nf_conntrack_msg_type type __maybe_unused, if (sigint) return NFCT_CB_STOP; - synchronize_rcu(); - spinlock_lock(&flow_list.lock); - if (!(what & ~(INCLUDE_IPV4 | INCLUDE_IPV6))) goto check_addr; @@ -1558,7 +1526,6 @@ check_addr: flow_list_new_entry(&flow_list, ct); skip_flow: - spinlock_unlock(&flow_list.lock); return NFCT_CB_CONTINUE; } @@ -1635,18 +1602,18 @@ static void *collector(void *null __maybe_unused) continue; panic("Error while polling: %s\n", strerror(errno)); - } else if (status == 0) { - continue; + } else if (status != 0) { + if (poll_fd[0].revents & POLLIN) + nfct_catch(ct_event); } - if (poll_fd[0].revents & POLLIN) - nfct_catch(ct_event); + if (!list_empty(&flow_list.free)) + flow_list_free(&flow_list); } - rcu_unregister_thread(); - flow_list_destroy(&flow_list); - spinlock_destroy(&flow_list.lock); + + rcu_unregister_thread(); nfct_close(ct_event); -- 2.11.0 -- You received this message because you are subscribed to the Google Groups "netsniff-ng" group. To unsubscribe from this group and stop receiving emails from it, send an email to netsniff-ng+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.