From: Michael Zintakis <michael.zintakis@googlemail.com>
To: netfilter-devel@vger.kernel.org
Cc: pablo@netfilter.org
Subject: [PATCH v3 nfacct 14/29] add sort option to the "list" command
Date: Wed, 10 Jul 2013 19:25:12 +0100 [thread overview]
Message-ID: <1373480727-11254-15-git-send-email-michael.zintakis@googlemail.com> (raw)
In-Reply-To: <1373480727-11254-1-git-send-email-michael.zintakis@googlemail.com>
* add a new "sort" option to the "list" command, allowing nfacct object listed
to be sorted by name (default), packets, bytes or not to be sorted at all (as
was the case up until now);
* add a new nfacct_list.c source (in addition to the nfacct_list.h)
implementing the main sort function, in addition to the existing linked-list
routines.
Signed-off-by: Michael Zintakis <michael.zintakis@googlemail.com>
---
src/Makefile.am | 2 +-
src/nfacct.c | 70 ++++++++++++++++++++++++-
src/nfacct_list.c | 149 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/nfacct_list.h | 4 ++
4 files changed, 222 insertions(+), 3 deletions(-)
create mode 100644 src/nfacct_list.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 133ce61..ae60a51 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -2,6 +2,6 @@ include $(top_srcdir)/Make_global.am
sbin_PROGRAMS = nfacct
-nfacct_SOURCES = nfacct.c nfacct_utils.c
+nfacct_SOURCES = nfacct.c nfacct_utils.c nfacct_list.c
nfacct_LDADD = ${LIBMNL_LIBS} ${LIBNETFILTER_ACCT_LIBS}
diff --git a/src/nfacct.c b/src/nfacct.c
index c778d1c..3eb8b68 100644
--- a/src/nfacct.c
+++ b/src/nfacct.c
@@ -53,6 +53,49 @@ static void free_nfa_list(void) {
}
}
+enum nfacct_sort_mode {
+ NFACCT_SORT_NONE = 0,
+ NFACCT_SORT_NAME,
+ NFACCT_SORT_PKTS,
+ NFACCT_SORT_BYTES,
+};
+
+static int nfacct_cmp(void *priv, struct nfacct_list_head *a, struct nfacct_list_head *b)
+{
+ struct nfa *nfa1, *nfa2;
+ uint64_t v1, v2;
+ enum nfacct_attr_type attr = NFACCT_ATTR_NAME;
+ enum nfacct_sort_mode *mode = (enum nfacct_sort_mode *)priv;
+
+ nfa1 = nfacct_list_entry(a, struct nfa, head);
+ nfa2 = nfacct_list_entry(b, struct nfa, head);
+
+ switch(*mode) {
+ case NFACCT_SORT_PKTS:
+ attr = NFACCT_ATTR_PKTS;
+ break;
+ case NFACCT_SORT_BYTES:
+ attr = NFACCT_ATTR_BYTES;
+ case NFACCT_SORT_NAME:
+ break;
+ default: /* unsorted */
+ return 0;
+ }
+ if (attr != NFACCT_ATTR_NAME) {
+ v1 = nfacct_attr_get_u64(nfa1->nfacct, attr);
+ v2 = nfacct_attr_get_u64(nfa2->nfacct, attr);
+ if (v1 < v2) {
+ return -1;
+ } else if (v1 > v2) {
+ return 1;
+ }
+ }
+ return strcmp(nfacct_attr_get_str(nfa1->nfacct,
+ NFACCT_ATTR_NAME),
+ nfacct_attr_get_str(nfa2->nfacct,
+ NFACCT_ATTR_NAME));
+}
+
/* xml header & footer strings */
#define NFACCT_XML_HEADER "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" \
"<nfacct>\n"
@@ -219,7 +262,7 @@ err:
static int nfacct_cmd_list(int argc, char *argv[])
{
bool nfnl_msg = false, xml = false;
- bool b_fmt = false;
+ bool b_fmt = false, b_sort = false;
struct mnl_socket *nl;
char buf[70000];
struct nlmsghdr *nlh;
@@ -228,6 +271,7 @@ static int nfacct_cmd_list(int argc, char *argv[])
struct nfa *nfa = NULL;
uint8_t nfnl_cmd = NFNL_MSG_ACCT_GET;
uint16_t fmt = NFACCT_FMT_MAX;
+ enum nfacct_sort_mode sort_mode = NFACCT_SORT_NAME;
while (argc > 0) {
if (!nfnl_msg && nfacct_matches(argv[0],"reset")) {
@@ -235,6 +279,21 @@ static int nfacct_cmd_list(int argc, char *argv[])
nfnl_msg = true;
} else if (!xml && nfacct_matches(argv[0],"xml")) {
xml = true;
+ } else if (!b_sort && nfacct_matches(argv[0],"sort")) {
+ NFACCT_GET_NEXT_ARG();
+ if (nfacct_matches(argv[0],"name")) {
+ sort_mode = NFACCT_SORT_NAME;
+ } else if (nfacct_matches(argv[0],"packets") ||
+ nfacct_matches(argv[0],"pkts")) {
+ sort_mode = NFACCT_SORT_PKTS;
+ } else if (nfacct_matches(argv[0],"bytes")) {
+ sort_mode = NFACCT_SORT_BYTES;
+ } else if (nfacct_matches(argv[0],"none")) {
+ sort_mode = NFACCT_SORT_NONE;
+ } else {
+ NFACCT_RET_ARG_ERR();
+ }
+ b_sort = true;
} else if (!b_fmt && (nfacct_matches(argv[0],"format") ||
nfacct_matches(argv[0],"fmt"))) {
NFACCT_GET_NEXT_ARG();
@@ -288,6 +347,9 @@ static int nfacct_cmd_list(int argc, char *argv[])
if (xml)
printf(NFACCT_XML_HEADER);
+ if (sort_mode != NFACCT_SORT_NONE)
+ nfacct_list_sort(&sort_mode, &nfa_list, nfacct_cmp);
+
nfacct_list_for_each_entry(nfa, &nfa_list, head) {
nfacct_snprintf_with_options(buf, ARRAY_SIZE(buf),
nfa->nfacct,
@@ -667,11 +729,12 @@ static const char help_msg[] =
" version\t\tDisplay version and disclaimer\n"
" help\t\t\tDisplay this help message\n\n"
"Parameters:\n"
- " LST_PARAMS := [ reset ] [ format FMT_SPEC ] [ xml ]\n"
+ " LST_PARAMS := [ reset ] [ format FMT_SPEC ] [ sort SORT_SPEC ] [ xml ]\n"
" ADD_PARAMS := [ replace ]\n"
" GET_PARAMS := [ reset ] [ format FMT_SPEC ] [ xml ]\n"
" RST_PARAMS := [ flush ] [ replace ]\n"
" FMT_SPEC := { [FMT] | [,] | [FMT] ... }\n"
+ " SORT_SPEC := { none | name | packets | bytes }"
" FMT := { def | raw | 3pl | iec | kib | mib | gib | tib | pib |"
" eib |\n"
" \t si | kb | mb | gb | tb | pb | eb }\n";
@@ -691,6 +754,7 @@ static int nfacct_cmd_save(int argc, char *argv[])
int ret = -1, i;
bool ignore_width = true;
struct nfa *nfa = NULL;
+ enum nfacct_sort_mode sort_mode = NFACCT_SORT_NAME;
if (argc > 0) {
NFACCT_RET_ERR("too many arguments");
@@ -733,6 +797,8 @@ static int nfacct_cmd_save(int argc, char *argv[])
}
mnl_socket_close(nl);
+ nfacct_list_sort(&sort_mode, &nfa_list, nfacct_cmp);
+
nfacct_list_for_each_entry(nfa, &nfa_list, head) {
nfacct_snprintf_with_options(buf, ARRAY_SIZE(buf),
nfa->nfacct,
diff --git a/src/nfacct_list.c b/src/nfacct_list.c
new file mode 100644
index 0000000..242ac0c
--- /dev/null
+++ b/src/nfacct_list.c
@@ -0,0 +1,149 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation version 2.1
+ * of the License.
+ */
+
+#include <string.h>
+
+#include "nfacct_list.h"
+#include "nfacct_utils.h"
+
+#define MAX_LIST_LENGTH_BITS 20
+
+/*
+ * Returns a list organized in an intermediate format suited
+ * to chaining of merge() calls: null-terminated, no reserved or
+ * sentinel head node, "prev" links not maintained.
+ */
+static struct nfacct_list_head *merge(void *priv,
+ int (*cmp)(void *priv,
+ struct nfacct_list_head *a,
+ struct nfacct_list_head *b),
+ struct nfacct_list_head *a,
+ struct nfacct_list_head *b)
+{
+ struct nfacct_list_head head, *tail = &head;
+
+ while (a && b) {
+ /* if equal, take 'a' -- important for sort stability */
+ if ((*cmp)(priv, a, b) <= 0) {
+ tail->next = a;
+ a = a->next;
+ } else {
+ tail->next = b;
+ b = b->next;
+ }
+ tail = tail->next;
+ }
+ tail->next = a?:b;
+ return head.next;
+}
+
+/*
+ * Combine final list merge with restoration of standard doubly-linked
+ * list structure. This approach duplicates code from merge(), but
+ * runs faster than the tidier alternatives of either a separate final
+ * prev-link restoration pass, or maintaining the prev links
+ * throughout.
+ */
+static void merge_and_restore_back_links(void *priv,
+ int (*cmp)(void *priv,
+ struct nfacct_list_head *a,
+ struct nfacct_list_head *b),
+ struct nfacct_list_head *head,
+ struct nfacct_list_head *a,
+ struct nfacct_list_head *b)
+{
+ struct nfacct_list_head *tail = head;
+
+ while (a && b) {
+ /* if equal, take 'a' -- important for sort stability */
+ if ((*cmp)(priv, a, b) <= 0) {
+ tail->next = a;
+ a->prev = tail;
+ a = a->next;
+ } else {
+ tail->next = b;
+ b->prev = tail;
+ b = b->next;
+ }
+ tail = tail->next;
+ }
+ tail->next = a ? : b;
+
+ do {
+ /*
+ * In worst cases this loop may run many iterations.
+ * Continue callbacks to the client even though no
+ * element comparison is needed, so the client's cmp()
+ * routine can invoke cond_resched() periodically.
+ */
+ (*cmp)(priv, tail->next, tail->next);
+
+ tail->next->prev = tail;
+ tail = tail->next;
+ } while (tail->next);
+
+ tail->next = head;
+ head->prev = tail;
+}
+
+/**
+ * nfacct_list_sort - sort a list
+ * @priv: private data, opaque to nfacct_list_sort(), passed to @cmp
+ * @head: the list to sort
+ * @cmp: the elements comparison function
+ *
+ * This function implements "merge sort", which has O(nlog(n))
+ * complexity.
+ *
+ * The comparison function @cmp must return a negative value if @a
+ * should sort before @b, and a positive value if @a should sort after
+ * @b. If @a and @b are equivalent, and their original relative
+ * ordering is to be preserved, @cmp must return 0.
+ */
+void nfacct_list_sort(void *priv, struct nfacct_list_head *head,
+ int (*cmp)(void *priv, struct nfacct_list_head *a,
+ struct nfacct_list_head *b))
+{
+ /* sorted partial lists -- last slot is a sentinel */
+ struct nfacct_list_head *part[MAX_LIST_LENGTH_BITS+1];
+ size_t lev; /* index into part[] */
+ size_t max_lev = 0;
+ struct nfacct_list_head *list;
+
+ if (nfacct_list_empty(head))
+ return;
+
+ memset(part, 0, sizeof(part));
+
+ head->prev->next = NULL;
+ list = head->next;
+
+ while (list) {
+ struct nfacct_list_head *cur = list;
+ list = list->next;
+ cur->next = NULL;
+
+ for (lev = 0; part[lev]; lev++) {
+ cur = merge(priv, cmp, part[lev], cur);
+ part[lev] = NULL;
+ }
+ if (lev > max_lev) {
+ if (lev >= ARRAY_SIZE(part)-1) {
+ lev--;
+ }
+ max_lev = lev;
+ }
+ part[lev] = cur;
+ }
+
+ for (lev = 0; lev < max_lev; lev++)
+ if (part[lev])
+ list = merge(priv, cmp, part[lev], list);
+
+ merge_and_restore_back_links(priv, cmp, head, part[max_lev], list);
+}
+
diff --git a/src/nfacct_list.h b/src/nfacct_list.h
index 5ef1b9d..1fe5674 100644
--- a/src/nfacct_list.h
+++ b/src/nfacct_list.h
@@ -52,6 +52,10 @@ static inline int nfacct_list_empty(struct nfacct_list_head *head)
return head->next == head;
}
+extern void nfacct_list_sort(void *priv, struct nfacct_list_head *head,
+ int (*cmp)(void *priv, struct nfacct_list_head *a,
+ struct nfacct_list_head *b));
+
#define nfacct_container_of(ptr, type, member) ({ \
typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - ((size_t) &((type *)0)->member));})
--
1.8.3.1
next prev parent reply other threads:[~2013-07-10 18:26 UTC|newest]
Thread overview: 50+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-07-10 18:24 [PATCH v3 0/29] nfacct changes and additions Michael Zintakis
2013-07-10 18:24 ` [PATCH v3 kernel 1/29] bugfix: pkts/bytes need to be specified simultaneously Michael Zintakis
2013-07-10 20:04 ` Florian Westphal
2013-07-11 18:56 ` Michael Zintakis
2013-07-10 18:25 ` [PATCH v3 kernel 2/29] bugfix: restore pkts/bytes counters in NLM_F_REPLACE Michael Zintakis
2013-07-10 18:25 ` [PATCH v3 libnetfilter_acct 3/29] bugfix: correct xml name parsing Michael Zintakis
2013-07-15 22:24 ` Pablo Neira Ayuso
2013-07-10 18:25 ` [PATCH v3 libnetfilter_acct 4/29] bugfix: correct (plain) " Michael Zintakis
2013-07-15 22:29 ` Pablo Neira Ayuso
2013-07-10 18:25 ` [PATCH v3 nfacct 5/29] bugfix: prevent 0-sized parameter being accepted Michael Zintakis
2013-07-10 18:25 ` [PATCH v3 nfacct 6/29] bugfix: prevent 0-sized nfacct name " Michael Zintakis
2013-07-10 18:25 ` [PATCH v3 nfacct 7/29] code-refactoring changes to the "command menu" Michael Zintakis
2013-07-15 22:41 ` Pablo Neira Ayuso
2013-07-10 18:25 ` [PATCH v3 nfacct 8/29] add 2 new options: "replace" and "flush" Michael Zintakis
2013-07-10 18:25 ` [PATCH v3 libnetfilter_acct 9/29] add *_SAVE template allowing save/restore Michael Zintakis
2013-07-10 18:25 ` [PATCH v3 libnetfilter_acct 10/29] add *_BONLY template to show bytes-only Michael Zintakis
2013-07-15 22:42 ` Pablo Neira Ayuso
2013-07-10 18:25 ` [PATCH v3 libnetfilter_acct 11/29] add variable width and on-the-fly formatting Michael Zintakis
2013-07-15 22:51 ` Pablo Neira Ayuso
2013-07-10 18:25 ` [PATCH v3 nfacct 12/29] add variable width and on-the-fly number formatting Michael Zintakis
2013-07-10 18:25 ` [PATCH v3 nfacct 13/29] add new "save" and correct existing "restore" commands Michael Zintakis
2013-07-10 18:25 ` Michael Zintakis [this message]
2013-07-10 18:25 ` [PATCH v3 nfacct 15/29] add "show bytes" option to "list" and "get" commands Michael Zintakis
2013-07-10 18:25 ` [PATCH v3 kernel 16/29] add permanent byte/packet format capability to nfacct Michael Zintakis
2013-07-10 20:00 ` Florian Westphal
2013-07-11 18:56 ` Michael Zintakis
2013-07-11 20:12 ` Florian Westphal
2013-07-14 8:29 ` Michael Zintakis
2013-07-10 18:25 ` [PATCH v3 libnetfilter_acct 17/29] add *permanent* number formatting support Michael Zintakis
2013-07-10 18:25 ` [PATCH v3 nfacct 18/29] add permanent number formatting to nfacct objects Michael Zintakis
2013-07-10 18:25 ` [PATCH v3 kernel 19/29] add byte threshold capability to nfacct Michael Zintakis
2013-07-10 20:00 ` Florian Westphal
2013-07-11 18:56 ` Michael Zintakis
2013-07-11 20:25 ` Florian Westphal
2013-07-17 19:44 ` Alexey Perevalov
2013-07-10 18:25 ` [PATCH v3 libnetfilter_acct 20/29] add byte threshold capability support Michael Zintakis
2013-07-10 18:25 ` [PATCH v3 nfacct 21/29] add byte threshold capabilities to nfacct objects Michael Zintakis
2013-07-10 18:25 ` [PATCH v3 libnetfilter_acct 22/29] add *_EXTENDED template support Michael Zintakis
2013-07-10 18:25 ` [PATCH v3 nfacct 23/29] add "show extended" option to "list" and "get" commands Michael Zintakis
2013-07-10 18:25 ` [PATCH v3 kernel 24/29] add packets and bytes mark capability to nfacct Michael Zintakis
2013-07-10 20:01 ` Florian Westphal
2013-07-11 18:56 ` Michael Zintakis
2013-07-11 1:14 ` Pablo Neira Ayuso
2013-07-11 18:56 ` Michael Zintakis
2013-07-10 18:25 ` [PATCH v3 libnetfilter_acct 25/29] add packets/bytes mark capability support Michael Zintakis
2013-07-10 18:25 ` [PATCH v3 nfacct 26/29] add setmark and clrmark to "get" and "list" commands Michael Zintakis
2013-07-10 18:25 ` [PATCH v3 libnetfilter_acct 27/29] add *_MONLY template support Michael Zintakis
2013-07-10 18:25 ` [PATCH v3 nfacct 28/29] add "show marks" option to "list" and "get" commands Michael Zintakis
2013-07-10 18:25 ` [PATCH v3 nfacct 29/29] change man page to describe all new features Michael Zintakis
2013-07-15 12:36 ` [0/29] nfacct changes and additions Pablo Neira Ayuso
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1373480727-11254-15-git-send-email-michael.zintakis@googlemail.com \
--to=michael.zintakis@googlemail.com \
--cc=netfilter-devel@vger.kernel.org \
--cc=pablo@netfilter.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).