Root/kernel/slow-work-debugfs.c

1/* Slow work debugging
2 *
3 * Copyright (C) 2009 Red Hat, Inc. All Rights Reserved.
4 * Written by David Howells (dhowells@redhat.com)
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public Licence
8 * as published by the Free Software Foundation; either version
9 * 2 of the Licence, or (at your option) any later version.
10 */
11
12#include <linux/module.h>
13#include <linux/slow-work.h>
14#include <linux/fs.h>
15#include <linux/time.h>
16#include <linux/seq_file.h>
17#include "slow-work.h"
18
19#define ITERATOR_SHIFT (BITS_PER_LONG - 4)
20#define ITERATOR_SELECTOR (0xfUL << ITERATOR_SHIFT)
21#define ITERATOR_COUNTER (~ITERATOR_SELECTOR)
22
23void slow_work_new_thread_desc(struct slow_work *work, struct seq_file *m)
24{
25    seq_puts(m, "Slow-work: New thread");
26}
27
28/*
29 * Render the time mark field on a work item into a 5-char time with units plus
30 * a space
31 */
32static void slow_work_print_mark(struct seq_file *m, struct slow_work *work)
33{
34    struct timespec now, diff;
35
36    now = CURRENT_TIME;
37    diff = timespec_sub(now, work->mark);
38
39    if (diff.tv_sec < 0)
40        seq_puts(m, " -ve ");
41    else if (diff.tv_sec == 0 && diff.tv_nsec < 1000)
42        seq_printf(m, "%3luns ", diff.tv_nsec);
43    else if (diff.tv_sec == 0 && diff.tv_nsec < 1000000)
44        seq_printf(m, "%3luus ", diff.tv_nsec / 1000);
45    else if (diff.tv_sec == 0 && diff.tv_nsec < 1000000000)
46        seq_printf(m, "%3lums ", diff.tv_nsec / 1000000);
47    else if (diff.tv_sec <= 1)
48        seq_puts(m, " 1s ");
49    else if (diff.tv_sec < 60)
50        seq_printf(m, "%4lus ", diff.tv_sec);
51    else if (diff.tv_sec < 60 * 60)
52        seq_printf(m, "%4lum ", diff.tv_sec / 60);
53    else if (diff.tv_sec < 60 * 60 * 24)
54        seq_printf(m, "%4luh ", diff.tv_sec / 3600);
55    else
56        seq_puts(m, "exces ");
57}
58
59/*
60 * Describe a slow work item for debugfs
61 */
62static int slow_work_runqueue_show(struct seq_file *m, void *v)
63{
64    struct slow_work *work;
65    struct list_head *p = v;
66    unsigned long id;
67
68    switch ((unsigned long) v) {
69    case 1:
70        seq_puts(m, "THR PID ITEM ADDR FL MARK DESC\n");
71        return 0;
72    case 2:
73        seq_puts(m, "=== ===== ================ == ===== ==========\n");
74        return 0;
75
76    case 3 ... 3 + SLOW_WORK_THREAD_LIMIT - 1:
77        id = (unsigned long) v - 3;
78
79        read_lock(&slow_work_execs_lock);
80        work = slow_work_execs[id];
81        if (work) {
82            smp_read_barrier_depends();
83
84            seq_printf(m, "%3lu %5d %16p %2lx ",
85                   id, slow_work_pids[id], work, work->flags);
86            slow_work_print_mark(m, work);
87
88            if (work->ops->desc)
89                work->ops->desc(work, m);
90            seq_putc(m, '\n');
91        }
92        read_unlock(&slow_work_execs_lock);
93        return 0;
94
95    default:
96        work = list_entry(p, struct slow_work, link);
97        seq_printf(m, "%3s - %16p %2lx ",
98               work->flags & SLOW_WORK_VERY_SLOW ? "vsq" : "sq",
99               work, work->flags);
100        slow_work_print_mark(m, work);
101
102        if (work->ops->desc)
103            work->ops->desc(work, m);
104        seq_putc(m, '\n');
105        return 0;
106    }
107}
108
109/*
110 * map the iterator to a work item
111 */
112static void *slow_work_runqueue_index(struct seq_file *m, loff_t *_pos)
113{
114    struct list_head *p;
115    unsigned long count, id;
116
117    switch (*_pos >> ITERATOR_SHIFT) {
118    case 0x0:
119        if (*_pos == 0)
120            *_pos = 1;
121        if (*_pos < 3)
122            return (void *)(unsigned long) *_pos;
123        if (*_pos < 3 + SLOW_WORK_THREAD_LIMIT)
124            for (id = *_pos - 3;
125                 id < SLOW_WORK_THREAD_LIMIT;
126                 id++, (*_pos)++)
127                if (slow_work_execs[id])
128                    return (void *)(unsigned long) *_pos;
129        *_pos = 0x1UL << ITERATOR_SHIFT;
130
131    case 0x1:
132        count = *_pos & ITERATOR_COUNTER;
133        list_for_each(p, &slow_work_queue) {
134            if (count == 0)
135                return p;
136            count--;
137        }
138        *_pos = 0x2UL << ITERATOR_SHIFT;
139
140    case 0x2:
141        count = *_pos & ITERATOR_COUNTER;
142        list_for_each(p, &vslow_work_queue) {
143            if (count == 0)
144                return p;
145            count--;
146        }
147        *_pos = 0x3UL << ITERATOR_SHIFT;
148
149    default:
150        return NULL;
151    }
152}
153
154/*
155 * set up the iterator to start reading from the first line
156 */
157static void *slow_work_runqueue_start(struct seq_file *m, loff_t *_pos)
158{
159    spin_lock_irq(&slow_work_queue_lock);
160    return slow_work_runqueue_index(m, _pos);
161}
162
163/*
164 * move to the next line
165 */
166static void *slow_work_runqueue_next(struct seq_file *m, void *v, loff_t *_pos)
167{
168    struct list_head *p = v;
169    unsigned long selector = *_pos >> ITERATOR_SHIFT;
170
171    (*_pos)++;
172    switch (selector) {
173    case 0x0:
174        return slow_work_runqueue_index(m, _pos);
175
176    case 0x1:
177        if (*_pos >> ITERATOR_SHIFT == 0x1) {
178            p = p->next;
179            if (p != &slow_work_queue)
180                return p;
181        }
182        *_pos = 0x2UL << ITERATOR_SHIFT;
183        p = &vslow_work_queue;
184
185    case 0x2:
186        if (*_pos >> ITERATOR_SHIFT == 0x2) {
187            p = p->next;
188            if (p != &vslow_work_queue)
189                return p;
190        }
191        *_pos = 0x3UL << ITERATOR_SHIFT;
192
193    default:
194        return NULL;
195    }
196}
197
198/*
199 * clean up after reading
200 */
201static void slow_work_runqueue_stop(struct seq_file *m, void *v)
202{
203    spin_unlock_irq(&slow_work_queue_lock);
204}
205
206static const struct seq_operations slow_work_runqueue_ops = {
207    .start = slow_work_runqueue_start,
208    .stop = slow_work_runqueue_stop,
209    .next = slow_work_runqueue_next,
210    .show = slow_work_runqueue_show,
211};
212
213/*
214 * open "/sys/kernel/debug/slow_work/runqueue" to list queue contents
215 */
216static int slow_work_runqueue_open(struct inode *inode, struct file *file)
217{
218    return seq_open(file, &slow_work_runqueue_ops);
219}
220
221const struct file_operations slow_work_runqueue_fops = {
222    .owner = THIS_MODULE,
223    .open = slow_work_runqueue_open,
224    .read = seq_read,
225    .llseek = seq_lseek,
226    .release = seq_release,
227};
228

Archive Download this file



interactive