Commit 84ec0864 authored by Marek Lindner's avatar Marek Lindner Committed by Greg Kroah-Hartman
Browse files

Staging: batman-adv: add routing debug log accessible via debugfs



All routing debug messages are saved in a ring buffer that can be
read via the debugfs file "log".
Note that CONFIG_BATMAN_ADV_DEBUG must be activated to have the
debug logs compiled in.

Signed-off-by: default avatarMarek Lindner <lindner_marek@yahoo.de>
Signed-off-by: default avatarSven Eckelmann <sven.eckelmann@gmx.de>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent e75fece2
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -106,11 +106,14 @@ static void new_aggregated_packet(unsigned char *packet_buff,
{
	struct forw_packet *forw_packet_aggr;
	unsigned long flags;
	/* FIXME: each batman_if will be attached to a softif */
	struct bat_priv *bat_priv = netdev_priv(soft_device);

	/* own packet should always be scheduled */
	if (!own_packet) {
		if (!atomic_dec_not_zero(&batman_queue_left)) {
			bat_dbg(DBG_BATMAN, "batman packet queue full\n");
			bat_dbg(DBG_BATMAN, bat_priv,
				"batman packet queue full\n");
			return;
		}
	}
+190 −0
Original line number Diff line number Diff line
@@ -31,6 +31,193 @@

static struct dentry *bat_debugfs;

#ifdef CONFIG_BATMAN_ADV_DEBUG
#define LOG_BUFF_MASK (log_buff_len-1)
#define LOG_BUFF(idx) (debug_log->log_buff[(idx) & LOG_BUFF_MASK])

static int log_buff_len = LOG_BUF_LEN;

static void emit_log_char(struct debug_log *debug_log, char c)
{
	LOG_BUFF(debug_log->log_end) = c;
	debug_log->log_end++;

	if (debug_log->log_end - debug_log->log_start > log_buff_len)
		debug_log->log_start = debug_log->log_end - log_buff_len;
}

static int fdebug_log(struct debug_log *debug_log, char *fmt, ...)
{
	int printed_len;
	va_list args;
	static char debug_log_buf[256];
	char *p;
	unsigned long flags;

	if (!debug_log)
		return 0;

	spin_lock_irqsave(&debug_log->lock, flags);
	va_start(args, fmt);
	printed_len = vscnprintf(debug_log_buf, sizeof(debug_log_buf),
				 fmt, args);
	va_end(args);

	for (p = debug_log_buf; *p != 0; p++)
		emit_log_char(debug_log, *p);

	spin_unlock_irqrestore(&debug_log->lock, flags);

	wake_up(&debug_log->queue_wait);

	return 0;
}

int debug_log(struct bat_priv *bat_priv, char *fmt, ...)
{
	va_list args;
	char tmp_log_buf[256];

	va_start(args, fmt);
	vscnprintf(tmp_log_buf, sizeof(tmp_log_buf), fmt, args);
	fdebug_log(bat_priv->debug_log, "[%10u] %s",
		   (jiffies / HZ), tmp_log_buf);
	va_end(args);

	return 0;
}

static int log_open(struct inode *inode, struct file *file)
{
	file->private_data = inode->i_private;
	inc_module_count();
	return 0;
}

static int log_release(struct inode *inode, struct file *file)
{
	dec_module_count();
	return 0;
}

static ssize_t log_read(struct file *file, char __user *buf,
			size_t count, loff_t *ppos)
{
	struct bat_priv *bat_priv = (struct bat_priv *)file->private_data;
	struct debug_log *debug_log = bat_priv->debug_log;
	int error, i = 0;
	char c;
	unsigned long flags;

	if ((file->f_flags & O_NONBLOCK) &&
	    !(debug_log->log_end - debug_log->log_start))
		return -EAGAIN;

	if ((!buf) || (count < 0))
		return -EINVAL;

	if (count == 0)
		return 0;

	if (!access_ok(VERIFY_WRITE, buf, count))
		return -EFAULT;

	error = wait_event_interruptible(debug_log->queue_wait,
				(debug_log->log_start - debug_log->log_end));

	if (error)
		return error;

	spin_lock_irqsave(&debug_log->lock, flags);

	while ((!error) && (i < count) &&
	       (debug_log->log_start != debug_log->log_end)) {
		c = LOG_BUFF(debug_log->log_start);

		debug_log->log_start++;

		spin_unlock_irqrestore(&debug_log->lock, flags);

		error = __put_user(c, buf);

		spin_lock_irqsave(&debug_log->lock, flags);

		buf++;
		i++;

	}

	spin_unlock_irqrestore(&debug_log->lock, flags);

	if (!error)
		return i;

	return error;
}

static unsigned int log_poll(struct file *file, poll_table *wait)
{
	struct bat_priv *bat_priv = (struct bat_priv *)file->private_data;
	struct debug_log *debug_log = bat_priv->debug_log;

	poll_wait(file, &debug_log->queue_wait, wait);

	if (debug_log->log_end - debug_log->log_start)
		return POLLIN | POLLRDNORM;

	return 0;
}

static const struct file_operations log_fops = {
	.open           = log_open,
	.release        = log_release,
	.read           = log_read,
	.poll           = log_poll,
};

static int debug_log_setup(struct bat_priv *bat_priv)
{
	struct dentry *d;

	if (!bat_priv->debug_dir)
		goto err;

	bat_priv->debug_log = kzalloc(sizeof(struct debug_log), GFP_ATOMIC);
	if (!bat_priv->debug_log)
		goto err;

	spin_lock_init(&bat_priv->debug_log->lock);
	init_waitqueue_head(&bat_priv->debug_log->queue_wait);

	d = debugfs_create_file("log", S_IFREG | S_IRUSR,
				bat_priv->debug_dir, bat_priv, &log_fops);
	if (d)
		goto err;

	return 0;

err:
	return 1;
}

static void debug_log_cleanup(struct bat_priv *bat_priv)
{
	kfree(bat_priv->debug_log);
	bat_priv->debug_log = NULL;
}
#else /* CONFIG_BATMAN_ADV_DEBUG */
static int debug_log_setup(struct bat_priv *bat_priv)
{
	bat_priv->debug_log = NULL;
	return 0;
}

static void debug_log_cleanup(struct bat_priv *bat_priv)
{
	return;
}
#endif

static int originators_open(struct inode *inode, struct file *file)
{
	struct net_device *net_dev = (struct net_device *)inode->i_private;
@@ -114,6 +301,7 @@ int debugfs_add_meshif(struct net_device *dev)
		goto out;

	bat_socket_setup(bat_priv);
	debug_log_setup(bat_priv);

	for (bat_debug = mesh_debuginfos; *bat_debug; ++bat_debug) {
		file = debugfs_create_file(((*bat_debug)->attr).name,
@@ -143,6 +331,8 @@ void debugfs_del_meshif(struct net_device *dev)
{
	struct bat_priv *bat_priv = netdev_priv(dev);

	debug_log_cleanup(bat_priv);

	if (bat_debugfs) {
		debugfs_remove_recursive(bat_priv->debug_dir);
		bat_priv->debug_dir = NULL;
+54 −0
Original line number Diff line number Diff line
@@ -229,18 +229,70 @@ static ssize_t store_orig_interval(struct kobject *kobj, struct attribute *attr,
	return count;
}

#ifdef CONFIG_BATMAN_ADV_DEBUG
static ssize_t show_log_level(struct kobject *kobj, struct attribute *attr,
			     char *buff)
{
	struct device *dev = to_dev(kobj->parent);
	struct bat_priv *bat_priv = netdev_priv(to_net_dev(dev));
	int log_level = atomic_read(&bat_priv->log_level);

	return sprintf(buff, "%d\n", log_level);
}

static ssize_t store_log_level(struct kobject *kobj, struct attribute *attr,
			      char *buff, size_t count)
{
	struct device *dev = to_dev(kobj->parent);
	struct net_device *net_dev = to_net_dev(dev);
	struct bat_priv *bat_priv = netdev_priv(net_dev);
	unsigned long log_level_tmp;
	int ret;

	ret = strict_strtoul(buff, 10, &log_level_tmp);
	if (ret) {
		printk(KERN_INFO "batman-adv:Invalid parameter for 'log_level' setting on mesh %s received: %s\n",
		       net_dev->name, buff);
		return -EINVAL;
	}

	if (log_level_tmp > 3) {
		printk(KERN_INFO "batman-adv:New log level too big: %li (max: %i)\n",
		       log_level_tmp, 3);
		return -EINVAL;
	}

	if (atomic_read(&bat_priv->log_level) == log_level_tmp)
		return count;

	printk(KERN_INFO
	       "batman-adv:Changing log level from: %i to: %li on mesh: %s\n",
	       atomic_read(&bat_priv->log_level),
	       log_level_tmp, net_dev->name);

	atomic_set(&bat_priv->log_level, (unsigned)log_level_tmp);
	return count;
}
#endif

static BAT_ATTR(aggregated_ogms, S_IRUGO | S_IWUSR,
		show_aggr_ogms, store_aggr_ogms);
static BAT_ATTR(bonding, S_IRUGO | S_IWUSR, show_bond, store_bond);
static BAT_ATTR(vis_mode, S_IRUGO | S_IWUSR, show_vis_mode, store_vis_mode);
static BAT_ATTR(orig_interval, S_IRUGO | S_IWUSR,
		show_orig_interval, store_orig_interval);
#ifdef CONFIG_BATMAN_ADV_DEBUG
static BAT_ATTR(log_level, S_IRUGO | S_IWUSR, show_log_level, store_log_level);
#endif

static struct bat_attribute *mesh_attrs[] = {
	&bat_attr_aggregated_ogms,
	&bat_attr_bonding,
	&bat_attr_vis_mode,
	&bat_attr_orig_interval,
#ifdef CONFIG_BATMAN_ADV_DEBUG
	&bat_attr_log_level,
#endif
	NULL,
};

@@ -257,6 +309,8 @@ int sysfs_add_meshif(struct net_device *dev)
	atomic_set(&bat_priv->bonding_enabled, 0);
	atomic_set(&bat_priv->vis_mode, VIS_TYPE_CLIENT_UPDATE);
	atomic_set(&bat_priv->orig_interval, 1000);
	atomic_set(&bat_priv->log_level, 0);

	bat_priv->primary_if = NULL;
	bat_priv->num_ifaces = 0;

+5 −2
Original line number Diff line number Diff line
@@ -128,6 +128,9 @@ static void bit_reset_window(TYPE_OF_WORD *seq_bits)
char bit_get_packet(TYPE_OF_WORD *seq_bits, int32_t seq_num_diff,
		    int8_t set_mark)
{
	/* FIXME: each orig_node->batman_if will be attached to a softif */
	struct bat_priv *bat_priv = netdev_priv(soft_device);

	/* sequence number is slightly older. We already got a sequence number
	 * higher than this one, so we just mark it. */

@@ -152,7 +155,7 @@ char bit_get_packet(TYPE_OF_WORD *seq_bits, int32_t seq_num_diff,

	if ((seq_num_diff >= TQ_LOCAL_WINDOW_SIZE)
		|| (seq_num_diff < EXPECTED_SEQNO_RANGE)) {
		bat_dbg(DBG_BATMAN,
		bat_dbg(DBG_BATMAN, bat_priv,
			"We missed a lot of packets (%i) !\n",
			seq_num_diff - 1);
		bit_reset_window(seq_bits);
@@ -169,7 +172,7 @@ char bit_get_packet(TYPE_OF_WORD *seq_bits, int32_t seq_num_diff,
	if ((seq_num_diff <= -TQ_LOCAL_WINDOW_SIZE)
		|| (seq_num_diff >= EXPECTED_SEQNO_RANGE)) {

		bat_dbg(DBG_BATMAN,
		bat_dbg(DBG_BATMAN, bat_priv,
			"Other host probably restarted!\n");

		bit_reset_window(seq_bits);
+3 −1
Original line number Diff line number Diff line
@@ -443,6 +443,8 @@ static int batman_skb_recv_finish(struct sk_buff *skb)
int batman_skb_recv(struct sk_buff *skb, struct net_device *dev,
	struct packet_type *ptype, struct net_device *orig_dev)
{
	/* FIXME: each orig_node->batman_if will be attached to a softif */
	struct bat_priv *bat_priv = netdev_priv(soft_device);
	struct batman_packet *batman_packet;
	struct batman_if *batman_if;
	struct net_device_stats *stats;
@@ -490,7 +492,7 @@ int batman_skb_recv(struct sk_buff *skb, struct net_device *dev,
	batman_packet = (struct batman_packet *)skb->data;

	if (batman_packet->version != COMPAT_VERSION) {
		bat_dbg(DBG_BATMAN,
		bat_dbg(DBG_BATMAN, bat_priv,
			"Drop packet: incompatible batman version (%i)\n",
			batman_packet->version);
		goto err_free;
Loading