aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/md/dm-io.c
blob: 9de000131a8a2cb729c9e66858d30ed0d5700088 (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
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
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
/*
 * Copyright (C) 2003 Sistina Software
 *
 * This file is released under the GPL.
 */

#include "dm-io.h"

#include <linux/bio.h>
#include <linux/mempool.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/slab.h>

static struct bio_set *_bios;

/* FIXME: can we shrink this ? */
struct io {
	unsigned long error;
	atomic_t count;
	struct task_struct *sleeper;
	io_notify_fn callback;
	void *context;
};

/*
 * io contexts are only dynamically allocated for asynchronous
 * io.  Since async io is likely to be the majority of io we'll
 * have the same number of io contexts as buffer heads ! (FIXME:
 * must reduce this).
 */
static unsigned _num_ios;
static mempool_t *_io_pool;

static void *alloc_io(unsigned int __nocast gfp_mask, void *pool_data)
{
	return kmalloc(sizeof(struct io), gfp_mask);
}

static void free_io(void *element, void *pool_data)
{
	kfree(element);
}

static unsigned int pages_to_ios(unsigned int pages)
{
	return 4 * pages;	/* too many ? */
}

static int resize_pool(unsigned int new_ios)
{
	int r = 0;

	if (_io_pool) {
		if (new_ios == 0) {
			/* free off the pool */
			mempool_destroy(_io_pool);
			_io_pool = NULL;
			bioset_free(_bios);

		} else {
			/* resize the pool */
			r = mempool_resize(_io_pool, new_ios, GFP_KERNEL);
		}

	} else {
		/* create new pool */
		_io_pool = mempool_create(new_ios, alloc_io, free_io, NULL);
		if (!_io_pool)
			return -ENOMEM;

		_bios = bioset_create(16, 16, 4);
		if (!_bios) {
			mempool_destroy(_io_pool);
			_io_pool = NULL;
			return -ENOMEM;
		}
	}

	if (!r)
		_num_ios = new_ios;

	return r;
}

int dm_io_get(unsigned int num_pages)
{
	return resize_pool(_num_ios + pages_to_ios(num_pages));
}

void dm_io_put(unsigned int num_pages)
{
	resize_pool(_num_ios - pages_to_ios(num_pages));
}

/*-----------------------------------------------------------------
 * We need to keep track of which region a bio is doing io for.
 * In order to save a memory allocation we store this the last
 * bvec which we know is unused (blech).
 * XXX This is ugly and can OOPS with some configs... find another way.
 *---------------------------------------------------------------*/
static inline void bio_set_region(struct bio *bio, unsigned region)
{
	bio->bi_io_vec[bio->bi_max_vecs - 1].bv_len = region;
}

static inline unsigned bio_get_region(struct bio *bio)
{
	return bio->bi_io_vec[bio->bi_max_vecs - 1].bv_len;
}

/*-----------------------------------------------------------------
 * We need an io object to keep track of the number of bios that
 * have been dispatched for a particular io.
 *---------------------------------------------------------------*/
static void dec_count(struct io *io, unsigned int region, int error)
{
	if (error)
		set_bit(region, &io->error);

	if (atomic_dec_and_test(&io->count)) {
		if (io->sleeper)
			wake_up_process(io->sleeper);

		else {
			int r = io->error;
			io_notify_fn fn = io->callback;
			void *context = io->context;

			mempool_free(io, _io_pool);
			fn(r, context);
		}
	}
}

static int endio(struct bio *bio, unsigned int done, int error)
{
	struct io *io = (struct io *) bio->bi_private;

	/* keep going until we've finished */
	if (bio->bi_size)
		return 1;

	if (error && bio_data_dir(bio) == READ)
		zero_fill_bio(bio);

	dec_count(io, bio_get_region(bio), error);
	bio_put(bio);

	return 0;
}

/*-----------------------------------------------------------------
 * These little objects provide an abstraction for getting a new
 * destination page for io.
 *---------------------------------------------------------------*/
struct dpages {
	void (*get_page)(struct dpages *dp,
			 struct page **p, unsigned long *len, unsigned *offset);
	void (*next_page)(struct dpages *dp);

	unsigned context_u;
	void *context_ptr;
};

/*
 * Functions for getting the pages from a list.
 */
static void list_get_page(struct dpages *dp,
		  struct page **p, unsigned long *len, unsigned *offset)
{
	unsigned o = dp->context_u;
	struct page_list *pl = (struct page_list *) dp->context_ptr;

	*p = pl->page;
	*len = PAGE_SIZE - o;
	*offset = o;
}

static void list_next_page(struct dpages *dp)
{
	struct page_list *pl = (struct page_list *) dp->context_ptr;
	dp->context_ptr = pl->next;
	dp->context_u = 0;
}

static void list_dp_init(struct dpages *dp, struct page_list *pl, unsigned offset)
{
	dp->get_page = list_get_page;
	dp->next_page = list_next_page;
	dp->context_u = offset;
	dp->context_ptr = pl;
}

/*
 * Functions for getting the pages from a bvec.
 */
static void bvec_get_page(struct dpages *dp,
		  struct page **p, unsigned long *len, unsigned *offset)
{
	struct bio_vec *bvec = (struct bio_vec *) dp->context_ptr;
	*p = bvec->bv_page;
	*len = bvec->bv_len;
	*offset = bvec->bv_offset;
}

static void bvec_next_page(struct dpages *dp)
{
	struct bio_vec *bvec = (struct bio_vec *) dp->context_ptr;
	dp->context_ptr = bvec + 1;
}

static void bvec_dp_init(struct dpages *dp, struct bio_vec *bvec)
{
	dp->get_page = bvec_get_page;
	dp->next_page = bvec_next_page;
	dp->context_ptr = bvec;
}

static void vm_get_page(struct dpages *dp,
		 struct page **p, unsigned long *len, unsigned *offset)
{
	*p = vmalloc_to_page(dp->context_ptr);
	*offset = dp->context_u;
	*len = PAGE_SIZE - dp->context_u;
}

static void vm_next_page(struct dpages *dp)
{
	dp->context_ptr += PAGE_SIZE - dp->context_u;
	dp->context_u = 0;
}

static void vm_dp_init(struct dpages *dp, void *data)
{
	dp->get_page = vm_get_page;
	dp->next_page = vm_next_page;
	dp->context_u = ((unsigned long) data) & (PAGE_SIZE - 1);
	dp->context_ptr = data;
}

static void dm_bio_destructor(struct bio *bio)
{
	bio_free(bio, _bios);
}

/*-----------------------------------------------------------------
 * IO routines that accept a list of pages.
 *---------------------------------------------------------------*/
static void do_region(int rw, unsigned int region, struct io_region *where,
		      struct dpages *dp, struct io *io)
{
	struct bio *bio;
	struct page *page;
	unsigned long len;
	unsigned offset;
	unsigned num_bvecs;
	sector_t remaining = where->count;

	while (remaining) {
		/*
		 * Allocate a suitably sized bio, we add an extra
		 * bvec for bio_get/set_region().
		 */
		num_bvecs = (remaining / (PAGE_SIZE >> 9)) + 2;
		bio = bio_alloc_bioset(GFP_NOIO, num_bvecs, _bios);
		bio->bi_sector = where->sector + (where->count - remaining);
		bio->bi_bdev = where->bdev;
		bio->bi_end_io = endio;
		bio->bi_private = io;
		bio->bi_destructor = dm_bio_destructor;
		bio_set_region(bio, region);

		/*
		 * Try and add as many pages as possible.
		 */
		while (remaining) {
			dp->get_page(dp, &page, &len, &offset);
			len = min(len, to_bytes(remaining));
			if (!bio_add_page(bio, page, len, offset))
				break;

			offset = 0;
			remaining -= to_sector(len);
			dp->next_page(dp);
		}

		atomic_inc(&io->count);
		submit_bio(rw, bio);
	}
}

static void dispatch_io(int rw, unsigned int num_regions,
			struct io_region *where, struct dpages *dp,
			struct io *io, int sync)
{
	int i;
	struct dpages old_pages = *dp;

	if (sync)
		rw |= (1 << BIO_RW_SYNC);

	/*
	 * For multiple regions we need to be careful to rewind
	 * the dp object for each call to do_region.
	 */
	for (i = 0; i < num_regions; i++) {
		*dp = old_pages;
		if (where[i].count)
			do_region(rw, i, where + i, dp, io);
	}

	/*
	 * Drop the extra refence that we were holding to avoid
	 * the io being completed too early.
	 */
	dec_count(io, 0, 0);
}

static int sync_io(unsigned int num_regions, struct io_region *where,
	    int rw, struct dpages *dp, unsigned long *error_bits)
{
	struct io io;

	if (num_regions > 1 && rw != WRITE) {
		WARN_ON(1);
		return -EIO;
	}

	io.error = 0;
	atomic_set(&io.count, 1); /* see dispatch_io() */
	io.sleeper = current;

	dispatch_io(rw, num_regions, where, dp, &io, 1);

	while (1) {
		set_current_state(TASK_UNINTERRUPTIBLE);

		if (!atomic_read(&io.count) || signal_pending(current))
			break;

		io_schedule();
	}
	set_current_state(TASK_RUNNING);

	if (atomic_read(&io.count))
		return -EINTR;

	*error_bits = io.error;
	return io.error ? -EIO : 0;
}

static int async_io(unsigned int num_regions, struct io_region *where, int rw,
	     struct dpages *dp, io_notify_fn fn, void *context)
{
	struct io *io;

	if (num_regions > 1 && rw != WRITE) {
		WARN_ON(1);
		fn(1, context);
		return -EIO;
	}

	io = mempool_alloc(_io_pool, GFP_NOIO);
	io->error = 0;
	atomic_set(&io->count, 1); /* see dispatch_io() */
	io->sleeper = NULL;
	io->callback = fn;
	io->context = context;

	dispatch_io(rw, num_regions, where, dp, io, 0);
	return 0;
}

int dm_io_sync(unsigned int num_regions, struct io_region *where, int rw,
	       struct page_list *pl, unsigned int offset,
	       unsigned long *error_bits)
{
	struct dpages dp;
	list_dp_init(&dp, pl, offset);
	return sync_io(num_regions, where, rw, &dp, error_bits);
}

int dm_io_sync_bvec(unsigned int num_regions, struct io_region *where, int rw,
		    struct bio_vec *bvec, unsigned long *error_bits)
{
	struct dpages dp;
	bvec_dp_init(&dp, bvec);
	return sync_io(num_regions, where, rw, &dp, error_bits);
}

int dm_io_sync_vm(unsigned int num_regions, struct io_region *where, int rw,
		  void *data, unsigned long *error_bits)
{
	struct dpages dp;
	vm_dp_init(&dp, data);
	return sync_io(num_regions, where, rw, &dp, error_bits);
}

int dm_io_async(unsigned int num_regions, struct io_region *where, int rw,
		struct page_list *pl, unsigned int offset,
		io_notify_fn fn, void *context)
{
	struct dpages dp;
	list_dp_init(&dp, pl, offset);
	return async_io(num_regions, where, rw, &dp, fn, context);
}

int dm_io_async_bvec(unsigned int num_regions, struct io_region *where, int rw,
		     struct bio_vec *bvec, io_notify_fn fn, void *context)
{
	struct dpages dp;
	bvec_dp_init(&dp, bvec);
	return async_io(num_regions, where, rw, &dp, fn, context);
}

int dm_io_async_vm(unsigned int num_regions, struct io_region *where, int rw,
		   void *data, io_notify_fn fn, void *context)
{
	struct dpages dp;
	vm_dp_init(&dp, data);
	return async_io(num_regions, where, rw, &dp, fn, context);
}

EXPORT_SYMBOL(dm_io_get);
EXPORT_SYMBOL(dm_io_put);
EXPORT_SYMBOL(dm_io_sync);
EXPORT_SYMBOL(dm_io_async);
EXPORT_SYMBOL(dm_io_sync_bvec);
EXPORT_SYMBOL(dm_io_async_bvec);
EXPORT_SYMBOL(dm_io_sync_vm);
EXPORT_SYMBOL(dm_io_async_vm);
l com"> * These functions re-use the assembly code in head.S, which * already provide the required functionality. */ extern struct proc_info_list *lookup_processor_type(unsigned int); extern struct machine_desc *lookup_machine_type(unsigned int); static void __init setup_processor(void) { struct proc_info_list *list; /* * locate processor in the list of supported processor * types. The linker builds this table for us from the * entries in arch/arm/mm/proc-*.S */ list = lookup_processor_type(processor_id); if (!list) { printk("CPU configuration botched (ID %08x), unable " "to continue.\n", processor_id); while (1); } cpu_name = list->cpu_name; #ifdef MULTI_CPU processor = *list->proc; #endif #ifdef MULTI_TLB cpu_tlb = *list->tlb; #endif #ifdef MULTI_USER cpu_user = *list->user; #endif #ifdef MULTI_CACHE cpu_cache = *list->cache; #endif printk("CPU: %s [%08x] revision %d (ARMv%s), cr=%08lx\n", cpu_name, processor_id, (int)processor_id & 15, proc_arch[cpu_architecture()], cr_alignment); sprintf(init_utsname()->machine, "%s%c", list->arch_name, ENDIANNESS); sprintf(elf_platform, "%s%c", list->elf_name, ENDIANNESS); elf_hwcap = list->elf_hwcap; #ifndef CONFIG_ARM_THUMB elf_hwcap &= ~HWCAP_THUMB; #endif #ifndef CONFIG_VFP elf_hwcap &= ~HWCAP_VFP; #endif cpu_proc_init(); } /* * cpu_init - initialise one CPU. * * cpu_init dumps the cache information, initialises SMP specific * information, and sets up the per-CPU stacks. */ void cpu_init(void) { unsigned int cpu = smp_processor_id(); struct stack *stk = &stacks[cpu]; if (cpu >= NR_CPUS) { printk(KERN_CRIT "CPU%u: bad primary CPU number\n", cpu); BUG(); } if (system_state == SYSTEM_BOOTING) dump_cpu_info(cpu); /* * setup stacks for re-entrant exception handlers */ __asm__ ( "msr cpsr_c, %1\n\t" "add sp, %0, %2\n\t" "msr cpsr_c, %3\n\t" "add sp, %0, %4\n\t" "msr cpsr_c, %5\n\t" "add sp, %0, %6\n\t" "msr cpsr_c, %7" : : "r" (stk), "I" (PSR_F_BIT | PSR_I_BIT | IRQ_MODE), "I" (offsetof(struct stack, irq[0])), "I" (PSR_F_BIT | PSR_I_BIT | ABT_MODE), "I" (offsetof(struct stack, abt[0])), "I" (PSR_F_BIT | PSR_I_BIT | UND_MODE), "I" (offsetof(struct stack, und[0])), "I" (PSR_F_BIT | PSR_I_BIT | SVC_MODE) : "r14"); } static struct machine_desc * __init setup_machine(unsigned int nr) { struct machine_desc *list; /* * locate machine in the list of supported machines. */ list = lookup_machine_type(nr); if (!list) { printk("Machine configuration botched (nr %d), unable " "to continue.\n", nr); while (1); } printk("Machine: %s\n", list->name); return list; } static void __init early_initrd(char **p) { unsigned long start, size; start = memparse(*p, p); if (**p == ',') { size = memparse((*p) + 1, p); phys_initrd_start = start; phys_initrd_size = size; } } __early_param("initrd=", early_initrd); static void __init arm_add_memory(unsigned long start, unsigned long size) { struct membank *bank; /* * Ensure that start/size are aligned to a page boundary. * Size is appropriately rounded down, start is rounded up. */ size -= start & ~PAGE_MASK; bank = &meminfo.bank[meminfo.nr_banks++]; bank->start = PAGE_ALIGN(start); bank->size = size & PAGE_MASK; bank->node = PHYS_TO_NID(start); } /* * Pick out the memory size. We look for mem=size@start, * where start and size are "size[KkMm]" */ static void __init early_mem(char **p) { static int usermem __initdata = 0; unsigned long size, start; /* * If the user specifies memory size, we * blow away any automatically generated * size. */ if (usermem == 0) { usermem = 1; meminfo.nr_banks = 0; } start = PHYS_OFFSET; size = memparse(*p, p); if (**p == '@') start = memparse(*p + 1, p); arm_add_memory(start, size); } __early_param("mem=", early_mem); /* * Initial parsing of the command line. */ static void __init parse_cmdline(char **cmdline_p, char *from) { char c = ' ', *to = command_line; int len = 0; for (;;) { if (c == ' ') { extern struct early_params __early_begin, __early_end; struct early_params *p; for (p = &__early_begin; p < &__early_end; p++) { int len = strlen(p->arg); if (memcmp(from, p->arg, len) == 0) { if (to != command_line) to -= 1; from += len; p->fn(&from); while (*from != ' ' && *from != '\0') from++; break; } } } c = *from++; if (!c) break; if (COMMAND_LINE_SIZE <= ++len) break; *to++ = c; } *to = '\0'; *cmdline_p = command_line; } static void __init setup_ramdisk(int doload, int prompt, int image_start, unsigned int rd_sz) { #ifdef CONFIG_BLK_DEV_RAM extern int rd_size, rd_image_start, rd_prompt, rd_doload; rd_image_start = image_start; rd_prompt = prompt; rd_doload = doload; if (rd_sz) rd_size = rd_sz; #endif } static void __init request_standard_resources(struct meminfo *mi, struct machine_desc *mdesc) { struct resource *res; int i; kernel_code.start = virt_to_phys(&_text); kernel_code.end = virt_to_phys(&_etext - 1); kernel_data.start = virt_to_phys(&__data_start); kernel_data.end = virt_to_phys(&_end - 1); for (i = 0; i < mi->nr_banks; i++) { unsigned long virt_start, virt_end; if (mi->bank[i].size == 0) continue; virt_start = __phys_to_virt(mi->bank[i].start); virt_end = virt_start + mi->bank[i].size - 1; res = alloc_bootmem_low(sizeof(*res)); res->name = "System RAM"; res->start = __virt_to_phys(virt_start); res->end = __virt_to_phys(virt_end); res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; request_resource(&iomem_resource, res); if (kernel_code.start >= res->start && kernel_code.end <= res->end) request_resource(res, &kernel_code); if (kernel_data.start >= res->start && kernel_data.end <= res->end) request_resource(res, &kernel_data); } if (mdesc->video_start) { video_ram.start = mdesc->video_start; video_ram.end = mdesc->video_end; request_resource(&iomem_resource, &video_ram); } /* * Some machines don't have the possibility of ever * possessing lp0, lp1 or lp2 */ if (mdesc->reserve_lp0) request_resource(&ioport_resource, &lp0); if (mdesc->reserve_lp1) request_resource(&ioport_resource, &lp1); if (mdesc->reserve_lp2) request_resource(&ioport_resource, &lp2); } /* * Tag parsing. * * This is the new way of passing data to the kernel at boot time. Rather * than passing a fixed inflexible structure to the kernel, we pass a list * of variable-sized tags to the kernel. The first tag must be a ATAG_CORE * tag for the list to be recognised (to distinguish the tagged list from * a param_struct). The list is terminated with a zero-length tag (this tag * is not parsed in any way). */ static int __init parse_tag_core(const struct tag *tag) { if (tag->hdr.size > 2) { if ((tag->u.core.flags & 1) == 0) root_mountflags &= ~MS_RDONLY; ROOT_DEV = old_decode_dev(tag->u.core.rootdev); } return 0; } __tagtable(ATAG_CORE, parse_tag_core); static int __init parse_tag_mem32(const struct tag *tag) { if (meminfo.nr_banks >= NR_BANKS) { printk(KERN_WARNING "Ignoring memory bank 0x%08x size %dKB\n", tag->u.mem.start, tag->u.mem.size / 1024); return -EINVAL; } arm_add_memory(tag->u.mem.start, tag->u.mem.size); return 0; } __tagtable(ATAG_MEM, parse_tag_mem32); #if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_DUMMY_CONSOLE) struct screen_info screen_info = { .orig_video_lines = 30, .orig_video_cols = 80, .orig_video_mode = 0, .orig_video_ega_bx = 0, .orig_video_isVGA = 1, .orig_video_points = 8 }; static int __init parse_tag_videotext(const struct tag *tag) { screen_info.orig_x = tag->u.videotext.x; screen_info.orig_y = tag->u.videotext.y; screen_info.orig_video_page = tag->u.videotext.video_page; screen_info.orig_video_mode = tag->u.videotext.video_mode; screen_info.orig_video_cols = tag->u.videotext.video_cols; screen_info.orig_video_ega_bx = tag->u.videotext.video_ega_bx; screen_info.orig_video_lines = tag->u.videotext.video_lines; screen_info.orig_video_isVGA = tag->u.videotext.video_isvga; screen_info.orig_video_points = tag->u.videotext.video_points; return 0; } __tagtable(ATAG_VIDEOTEXT, parse_tag_videotext); #endif static int __init parse_tag_ramdisk(const struct tag *tag) { setup_ramdisk((tag->u.ramdisk.flags & 1) == 0, (tag->u.ramdisk.flags & 2) == 0, tag->u.ramdisk.start, tag->u.ramdisk.size); return 0; } __tagtable(ATAG_RAMDISK, parse_tag_ramdisk); static int __init parse_tag_initrd(const struct tag *tag) { printk(KERN_WARNING "ATAG_INITRD is deprecated; " "please update your bootloader.\n"); phys_initrd_start = __virt_to_phys(tag->u.initrd.start); phys_initrd_size = tag->u.initrd.size; return 0; } __tagtable(ATAG_INITRD, parse_tag_initrd); static int __init parse_tag_initrd2(const struct tag *tag) { phys_initrd_start = tag->u.initrd.start; phys_initrd_size = tag->u.initrd.size; return 0; } __tagtable(ATAG_INITRD2, parse_tag_initrd2); static int __init parse_tag_serialnr(const struct tag *tag) { system_serial_low = tag->u.serialnr.low; system_serial_high = tag->u.serialnr.high; return 0; } __tagtable(ATAG_SERIAL, parse_tag_serialnr); static int __init parse_tag_revision(const struct tag *tag) { system_rev = tag->u.revision.rev; return 0; } __tagtable(ATAG_REVISION, parse_tag_revision); static int __init parse_tag_cmdline(const struct tag *tag) { strlcpy(default_command_line, tag->u.cmdline.cmdline, COMMAND_LINE_SIZE); return 0; } __tagtable(ATAG_CMDLINE, parse_tag_cmdline); /* * Scan the tag table for this tag, and call its parse function. * The tag table is built by the linker from all the __tagtable * declarations. */ static int __init parse_tag(const struct tag *tag) { extern struct tagtable __tagtable_begin, __tagtable_end; struct tagtable *t; for (t = &__tagtable_begin; t < &__tagtable_end; t++) if (tag->hdr.tag == t->tag) { t->parse(tag); break; } return t < &__tagtable_end; } /* * Parse all tags in the list, checking both the global and architecture * specific tag tables. */ static void __init parse_tags(const struct tag *t) { for (; t->hdr.size; t = tag_next(t)) if (!parse_tag(t)) printk(KERN_WARNING "Ignoring unrecognised tag 0x%08x\n", t->hdr.tag); } /* * This holds our defaults. */ static struct init_tags { struct tag_header hdr1; struct tag_core core; struct tag_header hdr2; struct tag_mem32 mem; struct tag_header hdr3; } init_tags __initdata = { { tag_size(tag_core), ATAG_CORE }, { 1, PAGE_SIZE, 0xff }, { tag_size(tag_mem32), ATAG_MEM }, { MEM_SIZE, PHYS_OFFSET }, { 0, ATAG_NONE } }; static void (*init_machine)(void) __initdata; static int __init customize_machine(void) { /* customizes platform devices, or adds new ones */ if (init_machine) init_machine(); return 0; } arch_initcall(customize_machine); void __init setup_arch(char **cmdline_p) { struct tag *tags = (struct tag *)&init_tags; struct machine_desc *mdesc; char *from = default_command_line; setup_processor(); mdesc = setup_machine(machine_arch_type); machine_name = mdesc->name; if (mdesc->soft_reboot) reboot_setup("s"); if (mdesc->boot_params) tags = phys_to_virt(mdesc->boot_params); /* * If we have the old style parameters, convert them to * a tag list. */ if (tags->hdr.tag != ATAG_CORE) convert_to_tag_list(tags); if (tags->hdr.tag != ATAG_CORE) tags = (struct tag *)&init_tags; if (mdesc->fixup) mdesc->fixup(mdesc, tags, &from, &meminfo); if (tags->hdr.tag == ATAG_CORE) { if (meminfo.nr_banks != 0) squash_mem_tags(tags); parse_tags(tags); } init_mm.start_code = (unsigned long) &_text; init_mm.end_code = (unsigned long) &_etext; init_mm.end_data = (unsigned long) &_edata; init_mm.brk = (unsigned long) &_end; memcpy(saved_command_line, from, COMMAND_LINE_SIZE); saved_command_line[COMMAND_LINE_SIZE-1] = '\0'; parse_cmdline(cmdline_p, from); paging_init(&meminfo, mdesc); request_standard_resources(&meminfo, mdesc); #ifdef CONFIG_SMP smp_init_cpus(); #endif cpu_init(); /* * Set up various architecture-specific pointers */ init_arch_irq = mdesc->init_irq; system_timer = mdesc->timer; init_machine = mdesc->init_machine; #ifdef CONFIG_VT #if defined(CONFIG_VGA_CONSOLE) conswitchp = &vga_con; #elif defined(CONFIG_DUMMY_CONSOLE) conswitchp = &dummy_con; #endif #endif } static int __init topology_init(void) { int cpu; for_each_possible_cpu(cpu) register_cpu(&per_cpu(cpu_data, cpu).cpu, cpu); return 0; } subsys_initcall(topology_init); static const char *hwcap_str[] = { "swp", "half", "thumb", "26bit", "fastmult", "fpa", "vfp", "edsp", "java", "iwmmxt", NULL }; static void c_show_cache(struct seq_file *m, const char *type, unsigned int cache) { unsigned int mult = 2 + (CACHE_M(cache) ? 1 : 0); seq_printf(m, "%s size\t\t: %d\n" "%s assoc\t\t: %d\n" "%s line length\t: %d\n" "%s sets\t\t: %d\n", type, mult << (8 + CACHE_SIZE(cache)), type, (mult << CACHE_ASSOC(cache)) >> 1, type, 8 << CACHE_LINE(cache), type, 1 << (6 + CACHE_SIZE(cache) - CACHE_ASSOC(cache) - CACHE_LINE(cache))); } static int c_show(struct seq_file *m, void *v) { int i; seq_printf(m, "Processor\t: %s rev %d (%s)\n", cpu_name, (int)processor_id & 15, elf_platform); #if defined(CONFIG_SMP) for_each_online_cpu(i) { /* * glibc reads /proc/cpuinfo to determine the number of * online processors, looking for lines beginning with * "processor". Give glibc what it expects. */ seq_printf(m, "processor\t: %d\n", i); seq_printf(m, "BogoMIPS\t: %lu.%02lu\n\n", per_cpu(cpu_data, i).loops_per_jiffy / (500000UL/HZ), (per_cpu(cpu_data, i).loops_per_jiffy / (5000UL/HZ)) % 100); } #else /* CONFIG_SMP */ seq_printf(m, "BogoMIPS\t: %lu.%02lu\n", loops_per_jiffy / (500000/HZ), (loops_per_jiffy / (5000/HZ)) % 100); #endif /* dump out the processor features */ seq_puts(m, "Features\t: "); for (i = 0; hwcap_str[i]; i++)