Subject: initrd problem

From: Ed Martini <[email-masked]>
Date: Thu, 10 Mar 2005 15:42:04 -0800

Background:

I'm trying to get 2.6.11 to run on a MIPS Malta board with Yamon.  The 
kernel that comes with the board is 2.4.18 with an embedded ramdisk that 
runs some scripts to install RPMS via NFS or CD-ROM.  The kernel is 
converted to s-records via objcopy(1), and loaded into memory via tftp.  
I want to do something similar with 2.6.latest.

Problem:

On or about Nov 21 of last year, the CONFIG_EMBEDDED_RAMDISK disappeared.

http://www.linux-mips.org/archives/linux-mips/2004-11/msg00135.html

In it's place it is suggested to use the tools in arch/mips/boot, so I 
tried it.  I can cross-compile the kernel, and I get an ELF vmlinux.  I 
can convert it to ecoff with elf2ecoff, and attach an initrd image with 
addinitrd.  The problem begins here.  I end up with an ecoff format 
kernel which is not recognized by objcopy(1), and therefore no s-records.

It seems there is a program called gensrec that would do the job, but 
google doesn't want to tell me where to get it.  Some IRIX binary perhaps?

Solution?

Should I put CONFIG_EMBEDDED_RAMDISK and its ilk back into my kernel, or 
write an ELF version of addinitrd?  Other ideas?

Thanks in advance.

---

From: Kumba <[email-masked]>
Date: Thu, 10 Mar 2005 19:41:36 -0500

> write an ELF version of addinitrd?  Other ideas?

The future is purportedly in the feature known as initramfs.  See the file 
Documentation/early-userpace/README for more details on how that is supposed 
to work.

That said, I tried initramfs a few times, but either due to lack of 
understanding, or broken support code in 2.6.10, I couldn't get it to properly 
load an initrd bundled in, so I forward-ported a patch I wrote that originally 
fixed CONFIG_EMBEDDED_RAMDISK to work with any ABI to 2.6.10, and it worked 
rather well.  I'm sticking with this method until I find more/better docs on 
how to use initramfs properly.

If you're interested, the patch I use can be found here:
http://dev.gentoo.org/~kumba/mips/misc/misc-2.6.10-add-ramdisk-back.patch [2.6.10]
http://dev.gentoo.org/~kumba/mips/misc/misc-2.6.11-add-ramdisk-back.patch [2.6.11]

--Kumba

-- 
"Such is oft the course of deeds that move the wheels of the world: small 
hands do them because they must, while the eyes of the great are elsewhere." 
--Elrond

---

From: Ralf Baechle <[email-masked]>
Date: Mon, 14 Mar 2005 11:01:01 +0000

> kernel which is not recognized by objcopy(1), and therefore no s-records.

I guess that was an act of desperation - YAMON needs SRECs to be happy
but doesn't know anything about SRECs.

> google doesn't want to tell me where to get it.  Some IRIX binary perhaps?

make vmlinux.srec; the resulting file will be in arch/mips/boot/vmlinux.srec.

> write an ELF version of addinitrd?  Other ideas?

Things vanish for a reason ...  Try CONFIG_INITRAMFS_SOURCE instead.

  Ralf

---

From: Ed Martini <[email-masked]>
Date: Tue, 15 Mar 2005 14:37:45 -0800

>Things vanish for a reason ...  Try CONFIG_INITRAMFS_SOURCE instead.
Ok.  Then let's get rid of it completly, and provide a replacement that 
works.

There were vestiges of embedded initrd in the ld script that were 
confusing when trying to sort things out. That, in conjunction with 
Documentation/initrd.txt made it hard to discover early user space and 
initramfs when coming from the old world (2.4).

Also, unless you move the location of .init.ramfs, it gets freed twice, 
leading to a panic.

 From the documentation alone it's impossible to figure out how to build 
your initramfs.  In various places the docs refer to the initial 
executable as /linuxrc, /kinit, /init, and possibly others.  If you read 
init/main.c you see that for an initramfs, your initial process will be 
started from /init.

diff -urN linux-2.6.11-linux-mips.org/arch/mips/kernel/vmlinux.lds.S 
linux/arch/mips/kernel/vmlinux.lds.S
--- linux-2.6.11-linux-mips.org/arch/mips/kernel/vmlinux.lds.S 
2005-03-15 13:41:51.000000000 -0800
+++ linux/arch/mips/kernel/vmlinux.lds.S 2005-03-15 14:34:00.339164936 -0800
@@ -54,13 +54,6 @@

     *(.data)

-   /* Align the initial ramdisk image (INITRD) on page boundaries. */
-   . = ALIGN(4096);
-   __rd_start = .;
-   *(.initrd)
-   . = ALIGN(4096);
-   __rd_end = .;
-
     CONSTRUCTORS
   }
   _gp = . + 0x8000;
@@ -82,6 +75,13 @@

   _edata =  .;   /* End of data section */

+   /* Align the initial ramfs image on page boundaries. */
+   /* It will be freed by init/initramfs.c */
+  . = ALIGN(4096);
+  __initramfs_start = .;
+  .init.ramfs : { *(.init.ramfs) }
+  __initramfs_end = .;
+
   /* will be freed after init */
   . = ALIGN(4096);  /* Init code and data */
   __init_begin = .;
@@ -118,10 +118,6 @@
   .con_initcall.init : { *(.con_initcall.init) }
   __con_initcall_end = .;
   SECURITY_INIT
-  . = ALIGN(4096);
-  __initramfs_start = .;
-  .init.ramfs : { *(.init.ramfs) }
-  __initramfs_end = .;
   . = ALIGN(32);
   __per_cpu_start = .;
   .data.percpu  : { *(.data.percpu) }

---

From: Ralf Baechle <[email-masked]>
Date: Wed, 16 Mar 2005 12:06:47 +0000

> Ok.  Then let's get rid of it completly, and provide a replacement that 

Way to go.

> initramfs when coming from the old world (2.4).

Correct and I've applied that part of your patch already while thinking
about the problem you describe below.

> Also, unless you move the location of .init.ramfs, it gets freed twice, 

Interesting one.  When I tested the code recently it did work for me and
it shouldn't have changed since.  The way this is supposed to work is
by setting the page_count to 1 by using set_page_count and unmarking the
page as reserved, so after that point a free_page should actually succeed -
even if done twice as you've observed, first in populate_rootfs then
once more in free_initmem.

It seems a frighteningly bad idea though since it relies on no memory
from the initrd range being allocated between the two calls - or it would
end being freed by force, in use or not.  On startup Linux tends to
allocate memory starting from high address towards low addresses which
must be why we usually get away with this one.

> init/main.c you see that for an initramfs, your initial process will be 

I guess I read the code so I didn't notice the lacking qualities of the
documentation ...

  Ralf

---

From: Ed Martini <[email-masked]>
Date: Wed, 16 Mar 2005 16:23:45 -0800

What fails is when CONFIG_BLK_DEV_INITRD and CONFIG_INITRAMFS_SOURCE are 
both set.  I realize (now) that I don't need initrd at all, and 
initramfs works fine by itself, as you have tested.  It seems that the 
kernel may need some exclusion between these two mechanisms while the 
difference is sorted out.

I'm not totally up on Kconfig files, but maybe something like this in 
drivers/block/Kconfig:

config INITRAMFS_SOURCE
        string "Initramfs source file(s)"
        default ""
        depends on BLK_DEV_INITRD=n

Regarding the documenation it would have been helpful to me if 
Documentation/initrd.txt had a reference to 
Documentation/early-userspace/README.  I'm not sure who maintains that 
directory, or I'd send a suggestion.  tldp.org?

THX
Ed Martini

Original context: 
http://www.linux-mips.org/archives/linux-mips/2005-03/index.html

---

From: Ed Martini <[email-masked]>
Date: Fri, 25 Mar 2005 11:24:05 -0800

In include/asm-mips/ atomic.h, bitops.h and system.h there are a bunch 
of inline functions which contain this logic:

        if (cpu_has_llsc && R10000_LLSC_WAR) {
                __asm__ (stuff)
        } else if (cpu_has_llsc) {
                __asm__ (other stuff)
        } else {
                C lang stuff;
        }

My two observations relate to both code size and runtime performance.  
These observations don't affect my situation, so I'm not inclined to 
spend a bunch of time on it, but maybe someone else is interested.  This 
should be especially interesting since these inline functions are used 
all over the kernel, so it might actually make a marginally significant 
difference.

I suppose there's a reason this code is the way it is.  If so, feel free 
to ignore me or flame away.

1. If the first part of the if were an ifdef instead it would result in 
a code size reduction as well as a runtime performance gain.

2. In atomic.h the "C lang stuff" is wrapped with a spinlock.  In the 
SMP case the spinlock will result in code that contains ll and sc 
instructions, so I infer that there are no SMP system configs that use 
CPUs that don't have the ll and sc instructions. 

Paranoid version:
-----
        if (cpu_has_llsc) {
#ifdef R10000_LLSC_WAR
                __asm__ (stuff)
#else
                __asm__ (other stuff)
#endif
        } else {
#ifdef CONFIG_SMP
                panic("SMP on CPUs with no LLSC is broken\n");
#else
                C lang stuff;
#endif
        }
-----
Most efficient version:
-----
#ifndef CONFIG_SMP
        if (cpu_has_llsc)
#endif
        {
#ifdef R10000_LLSC_WAR
                __asm__ (stuff)
#else
                __asm__ (other stuff)
#endif
        }
-----

---

From: Daniel Jacobowitz <[email-masked]>
Date: Fri, 25 Mar 2005 14:37:59 -0500

> a code size reduction as well as a runtime performance gain.

You should spend a little time playing with an optimizing compiler. 
They're capable of working out when a condition will always be false.

> CPUs that don't have the ll and sc instructions. 

That's correct.  It is not practical to implement SMP without a mutex
primitive.

-- 
Daniel Jacobowitz
CodeSourcery, LLC

---

From: Ed Martini <[email-masked]>
Date: Fri, 25 Mar 2005 14:46:04 -0800

>They're capable of working out when a condition will always be false.
Yes, but in the case where R10000_LLSC_WAR is true, but cpu_has_llsc 
returns false there are still two wasted tests, and two blocks of code 
that the compiler can't optimize out.

---

From: Daniel Jacobowitz <[email-masked]>
Date: Fri, 25 Mar 2005 17:53:35 -0500

> that the compiler can't optimize out.

Not only is cpu_has_llsc often a constant, R10000_LLSC_WAR will never
be true if the CPU does not have LL/SC.

-- 
Daniel Jacobowitz
CodeSourcery, LLC