Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8fba10a4 authored by David Howells's avatar David Howells
Browse files

SLOW_WORK: Allow the work items to be viewed through a /proc file



Allow the executing and queued work items to be viewed through a /proc file
for debugging purposes.  The contents look something like the following:

    THR PID   ITEM ADDR        FL MARK  DESC
    === ===== ================ == ===== ==========
      0  3005 ffff880023f52348  a 952ms FSC: OBJ17d3: LOOK
      1  3006 ffff880024e33668  2 160ms FSC: OBJ17e5 OP60d3b: Write1/Store fl=2
      2  3165 ffff8800296dd180  a 424ms FSC: OBJ17e4: LOOK
      3  4089 ffff8800262c8d78  a 212ms FSC: OBJ17ea: CRTN
      4  4090 ffff88002792bed8  2 388ms FSC: OBJ17e8 OP60d36: Write1/Store fl=2
      5  4092 ffff88002a0ef308  2 388ms FSC: OBJ17e7 OP60d2e: Write1/Store fl=2
      6  4094 ffff88002abaf4b8  2 132ms FSC: OBJ17e2 OP60d4e: Write1/Store fl=2
      7  4095 ffff88002bb188e0  a 388ms FSC: OBJ17e9: CRTN
    vsq     - ffff880023d99668  1 308ms FSC: OBJ17e0 OP60f91: Write1/EnQ fl=2
    vsq     - ffff8800295d1740  1 212ms FSC: OBJ16be OP4d4b6: Write1/EnQ fl=2
    vsq     - ffff880025ba3308  1 160ms FSC: OBJ179a OP58dec: Write1/EnQ fl=2
    vsq     - ffff880024ec83e0  1 160ms FSC: OBJ17ae OP599f2: Write1/EnQ fl=2
    vsq     - ffff880026618e00  1 160ms FSC: OBJ17e6 OP60d33: Write1/EnQ fl=2
    vsq     - ffff880025a2a4b8  1 132ms FSC: OBJ16a2 OP4d583: Write1/EnQ fl=2
    vsq     - ffff880023cbe6d8  9 212ms FSC: OBJ17eb: LOOK
    vsq     - ffff880024d37590  9 212ms FSC: OBJ17ec: LOOK
    vsq     - ffff880027746cb0  9 212ms FSC: OBJ17ed: LOOK
    vsq     - ffff880024d37ae8  9 212ms FSC: OBJ17ee: LOOK
    vsq     - ffff880024d37cb0  9 212ms FSC: OBJ17ef: LOOK
    vsq     - ffff880025036550  9 212ms FSC: OBJ17f0: LOOK
    vsq     - ffff8800250368e0  9 212ms FSC: OBJ17f1: LOOK
    vsq     - ffff880025036aa8  9 212ms FSC: OBJ17f2: LOOK

In the 'THR' column, executing items show the thread they're occupying and
queued threads indicate which queue they're on.  'PID' shows the process ID of
a slow-work thread that's executing something.  'FL' shows the work item flags.
'MARK' indicates how long since an item was queued or began executing.  Lastly,
the 'DESC' column permits the owner of an item to give some information.

Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
parent 6b8268b1
Loading
Loading
Loading
Loading
+59 −1
Original line number Original line Diff line number Diff line
@@ -149,7 +149,8 @@ ITEM OPERATIONS
===============
===============


Each work item requires a table of operations of type struct slow_work_ops.
Each work item requires a table of operations of type struct slow_work_ops.
Only ->execute() is required, getting and putting of a reference are optional.
Only ->execute() is required; the getting and putting of a reference and the
describing of an item are all optional.


 (*) Get a reference on an item:
 (*) Get a reference on an item:


@@ -179,6 +180,16 @@ Only ->execute() is required, getting and putting of a reference are optional.
     This should perform the work required of the item.  It may sleep, it may
     This should perform the work required of the item.  It may sleep, it may
     perform disk I/O and it may wait for locks.
     perform disk I/O and it may wait for locks.


 (*) View an item through /proc:

	void (*desc)(struct slow_work *work, struct seq_file *m);

     If supplied, this should print to 'm' a small string describing the work
     the item is to do.  This should be no more than about 40 characters, and
     shouldn't include a newline character.

     See the 'Viewing executing and queued items' section below.



==================
==================
POOL CONFIGURATION
POOL CONFIGURATION
@@ -203,3 +214,50 @@ The slow-work thread pool has a number of configurables:
     is bounded to between 1 and one fewer than the number of active threads.
     is bounded to between 1 and one fewer than the number of active threads.
     This ensures there is always at least one thread that can process very
     This ensures there is always at least one thread that can process very
     slow work items, and always at least one thread that won't.
     slow work items, and always at least one thread that won't.


==================================
VIEWING EXECUTING AND QUEUED ITEMS
==================================

If CONFIG_SLOW_WORK_PROC is enabled, a proc file is made available:

	/proc/slow_work_rq

through which the list of work items being executed and the queues of items to
be executed may be viewed.  The owner of a work item is given the chance to
add some information of its own.

The contents look something like the following:

    THR PID   ITEM ADDR        FL MARK  DESC
    === ===== ================ == ===== ==========
      0  3005 ffff880023f52348  a 952ms FSC: OBJ17d3: LOOK
      1  3006 ffff880024e33668  2 160ms FSC: OBJ17e5 OP60d3b: Write1/Store fl=2
      2  3165 ffff8800296dd180  a 424ms FSC: OBJ17e4: LOOK
      3  4089 ffff8800262c8d78  a 212ms FSC: OBJ17ea: CRTN
      4  4090 ffff88002792bed8  2 388ms FSC: OBJ17e8 OP60d36: Write1/Store fl=2
      5  4092 ffff88002a0ef308  2 388ms FSC: OBJ17e7 OP60d2e: Write1/Store fl=2
      6  4094 ffff88002abaf4b8  2 132ms FSC: OBJ17e2 OP60d4e: Write1/Store fl=2
      7  4095 ffff88002bb188e0  a 388ms FSC: OBJ17e9: CRTN
    vsq     - ffff880023d99668  1 308ms FSC: OBJ17e0 OP60f91: Write1/EnQ fl=2
    vsq     - ffff8800295d1740  1 212ms FSC: OBJ16be OP4d4b6: Write1/EnQ fl=2
    vsq     - ffff880025ba3308  1 160ms FSC: OBJ179a OP58dec: Write1/EnQ fl=2
    vsq     - ffff880024ec83e0  1 160ms FSC: OBJ17ae OP599f2: Write1/EnQ fl=2
    vsq     - ffff880026618e00  1 160ms FSC: OBJ17e6 OP60d33: Write1/EnQ fl=2
    vsq     - ffff880025a2a4b8  1 132ms FSC: OBJ16a2 OP4d583: Write1/EnQ fl=2
    vsq     - ffff880023cbe6d8  9 212ms FSC: OBJ17eb: LOOK
    vsq     - ffff880024d37590  9 212ms FSC: OBJ17ec: LOOK
    vsq     - ffff880027746cb0  9 212ms FSC: OBJ17ed: LOOK
    vsq     - ffff880024d37ae8  9 212ms FSC: OBJ17ee: LOOK
    vsq     - ffff880024d37cb0  9 212ms FSC: OBJ17ef: LOOK
    vsq     - ffff880025036550  9 212ms FSC: OBJ17f0: LOOK
    vsq     - ffff8800250368e0  9 212ms FSC: OBJ17f1: LOOK
    vsq     - ffff880025036aa8  9 212ms FSC: OBJ17f2: LOOK

In the 'THR' column, executing items show the thread they're occupying and
queued threads indicate which queue they're on.  'PID' shows the process ID of
a slow-work thread that's executing something.  'FL' shows the work item flags.
'MARK' indicates how long since an item was queued or began executing.  Lastly,
the 'DESC' column permits the owner of an item to give some information.
+11 −0
Original line number Original line Diff line number Diff line
@@ -20,6 +20,9 @@
#include <linux/timer.h>
#include <linux/timer.h>


struct slow_work;
struct slow_work;
#ifdef CONFIG_SLOW_WORK_PROC
struct seq_file;
#endif


/*
/*
 * The operations used to support slow work items
 * The operations used to support slow work items
@@ -38,6 +41,11 @@ struct slow_work_ops {


	/* execute a work item */
	/* execute a work item */
	void (*execute)(struct slow_work *work);
	void (*execute)(struct slow_work *work);

#ifdef CONFIG_SLOW_WORK_PROC
	/* describe a work item for /proc */
	void (*desc)(struct slow_work *work, struct seq_file *m);
#endif
};
};


/*
/*
@@ -56,6 +64,9 @@ struct slow_work {
#define SLOW_WORK_DELAYED	5	/* item is struct delayed_slow_work with active timer */
#define SLOW_WORK_DELAYED	5	/* item is struct delayed_slow_work with active timer */
	const struct slow_work_ops *ops; /* operations table for this item */
	const struct slow_work_ops *ops; /* operations table for this item */
	struct list_head	link;	/* link in queue */
	struct list_head	link;	/* link in queue */
#ifdef CONFIG_SLOW_WORK_PROC
	struct timespec		mark;	/* jiffies at which queued or exec begun */
#endif
};
};


struct delayed_slow_work {
struct delayed_slow_work {
+10 −0
Original line number Original line Diff line number Diff line
@@ -1098,6 +1098,16 @@ config SLOW_WORK


	  See Documentation/slow-work.txt.
	  See Documentation/slow-work.txt.


config SLOW_WORK_PROC
	bool "Slow work debugging through /proc"
	default n
	depends on SLOW_WORK && PROC_FS
	help
	  Display the contents of the slow work run queue through /proc,
	  including items currently executing.

	  See Documentation/slow-work.txt.

endmenu		# General setup
endmenu		# General setup


config HAVE_GENERIC_DMA_COHERENT
config HAVE_GENERIC_DMA_COHERENT
+1 −0
Original line number Original line Diff line number Diff line
@@ -94,6 +94,7 @@ obj-$(CONFIG_X86_DS) += trace/
obj-$(CONFIG_RING_BUFFER) += trace/
obj-$(CONFIG_RING_BUFFER) += trace/
obj-$(CONFIG_SMP) += sched_cpupri.o
obj-$(CONFIG_SMP) += sched_cpupri.o
obj-$(CONFIG_SLOW_WORK) += slow-work.o
obj-$(CONFIG_SLOW_WORK) += slow-work.o
obj-$(CONFIG_SLOW_WORK_PROC) += slow-work-proc.o
obj-$(CONFIG_PERF_EVENTS) += perf_event.o
obj-$(CONFIG_PERF_EVENTS) += perf_event.o


ifneq ($(CONFIG_SCHED_OMIT_FRAME_POINTER),y)
ifneq ($(CONFIG_SCHED_OMIT_FRAME_POINTER),y)
+227 −0
Original line number Original line Diff line number Diff line
/* Slow work debugging
 *
 * Copyright (C) 2009 Red Hat, Inc. All Rights Reserved.
 * Written by David Howells (dhowells@redhat.com)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public Licence
 * as published by the Free Software Foundation; either version
 * 2 of the Licence, or (at your option) any later version.
 */

#include <linux/module.h>
#include <linux/slow-work.h>
#include <linux/fs.h>
#include <linux/time.h>
#include <linux/seq_file.h>
#include "slow-work.h"

#define ITERATOR_SHIFT		(BITS_PER_LONG - 4)
#define ITERATOR_SELECTOR	(0xfUL << ITERATOR_SHIFT)
#define ITERATOR_COUNTER	(~ITERATOR_SELECTOR)

void slow_work_new_thread_desc(struct slow_work *work, struct seq_file *m)
{
	seq_puts(m, "Slow-work: New thread");
}

/*
 * Render the time mark field on a work item into a 5-char time with units plus
 * a space
 */
static void slow_work_print_mark(struct seq_file *m, struct slow_work *work)
{
	struct timespec now, diff;

	now = CURRENT_TIME;
	diff = timespec_sub(now, work->mark);

	if (diff.tv_sec < 0)
		seq_puts(m, "  -ve ");
	else if (diff.tv_sec == 0 && diff.tv_nsec < 1000)
		seq_printf(m, "%3luns ", diff.tv_nsec);
	else if (diff.tv_sec == 0 && diff.tv_nsec < 1000000)
		seq_printf(m, "%3luus ", diff.tv_nsec / 1000);
	else if (diff.tv_sec == 0 && diff.tv_nsec < 1000000000)
		seq_printf(m, "%3lums ", diff.tv_nsec / 1000000);
	else if (diff.tv_sec <= 1)
		seq_puts(m, "   1s ");
	else if (diff.tv_sec < 60)
		seq_printf(m, "%4lus ", diff.tv_sec);
	else if (diff.tv_sec < 60 * 60)
		seq_printf(m, "%4lum ", diff.tv_sec / 60);
	else if (diff.tv_sec < 60 * 60 * 24)
		seq_printf(m, "%4luh ", diff.tv_sec / 3600);
	else
		seq_puts(m, "exces ");
}

/*
 * Describe a slow work item for /proc
 */
static int slow_work_runqueue_show(struct seq_file *m, void *v)
{
	struct slow_work *work;
	struct list_head *p = v;
	unsigned long id;

	switch ((unsigned long) v) {
	case 1:
		seq_puts(m, "THR PID   ITEM ADDR        FL MARK  DESC\n");
		return 0;
	case 2:
		seq_puts(m, "=== ===== ================ == ===== ==========\n");
		return 0;

	case 3 ... 3 + SLOW_WORK_THREAD_LIMIT - 1:
		id = (unsigned long) v - 3;

		read_lock(&slow_work_execs_lock);
		work = slow_work_execs[id];
		if (work) {
			smp_read_barrier_depends();

			seq_printf(m, "%3lu %5d %16p %2lx ",
				   id, slow_work_pids[id], work, work->flags);
			slow_work_print_mark(m, work);

			if (work->ops->desc)
				work->ops->desc(work, m);
			seq_putc(m, '\n');
		}
		read_unlock(&slow_work_execs_lock);
		return 0;

	default:
		work = list_entry(p, struct slow_work, link);
		seq_printf(m, "%3s     - %16p %2lx ",
			   work->flags & SLOW_WORK_VERY_SLOW ? "vsq" : "sq",
			   work, work->flags);
		slow_work_print_mark(m, work);

		if (work->ops->desc)
			work->ops->desc(work, m);
		seq_putc(m, '\n');
		return 0;
	}
}

/*
 * map the iterator to a work item
 */
static void *slow_work_runqueue_index(struct seq_file *m, loff_t *_pos)
{
	struct list_head *p;
	unsigned long count, id;

	switch (*_pos >> ITERATOR_SHIFT) {
	case 0x0:
		if (*_pos == 0)
			*_pos = 1;
		if (*_pos < 3)
			return (void *)(unsigned long) *_pos;
		if (*_pos < 3 + SLOW_WORK_THREAD_LIMIT)
			for (id = *_pos - 3;
			     id < SLOW_WORK_THREAD_LIMIT;
			     id++, (*_pos)++)
				if (slow_work_execs[id])
					return (void *)(unsigned long) *_pos;
		*_pos = 0x1UL << ITERATOR_SHIFT;

	case 0x1:
		count = *_pos & ITERATOR_COUNTER;
		list_for_each(p, &slow_work_queue) {
			if (count == 0)
				return p;
			count--;
		}
		*_pos = 0x2UL << ITERATOR_SHIFT;

	case 0x2:
		count = *_pos & ITERATOR_COUNTER;
		list_for_each(p, &vslow_work_queue) {
			if (count == 0)
				return p;
			count--;
		}
		*_pos = 0x3UL << ITERATOR_SHIFT;

	default:
		return NULL;
	}
}

/*
 * set up the iterator to start reading from the first line
 */
static void *slow_work_runqueue_start(struct seq_file *m, loff_t *_pos)
{
	spin_lock_irq(&slow_work_queue_lock);
	return slow_work_runqueue_index(m, _pos);
}

/*
 * move to the next line
 */
static void *slow_work_runqueue_next(struct seq_file *m, void *v, loff_t *_pos)
{
	struct list_head *p = v;
	unsigned long selector = *_pos >> ITERATOR_SHIFT;

	(*_pos)++;
	switch (selector) {
	case 0x0:
		return slow_work_runqueue_index(m, _pos);

	case 0x1:
		if (*_pos >> ITERATOR_SHIFT == 0x1) {
			p = p->next;
			if (p != &slow_work_queue)
				return p;
		}
		*_pos = 0x2UL << ITERATOR_SHIFT;
		p = &vslow_work_queue;

	case 0x2:
		if (*_pos >> ITERATOR_SHIFT == 0x2) {
			p = p->next;
			if (p != &vslow_work_queue)
				return p;
		}
		*_pos = 0x3UL << ITERATOR_SHIFT;

	default:
		return NULL;
	}
}

/*
 * clean up after reading
 */
static void slow_work_runqueue_stop(struct seq_file *m, void *v)
{
	spin_unlock_irq(&slow_work_queue_lock);
}

static const struct seq_operations slow_work_runqueue_ops = {
	.start		= slow_work_runqueue_start,
	.stop		= slow_work_runqueue_stop,
	.next		= slow_work_runqueue_next,
	.show		= slow_work_runqueue_show,
};

/*
 * open "/proc/slow_work_rq" to list queue contents
 */
static int slow_work_runqueue_open(struct inode *inode, struct file *file)
{
	return seq_open(file, &slow_work_runqueue_ops);
}

const struct file_operations slow_work_runqueue_fops = {
	.owner		= THIS_MODULE,
	.open		= slow_work_runqueue_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= seq_release,
};
Loading