aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/rtmutex-debug.c
blob: 5fcb4fe645e245a4a6c043aedbf1c212dbefadf0 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
/*
 * RT-Mutexes: blocking mutual exclusion locks with PI support
 *
 * started by Ingo Molnar and Thomas Gleixner:
 *
 *  Copyright (C) 2004-2006 Red Hat, Inc., Ingo Molnar <mingo@redhat.com>
 *  Copyright (C) 2006 Timesys Corp., Thomas Gleixner <tglx@timesys.com>
 *
 * This code is based on the rt.c implementation in the preempt-rt tree.
 * Portions of said code are
 *
 *  Copyright (C) 2004  LynuxWorks, Inc., Igor Manyilov, Bill Huey
 *  Copyright (C) 2006  Esben Nielsen
 *  Copyright (C) 2006  Kihon Technologies Inc.,
 *			Steven Rostedt <rostedt@goodmis.org>
 *
 * See rt.c in preempt-rt for proper credits and further information
 */
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/kallsyms.h>
#include <linux/syscalls.h>
#include <linux/interrupt.h>
#include <linux/plist.h>
#include <linux/fs.h>
#include <linux/debug_locks.h>

#include "rtmutex_common.h"

# define TRACE_WARN_ON(x)			WARN_ON(x)
# define TRACE_BUG_ON(x)			BUG_ON(x)

# define TRACE_OFF()						\
do {								\
	if (rt_trace_on) {					\
		rt_trace_on = 0;				\
		console_verbose();				\
		if (spin_is_locked(&current->pi_lock))		\
			spin_unlock(&current->pi_lock);		\
	}							\
} while (0)

# define TRACE_OFF_NOLOCK()					\
do {								\
	if (rt_trace_on) {					\
		rt_trace_on = 0;				\
		console_verbose();				\
	}							\
} while (0)

# define TRACE_BUG_LOCKED()			\
do {						\
	TRACE_OFF();				\
	BUG();					\
} while (0)

# define TRACE_WARN_ON_LOCKED(c)		\
do {						\
	if (unlikely(c)) {			\
		TRACE_OFF();			\
		WARN_ON(1);			\
	}					\
} while (0)

# define TRACE_BUG_ON_LOCKED(c)			\
do {						\
	if (unlikely(c))			\
		TRACE_BUG_LOCKED();		\
} while (0)

#ifdef CONFIG_SMP
# define SMP_TRACE_BUG_ON_LOCKED(c)	TRACE_BUG_ON_LOCKED(c)
#else
# define SMP_TRACE_BUG_ON_LOCKED(c)	do { } while (0)
#endif

/*
 * deadlock detection flag. We turn it off when we detect
 * the first problem because we dont want to recurse back
 * into the tracing code when doing error printk or
 * executing a BUG():
 */
static int rt_trace_on = 1;

static void printk_task(struct task_struct *p)
{
	if (p)
		printk("%16s:%5d [%p, %3d]", p->comm, task_pid_nr(p), p, p->prio);
	else
		printk("<none>");
}

static void printk_lock(struct rt_mutex *lock, int print_owner)
{
	if (lock->name)
		printk(" [%p] {%s}\n",
			lock, lock->name);
	else
		printk(" [%p] {%s:%d}\n",
			lock, lock->file, lock->line);

	if (print_owner && rt_mutex_owner(lock)) {
		printk(".. ->owner: %p\n", lock->owner);
		printk(".. held by:  ");
		printk_task(rt_mutex_owner(lock));
		printk("\n");
	}
}

void rt_mutex_debug_task_free(struct task_struct *task)
{
	WARN_ON(!plist_head_empty(&task->pi_waiters));
	WARN_ON(task->pi_blocked_on);
}

/*
 * We fill out the fields in the waiter to store the information about
 * the deadlock. We print when we return. act_waiter can be NULL in
 * case of a remove waiter operation.
 */
void debug_rt_mutex_deadlock(int detect, struct rt_mutex_waiter *act_waiter,
			     struct rt_mutex *lock)
{
	struct task_struct *task;

	if (!rt_trace_on || detect || !act_waiter)
		return;

	task = rt_mutex_owner(act_waiter->lock);
	if (task && task != current) {
		act_waiter->deadlock_task_pid = get_pid(task_pid(task));
		act_waiter->deadlock_lock = lock;
	}
}

void debug_rt_mutex_print_deadlock(struct rt_mutex_waiter *waiter)
{
	struct task_struct *task;

	if (!waiter->deadlock_lock || !rt_trace_on)
		return;

	rcu_read_lock();
	task = pid_task(waiter->deadlock_task_pid, PIDTYPE_PID);
	if (!task) {
		rcu_read_unlock();
		return;
	}

	TRACE_OFF_NOLOCK();

	printk("\n============================================\n");
	printk(  "[ BUG: circular locking deadlock detected! ]\n");
	printk(  "--------------------------------------------\n");
	printk("%s/%d is deadlocking current task %s/%d\n\n",
	       task->comm, task_pid_nr(task),
	       current->comm, task_pid_nr(current));

	printk("\n1) %s/%d is trying to acquire this lock:\n",
	       current->comm, task_pid_nr(current));
	printk_lock(waiter->lock, 1);

	printk("\n2) %s/%d is blocked on this lock:\n",
		task->comm, task_pid_nr(task));
	printk_lock(waiter->deadlock_lock, 1);

	debug_show_held_locks(current);
	debug_show_held_locks(task);

	printk("\n%s/%d's [blocked] stackdump:\n\n",
		task->comm, task_pid_nr(task));
	show_stack(task, NULL);
	printk("\n%s/%d's [current] stackdump:\n\n",
		current->comm, task_pid_nr(current));
	dump_stack();
	debug_show_all_locks();
	rcu_read_unlock();

	printk("[ turning off deadlock detection."
	       "Please report this trace. ]\n\n");
	local_irq_disable();
}

void debug_rt_mutex_lock(struct rt_mutex *lock)
{
}

void debug_rt_mutex_unlock(struct rt_mutex *lock)
{
	TRACE_WARN_ON_LOCKED(rt_mutex_owner(lock) != current);
}

void
debug_rt_mutex_proxy_lock(struct rt_mutex *lock, struct task_struct *powner)
{
}

void debug_rt_mutex_proxy_unlock(struct rt_mutex *lock)
{
	TRACE_WARN_ON_LOCKED(!rt_mutex_owner(lock));
}

void debug_rt_mutex_init_waiter(struct rt_mutex_waiter *waiter)
{
	memset(waiter, 0x11, sizeof(*waiter));
	plist_node_init(&waiter->list_entry, MAX_PRIO);
	plist_node_init(&waiter->pi_list_entry, MAX_PRIO);
	waiter->deadlock_task_pid = NULL;
}

void debug_rt_mutex_free_waiter(struct rt_mutex_waiter *waiter)
{
	put_pid(waiter->deadlock_task_pid);
	TRACE_WARN_ON(!plist_node_empty(&waiter->list_entry));
	TRACE_WARN_ON(!plist_node_empty(&waiter->pi_list_entry));
	TRACE_WARN_ON(waiter->task);
	memset(waiter, 0x22, sizeof(*waiter));
}

void debug_rt_mutex_init(struct rt_mutex *lock, const char *name)
{
	/*
	 * Make sure we are not reinitializing a held lock:
	 */
	debug_check_no_locks_freed((void *)lock, sizeof(*lock));
	lock->name = name;
}

void
rt_mutex_deadlock_account_lock(struct rt_mutex *lock, struct task_struct *task)
{
}

void rt_mutex_deadlock_account_unlock(struct task_struct *task)
{
}

on_printed; /* Values from various config regs. */ unsigned char idreg; unsigned char reg4; const char *ifmap[] = {"UTP No Link", "", "UTP/AUI", "UTP/BNC"}; if (!request_region(ioaddr, ULTRA32_IO_EXTENT, DRV_NAME)) return -EBUSY; if (inb(ioaddr + ULTRA32_IDPORT) == 0xff || inl(ioaddr + ULTRA32_IDPORT) != ULTRA32_ID) { retval = -ENODEV; goto out; } media = inb(ioaddr + ULTRA32_CFG7) & 0x03; edge = inb(ioaddr + ULTRA32_CFG5) & 0x08; printk("SMC Ultra32 in EISA Slot %d, Media: %s, %s IRQs.\n", ioaddr >> 12, ifmap[media], (edge ? "Edge Triggered" : "Level Sensitive")); idreg = inb(ioaddr + 7); reg4 = inb(ioaddr + 4) & 0x7f; /* Check the ID nibble. */ if ((idreg & 0xf0) != 0x20) { /* SMC Ultra */ retval = -ENODEV; goto out; } /* Select the station address register set. */ outb(reg4, ioaddr + 4); for (i = 0; i < 8; i++) checksum += inb(ioaddr + 8 + i); if ((checksum & 0xff) != 0xff) { retval = -ENODEV; goto out; } if (ei_debug && version_printed++ == 0) printk(version); model_name = "SMC Ultra32"; printk("%s: %s at 0x%X,", dev->name, model_name, ioaddr); for (i = 0; i < 6; i++) printk(" %2.2X", dev->dev_addr[i] = inb(ioaddr + 8 + i)); /* Switch from the station address to the alternate register set and read the useful registers there. */ outb(0x80 | reg4, ioaddr + 4); /* Enable FINE16 mode to avoid BIOS ROM width mismatches @ reboot. */ outb(0x80 | inb(ioaddr + 0x0c), ioaddr + 0x0c); /* Reset RAM addr. */ outb(0x00, ioaddr + 0x0b); /* Switch back to the station address register set so that the MS-DOS driver can find the card after a warm boot. */ outb(reg4, ioaddr + 4); if ((inb(ioaddr + ULTRA32_CFG5) & 0x40) == 0) { printk("\nsmc-ultra32: Card RAM is disabled! " "Run EISA config utility.\n"); retval = -ENODEV; goto out; } if ((inb(ioaddr + ULTRA32_CFG2) & 0x04) == 0) printk("\nsmc-ultra32: Ignoring Bus-Master enable bit. " "Run EISA config utility.\n"); if (dev->irq < 2) { unsigned char irqmap[] = {0, 9, 3, 5, 7, 10, 11, 15}; int irq = irqmap[inb(ioaddr + ULTRA32_CFG5) & 0x07]; if (irq == 0) { printk(", failed to detect IRQ line.\n"); retval = -EAGAIN; goto out; } dev->irq = irq; } /* The 8390 isn't at the base address, so fake the offset */ dev->base_addr = ioaddr + ULTRA32_NIC_OFFSET; /* Save RAM address in the unused reg0 to avoid excess inb's. */ ei_status.reg0 = inb(ioaddr + ULTRA32_CFG3) & 0xfc; dev->mem_start = 0xc0000 + ((ei_status.reg0 & 0x7c) << 11); ei_status.name = model_name; ei_status.word16 = 1; ei_status.tx_start_page = 0; ei_status.rx_start_page = TX_PAGES; /* All Ultra32 cards have 32KB memory with an 8KB window. */ ei_status.stop_page = 128; ei_status.mem = ioremap(dev->mem_start, 0x2000); if (!ei_status.mem) { printk(", failed to ioremap.\n"); retval = -ENOMEM; goto out; } dev->mem_end = dev->mem_start + 0x1fff; printk(", IRQ %d, 32KB memory, 8KB window at 0x%lx-0x%lx.\n", dev->irq, dev->mem_start, dev->mem_end); ei_status.block_input = &ultra32_block_input; ei_status.block_output = &ultra32_block_output; ei_status.get_8390_hdr = &ultra32_get_8390_hdr; ei_status.reset_8390 = &ultra32_reset_8390; dev->open = &ultra32_open; dev->stop = &ultra32_close; #ifdef CONFIG_NET_POLL_CONTROLLER dev->poll_controller = ei_poll; #endif NS8390_init(dev, 0); return 0; out: release_region(ioaddr, ULTRA32_IO_EXTENT); return retval; } static int ultra32_open(struct net_device *dev) { int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET; /* ASIC addr */ int irq_flags = (inb(ioaddr + ULTRA32_CFG5) & 0x08) ? 0 : IRQF_SHARED; int retval; retval = request_irq(dev->irq, ei_interrupt, irq_flags, dev->name, dev); if (retval) return retval; outb(ULTRA32_MEMENB, ioaddr); /* Enable Shared Memory. */ outb(0x80, ioaddr + ULTRA32_CFG6); /* Enable Interrupts. */ outb(0x84, ioaddr + 5); /* Enable MEM16 & Disable Bus Master. */ outb(0x01, ioaddr + 6); /* Enable Interrupts. */ /* Set the early receive warning level in window 0 high enough not to receive ERW interrupts. */ outb_p(E8390_NODMA+E8390_PAGE0, dev->base_addr); outb(0xff, dev->base_addr + EN0_ERWCNT); ei_open(dev); return 0; } static int ultra32_close(struct net_device *dev) { int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET; /* CMDREG */ netif_stop_queue(dev); if (ei_debug > 1) printk("%s: Shutting down ethercard.\n", dev->name); outb(0x00, ioaddr + ULTRA32_CFG6); /* Disable Interrupts. */ outb(0x00, ioaddr + 6); /* Disable interrupts. */ free_irq(dev->irq, dev); NS8390_init(dev, 0); return 0; } static void ultra32_reset_8390(struct net_device *dev) { int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET; /* ASIC base addr */ outb(ULTRA32_RESET, ioaddr); if (ei_debug > 1) printk("resetting Ultra32, t=%ld...", jiffies); ei_status.txing = 0; outb(ULTRA32_MEMENB, ioaddr); /* Enable Shared Memory. */ outb(0x80, ioaddr + ULTRA32_CFG6); /* Enable Interrupts. */ outb(0x84, ioaddr + 5); /* Enable MEM16 & Disable Bus Master. */ outb(0x01, ioaddr + 6); /* Enable Interrupts. */ if (ei_debug > 1) printk("reset done\n"); return; } /* Grab the 8390 specific header. Similar to the block_input routine, but we don't need to be concerned with ring wrap as the header will be at the start of a page, so we optimize accordingly. */ static void ultra32_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page) { void __iomem *hdr_start = ei_status.mem + ((ring_page & 0x1f) << 8); unsigned int RamReg = dev->base_addr - ULTRA32_NIC_OFFSET + ULTRA32_CFG3; /* Select correct 8KB Window. */ outb(ei_status.reg0 | ((ring_page & 0x60) >> 5), RamReg); #ifdef __BIG_ENDIAN /* Officially this is what we are doing, but the readl() is faster */ /* unfortunately it isn't endian aware of the struct */ memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr)); hdr->count = le16_to_cpu(hdr->count); #else ((unsigned int*)hdr)[0] = readl(hdr_start); #endif } /* Block input and output are easy on shared memory ethercards, the only complication is when the ring buffer wraps, or in this case, when a packet spans an 8KB boundary. Note that the current 8KB segment is already set by the get_8390_hdr routine. */ static void ultra32_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset) { void __iomem *xfer_start = ei_status.mem + (ring_offset & 0x1fff); unsigned int RamReg = dev->base_addr - ULTRA32_NIC_OFFSET + ULTRA32_CFG3; if ((ring_offset & ~0x1fff) != ((ring_offset + count - 1) & ~0x1fff)) { int semi_count = 8192 - (ring_offset & 0x1FFF); memcpy_fromio(skb->data, xfer_start, semi_count); count -= semi_count; if (ring_offset < 96*256) { /* Select next 8KB Window. */ ring_offset += semi_count; outb(ei_status.reg0 | ((ring_offset & 0x6000) >> 13), RamReg); memcpy_fromio(skb->data + semi_count, ei_status.mem, count); } else { /* Select first 8KB Window. */ outb(ei_status.reg0, RamReg); memcpy_fromio(skb->data + semi_count, ei_status.mem + TX_PAGES * 256, count); } } else { /* Packet is in one chunk -- we can copy + cksum. */ eth_io_copy_and_sum(skb, xfer_start, count, 0); } } static void ultra32_block_output(struct net_device *dev, int count, const unsigned char *buf, int start_page) { void __iomem *xfer_start = ei_status.mem + (start_page<<8); unsigned int RamReg = dev->base_addr - ULTRA32_NIC_OFFSET + ULTRA32_CFG3; /* Select first 8KB Window. */ outb(ei_status.reg0, RamReg); memcpy_toio(xfer_start, buf, count); } #ifdef MODULE #define MAX_ULTRA32_CARDS 4 /* Max number of Ultra cards per module */ static struct net_device *dev_ultra[MAX_ULTRA32_CARDS]; MODULE_DESCRIPTION("SMC Ultra32 EISA ethernet driver"); MODULE_LICENSE("GPL"); int __init init_module(void) { int this_dev, found = 0; for (this_dev = 0; this_dev < MAX_ULTRA32_CARDS; this_dev++) { struct net_device *dev = ultra32_probe(-1); if (IS_ERR(dev)) break; dev_ultra[found++] = dev; } if (found) return 0; printk(KERN_WARNING "smc-ultra32.c: No SMC Ultra32 found.\n"); return -ENXIO; } void cleanup_module(void) { int this_dev; for (this_dev = 0; this_dev < MAX_ULTRA32_CARDS; this_dev++) { struct net_device *dev = dev_ultra[this_dev]; if (dev) { unregister_netdev(dev); cleanup_card(dev); free_netdev(dev); } } } #endif /* MODULE */