Index: ksyslog/trunk/ksyslog.c
===================================================================
--- ksyslog/trunk/ksyslog.c	(revision 247)
+++ ksyslog/trunk/ksyslog.c	(revision 250)
@@ -12,4 +12,6 @@
 #include <linux/namei.h>
 #include <linux/proc_fs.h>
+#include <linux/u64_stats_sync.h>
+#include <linux/percpu.h>
 #include <net/udp.h>
 #include "compat.h"
@@ -27,7 +29,6 @@
 static struct proc_dir_entry *ksyslog_procdir = NULL;
 static struct proc_dir_entry *ksyslog_proc_queue = NULL;
-static struct proc_dir_entry *ksyslog_proc_nr_queued = NULL;
-static struct proc_dir_entry *ksyslog_proc_nr_written = NULL;
-static struct proc_dir_entry *ksyslog_proc_nr_dropped = NULL;
+static struct proc_dir_entry *ksyslog_proc_size = NULL;
+static struct proc_dir_entry *ksyslog_proc_stats = NULL;
 #endif
 
@@ -35,5 +36,5 @@
 static ushort ksyslog_port = 514;
 static char *ksyslog_path = "/var/log/ksyslog.log";
-static ulong ksyslog_queue_length = 4096;
+static ulong ksyslog_queue_size_max = 4096;
 static ulong ksyslog_flush_interval = 45;  /* milliseconds */
 
@@ -41,8 +42,8 @@
 module_param(ksyslog_port, ushort, 0444);
 module_param(ksyslog_path, charp, 0644);
-module_param(ksyslog_queue_length, ulong, 0644);
+module_param(ksyslog_queue_size_max, ulong, 0644);
 module_param(ksyslog_flush_interval, ulong, 0644);
 
-static void
+static int
 ksyslog_queue_init(struct ksyslog_queue *queue)
 {
@@ -50,7 +51,17 @@
 	INIT_LIST_HEAD(&queue->head);
 	spin_lock_init(&queue->lock);
-	atomic64_set(&queue->nr_queued, 0);
-	atomic64_set(&queue->nr_written, 0);
-	atomic64_set(&queue->nr_dropped, 0);
+	atomic64_set(&queue->size, 0);
+	queue->stats = alloc_percpu(struct ksyslog_stats);
+	if (unlikely(queue->stats == NULL))
+		return -ENOMEM;
+	return 0;
+}
+
+static void
+ksyslog_queue_uninit(struct ksyslog_queue *queue)
+{
+	if (likely(queue->stats))
+		free_percpu(queue->stats);
+	queue->stats = NULL;
 }
 
@@ -114,5 +125,5 @@
 ksyslog_drop_warning(const struct ksyslog_entry *entry)
 {
-	pr_warn("dropped: %llu %s.%s %u.%u.%u.%u %.*s\n",
+	pr_warn("ksyslog: dropped: %llu %s.%s %u.%u.%u.%u %.*s\n",
 		timeval_to_ns(&entry->tv) / 1000 / 1000 / 1000,
 		ksyslog_facility_str(entry->facility),
@@ -228,8 +239,8 @@
 ksyslog_entry_add(struct ksyslog_queue *queue, struct ksyslog_entry *entry)
 {
-	if (unlikely(atomic64_read(&queue->nr_queued) >= ksyslog_queue_length))
+	if (unlikely(atomic64_read(&queue->size) >= ksyslog_queue_size_max))
 		return -ENOBUFS;
 	list_add_tail_rcu(&entry->list, &queue->head);
-	WARN_ON(atomic64_inc_return(&queue->nr_queued) > ksyslog_queue_length);
+	WARN_ON(atomic64_inc_return(&queue->size) > ksyslog_queue_size_max);
 	return 0;
 }
@@ -238,5 +249,5 @@
 ksyslog_entry_del(struct ksyslog_queue *queue, struct ksyslog_entry *entry, bool free)
 {
-	WARN_ON(atomic64_dec_return(&queue->nr_queued) < 0);
+	WARN_ON(atomic64_dec_return(&queue->size) < 0);
 	list_del_rcu(&entry->list);
 	if (free)
@@ -261,6 +272,6 @@
 		ksyslog_entry_del(from, entry, false);
 		if (unlikely(ksyslog_entry_add(to, entry))) {
-			atomic64_inc(&from->nr_dropped);
-			atomic64_inc(&to->nr_dropped);
+			ksyslog_stats_add_drop(from, entry->length);
+			ksyslog_stats_add_drop(to, entry->length);
 			ksyslog_drop_warning(entry);
 			call_rcu(&entry->rcu, ksyslog_entry_free);
@@ -288,5 +299,6 @@
 	struct ksyslog_queue write_queue;
 
-	ksyslog_queue_init(&write_queue);
+	if (ksyslog_queue_init(&write_queue))
+		goto out;
 
 	spin_lock_bh(&ksyslog_queue.lock);
@@ -294,5 +306,5 @@
 	spin_unlock_bh(&ksyslog_queue.lock);
 
-	if (atomic64_read(&write_queue.nr_queued) <= 0)
+	if (atomic64_read(&write_queue.size) <= 0)
 		goto out;
 
@@ -325,5 +337,5 @@
 		}
 
-		atomic64_inc(&ksyslog_queue.nr_written);
+		ksyslog_stats_add_write(&ksyslog_queue, entry->length);
 		kfree(buf);
 		call_rcu(&entry->rcu, ksyslog_entry_free);
@@ -333,5 +345,5 @@
 		spin_lock_bh(&ksyslog_queue.lock);
 		if (unlikely(ksyslog_entry_add(&ksyslog_queue, entry))) {
-			atomic64_inc(&ksyslog_queue.nr_dropped);
+			ksyslog_stats_add_drop(&ksyslog_queue, entry->length);
 			ksyslog_drop_warning(entry);
 			call_rcu(&entry->rcu, ksyslog_entry_free);
@@ -344,4 +356,5 @@
 
 out:
+	ksyslog_queue_uninit(&write_queue);
 	ksyslog_work_register(ksyslog_flush_interval);
 }
@@ -355,16 +368,27 @@
 	struct ksyslog_entry *entry;
 
-	if (unlikely(skb_linearize(skb)))
-		goto err;
+	if (unlikely(skb_linearize(skb))) {
+		ksyslog_stats_add_drop(&ksyslog_queue, skb->len);
+		goto out;
+	}
 
 	iph = ip_hdr(skb);
 	udph = udp_hdr(skb);
 
-	if (unlikely(!skb_pull(skb, sizeof(*udph))))
-		goto err;
+	if (unlikely(!skb_pull(skb, sizeof(*udph)))) {
+		ksyslog_stats_add_drop(&ksyslog_queue, skb->len);
+		goto out;
+	}
 
 	entry = ksyslog_entry_create(skb, iph, udph);
-	if (unlikely(IS_ERR(entry)))
-		goto err;
+	if (unlikely(IS_ERR(entry))) {
+		if (PTR_ERR(entry) == -EINVAL) {
+			ksyslog_stats_add_discard(&ksyslog_queue, skb->len);
+			goto out;
+		}
+
+		ksyslog_stats_add_drop(&ksyslog_queue, skb->len);
+		goto out;
+	}
 
 	spin_lock_bh(&ksyslog_queue.lock);
@@ -373,7 +397,8 @@
 
 	if (unlikely(err)) {
+		ksyslog_stats_add_drop(&ksyslog_queue, entry->length);
 		ksyslog_drop_warning(entry);
 		ksyslog_entry_free(&entry->rcu);
-		goto err;
+		goto out;
 	}
 
@@ -381,8 +406,4 @@
 	consume_skb(skb);
 	return 0;
-
-err:
-	atomic64_inc(&ksyslog_queue.nr_dropped);
-	goto out;
 }
 
@@ -460,45 +481,71 @@
 
 static int
-ksyslog_nr_queued_seq_show(struct seq_file *seq, void *v)
-{
-	seq_printf(seq, "%lu\n", atomic64_read(&ksyslog_queue.nr_queued));
+ksyslog_size_seq_show(struct seq_file *seq, void *v)
+{
+	seq_printf(seq, "%lu\n", atomic64_read(&ksyslog_queue.size));
 	return 0;
 }
 
 static int
-ksyslog_nr_queued_seq_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, ksyslog_nr_queued_seq_show, PDE_DATA(inode));
-}
-
-static int
-ksyslog_nr_written_seq_show(struct seq_file *seq, void *v)
-{
-	seq_printf(seq, "%lu\n", atomic64_read(&ksyslog_queue.nr_written));
+ksyslog_size_seq_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, ksyslog_size_seq_show, PDE_DATA(inode));
+}
+
+static int
+ksyslog_stats_seq_show(struct seq_file *seq, void *v)
+{
+	int i;
+	struct ksyslog_stats stats;
+
+	memset(&stats, 0, sizeof(stats));
+
+	for_each_possible_cpu(i) {
+		const struct ksyslog_stats *percpu_stats;
+		struct ksyslog_stats local_stats;
+		unsigned int start;
+
+		percpu_stats = per_cpu_ptr(ksyslog_queue.stats, i);
+
+		do {
+			start = u64_stats_fetch_begin_bh(&percpu_stats->sync);
+			local_stats = *percpu_stats;
+		} while (u64_stats_fetch_retry_bh(&percpu_stats->sync, start));
+
+		stats.write_bytes += local_stats.write_bytes;
+		stats.write_packets += local_stats.write_packets;
+		stats.drop_bytes += local_stats.drop_bytes;
+		stats.drop_packets += local_stats.drop_packets;
+		stats.discard_bytes += local_stats.discard_bytes;
+		stats.discard_packets += local_stats.discard_packets;
+	}
+
+	seq_puts(seq,   "{\n");
+	seq_puts(seq,   "  \"write\": {\n");
+	seq_printf(seq, "    \"bytes\":   \"%llu\",\n", stats.write_bytes);
+	seq_printf(seq, "    \"packets\": \"%llu\"\n", stats.write_packets);
+	seq_puts(seq,   "  },\n");
+	seq_puts(seq,   "  \"drop\": {\n");
+	seq_printf(seq, "    \"bytes\":   \"%llu\",\n", stats.drop_bytes);
+	seq_printf(seq, "    \"packets\": \"%llu\"\n", stats.drop_packets);
+	seq_puts(seq,   "  },\n");
+	seq_puts(seq,   "  \"discard\": {\n");
+	seq_printf(seq, "    \"bytes\":   \"%llu\",\n", stats.discard_bytes);
+	seq_printf(seq, "    \"packets\": \"%llu\"\n", stats.discard_packets);
+	seq_puts(seq,   "  }\n");
+	seq_puts(seq,   "}\n");
+
 	return 0;
 }
 
 static int
-ksyslog_nr_written_seq_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, ksyslog_nr_written_seq_show, PDE_DATA(inode));
-}
-
-static int
-ksyslog_nr_dropped_seq_show(struct seq_file *seq, void *v)
-{
-	seq_printf(seq, "%lu\n", atomic64_read(&ksyslog_queue.nr_dropped));
-	return 0;
-}
-
-static int
-ksyslog_nr_dropped_seq_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, ksyslog_nr_dropped_seq_show, PDE_DATA(inode));
-}
-
-static struct file_operations ksyslog_nr_queued_fops = {
+ksyslog_stats_seq_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, ksyslog_stats_seq_show, PDE_DATA(inode));
+}
+
+static struct file_operations ksyslog_size_fops = {
 	.owner   = THIS_MODULE,
-	.open    = ksyslog_nr_queued_seq_open,
+	.open    = ksyslog_size_seq_open,
 	.read    = seq_read,
 	.llseek  = seq_lseek,
@@ -506,7 +553,7 @@
 };
 
-static struct file_operations ksyslog_nr_written_fops = {
+static struct file_operations ksyslog_stats_fops = {
 	.owner   = THIS_MODULE,
-	.open    = ksyslog_nr_written_seq_open,
+	.open    = ksyslog_stats_seq_open,
 	.read    = seq_read,
 	.llseek  = seq_lseek,
@@ -514,12 +561,4 @@
 };
 
-static struct file_operations ksyslog_nr_dropped_fops = {
-	.owner   = THIS_MODULE,
-	.open    = ksyslog_nr_dropped_seq_open,
-	.read    = seq_read,
-	.llseek  = seq_lseek,
-	.release = single_release,
-};
-
 static void
 ksyslog_proc_destroy(void)
@@ -529,15 +568,11 @@
 	ksyslog_proc_queue = NULL;
 
-	if (ksyslog_proc_nr_queued)
-		remove_proc_entry("nr_queued", ksyslog_procdir);
-	ksyslog_proc_nr_queued = NULL;
-
-	if (ksyslog_proc_nr_written)
-		remove_proc_entry("nr_written", ksyslog_procdir);
-	ksyslog_proc_nr_written = NULL;
-
-	if (ksyslog_proc_nr_dropped)
-		remove_proc_entry("nr_dropped", ksyslog_procdir);
-	ksyslog_proc_nr_dropped = NULL;
+	if (ksyslog_proc_size)
+		remove_proc_entry("size", ksyslog_procdir);
+	ksyslog_proc_size = NULL;
+
+	if (ksyslog_proc_stats)
+		remove_proc_entry("stats", ksyslog_procdir);
+	ksyslog_proc_stats = NULL;
 
 	if (ksyslog_procdir)
@@ -551,5 +586,5 @@
 	ksyslog_procdir = proc_mkdir("ksyslog", NULL);
 	if (ksyslog_procdir == NULL) {
-		pr_err("proc_mkdir failed\n");
+		pr_err("ksyslog: proc_mkdir failed\n");
 		goto err;
 	}
@@ -558,26 +593,19 @@
 					      &ksyslog_queue_fops, &ksyslog_queue.head);
 	if (ksyslog_proc_queue == NULL) {
-		pr_err("proc_create(queue) failed\n");
-		goto err;
-	}
-
-	ksyslog_proc_nr_queued = proc_create("nr_queued", S_IRUGO, ksyslog_procdir,
-					     &ksyslog_nr_queued_fops);
-	if (ksyslog_proc_nr_queued == NULL) {
-		pr_err("proc_create(nr_queued) failed\n");
-		goto err;
-	}
-
-	ksyslog_proc_nr_written = proc_create("nr_written", S_IRUGO, ksyslog_procdir,
-					      &ksyslog_nr_written_fops);
-	if (ksyslog_proc_nr_written == NULL) {
-		pr_err("proc_create(nr_written) failed\n");
-		goto err;
-	}
-
-	ksyslog_proc_nr_dropped = proc_create("nr_dropped", S_IRUGO, ksyslog_procdir,
-					      &ksyslog_nr_dropped_fops);
-	if (ksyslog_proc_nr_dropped == NULL) {
-		pr_err("proc_create(nr_dropped) failed\n");
+		pr_err("ksyslog: proc_create(queue) failed\n");
+		goto err;
+	}
+
+	ksyslog_proc_size = proc_create("size", S_IRUGO, ksyslog_procdir,
+					&ksyslog_size_fops);
+	if (ksyslog_proc_size == NULL) {
+		pr_err("ksyslog: proc_create(size) failed\n");
+		goto err;
+	}
+
+	ksyslog_proc_stats = proc_create("stats", S_IRUGO, ksyslog_procdir,
+					 &ksyslog_stats_fops);
+	if (ksyslog_proc_stats == NULL) {
+		pr_err("ksyslog: proc_create(stats) failed\n");
 		goto err;
 	}
@@ -610,4 +638,6 @@
 	ksyslog_entry_destroy(&ksyslog_queue);
 	rcu_barrier();
+
+	ksyslog_queue_uninit(&ksyslog_queue);
 }
 
@@ -618,5 +648,7 @@
 	struct sockaddr_in sin;
 
-	ksyslog_queue_init(&ksyslog_queue);
+	err = ksyslog_queue_init(&ksyslog_queue);
+	if (err)
+		goto err;
 
 #ifdef CONFIG_PROC_FS
@@ -628,5 +660,5 @@
 	ksyslog_wq = create_singlethread_workqueue("ksyslog");
 	if (ksyslog_wq == NULL) {
-		pr_err("create_workqueue failed\n");
+		pr_err("ksyslog: create_workqueue failed\n");
 		err = -ENOMEM;
 		goto err;
@@ -637,5 +669,5 @@
 	err = sock_create(AF_INET, SOCK_DGRAM, 0, &ksyslog_rcv_sk);
 	if (err) {
-		pr_err("sock_create failed\n");
+		pr_err("ksyslog: sock_create failed\n");
 		goto err;
 	}
@@ -648,5 +680,5 @@
 			  sizeof(struct sockaddr_in));
 	if (err) {
-		pr_err("kernel_bind failed\n");
+		pr_err("ksyslog: kernel_bind failed\n");
 		goto err;
 	}
