m68k/atari: Reserve some ST-RAM early on for device buffer use

Based on an original patch from Michael Schmitz:

Because mem_init() is now called before device init, devices that rely on
ST-RAM may find all ST-RAM already allocated to other users by the time
device init happens. In particular, a large initrd RAM disk may use up
enough of ST-RAM to cause atari_stram_alloc() to resort to
__get_dma_pages() allocation.

In the current state of Atari memory management, all of RAM is marked
DMA capable, so __get_dma_pages() may well return RAM that is not in actual
fact DMA capable. Using this for frame buffer or SCSI DMA buffer causes
subtle failure.

The ST-RAM allocator has been changed to allocate memory from a pool of
reserved ST-RAM of configurable size, set aside on ST-RAM init (i.e.
before mem_init()). As long as this pool is not exhausted, allocation of
real ST-RAM can be guaranteed.

Other changes:
  - Replace the custom allocator in the ST-RAM pool by the existing allocator
    in the resource subsystem,
  - Remove mem_init_done and its hook, as memory init is now done before
    device init,
  - Remove /proc/stram, as ST-RAM usage now shows up under /proc/iomem, e.g.

	005f2000-006f1fff : ST-RAM Pool
	  005f2000-0063dfff : atafb
	  0063e000-00641fff : ataflop
	  00642000-00642fff : SCSI

Signed-off-by: Michael Schmitz <schmitz@debian.org>
[Andreas Schwab <schwab@linux-m68k.org>: Use memparse()]
[Geert: Use the resource subsystem instead of a custom allocator]
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
This commit is contained in:
Michael Schmitz 2010-11-01 19:54:00 +01:00 committed by Geert Uytterhoeven
parent b7785e9543
commit 217bbd8188
4 changed files with 68 additions and 296 deletions

View File

@ -372,12 +372,6 @@ config AMIGA_PCMCIA
Include support in the kernel for pcmcia on Amiga 1200 and Amiga Include support in the kernel for pcmcia on Amiga 1200 and Amiga
600. If you intend to use pcmcia cards say Y; otherwise say N. 600. If you intend to use pcmcia cards say Y; otherwise say N.
config STRAM_PROC
bool "ST-RAM statistics in /proc"
depends on ATARI
help
Say Y here to report ST-RAM usage statistics in /proc/stram.
config HEARTBEAT config HEARTBEAT
bool "Use power LED as a heartbeat" if AMIGA || APOLLO || ATARI || MAC ||Q40 bool "Use power LED as a heartbeat" if AMIGA || APOLLO || ATARI || MAC ||Q40
default y if !AMIGA && !APOLLO && !ATARI && !MAC && !Q40 && HP300 default y if !AMIGA && !APOLLO && !ATARI && !MAC && !Q40 && HP300

View File

@ -1,5 +1,5 @@
/* /*
* arch/m68k/atari/stram.c: Functions for ST-RAM allocations * Functions for ST-RAM allocations
* *
* Copyright 1994-97 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> * Copyright 1994-97 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
* *
@ -30,91 +30,35 @@
#include <asm/atari_stram.h> #include <asm/atari_stram.h>
#include <asm/io.h> #include <asm/io.h>
#undef DEBUG
#ifdef DEBUG
#define DPRINTK(fmt,args...) printk( fmt, ##args )
#else
#define DPRINTK(fmt,args...)
#endif
#if defined(CONFIG_PROC_FS) && defined(CONFIG_STRAM_PROC)
/* abbrev for the && above... */
#define DO_PROC
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#endif
/* /*
* ++roman: * The ST-RAM allocator allocates memory from a pool of reserved ST-RAM of
* * configurable size, set aside on ST-RAM init.
* New version of ST-Ram buffer allocation. Instead of using the * As long as this pool is not exhausted, allocation of real ST-RAM can be
* 1 MB - 4 KB that remain when the ST-Ram chunk starts at $1000 * guaranteed.
* (1 MB granularity!), such buffers are reserved like this:
*
* - If the kernel resides in ST-Ram anyway, we can take the buffer
* from behind the current kernel data space the normal way
* (incrementing start_mem).
*
* - If the kernel is in TT-Ram, stram_init() initializes start and
* end of the available region. Buffers are allocated from there
* and mem_init() later marks the such used pages as reserved.
* Since each TT-Ram chunk is at least 4 MB in size, I hope there
* won't be an overrun of the ST-Ram region by normal kernel data
* space.
*
* For that, ST-Ram may only be allocated while kernel initialization
* is going on, or exactly: before mem_init() is called. There is also
* no provision now for freeing ST-Ram buffers. It seems that isn't
* really needed.
*
*/ */
/* Start and end (virtual) of ST-RAM */
static void *stram_start, *stram_end;
/* set after memory_init() executed and allocations via start_mem aren't
* possible anymore */
static int mem_init_done;
/* set if kernel is in ST-RAM */ /* set if kernel is in ST-RAM */
static int kernel_in_stram; static int kernel_in_stram;
typedef struct stram_block { static struct resource stram_pool = {
struct stram_block *next; .name = "ST-RAM Pool"
void *start; };
unsigned long size;
unsigned flags;
const char *owner;
} BLOCK;
/* values for flags field */ static unsigned long pool_size = 1024*1024;
#define BLOCK_FREE 0x01 /* free structure in the BLOCKs pool */
#define BLOCK_KMALLOCED 0x02 /* structure allocated by kmalloc() */
#define BLOCK_GFP 0x08 /* block allocated with __get_dma_pages() */
/* list of allocated blocks */
static BLOCK *alloc_list;
/* We can't always use kmalloc() to allocate BLOCK structures, since static int __init atari_stram_setup(char *arg)
* stram_alloc() can be called rather early. So we need some pool of {
* statically allocated structures. 20 of them is more than enough, so in most if (!MACH_IS_ATARI)
* cases we never should need to call kmalloc(). */ return 0;
#define N_STATIC_BLOCKS 20
static BLOCK static_blocks[N_STATIC_BLOCKS];
/***************************** Prototypes *****************************/ pool_size = memparse(arg, NULL);
return 0;
}
static BLOCK *add_region( void *addr, unsigned long size ); early_param("stram_pool", atari_stram_setup);
static BLOCK *find_region( void *addr );
static int remove_region( BLOCK *block );
/************************* End of Prototypes **************************/
/* ------------------------------------------------------------------------ */
/* Public Interface */
/* ------------------------------------------------------------------------ */
/* /*
* This init function is called very early by atari/config.c * This init function is called very early by atari/config.c
@ -123,25 +67,23 @@ static int remove_region( BLOCK *block );
void __init atari_stram_init(void) void __init atari_stram_init(void)
{ {
int i; int i;
void *stram_start;
/* initialize static blocks */ /*
for( i = 0; i < N_STATIC_BLOCKS; ++i ) * determine whether kernel code resides in ST-RAM
static_blocks[i].flags = BLOCK_FREE; * (then ST-RAM is the first memory block at virtual 0x0)
*/
/* determine whether kernel code resides in ST-RAM (then ST-RAM is the
* first memory block at virtual 0x0) */
stram_start = phys_to_virt(0); stram_start = phys_to_virt(0);
kernel_in_stram = (stram_start == 0); kernel_in_stram = (stram_start == 0);
for( i = 0; i < m68k_num_memory; ++i ) { for (i = 0; i < m68k_num_memory; ++i) {
if (m68k_memory[i].addr == 0) { if (m68k_memory[i].addr == 0) {
/* skip first 2kB or page (supervisor-only!) */
stram_end = stram_start + m68k_memory[i].size;
return; return;
} }
} }
/* Should never come here! (There is always ST-Ram!) */ /* Should never come here! (There is always ST-Ram!) */
panic( "atari_stram_init: no ST-RAM found!" ); panic("atari_stram_init: no ST-RAM found!");
} }
@ -151,226 +93,68 @@ void __init atari_stram_init(void)
*/ */
void __init atari_stram_reserve_pages(void *start_mem) void __init atari_stram_reserve_pages(void *start_mem)
{ {
/* always reserve first page of ST-RAM, the first 2 kB are /*
* supervisor-only! */ * always reserve first page of ST-RAM, the first 2 KiB are
* supervisor-only!
*/
if (!kernel_in_stram) if (!kernel_in_stram)
reserve_bootmem(0, PAGE_SIZE, BOOTMEM_DEFAULT); reserve_bootmem(0, PAGE_SIZE, BOOTMEM_DEFAULT);
} stram_pool.start = (resource_size_t)alloc_bootmem_low_pages(pool_size);
stram_pool.end = stram_pool.start + pool_size - 1;
request_resource(&iomem_resource, &stram_pool);
void atari_stram_mem_init_hook (void) pr_debug("atari_stram pool: size = %lu bytes, resource = %pR\n",
{ pool_size, &stram_pool);
mem_init_done = 1;
} }
/* void *atari_stram_alloc(unsigned long size, const char *owner)
* This is main public interface: somehow allocate a ST-RAM block
*
* - If we're before mem_init(), we have to make a static allocation. The
* region is taken in the kernel data area (if the kernel is in ST-RAM) or
* from the start of ST-RAM (if the kernel is in TT-RAM) and added to the
* rsvd_stram_* region. The ST-RAM is somewhere in the middle of kernel
* address space in the latter case.
*
* - If mem_init() already has been called, try with __get_dma_pages().
* This has the disadvantage that it's very hard to get more than 1 page,
* and it is likely to fail :-(
*
*/
void *atari_stram_alloc(long size, const char *owner)
{ {
void *addr = NULL; struct resource *res;
BLOCK *block; int error;
int flags;
DPRINTK("atari_stram_alloc(size=%08lx,owner=%s)\n", size, owner); pr_debug("atari_stram_alloc: allocate %lu bytes\n", size);
if (!mem_init_done) /* round up */
return alloc_bootmem_low(size); size = PAGE_ALIGN(size);
else {
/* After mem_init(): can only resort to __get_dma_pages() */ res = kzalloc(sizeof(struct resource), GFP_KERNEL);
addr = (void *)__get_dma_pages(GFP_KERNEL, get_order(size)); if (!res)
flags = BLOCK_GFP; return NULL;
DPRINTK( "atari_stram_alloc: after mem_init, "
"get_pages=%p\n", addr ); res->name = owner;
error = allocate_resource(&stram_pool, res, size, 0, UINT_MAX,
PAGE_SIZE, NULL, NULL);
if (error < 0) {
pr_err("atari_stram_alloc: allocate_resource() failed %d!\n",
error);
kfree(res);
return NULL;
} }
if (addr) { pr_debug("atari_stram_alloc: returning %pR\n", res);
if (!(block = add_region( addr, size ))) { return (void *)res->start;
/* out of memory for BLOCK structure :-( */
DPRINTK( "atari_stram_alloc: out of mem for BLOCK -- "
"freeing again\n" );
free_pages((unsigned long)addr, get_order(size));
return( NULL );
}
block->owner = owner;
block->flags |= flags;
}
return( addr );
} }
EXPORT_SYMBOL(atari_stram_alloc); EXPORT_SYMBOL(atari_stram_alloc);
void atari_stram_free( void *addr )
void atari_stram_free(void *addr)
{ {
BLOCK *block; unsigned long start = (unsigned long)addr;
struct resource *res;
unsigned long size;
DPRINTK( "atari_stram_free(addr=%p)\n", addr ); res = lookup_resource(&stram_pool, start);
if (!res) {
if (!(block = find_region( addr ))) { pr_err("atari_stram_free: trying to free nonexistent region "
printk( KERN_ERR "Attempt to free non-allocated ST-RAM block at %p " "at %p\n", addr);
"from %p\n", addr, __builtin_return_address(0) );
return; return;
} }
DPRINTK( "atari_stram_free: found block (%p): size=%08lx, owner=%s, "
"flags=%02x\n", block, block->size, block->owner, block->flags );
if (!(block->flags & BLOCK_GFP)) size = resource_size(res);
goto fail; pr_debug("atari_stram_free: free %lu bytes at %p\n", size, addr);
release_resource(res);
DPRINTK("atari_stram_free: is kmalloced, order_size=%d\n", kfree(res);
get_order(block->size));
free_pages((unsigned long)addr, get_order(block->size));
remove_region( block );
return;
fail:
printk( KERN_ERR "atari_stram_free: cannot free block at %p "
"(called from %p)\n", addr, __builtin_return_address(0) );
} }
EXPORT_SYMBOL(atari_stram_free); EXPORT_SYMBOL(atari_stram_free);
/* ------------------------------------------------------------------------ */
/* Region Management */
/* ------------------------------------------------------------------------ */
/* insert a region into the alloced list (sorted) */
static BLOCK *add_region( void *addr, unsigned long size )
{
BLOCK **p, *n = NULL;
int i;
for( i = 0; i < N_STATIC_BLOCKS; ++i ) {
if (static_blocks[i].flags & BLOCK_FREE) {
n = &static_blocks[i];
n->flags = 0;
break;
}
}
if (!n && mem_init_done) {
/* if statics block pool exhausted and we can call kmalloc() already
* (after mem_init()), try that */
n = kmalloc( sizeof(BLOCK), GFP_KERNEL );
if (n)
n->flags = BLOCK_KMALLOCED;
}
if (!n) {
printk( KERN_ERR "Out of memory for ST-RAM descriptor blocks\n" );
return( NULL );
}
n->start = addr;
n->size = size;
for( p = &alloc_list; *p; p = &((*p)->next) )
if ((*p)->start > addr) break;
n->next = *p;
*p = n;
return( n );
}
/* find a region (by start addr) in the alloced list */
static BLOCK *find_region( void *addr )
{
BLOCK *p;
for( p = alloc_list; p; p = p->next ) {
if (p->start == addr)
return( p );
if (p->start > addr)
break;
}
return( NULL );
}
/* remove a block from the alloced list */
static int remove_region( BLOCK *block )
{
BLOCK **p;
for( p = &alloc_list; *p; p = &((*p)->next) )
if (*p == block) break;
if (!*p)
return( 0 );
*p = block->next;
if (block->flags & BLOCK_KMALLOCED)
kfree( block );
else
block->flags |= BLOCK_FREE;
return( 1 );
}
/* ------------------------------------------------------------------------ */
/* /proc statistics file stuff */
/* ------------------------------------------------------------------------ */
#ifdef DO_PROC
#define PRINT_PROC(fmt,args...) seq_printf( m, fmt, ##args )
static int stram_proc_show(struct seq_file *m, void *v)
{
BLOCK *p;
PRINT_PROC("Total ST-RAM: %8u kB\n",
(stram_end - stram_start) >> 10);
PRINT_PROC( "Allocated regions:\n" );
for( p = alloc_list; p; p = p->next ) {
PRINT_PROC("0x%08lx-0x%08lx: %s (",
virt_to_phys(p->start),
virt_to_phys(p->start+p->size-1),
p->owner);
if (p->flags & BLOCK_GFP)
PRINT_PROC( "page-alloced)\n" );
else
PRINT_PROC( "??)\n" );
}
return 0;
}
static int stram_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, stram_proc_show, NULL);
}
static const struct file_operations stram_proc_fops = {
.open = stram_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int __init proc_stram_init(void)
{
proc_create("stram", 0, NULL, &stram_proc_fops);
return 0;
}
module_init(proc_stram_init);
#endif
/*
* Local variables:
* c-indent-level: 4
* tab-width: 4
* End:
*/

View File

@ -6,12 +6,11 @@
*/ */
/* public interface */ /* public interface */
void *atari_stram_alloc(long size, const char *owner); void *atari_stram_alloc(unsigned long size, const char *owner);
void atari_stram_free(void *); void atari_stram_free(void *);
/* functions called internally by other parts of the kernel */ /* functions called internally by other parts of the kernel */
void atari_stram_init(void); void atari_stram_init(void);
void atari_stram_reserve_pages(void *start_mem); void atari_stram_reserve_pages(void *start_mem);
void atari_stram_mem_init_hook (void);
#endif /*_M68K_ATARI_STRAM_H */ #endif /*_M68K_ATARI_STRAM_H */

View File

@ -83,11 +83,6 @@ void __init mem_init(void)
int initpages = 0; int initpages = 0;
int i; int i;
#ifdef CONFIG_ATARI
if (MACH_IS_ATARI)
atari_stram_mem_init_hook();
#endif
/* this will put all memory onto the freelists */ /* this will put all memory onto the freelists */
totalram_pages = num_physpages = 0; totalram_pages = num_physpages = 0;
for_each_online_pgdat(pgdat) { for_each_online_pgdat(pgdat) {