netfilter: xt_qtaguid: fix crash after using delete ctrl command
JP Abgrall [Mon, 26 Sep 2011 02:24:02 +0000 (19:24 -0700)]
* Crash fix
The delete command would delete a socket tag entry without removing it
from the proc_qtu_data { ..., sock_tag_list, }.
This in turn would cause an exiting process to crash while cleaning up
its matching proc_qtu_data.

* Added more aggressive tracking/cleanup of proc_qtu_data
This should allow one process to cleanup qtu_tag_data{} left around from
processes that didn't use resource tracking via /dev/xt_qtaguid.

* Debug printing tweaks
Better code inclusion/exclusion handling,
and extra debug out of full state.

Change-Id: I735965af2962ffcd7f3021cdc0068b3ab21245c2
Signed-off-by: JP Abgrall <jpa@google.com>

net/netfilter/xt_qtaguid.c
net/netfilter/xt_qtaguid_internal.h
net/netfilter/xt_qtaguid_print.c
net/netfilter/xt_qtaguid_print.h

index c42e486..32d855b 100644 (file)
@@ -102,11 +102,10 @@ static bool qtu_proc_handling_passive;
 module_param_named(tag_tracking_passive, qtu_proc_handling_passive, bool,
                   S_IRUGO | S_IWUSR);
 
-
 #define QTU_DEV_NAME "xt_qtaguid"
 
-uint debug_mask = DEFAULT_DEBUG_MASK;
-module_param(debug_mask, uint, S_IRUGO | S_IWUSR);
+uint qtaguid_debug_mask = DEFAULT_DEBUG_MASK;
+module_param_named(debug_mask, qtaguid_debug_mask, uint, S_IRUGO | S_IWUSR);
 
 /*---------------------------------------------------------------------------*/
 static const char *iface_stat_procdirname = "iface_stat";
@@ -125,70 +124,92 @@ static struct proc_dir_entry *iface_stat_all_procfile;
  * Notice how sock_tag_list_lock is held sometimes when uid_tag_data_tree_lock
  * is acquired.
  *
- * Call tree with all lock holders as of 2011-09-06:
- *
- *   qtaguid_ctrl_parse()
- *     ctrl_cmd_delete()
- *       sock_tag_list_lock
- *       tag_counter_set_list_lock
- *       iface_stat_list_lock
- *         iface_entry->tag_stat_list_lock
- *       uid_tag_data_tree_lock
- *     ctrl_cmd_counter_set()
- *       tag_counter_set_list_lock
- *     ctrl_cmd_tag()
- *       sock_tag_list_lock
- *         get_tag_ref()
- *           uid_tag_data_tree_lock
- *       uid_tag_data_tree_lock
- *     ctrl_cmd_untag()
- *       sock_tag_list_lock
- *         uid_tag_data_tree_lock
+ * Call tree with all lock holders as of 2011-09-25:
  *
- *   qtaguid_mt()
- *     account_for_uid()
- *       if_tag_stat_update()
- *     get_sock_stat()
- *       sock_tag_list_lock
- *        iface_entry->tag_stat_list_lock
- *        tag_stat_update()
- *          get_active_counter_set()
- *            tag_counter_set_list_lock
+ * iface_stat_all_proc_read()
+ *   iface_stat_list_lock
+ *     (struct iface_stat)
  *
- *   iface_netdev_event_handler()
- *     iface_stat_create()
- *       iface_stat_list_lock
- *     iface_stat_update()
- *       iface_stat_list_lock
+ * qtaguid_ctrl_proc_read()
+ *   sock_tag_list_lock
+ *     (sock_tag_tree)
+ *     (struct proc_qtu_data->sock_tag_list)
+ *   prdebug_full_state()
+ *     sock_tag_list_lock
+ *       (sock_tag_tree)
+ *     uid_tag_data_tree_lock
+ *       (uid_tag_data_tree)
+ *       (proc_qtu_data_tree)
+ *     iface_stat_list_lock
  *
- *   iface_inet6addr_event_handler()
- *     iface_stat_create_ipv6()
- *       iface_stat_list_lock
- *     iface_stat_update()
- *       iface_stat_list_lock
+ * qtaguid_stats_proc_read()
+ *   iface_stat_list_lock
+ *     struct iface_stat->tag_stat_list_lock
  *
- *   iface_inetaddr_event_handler()
- *     iface_stat_create()
- *       iface_stat_list_lock
- *     iface_stat_update()
- *       iface_stat_list_lock
+ * qtudev_open()
+ *   uid_tag_data_tree_lock
  *
- *   qtaguid_ctrl_proc_read()
- *     sock_tag_list_lock
+ * qtudev_release()
+ *   sock_tag_data_list_lock
+ *     uid_tag_data_tree_lock
+ *   prdebug_full_state()
  *     sock_tag_list_lock
  *     uid_tag_data_tree_lock
  *     iface_stat_list_lock
  *
- *   qtaguid_stats_proc_read()
+ * iface_netdev_event_handler()
+ *   iface_stat_create()
+ *     iface_stat_list_lock
+ *   iface_stat_update()
  *     iface_stat_list_lock
- *       iface_entry->tag_stat_list_lock
  *
- *   qtudev_open()
- *     uid_tag_data_tree_lock
+ * iface_inetaddr_event_handler()
+ *   iface_stat_create()
+ *     iface_stat_list_lock
+ *   iface_stat_update()
+ *     iface_stat_list_lock
+ *
+ * iface_inet6addr_event_handler()
+ *   iface_stat_create_ipv6()
+ *     iface_stat_list_lock
+ *   iface_stat_update()
+ *     iface_stat_list_lock
  *
- *   qtud_dev_release()
+ * qtaguid_mt()
+ *   account_for_uid()
+ *     if_tag_stat_update()
+ *       get_sock_stat()
+ *         sock_tag_list_lock
+ *       struct iface_stat->tag_stat_list_lock
+ *         tag_stat_update()
+ *           get_active_counter_set()
+ *             tag_counter_set_list_lock
+ *         tag_stat_update()
+ *           get_active_counter_set()
+ *             tag_counter_set_list_lock
+ *
+ *
+ * qtaguid_ctrl_parse()
+ *   ctrl_cmd_delete()
+ *     sock_tag_list_lock
+ *     tag_counter_set_list_lock
+ *     iface_stat_list_lock
+ *       struct iface_stat->tag_stat_list_lock
+ *     uid_tag_data_tree_lock
+ *   ctrl_cmd_counter_set()
+ *     tag_counter_set_list_lock
+ *   ctrl_cmd_tag()
  *     sock_tag_list_lock
+ *       (sock_tag_tree)
+ *       get_tag_ref()
+ *         uid_tag_data_tree_lock
+ *           (uid_tag_data_tree)
  *       uid_tag_data_tree_lock
+ *         (proc_qtu_data_tree)
+ *   ctrl_cmd_untag()
+ *     sock_tag_list_lock
+ *     uid_tag_data_tree_lock
+ *
  */
 static LIST_HEAD(iface_stat_list);
 static DEFINE_SPINLOCK(iface_stat_list_lock);
@@ -557,8 +578,8 @@ static struct tag_ref *new_tag_ref(tag_t new_tag,
        utd_entry->num_active_tags++;
        tag_ref_tree_insert(tr_entry, &utd_entry->tag_ref_tree);
        DR_DEBUG("qtaguid: new_tag_ref(0x%llx): "
-                " inserted new tag ref\n",
-                new_tag);
+                " inserted new tag ref %p\n",
+                new_tag, tr_entry);
        return tr_entry;
 
 err_res:
@@ -618,7 +639,8 @@ static struct tag_ref *get_tag_ref(tag_t full_tag,
 static void put_utd_entry(struct uid_tag_data *utd_entry)
 {
        /* Are we done with the UID tag data entry? */
-       if (RB_EMPTY_ROOT(&utd_entry->tag_ref_tree)) {
+       if (RB_EMPTY_ROOT(&utd_entry->tag_ref_tree) &&
+               !utd_entry->num_pqd) {
                DR_DEBUG("qtaguid: %s(): "
                         "erase utd_entry=%p uid=%u "
                         "by pid=%u tgid=%u uid=%u\n", __func__,
@@ -629,9 +651,11 @@ static void put_utd_entry(struct uid_tag_data *utd_entry)
                kfree(utd_entry);
        } else {
                DR_DEBUG("qtaguid: %s(): "
-                        "utd_entry=%p still has %d tags\n", __func__,
-                        utd_entry, utd_entry->num_active_tags);
-               BUG_ON(!utd_entry->num_active_tags);
+                        "utd_entry=%p still has %d tags %d proc_qtu_data\n",
+                        __func__, utd_entry, utd_entry->num_active_tags,
+                        utd_entry->num_pqd);
+               BUG_ON(!(utd_entry->num_active_tags ||
+                        utd_entry->num_pqd));
        }
 }
 
@@ -1309,8 +1333,8 @@ static void if_tag_stat_update(const char *ifname, uid_t uid,
                new_tag_stat = create_if_tag_stat(iface_entry, tag);
                new_tag_stat->parent_counters = uid_tag_counters;
        }
-       spin_unlock_bh(&iface_entry->tag_stat_list_lock);
        tag_stat_update(new_tag_stat, direction, proto, bytes);
+       spin_unlock_bh(&iface_entry->tag_stat_list_lock);
 }
 
 static int iface_netdev_event_handler(struct notifier_block *nb,
@@ -1672,6 +1696,50 @@ ret_res:
        return res;
 }
 
+#ifdef DDEBUG
+/* This function is not in xt_qtaguid_print.c because of locks visibility */
+static void prdebug_full_state(int indent_level, const char *fmt, ...)
+{
+       va_list args;
+       char *fmt_buff;
+       char *buff;
+
+       if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK))
+               return;
+
+       fmt_buff = kasprintf(GFP_ATOMIC,
+                            "qtaguid: %s(): %s {\n", __func__, fmt);
+       BUG_ON(!fmt_buff);
+       va_start(args, fmt);
+       buff = kvasprintf(GFP_ATOMIC,
+                         fmt_buff, args);
+       BUG_ON(!buff);
+       pr_debug("%s", buff);
+       kfree(fmt_buff);
+       kfree(buff);
+       va_end(args);
+
+       spin_lock_bh(&sock_tag_list_lock);
+       prdebug_sock_tag_tree(indent_level, &sock_tag_tree);
+       spin_unlock_bh(&sock_tag_list_lock);
+
+       spin_lock_bh(&sock_tag_list_lock);
+       spin_lock_bh(&uid_tag_data_tree_lock);
+       prdebug_uid_tag_data_tree(indent_level, &uid_tag_data_tree);
+       prdebug_proc_qtu_data_tree(indent_level, &proc_qtu_data_tree);
+       spin_unlock_bh(&uid_tag_data_tree_lock);
+       spin_unlock_bh(&sock_tag_list_lock);
+
+       spin_lock_bh(&iface_stat_list_lock);
+       prdebug_iface_stat_list(indent_level, &iface_stat_list);
+       spin_unlock_bh(&iface_stat_list_lock);
+
+       pr_debug("qtaguid: %s(): }\n", __func__);
+}
+#else
+static void prdebug_full_state(int indent_level, const char *fmt, ...) {}
+#endif
+
 /*
  * Procfs reader to get all active socket tags using style "1)" as described in
  * fs/proc/generic.c
@@ -1761,28 +1829,10 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned,
                (*num_items_returned)++;
        }
 
-#ifdef CDEBUG
        /* Count the following as part of the last item_index */
        if (item_index > items_to_skip) {
-               CT_DEBUG("qtaguid: proc ctrl state debug {\n");
-               spin_lock_bh(&sock_tag_list_lock);
-               prdebug_sock_tag_tree(indent_level, &sock_tag_tree);
-               spin_unlock_bh(&sock_tag_list_lock);
-
-               spin_lock_bh(&uid_tag_data_tree_lock);
-               prdebug_uid_tag_data_tree(indent_level, &uid_tag_data_tree);
-               prdebug_proc_qtu_data_tree(indent_level, &proc_qtu_data_tree);
-               spin_unlock_bh(&uid_tag_data_tree_lock);
-
-               spin_lock_bh(&iface_stat_list_lock);
-               prdebug_iface_stat_list(indent_level, &iface_stat_list);
-               spin_unlock_bh(&iface_stat_list_lock);
-
-               CT_DEBUG("qtaguid: proc ctrl state debug }\n");
-
-
+               prdebug_full_state(indent_level, "proc ctrl");
        }
-#endif
 
        *eof = 1;
        return outp - page;
@@ -1833,9 +1883,9 @@ static int ctrl_cmd_delete(const char *input)
        }
 
        tag = combine_atag_with_uid(acct_tag, uid);
-       CT_DEBUG("qtaguid: ctrl_delete(): "
+       CT_DEBUG("qtaguid: ctrl_delete(%s): "
                 "looking for tag=0x%llx (uid=%u)\n",
-                tag, uid);
+                input, tag, uid);
 
        /* Delete socket tags */
        spin_lock_bh(&sock_tag_list_lock);
@@ -1847,8 +1897,8 @@ static int ctrl_cmd_delete(const char *input)
                if (entry_uid != uid)
                        continue;
 
-               CT_DEBUG("qtaguid: ctrl_delete(): st tag=0x%llx (uid=%u)\n",
-                        st_entry->tag, entry_uid);
+               CT_DEBUG("qtaguid: ctrl_delete(%s): st tag=0x%llx (uid=%u)\n",
+                        input, st_entry->tag, entry_uid);
 
                if (!acct_tag || st_entry->tag == tag) {
                        rb_erase(&st_entry->sock_node, &sock_tag_tree);
@@ -1857,6 +1907,7 @@ static int ctrl_cmd_delete(const char *input)
                        tr_entry = lookup_tag_ref(st_entry->tag, NULL);
                        BUG_ON(tr_entry->num_sock_tags <= 0);
                        tr_entry->num_sock_tags--;
+                       list_del(&st_entry->list);
                }
        }
        spin_unlock_bh(&sock_tag_list_lock);
@@ -1868,8 +1919,9 @@ static int ctrl_cmd_delete(const char *input)
        /* Counter sets are only on the uid tag, not full tag */
        tcs_entry = tag_counter_set_tree_search(&tag_counter_set_tree, tag);
        if (tcs_entry) {
-               CT_DEBUG("qtaguid: ctrl_delete(): "
+               CT_DEBUG("qtaguid: ctrl_delete(%s): "
                         "erase tcs: tag=0x%llx (uid=%u) set=%d\n",
+                        input,
                         tcs_entry->tn.tag,
                         get_uid_from_tag(tcs_entry->tn.tag),
                         tcs_entry->active_set);
@@ -1891,16 +1943,16 @@ static int ctrl_cmd_delete(const char *input)
                        entry_uid = get_uid_from_tag(ts_entry->tn.tag);
                        node = rb_next(node);
 
-                       CT_DEBUG("qtaguid: ctrl_delete(): "
+                       CT_DEBUG("qtaguid: ctrl_delete(%s): "
                                 "ts tag=0x%llx (uid=%u)\n",
-                                ts_entry->tn.tag, entry_uid);
+                                input, ts_entry->tn.tag, entry_uid);
 
                        if (entry_uid != uid)
                                continue;
                        if (!acct_tag || ts_entry->tn.tag == tag) {
-                               CT_DEBUG("qtaguid: ctrl_delete(): "
+                               CT_DEBUG("qtaguid: ctrl_delete(%s): "
                                         "erase ts: %s 0x%llx %u\n",
-                                        iface_entry->ifname,
+                                        input, iface_entry->ifname,
                                         get_atag_from_tag(ts_entry->tn.tag),
                                         entry_uid);
                                rb_erase(&ts_entry->tn.node,
@@ -1920,9 +1972,9 @@ static int ctrl_cmd_delete(const char *input)
                entry_uid = utd_entry->uid;
                node = rb_next(node);
 
-               CT_DEBUG("qtaguid: ctrl_delete(): "
+               CT_DEBUG("qtaguid: ctrl_delete(%s): "
                         "utd uid=%u\n",
-                        entry_uid);
+                        input, entry_uid);
 
                if (entry_uid != uid)
                        continue;
@@ -2377,7 +2429,7 @@ static int pp_stats_line(struct proc_print_info *ppi, int cnt_set)
        return len;
 }
 
-bool pp_sets(struct proc_print_info *ppi)
+static bool pp_sets(struct proc_print_info *ppi)
 {
        int len;
        int counter_set;
@@ -2472,7 +2524,7 @@ static int qtudev_open(struct inode *inode, struct file *file)
 {
        struct uid_tag_data *utd_entry;
        struct proc_qtu_data  *pqd_entry;
-       struct proc_qtu_data  *new_pqd_entry = 0;
+       struct proc_qtu_data  *new_pqd_entry;
        int res;
        bool utd_entry_found;
 
@@ -2514,12 +2566,14 @@ static int qtudev_open(struct inode *inode, struct file *file)
        new_pqd_entry->pid = current->tgid;
        INIT_LIST_HEAD(&new_pqd_entry->sock_tag_list);
        new_pqd_entry->parent_tag_data = utd_entry;
+       utd_entry->num_pqd++;
 
        proc_qtu_data_tree_insert(new_pqd_entry,
                                  &proc_qtu_data_tree);
 
        spin_unlock_bh(&uid_tag_data_tree_lock);
-       DR_DEBUG("qtaguid: tracking data for uid=%u\n", current_fsuid());
+       DR_DEBUG("qtaguid: tracking data for uid=%u in pqd=%p\n",
+                current_fsuid(), new_pqd_entry);
        file->private_data = new_pqd_entry;
        return 0;
 
@@ -2559,12 +2613,6 @@ static int qtudev_release(struct inode *inode, struct file *file)
        spin_lock_bh(&sock_tag_list_lock);
        spin_lock_bh(&uid_tag_data_tree_lock);
 
-       /*
-        * If this proc didn't actually tag anything for itself, or has already
-        * willingly cleaned up itself ...
-        */
-       put_utd_entry(utd_entry);
-
        list_for_each_safe(entry, next, &pqd_entry->sock_tag_list) {
                st_entry = list_entry(entry, struct sock_tag, list);
                DR_DEBUG("qtaguid: %s(): "
@@ -2593,10 +2641,18 @@ static int qtudev_release(struct inode *inode, struct file *file)
                /* Can't sockfd_put() within spinlock, do it later. */
                sock_tag_tree_insert(st_entry, &st_to_free_tree);
 
-               /* Do not put_utd_entry(utd_entry) someone elses utd_entry */
+               /*
+                * Try to free the utd_entry if no other proc_qtu_data is
+                * using it (num_pqd is 0) and it doesn't have active tags
+                * (num_active_tags is 0).
+                */
+               put_utd_entry(utd_entry);
        }
 
        rb_erase(&pqd_entry->node, &proc_qtu_data_tree);
+       BUG_ON(pqd_entry->parent_tag_data->num_pqd < 1);
+       pqd_entry->parent_tag_data->num_pqd--;
+       put_utd_entry(pqd_entry->parent_tag_data);
        kfree(pqd_entry);
        file->private_data = NULL;
 
@@ -2606,7 +2662,8 @@ static int qtudev_release(struct inode *inode, struct file *file)
 
        sock_tag_tree_erase(&st_to_free_tree);
 
-
+       prdebug_full_state(0, "%s(): pid=%u tgid=%u", __func__,
+                          current->pid, current->tgid);
        return 0;
 }
 
index d39ee89..fdce0d0 100644 (file)
 #include <linux/spinlock_types.h>
 #include <linux/workqueue.h>
 
-/* Define/comment out these *DEBUG to compile in/out the pr_debug calls. */
 /* Iface handling */
-#define IDEBUG
+#define IDEBUG_MASK (1<<0)
 /* Iptable Matching. Per packet. */
-#define MDEBUG
+#define MDEBUG_MASK (1<<1)
 /* Red-black tree handling. Per packet. */
-#define RDEBUG
+#define RDEBUG_MASK (1<<2)
 /* procfs ctrl/stats handling */
-#define CDEBUG
+#define CDEBUG_MASK (1<<3)
 /* dev and resource tracking */
-#define DDEBUG
+#define DDEBUG_MASK (1<<4)
 
 /* E.g (IDEBUG_MASK | CDEBUG_MASK | DDEBUG_MASK) */
 #define DEFAULT_DEBUG_MASK 0
 
+/*
+ * (Un)Define these *DEBUG to compile out/in the pr_debug calls.
+ * All undef: text size ~ 0x3030; all def: ~ 0x4404.
+ */
+#define IDEBUG
+#define MDEBUG
+#define RDEBUG
+#define CDEBUG
+#define DDEBUG
 
-#define IDEBUG_MASK (1<<0)
-#define MDEBUG_MASK (1<<1)
-#define RDEBUG_MASK (1<<2)
-#define CDEBUG_MASK (1<<3)
-#define DDEBUG_MASK (1<<4)
-
-#define MSK_DEBUG(mask, ...) do {                       \
-               if (unlikely(debug_mask & (mask)))      \
-                       pr_debug(__VA_ARGS__);          \
+#define MSK_DEBUG(mask, ...) do {                           \
+               if (unlikely(qtaguid_debug_mask & (mask)))  \
+                       pr_debug(__VA_ARGS__);              \
        } while (0)
 #ifdef IDEBUG
 #define IF_DEBUG(...) MSK_DEBUG(IDEBUG_MASK, __VA_ARGS__)
@@ -67,7 +69,7 @@
 #define DR_DEBUG(...) no_printk(__VA_ARGS__)
 #endif
 
-extern uint debug_mask;
+extern uint qtaguid_debug_mask;
 
 /*---------------------------------------------------------------------------*/
 /*
@@ -286,6 +288,8 @@ struct uid_tag_data {
         * For the uid, how many accounting tags have been set.
         */
        int num_active_tags;
+       /* Track the number of proc_qtu_data that reference it */
+       int num_pqd;
        struct rb_root tag_ref_tree;
        /* No tag_node_tree_lock; use uid_tag_data_tree_lock */
 };
index 45d717f..3917678 100644 (file)
@@ -9,9 +9,13 @@
  */
 
 /*
- * There are run-time debug flags enabled via the debug_mask module param, or
- * via the DEFAULT_DEBUG_MASK. See xt_qtaguid_internal.h.
+ * Most of the functions in this file just waste time if DEBUG is not defined.
+ * The matching xt_qtaguid_print.h will static inline empty funcs if the needed
+ * debug flags ore not defined.
+ * Those funcs that fail to allocate memory will panic as there is no need to
+ * hobble allong just pretending to do the requested work.
  */
+
 #define DEBUG
 
 #include <linux/fs.h>
 #include "xt_qtaguid_internal.h"
 #include "xt_qtaguid_print.h"
 
+#ifdef DDEBUG
+
+static void _bug_on_err_or_null(void *ptr)
+{
+       if (IS_ERR_OR_NULL(ptr)) {
+               pr_err("qtaguid: kmalloc failed\n");
+               BUG();
+       }
+}
+
 char *pp_tag_t(tag_t *tag)
 {
+       char *res;
+
        if (!tag)
-               return kasprintf(GFP_ATOMIC, "tag_t@null{}");
-       return kasprintf(GFP_ATOMIC,
-                        "tag_t@%p{tag=0x%llx, uid=%u}",
-                        tag, *tag, get_uid_from_tag(*tag));
+               res = kasprintf(GFP_ATOMIC, "tag_t@null{}");
+       else
+               res = kasprintf(GFP_ATOMIC,
+                               "tag_t@%p{tag=0x%llx, uid=%u}",
+                               tag, *tag, get_uid_from_tag(*tag));
+       _bug_on_err_or_null(res);
+       return res;
 }
 
 char *pp_data_counters(struct data_counters *dc, bool showValues)
 {
+       char *res;
+
        if (!dc)
-               return kasprintf(GFP_ATOMIC, "data_counters@null{}");
-       if (showValues)
-               return kasprintf(
+               res = kasprintf(GFP_ATOMIC, "data_counters@null{}");
+       else if (showValues)
+               res = kasprintf(
                        GFP_ATOMIC, "data_counters@%p{"
                        "set0{"
                        "rx{"
@@ -85,7 +106,9 @@ char *pp_data_counters(struct data_counters *dc, bool showValues)
                        dc->bpc[1][IFS_TX][IFS_PROTO_OTHER].bytes,
                        dc->bpc[1][IFS_TX][IFS_PROTO_OTHER].packets);
        else
-               return kasprintf(GFP_ATOMIC, "data_counters@%p{...}", dc);
+               res = kasprintf(GFP_ATOMIC, "data_counters@%p{...}", dc);
+       _bug_on_err_or_null(res);
+       return res;
 }
 
 char *pp_tag_node(struct tag_node *tn)
@@ -93,12 +116,16 @@ char *pp_tag_node(struct tag_node *tn)
        char *tag_str;
        char *res;
 
-       if (!tn)
-               return kasprintf(GFP_ATOMIC, "tag_node@null{}");
+       if (!tn) {
+               res = kasprintf(GFP_ATOMIC, "tag_node@null{}");
+               _bug_on_err_or_null(res);
+               return res;
+       }
        tag_str = pp_tag_t(&tn->tag);
        res = kasprintf(GFP_ATOMIC,
                        "tag_node@%p{tag=%s}",
                        tn, tag_str);
+       _bug_on_err_or_null(res);
        kfree(tag_str);
        return res;
 }
@@ -108,12 +135,16 @@ char *pp_tag_ref(struct tag_ref *tr)
        char *tn_str;
        char *res;
 
-       if (!tr)
-               return kasprintf(GFP_ATOMIC, "tag_ref@null{}");
+       if (!tr) {
+               res = kasprintf(GFP_ATOMIC, "tag_ref@null{}");
+               _bug_on_err_or_null(res);
+               return res;
+       }
        tn_str = pp_tag_node(&tr->tn);
        res = kasprintf(GFP_ATOMIC,
                        "tag_ref@%p{%s, num_sock_tags=%d}",
                        tr, tn_str, tr->num_sock_tags);
+       _bug_on_err_or_null(res);
        kfree(tn_str);
        return res;
 }
@@ -125,14 +156,18 @@ char *pp_tag_stat(struct tag_stat *ts)
        char *parent_counters_str;
        char *res;
 
-       if (!ts)
-               return kasprintf(GFP_ATOMIC, "tag_stat@null{}");
+       if (!ts) {
+               res = kasprintf(GFP_ATOMIC, "tag_stat@null{}");
+               _bug_on_err_or_null(res);
+               return res;
+       }
        tn_str = pp_tag_node(&ts->tn);
        counters_str = pp_data_counters(&ts->counters, true);
        parent_counters_str = pp_data_counters(ts->parent_counters, false);
        res = kasprintf(GFP_ATOMIC,
                        "tag_stat@%p{%s, counters=%s, parent_counters=%s}",
                        ts, tn_str, counters_str, parent_counters_str);
+       _bug_on_err_or_null(res);
        kfree(tn_str);
        kfree(counters_str);
        kfree(parent_counters_str);
@@ -141,38 +176,42 @@ char *pp_tag_stat(struct tag_stat *ts)
 
 char *pp_iface_stat(struct iface_stat *is)
 {
+       char *res;
        if (!is)
-               return kasprintf(GFP_ATOMIC, "iface_stat@null{}");
-       return kasprintf(GFP_ATOMIC, "iface_stat@%p{"
-                        "list=list_head{...}, "
-                        "ifname=%s, "
-                        "total={rx={bytes=%llu, "
-                        "packets=%llu}, "
-                        "tx={bytes=%llu, "
-                        "packets=%llu}}, "
-                        "last_known_valid=%d, "
-                        "last_known={rx={bytes=%llu, "
-                        "packets=%llu}, "
-                        "tx={bytes=%llu, "
-                        "packets=%llu}}, "
-                        "active=%d, "
-                        "net_dev=%p, "
-                        "proc_ptr=%p, "
-                        "tag_stat_tree=rb_root{...}}",
-                        is,
-                        is->ifname,
-                        is->totals[IFS_RX].bytes,
-                        is->totals[IFS_RX].packets,
-                        is->totals[IFS_TX].bytes,
-                        is->totals[IFS_TX].packets,
-                        is->last_known_valid,
-                        is->last_known[IFS_RX].bytes,
-                        is->last_known[IFS_RX].packets,
-                        is->last_known[IFS_TX].bytes,
-                        is->last_known[IFS_TX].packets,
-                        is->active,
-                        is->net_dev,
-                        is->proc_ptr);
+               res = kasprintf(GFP_ATOMIC, "iface_stat@null{}");
+       else
+               res = kasprintf(GFP_ATOMIC, "iface_stat@%p{"
+                               "list=list_head{...}, "
+                               "ifname=%s, "
+                               "total={rx={bytes=%llu, "
+                               "packets=%llu}, "
+                               "tx={bytes=%llu, "
+                               "packets=%llu}}, "
+                               "last_known_valid=%d, "
+                               "last_known={rx={bytes=%llu, "
+                               "packets=%llu}, "
+                               "tx={bytes=%llu, "
+                               "packets=%llu}}, "
+                               "active=%d, "
+                               "net_dev=%p, "
+                               "proc_ptr=%p, "
+                               "tag_stat_tree=rb_root{...}}",
+                               is,
+                               is->ifname,
+                               is->totals[IFS_RX].bytes,
+                               is->totals[IFS_RX].packets,
+                               is->totals[IFS_TX].bytes,
+                               is->totals[IFS_TX].packets,
+                               is->last_known_valid,
+                               is->last_known[IFS_RX].bytes,
+                               is->last_known[IFS_RX].packets,
+                               is->last_known[IFS_TX].bytes,
+                               is->last_known[IFS_TX].packets,
+                               is->active,
+                               is->net_dev,
+                               is->proc_ptr);
+       _bug_on_err_or_null(res);
+       return res;
 }
 
 char *pp_sock_tag(struct sock_tag *st)
@@ -180,8 +219,11 @@ char *pp_sock_tag(struct sock_tag *st)
        char *tag_str;
        char *res;
 
-       if (!st)
-               return kasprintf(GFP_ATOMIC, "sock_tag@null{}");
+       if (!st) {
+               res = kasprintf(GFP_ATOMIC, "sock_tag@null{}");
+               _bug_on_err_or_null(res);
+               return res;
+       }
        tag_str = pp_tag_t(&st->tag);
        res = kasprintf(GFP_ATOMIC, "sock_tag@%p{"
                        "sock_node=rb_node{...}, "
@@ -190,6 +232,7 @@ char *pp_sock_tag(struct sock_tag *st)
                        st, st->sk, st->socket, atomic_long_read(
                                &st->socket->file->f_count),
                        st->pid, tag_str);
+       _bug_on_err_or_null(res);
        kfree(tag_str);
        return res;
 }
@@ -199,13 +242,16 @@ char *pp_uid_tag_data(struct uid_tag_data *utd)
        char *res;
 
        if (!utd)
-               return kasprintf(GFP_ATOMIC, "uid_tag_data@null{}");
-       res = kasprintf(GFP_ATOMIC, "uid_tag_data@%p{"
-                       "uid=%u, num_active_acct_tags=%d, "
-                       "tag_node_tree=rb_root{...}, "
-                       "proc_qtu_data_tree=rb_root{...}}",
-                       utd, utd->uid,
-                       utd->num_active_tags);
+               res = kasprintf(GFP_ATOMIC, "uid_tag_data@null{}");
+       else
+               res = kasprintf(GFP_ATOMIC, "uid_tag_data@%p{"
+                               "uid=%u, num_active_acct_tags=%d, "
+                               "num_pqd=%d, "
+                               "tag_node_tree=rb_root{...}, "
+                               "proc_qtu_data_tree=rb_root{...}}",
+                               utd, utd->uid,
+                               utd->num_active_tags, utd->num_pqd);
+       _bug_on_err_or_null(res);
        return res;
 }
 
@@ -214,8 +260,11 @@ char *pp_proc_qtu_data(struct proc_qtu_data *pqd)
        char *parent_tag_data_str;
        char *res;
 
-       if (!pqd)
-               return kasprintf(GFP_ATOMIC, "proc_qtu_data@null{}");
+       if (!pqd) {
+               res = kasprintf(GFP_ATOMIC, "proc_qtu_data@null{}");
+               _bug_on_err_or_null(res);
+               return res;
+       }
        parent_tag_data_str = pp_uid_tag_data(pqd->parent_tag_data);
        res = kasprintf(GFP_ATOMIC, "proc_qtu_data@%p{"
                        "node=rb_node{...}, pid=%u, "
@@ -223,6 +272,7 @@ char *pp_proc_qtu_data(struct proc_qtu_data *pqd)
                        "sock_tag_list=list_head{...}}",
                        pqd, pqd->pid, parent_tag_data_str
                );
+       _bug_on_err_or_null(res);
        kfree(parent_tag_data_str);
        return res;
 }
@@ -235,20 +285,29 @@ void prdebug_sock_tag_tree(int indent_level,
        struct sock_tag *sock_tag_entry;
        char *str;
 
+       if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK))
+               return;
+
+       if (RB_EMPTY_ROOT(sock_tag_tree)) {
+               str = "sock_tag_tree=rb_root{}";
+               pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
+               return;
+       }
+
        str = "sock_tag_tree=rb_root{";
-       CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str);
+       pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
        indent_level++;
        for (node = rb_first(sock_tag_tree);
             node;
             node = rb_next(node)) {
                sock_tag_entry = rb_entry(node, struct sock_tag, sock_node);
                str = pp_sock_tag(sock_tag_entry);
-               CT_DEBUG("%*d: %s,\n", indent_level*2, indent_level, str);
+               pr_debug("%*d: %s,\n", indent_level*2, indent_level, str);
                kfree(str);
        }
        indent_level--;
        str = "}";
-       CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str);
+       pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
 }
 
 void prdebug_sock_tag_list(int indent_level,
@@ -257,17 +316,26 @@ void prdebug_sock_tag_list(int indent_level,
        struct sock_tag *sock_tag_entry;
        char *str;
 
+       if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK))
+               return;
+
+       if (list_empty(sock_tag_list)) {
+               str = "sock_tag_list=list_head{}";
+               pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
+               return;
+       }
+
        str = "sock_tag_list=list_head{";
-       CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str);
+       pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
        indent_level++;
        list_for_each_entry(sock_tag_entry, sock_tag_list, list) {
                str = pp_sock_tag(sock_tag_entry);
-               CT_DEBUG("%*d: %s,\n", indent_level*2, indent_level, str);
+               pr_debug("%*d: %s,\n", indent_level*2, indent_level, str);
                kfree(str);
        }
        indent_level--;
        str = "}";
-       CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str);
+       pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
 }
 
 void prdebug_proc_qtu_data_tree(int indent_level,
@@ -277,8 +345,17 @@ void prdebug_proc_qtu_data_tree(int indent_level,
        struct rb_node *node;
        struct proc_qtu_data *proc_qtu_data_entry;
 
+       if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK))
+               return;
+
+       if (RB_EMPTY_ROOT(proc_qtu_data_tree)) {
+               str = "proc_qtu_data_tree=rb_root{}";
+               pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
+               return;
+       }
+
        str = "proc_qtu_data_tree=rb_root{";
-       CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str);
+       pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
        indent_level++;
        for (node = rb_first(proc_qtu_data_tree);
             node;
@@ -287,7 +364,7 @@ void prdebug_proc_qtu_data_tree(int indent_level,
                                               struct proc_qtu_data,
                                               node);
                str = pp_proc_qtu_data(proc_qtu_data_entry);
-               CT_DEBUG("%*d: %s,\n", indent_level*2, indent_level,
+               pr_debug("%*d: %s,\n", indent_level*2, indent_level,
                         str);
                kfree(str);
                indent_level++;
@@ -298,7 +375,7 @@ void prdebug_proc_qtu_data_tree(int indent_level,
        }
        indent_level--;
        str = "}";
-       CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str);
+       pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
 }
 
 void prdebug_tag_ref_tree(int indent_level, struct rb_root *tag_ref_tree)
@@ -307,8 +384,17 @@ void prdebug_tag_ref_tree(int indent_level, struct rb_root *tag_ref_tree)
        struct rb_node *node;
        struct tag_ref *tag_ref_entry;
 
+       if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK))
+               return;
+
+       if (RB_EMPTY_ROOT(tag_ref_tree)) {
+               str = "tag_ref_tree{}";
+               pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
+               return;
+       }
+
        str = "tag_ref_tree{";
-       CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str);
+       pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
        indent_level++;
        for (node = rb_first(tag_ref_tree);
             node;
@@ -317,13 +403,13 @@ void prdebug_tag_ref_tree(int indent_level, struct rb_root *tag_ref_tree)
                                         struct tag_ref,
                                         tn.node);
                str = pp_tag_ref(tag_ref_entry);
-               CT_DEBUG("%*d: %s,\n", indent_level*2, indent_level,
+               pr_debug("%*d: %s,\n", indent_level*2, indent_level,
                         str);
                kfree(str);
        }
        indent_level--;
        str = "}";
-       CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str);
+       pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
 }
 
 void prdebug_uid_tag_data_tree(int indent_level,
@@ -333,8 +419,17 @@ void prdebug_uid_tag_data_tree(int indent_level,
        struct rb_node *node;
        struct uid_tag_data *uid_tag_data_entry;
 
+       if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK))
+               return;
+
+       if (RB_EMPTY_ROOT(uid_tag_data_tree)) {
+               str = "uid_tag_data_tree=rb_root{}";
+               pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
+               return;
+       }
+
        str = "uid_tag_data_tree=rb_root{";
-       CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str);
+       pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
        indent_level++;
        for (node = rb_first(uid_tag_data_tree);
             node;
@@ -342,7 +437,7 @@ void prdebug_uid_tag_data_tree(int indent_level,
                uid_tag_data_entry = rb_entry(node, struct uid_tag_data,
                                              node);
                str = pp_uid_tag_data(uid_tag_data_entry);
-               CT_DEBUG("%*d: %s,\n", indent_level*2, indent_level, str);
+               pr_debug("%*d: %s,\n", indent_level*2, indent_level, str);
                kfree(str);
                if (!RB_EMPTY_ROOT(&uid_tag_data_entry->tag_ref_tree)) {
                        indent_level++;
@@ -353,7 +448,7 @@ void prdebug_uid_tag_data_tree(int indent_level,
        }
        indent_level--;
        str = "}";
-       CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str);
+       pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
 }
 
 void prdebug_tag_stat_tree(int indent_level,
@@ -363,21 +458,30 @@ void prdebug_tag_stat_tree(int indent_level,
        struct rb_node *node;
        struct tag_stat *ts_entry;
 
+       if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK))
+               return;
+
+       if (RB_EMPTY_ROOT(tag_stat_tree)) {
+               str = "tag_stat_tree{}";
+               pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
+               return;
+       }
+
        str = "tag_stat_tree{";
-       CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str);
+       pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
        indent_level++;
        for (node = rb_first(tag_stat_tree);
             node;
             node = rb_next(node)) {
                ts_entry = rb_entry(node, struct tag_stat, tn.node);
                str = pp_tag_stat(ts_entry);
-               CT_DEBUG("%*d: %s\n", indent_level*2, indent_level,
+               pr_debug("%*d: %s\n", indent_level*2, indent_level,
                         str);
                kfree(str);
        }
        indent_level--;
        str = "}";
-       CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str);
+       pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
 }
 
 void prdebug_iface_stat_list(int indent_level,
@@ -386,12 +490,21 @@ void prdebug_iface_stat_list(int indent_level,
        char *str;
        struct iface_stat *iface_entry;
 
+       if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK))
+               return;
+
+       if (list_empty(iface_stat_list)) {
+               str = "iface_stat_list=list_head{}";
+               pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
+               return;
+       }
+
        str = "iface_stat_list=list_head{";
-       CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str);
+       pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
        indent_level++;
        list_for_each_entry(iface_entry, iface_stat_list, list) {
                str = pp_iface_stat(iface_entry);
-               CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str);
+               pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
                kfree(str);
 
                spin_lock_bh(&iface_entry->tag_stat_list_lock);
@@ -405,9 +518,10 @@ void prdebug_iface_stat_list(int indent_level,
        }
        indent_level--;
        str = "}";
-       CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str);
+       pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
 }
 
+#endif  /* ifdef DDEBUG */
 /*------------------------------------------*/
 static const char * const netdev_event_strings[] = {
        "netdev_unknown",
index 3886228..b63871a 100644 (file)
@@ -12,6 +12,8 @@
 
 #include "xt_qtaguid_internal.h"
 
+#ifdef DDEBUG
+
 char *pp_tag_t(tag_t *tag);
 char *pp_data_counters(struct data_counters *dc, bool showValues);
 char *pp_tag_node(struct tag_node *tn);
@@ -37,6 +39,82 @@ void prdebug_tag_stat_tree(int indent_level,
 void prdebug_iface_stat_list(int indent_level,
                             struct list_head *iface_stat_list);
 
+#else
+
+/*------------------------------------------*/
+static inline char *pp_tag_t(tag_t *tag)
+{
+       return NULL;
+}
+static inline char *pp_data_counters(struct data_counters *dc, bool showValues)
+{
+       return NULL;
+}
+static inline char *pp_tag_node(struct tag_node *tn)
+{
+       return NULL;
+}
+static inline char *pp_tag_ref(struct tag_ref *tr)
+{
+       return NULL;
+}
+static inline char *pp_tag_stat(struct tag_stat *ts)
+{
+       return NULL;
+}
+static inline char *pp_iface_stat(struct iface_stat *is)
+{
+       return NULL;
+}
+static inline char *pp_sock_tag(struct sock_tag *st)
+{
+       return NULL;
+}
+static inline char *pp_uid_tag_data(struct uid_tag_data *qtd)
+{
+       return NULL;
+}
+static inline char *pp_proc_qtu_data(struct proc_qtu_data *pqd)
+{
+       return NULL;
+}
+
+/*------------------------------------------*/
+static inline
+void prdebug_sock_tag_list(int indent_level,
+                          struct list_head *sock_tag_list)
+{
+}
+static inline
+void prdebug_sock_tag_tree(int indent_level,
+                          struct rb_root *sock_tag_tree)
+{
+}
+static inline
+void prdebug_proc_qtu_data_tree(int indent_level,
+                               struct rb_root *proc_qtu_data_tree)
+{
+}
+static inline
+void prdebug_tag_ref_tree(int indent_level, struct rb_root *tag_ref_tree)
+{
+}
+static inline
+void prdebug_uid_tag_data_tree(int indent_level,
+                              struct rb_root *uid_tag_data_tree)
+{
+}
+static inline
+void prdebug_tag_stat_tree(int indent_level,
+                          struct rb_root *tag_stat_tree)
+{
+}
+static inline
+void prdebug_iface_stat_list(int indent_level,
+                            struct list_head *iface_stat_list)
+{
+}
+#endif
 /*------------------------------------------*/
 const char *netdev_evt_str(int netdev_event);
 #endif  /* ifndef __XT_QTAGUID_PRINT_H__ */