aboutsummaryrefslogtreecommitdiffstats
path: root/net/lapb/lapb_subr.c
diff options
context:
space:
mode:
authorDaniel Yeisley <dan.yeisley@unisys.com>2008-03-25 17:59:08 -0400
committerChristoph Lameter <clameter@sgi.com>2008-03-26 13:44:17 -0400
commitec1f5eeeb5a79a0d48036de649a3498da42db565 (patch)
tree29b34d9aae8b61633e8ad549864d97cc474ec6b2 /net/lapb/lapb_subr.c
parent53625b4204753b904addd40ca96d9ba802e6977d (diff)
slab: fix cache_cache bootstrap in kmem_cache_init()
Commit 556a169dab38b5100df6f4a45b655dddd3db94c1 ("slab: fix bootstrap on memoryless node") introduced bootstrap-time cache_cache list3s for all nodes but forgot that initkmem_list3 needs to be accessed by [somevalue + node]. This patch fixes list_add() corruption in mm/slab.c seen on the ES7000. Cc: Mel Gorman <mel@csn.ul.ie> Cc: Olaf Hering <olaf@aepfle.de> Cc: Christoph Lameter <clameter@sgi.com> Signed-off-by: Dan Yeisley <dan.yeisley@unisys.com> Signed-off-by: Pekka Enberg <penberg@cs.helsinki.fi> Signed-off-by: Christoph Lameter <clameter@sgi.com>
Diffstat (limited to 'net/lapb/lapb_subr.c')
0 files changed, 0 insertions, 0 deletions
5' href='#n135'>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 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
/*
 * sched_trace.c -- record scheduling events to a byte stream.
 */
#include <linux/spinlock.h>
#include <linux/semaphore.h>

#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/miscdevice.h>
#include <asm/uaccess.h>
#include <linux/module.h>
#include <linux/sysrq.h>

#include <linux/kfifo.h>

#include <litmus/sched_trace.h>
#include <litmus/litmus.h>

#define SCHED_TRACE_NAME "litmus/log"

/* Allocate a buffer of about 32k per CPU */
#define LITMUS_TRACE_BUF_PAGES 8
#define LITMUS_TRACE_BUF_SIZE (PAGE_SIZE * LITMUS_TRACE_BUF_PAGES * NR_CPUS)

/* Max length of one read from the buffer */
#define MAX_READ_LEN (64 * 1024)

/* Max length for one write --- from kernel --- to the buffer */
#define MSG_SIZE 255

/* Inner ring buffer structure */
typedef struct {
	rwlock_t	del_lock;

	/* the buffer */
	struct kfifo	kfifo;
} ring_buffer_t;

/* Main buffer structure */
typedef struct {
	ring_buffer_t 		buf;
	atomic_t		reader_cnt;
	struct semaphore	reader_mutex;
} trace_buffer_t;


/*
 * Inner buffer management functions
 */
void rb_init(ring_buffer_t* buf)
{
	rwlock_init(&buf->del_lock);
}

int rb_alloc_buf(ring_buffer_t* buf, unsigned int size)
{
	unsigned long flags;
	int ret = 0;

	write_lock_irqsave(&buf->del_lock, flags);

	/* kfifo size must be a power of 2
	 * atm kfifo alloc is automatically rounding the size
	 */
	ret = kfifo_alloc(&buf->kfifo, size, GFP_ATOMIC);

	write_unlock_irqrestore(&buf->del_lock, flags);

	if(ret < 0)
		printk(KERN_ERR "kfifo_alloc failed\n");

	return ret;
}

int rb_free_buf(ring_buffer_t* buf)
{
	unsigned long flags;

	write_lock_irqsave(&buf->del_lock, flags);

	BUG_ON(!kfifo_initialized(&buf->kfifo));
	kfifo_free(&buf->kfifo);

	write_unlock_irqrestore(&buf->del_lock, flags);

	return 0;
}

/*
 * Assumption: concurrent writes are serialized externally
 *
 * Will only succeed if there is enough space for all len bytes.
 */
int rb_put(ring_buffer_t* buf, char* mem, size_t len)
{
	unsigned long flags;
	int error = 0;

	read_lock_irqsave(&buf->del_lock, flags);

	if (!kfifo_initialized(&buf->kfifo)) {
		error = -ENODEV;
		goto out;
	}

	if((kfifo_in(&buf->kfifo, mem, len)) < len) {
		error = -ENOMEM;
		goto out;
	}

 out:
	read_unlock_irqrestore(&buf->del_lock, flags);
	return error;
}

/* Assumption: concurrent reads are serialized externally */
int rb_get(ring_buffer_t* buf, char* mem, size_t len)
{
	unsigned long flags;
	int error = 0;

	read_lock_irqsave(&buf->del_lock, flags);
	if (!kfifo_initialized(&buf->kfifo)) {
		error = -ENODEV;
		goto out;
	}

	error = kfifo_out(&buf->kfifo, (unsigned char*)mem, len);

 out:
	read_unlock_irqrestore(&buf->del_lock, flags);
	return error;
}

/*
 * Device Driver management
 */
static DEFINE_RAW_SPINLOCK(log_buffer_lock);
static trace_buffer_t log_buffer;

static void init_log_buffer(void)
{
	rb_init(&log_buffer.buf);
	atomic_set(&log_buffer.reader_cnt,0);
	init_MUTEX(&log_buffer.reader_mutex);
}

static DEFINE_PER_CPU(char[MSG_SIZE], fmt_buffer);

/*
 * sched_trace_log_message - Write to the trace buffer (log_buffer)
 *
 * This is the only function accessing the log_buffer from inside the
 * kernel for writing.
 * Concurrent access to sched_trace_log_message must be serialized using
 * log_buffer_lock
 * The maximum length of a formatted message is 255
 */
void sched_trace_log_message(const char* fmt, ...)
{
	unsigned long 	flags;
	va_list 	args;
	size_t		len;
	char*		buf;

	va_start(args, fmt);
	local_irq_save(flags);

	/* format message */
	buf = __get_cpu_var(fmt_buffer);
	len = vscnprintf(buf, MSG_SIZE, fmt, args);

	raw_spin_lock(&log_buffer_lock);
	/* Don't copy the trailing null byte, we don't want null bytes
	 * in a text file.
	 */
	rb_put(&log_buffer.buf, buf, len);
	raw_spin_unlock(&log_buffer_lock);

	local_irq_restore(flags);
	va_end(args);
}

/*
 * log_read - Read the trace buffer
 *
 * This function is called as a file operation from userspace.
 * Readers can sleep. Access is serialized through reader_mutex
 */
static ssize_t log_read(struct file *filp, char __user *to, size_t len,
		      loff_t *f_pos)
{
	/* we ignore f_pos, this is strictly sequential */

	ssize_t error = -EINVAL;
	char*   mem;
	trace_buffer_t *tbuf = filp->private_data;

	if (down_interruptible(&tbuf->reader_mutex)) {
		error = -ERESTARTSYS;
		goto out;
	}

	if (len > MAX_READ_LEN)
		len = MAX_READ_LEN;

	mem = kmalloc(len, GFP_KERNEL);
	if (!mem) {
		error = -ENOMEM;
		goto out_unlock;
	}

	error = rb_get(&tbuf->buf, mem, len);
	while (!error) {
		set_current_state(TASK_INTERRUPTIBLE);
		schedule_timeout(110);
		if (signal_pending(current))
			error = -ERESTARTSYS;
		else
			error = rb_get(&tbuf->buf, mem, len);
	}

	if (error > 0 && copy_to_user(to, mem, error))
		error = -EFAULT;

	kfree(mem);
 out_unlock:
	up(&tbuf->reader_mutex);
 out:
	return error;
}

/*
 * Enable redirection of printk() messages to the trace buffer.
 * Defined in kernel/printk.c
 */
extern int trace_override;
extern int trace_recurse;

/*
 * log_open - open the global log message ring buffer.
 */
static int log_open(struct inode *in, struct file *filp)
{
	int error = -EINVAL;
	trace_buffer_t* tbuf;

	tbuf = &log_buffer;

	if (down_interruptible(&tbuf->reader_mutex)) {
		error = -ERESTARTSYS;
		goto out;
	}

	/* first open must allocate buffers */
	if (atomic_inc_return(&tbuf->reader_cnt) == 1) {
		if ((error = rb_alloc_buf(&tbuf->buf, LITMUS_TRACE_BUF_SIZE)))
		{
			atomic_dec(&tbuf->reader_cnt);
			goto out_unlock;
		}
	}

	error = 0;
	filp->private_data = tbuf;

	printk(KERN_DEBUG
	       "sched_trace kfifo with buffer starting at: 0x%p\n",
	       (tbuf->buf.kfifo).buffer);

	/* override printk() */
	trace_override++;

 out_unlock:
	up(&tbuf->reader_mutex);
 out:
	return error;
}

static int log_release(struct inode *in, struct file *filp)
{
	int error = -EINVAL;
	trace_buffer_t* tbuf = filp->private_data;

	BUG_ON(!filp->private_data);

	if (down_interruptible(&tbuf->reader_mutex)) {
		error = -ERESTARTSYS;
		goto out;
	}

	/* last release must deallocate buffers */
	if (atomic_dec_return(&tbuf->reader_cnt) == 0) {
		error = rb_free_buf(&tbuf->buf);
	}

	/* release printk() overriding */
	trace_override--;

	printk(KERN_DEBUG "sched_trace kfifo released\n");

	up(&tbuf->reader_mutex);
 out:
	return error;
}

/*
 * log_fops  - The file operations for accessing the global LITMUS log message
 *             buffer.
 *
 * Except for opening the device file it uses the same operations as trace_fops.
 */
static struct file_operations log_fops = {
	.owner   = THIS_MODULE,
	.open    = log_open,
	.release = log_release,
	.read    = log_read,
};

static struct miscdevice litmus_log_dev = {
	.name    = SCHED_TRACE_NAME,
	.minor   = MISC_DYNAMIC_MINOR,
	.fops    = &log_fops,
};

#ifdef CONFIG_MAGIC_SYSRQ
void dump_trace_buffer(int max)
{
	char line[80];
	int len;
	int count = 0;

	/* potential, but very unlikely, race... */
	trace_recurse = 1;
	while ((max == 0 || count++ < max) &&
	       (len = rb_get(&log_buffer.buf, line, sizeof(line) - 1)) > 0) {
		line[len] = '\0';
		printk("%s", line);
	}
	trace_recurse = 0;
}

static void sysrq_dump_trace_buffer(int key, struct tty_struct *tty)
{
	dump_trace_buffer(100);
}

static struct sysrq_key_op sysrq_dump_trace_buffer_op = {
	.handler	= sysrq_dump_trace_buffer,
	.help_msg	= "dump-trace-buffer(Y)",
	.action_msg	= "writing content of TRACE() buffer",
};
#endif

static int __init init_sched_trace(void)
{
	printk("Initializing TRACE() device\n");
	init_log_buffer();

#ifdef CONFIG_MAGIC_SYSRQ
	/* offer some debugging help */
	if (!register_sysrq_key('y', &sysrq_dump_trace_buffer_op))
		printk("Registered dump-trace-buffer(Y) magic sysrq.\n");
	else
		printk("Could not register dump-trace-buffer(Y) magic sysrq.\n");
#endif


	return misc_register(&litmus_log_dev);
}

static void __exit exit_sched_trace(void)
{
	misc_deregister(&litmus_log_dev);
}

module_init(init_sched_trace);
module_exit(exit_sched_trace);