Index: ksyslog/trunk/ksyslog.c
===================================================================
--- ksyslog/trunk/ksyslog.c	(revision 240)
+++ ksyslog/trunk/ksyslog.c	(revision 241)
@@ -26,4 +26,6 @@
 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;
 #endif
 
@@ -31,5 +33,5 @@
 static ushort ksyslog_port = 514;
 static char *ksyslog_path = "/var/log/ksyslog.log";
-static uint ksyslog_queue_max = 2048;
+static ulong ksyslog_queue_length = 2048;
 static ulong ksyslog_flush_interval = 45;  /* milliseconds */
 
@@ -37,6 +39,16 @@
 module_param(ksyslog_port, ushort, 0444);
 module_param(ksyslog_path, charp, 0644);
-module_param(ksyslog_queue_max, uint, 0644);
+module_param(ksyslog_queue_length, ulong, 0644);
 module_param(ksyslog_flush_interval, ulong, 0644);
+
+static void
+ksyslog_queue_init(struct ksyslog_queue *queue)
+{
+	memset(queue, 0, sizeof(*queue));
+	INIT_LIST_HEAD(&queue->head);
+	atomic64_set(&queue->nr_queued, 0);
+	atomic64_set(&queue->nr_written, 0);
+	atomic64_set(&queue->nr_dropped, 0);
+}
 
 static int
@@ -214,8 +226,8 @@
 ksyslog_entry_add(struct ksyslog_queue *queue, struct ksyslog_entry *entry)
 {
-	if (unlikely(queue->length >= ksyslog_queue_max))
+	if (unlikely(atomic64_read(&queue->nr_queued) >= ksyslog_queue_length))
 		return -ENOBUFS;
 	list_add_tail_rcu(&entry->list, &queue->head);
-	WARN_ON(++queue->length > ksyslog_queue_max);
+	WARN_ON(atomic64_inc_return(&queue->nr_queued) > ksyslog_queue_length);
 	return 0;
 }
@@ -224,5 +236,5 @@
 ksyslog_entry_del(struct ksyslog_queue *queue, struct ksyslog_entry *entry, bool free)
 {
-	WARN_ON(--queue->length < 0);
+	WARN_ON(atomic64_dec_return(&queue->nr_queued) < 0);
 	list_del_rcu(&entry->list);
 	if (free)
@@ -247,4 +259,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_drop_warning(entry);
 			call_rcu(&entry->rcu, ksyslog_entry_free);
@@ -272,6 +286,5 @@
 	struct ksyslog_queue write_queue;
 
-	memset(&write_queue, 0, sizeof(write_queue));
-	INIT_LIST_HEAD(&write_queue.head);
+	ksyslog_queue_init(&write_queue);
 
 	spin_lock_bh(&ksyslog_queue_lock);
@@ -279,5 +292,5 @@
 	spin_unlock_bh(&ksyslog_queue_lock);
 
-	if (write_queue.length <= 0)
+	if (atomic64_read(&write_queue.nr_queued) <= 0)
 		goto out;
 
@@ -310,4 +323,5 @@
 		}
 
+		atomic64_inc(&ksyslog_queue.nr_written);
 		kfree(buf);
 		call_rcu(&entry->rcu, ksyslog_entry_free);
@@ -317,4 +331,5 @@
 		spin_lock_bh(&ksyslog_queue_lock);
 		if (unlikely(ksyslog_entry_add(&ksyslog_queue, entry))) {
+			atomic64_inc(&ksyslog_queue.nr_dropped);
 			ksyslog_drop_warning(entry);
 			call_rcu(&entry->rcu, ksyslog_entry_free);
@@ -338,21 +353,16 @@
 	struct ksyslog_entry *entry;
 
-	err = skb_linearize(skb);
-	if (unlikely(err))
-		goto out;
+	if (unlikely(skb_linearize(skb)))
+		goto err;
 
 	iph = ip_hdr(skb);
 	udph = udp_hdr(skb);
 
-	if (unlikely(!skb_pull(skb, sizeof(*udph)))) {
-		err = -EINVAL;
-		goto out;
-	}
+	if (unlikely(!skb_pull(skb, sizeof(*udph))))
+		goto err;
 
 	entry = ksyslog_entry_create(skb, iph, udph);
-	if (unlikely(IS_ERR(entry))) {
-		err = PTR_ERR(entry);
-		goto out;
-	}
+	if (unlikely(IS_ERR(entry)))
+		goto err;
 
 	spin_lock_bh(&ksyslog_queue_lock);
@@ -360,13 +370,17 @@
 	spin_unlock_bh(&ksyslog_queue_lock);
 
-	if (unlikely(err))
+	if (unlikely(err)) {
+		ksyslog_drop_warning(entry);
 		ksyslog_entry_free(&entry->rcu);
+		goto err;
+	}
 
 out:
-	if (unlikely(err))
-		UDP_INC_STATS_BH(sock_net(sk), UDP_MIB_INERRORS, IS_UDPLITE(sk));
-
 	consume_skb(skb);
 	return 0;
+
+err:
+	atomic64_inc(&ksyslog_queue.nr_dropped);
+	goto out;
 }
 
@@ -446,5 +460,5 @@
 ksyslog_nr_queued_seq_show(struct seq_file *seq, void *v)
 {
-	seq_printf(seq, "%u\n", ksyslog_queue.length);
+	seq_printf(seq, "%lu\n", atomic64_read(&ksyslog_queue.nr_queued));
 	return 0;
 }
@@ -454,4 +468,30 @@
 {
 	return single_open(file, ksyslog_nr_queued_seq_show, PDE(inode)->data);
+}
+
+static int
+ksyslog_nr_written_seq_show(struct seq_file *seq, void *v)
+{
+	seq_printf(seq, "%lu\n", atomic64_read(&ksyslog_queue.nr_written));
+	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(inode)->data);
+}
+
+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(inode)->data);
 }
 
@@ -464,4 +504,44 @@
 };
 
+static struct file_operations ksyslog_nr_written_fops = {
+	.owner   = THIS_MODULE,
+	.open    = ksyslog_nr_written_seq_open,
+	.read    = seq_read,
+	.llseek  = seq_lseek,
+	.release = single_release,
+};
+
+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)
+{
+	if (ksyslog_proc_queue)
+		remove_proc_entry(ksyslog_proc_queue->name, ksyslog_proc_queue->parent);
+	ksyslog_proc_queue = NULL;
+
+	if (ksyslog_proc_nr_queued)
+		remove_proc_entry(ksyslog_proc_nr_queued->name, ksyslog_proc_nr_queued->parent);
+	ksyslog_proc_nr_queued = NULL;
+
+	if (ksyslog_proc_nr_written)
+		remove_proc_entry(ksyslog_proc_nr_written->name, ksyslog_proc_nr_written->parent);
+	ksyslog_proc_nr_written = NULL;
+
+	if (ksyslog_proc_nr_dropped)
+		remove_proc_entry(ksyslog_proc_nr_dropped->name, ksyslog_proc_nr_dropped->parent);
+	ksyslog_proc_nr_dropped = NULL;
+
+	if (ksyslog_procdir)
+		remove_proc_entry(ksyslog_procdir->name, ksyslog_procdir->parent);
+	ksyslog_procdir = NULL;
+}
+
 static int
 ksyslog_proc_init(void)
@@ -470,5 +550,5 @@
 	if (ksyslog_procdir == NULL) {
 		pr_err("proc_mkdir failed\n");
-		return -ENOMEM;
+		goto err;
 	}
 
@@ -476,35 +556,34 @@
 					      &ksyslog_queue_fops, &ksyslog_queue.head);
 	if (ksyslog_proc_queue == NULL) {
-		remove_proc_entry(ksyslog_procdir->name, ksyslog_procdir->parent);
-		pr_err("proc_create_data failed\n");
-		return -ENOMEM;
+		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);
+					      &ksyslog_nr_queued_fops);
 	if (ksyslog_proc_nr_queued == NULL) {
-		remove_proc_entry(ksyslog_proc_queue->name, ksyslog_proc_queue->parent);
-		remove_proc_entry(ksyslog_procdir->name, ksyslog_procdir->parent);
-		pr_err("proc_create failed\n");
-		return -ENOMEM;
-	}
-
-	return 0;
-}
-
-static void
-ksyslog_proc_destroy(void)
-{
-	if (ksyslog_proc_nr_queued)
-		remove_proc_entry(ksyslog_proc_nr_queued->name, ksyslog_proc_nr_queued->parent);
-	ksyslog_proc_nr_queued = NULL;
-
-	if (ksyslog_proc_queue)
-		remove_proc_entry(ksyslog_proc_queue->name, ksyslog_proc_queue->parent);
-	ksyslog_proc_queue = NULL;
-
-	if (ksyslog_procdir)
-		remove_proc_entry(ksyslog_procdir->name, ksyslog_procdir->parent);
-	ksyslog_procdir = 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");
+		goto err;
+	}
+
+	return 0;
+
+err:
+	ksyslog_proc_destroy();
+	return -ENOMEM;
 }
 #endif
@@ -537,5 +616,5 @@
 	struct sockaddr_in sin;
 
-	INIT_LIST_HEAD(&ksyslog_queue.head);
+	ksyslog_queue_init(&ksyslog_queue);
 
 #ifdef CONFIG_PROC_FS
