domenica 27 maggio 2012

An 8E filesystem

Update 2012-10-06.

Introduction

This small post shows how create a 8 exabyte filesystem on a linux box.
8 Exabyte are 263 bytes. See this link [1] to wikipedia for knowing more on the number prefix. Anyway 8 Exabyte are 8192 Petabyte, which are 8388608 Terabyte..
Of course you we not be able to obtain a real 8E filesystem, but you will be able to simulate it.

Prerequisites

The following prerequisites are needed:
  • a Linux box with a modern kernel ( I tested with a 3.4 kernel)
  • a BTRFS filesystem,

Creating the file system

The idea is to create a "sparse" file[3], to mount it with the loopback device a then to create the filesystem.
  • Create the "sparse" file
    dd if=/dev/zero of=8E-file bs=1 count=1 seek=$(((1<<63)-2))
    1+0 records in
    1+0 records out
    1 byte (1 B) copied, 0.000268343 s, 3.7 kB/s
    ghigo@venice:/tmp$ ls -lh 8E-file 
    -rw-r--r-- 1 ghigo ghigo 8.0E Dec 25 23:15 8E-file
    
    The trick here is the option seek of the command dd. This option move the file pointer forward without writing (and without consuming)any byte.

  • Formatting the "sparse" file
    ghigo@venice:/tmp$ /sbin/mkfs.btrfs 8E-file
    WARNING! - Btrfs Btrfs v0.19 IS EXPERIMENTAL
    WARNING! - see http://btrfs.wiki.kernel.org before using
    
    fs created label (null) on 8E-file
     nodesize 4096 leafsize 4096 sectorsize 4096 size 8.00EB
    Btrfs Btrfs v0.19
    
  • Creating the loopback device
    ghigo@venice:/tmp$ sudo losetup -f 8E-file 
    ghigo@venice:/tmp$ sudo losetup -a
    /dev/loop0: [0012]:583736 (/tmp/8E-file)
    
    It must be point out to "formatting" the file and not the loopback device. This because the mkfs.btrfs issues a BLKDISCARD ioctl which hangs mkfs.btrfs when the loopback device is used (or may be it requires a bit of time to process 8EB of data :-) ).
    Update 2012-10-06: Now mkfs.btrfs has the option '-T' to avoid issuing a BLKDISCARD ioctl.

  • Mount the loopback device and test it
    ghigo@venice:/tmp$ sudo mount /dev/loop0 /mnt/test
    ghigo@venice:/tmp$ df -h /mnt/test
    Filesystem      Size  Used Avail Use% Mounted on
    /dev/loop0      8.0E   56K  8.0E   1% /mnt/test
    

The maximum btrfs file size

The btrfs filesystems specifications state that the maximum file size is 16EB, the same limits is applied to the filesystem size.
However the linux kernel has a lower limit. In fact I was never able to create a file (even sparse) greater than 8EB. In the file header include/linux/fs.h of the linux kernel is reported:

  /* Page cache limit. The filesystems should put that into their
     s_maxbytes limits, otherwise bad things can happen in VM. */

  #if BITS_PER_LONG==32

  #define MAX_LFS_FILESIZE \
 (((u64)PAGE_CACHE_SIZE << (BITS_PER_LONG-1))-1)

  #elif BITS_PER_LONG==64

  #define MAX_LFS_FILESIZE 0x7fffffffffffffffUL

  #endif
This means that in a x86 environment (BITS_PER_LONG == 32) the maximum file size is about 8TB; instead in a x86-64 bit machine, the limit is 8EB ( == 0x7fffffffffffffff)

Note

This idea of post started from a message in the btrfs mailing list [2].

Reference

domenica 12 febbraio 2012

Remote laptop power-on

Preamble

My father has a laptop which is used as fixed computer. This is a very common usage: he has bought an external mouse, keyboard and monitor which are always connected to the laptop.
This set-up has the advantage of the comfort of a fixed station, and the portability of a laptop when needed.
However there are some disadvantages, the most annoying thing is the fact that the switch-on button is in a very uncomfortable position. From long time the manufactures moved the button from the side of the laptop to below the monitor. So the user should open the laptop before doing the switch-on.
In the case of my father, he put the laptop below the external monitor; so it is not easy for him to open the monitor and switch-on the computer.

I am still guessing the reason why the manufacturer moved the button from the side to below the monitor..

To avoid to open the laptop, a possible solution is to buy a docking station. But this is a very expensive solution. Also not all the laptop (noticeably the cheapest ones) have a docking station.

Finally the light

At work, my colleagues bought a support for a laptop for some exposition. This is a very expensive solution (around 1-2000 euros), but it has some very interesting solution (the desk is moveable, there are some drawers electronically blocked; a pin is required for the unlock...). But the one which caught my attention was how the laptop can be switched-on. It uses the WAKE ON LAN facility to power-on the laptop. Because the laptop could be inside this support, an external button is connected to a "magic black box"(tm), which is able to emit the WOL packet. Of course the laptop should be connected via lan to this "magic black box".

WAKE ON LAN

The WAKE ON LAN is a facility that every modern PC has. From wikipedia [1]


Wake-on-LAN (WOL) is an Ethernet computer networking standard that allows a computer to be turned on or woken up by a network message.

The message is usually sent by a program executed on another computer on the same local area network. It is also possible to initiate the message from another network by using Subnet directed broadcasts or a WOL gateway service. Equivalent terms include wake on WAN, remote wake-up, power on by LAN, power up by LAN, resume by LAN, resume on LAN, and wake up on LAN. In case the computer being woken is communicating via Wi-Fi, a supplementary standard called Wake on Wireless LAN (WoWLAN) must be employed.


After seeing this solution, I started to think how implement this for my father. I never mind to buy this "laptop support" due to its cost. I even searched on ebay, looking for something capable to act as WON "generator". I also evaluated if some embedded computer (like arduino, or raspberry_pi) could solve this problem. At the end I found that a router equipped with a custom firmware like DD-WRT [2] (or an equivalent one) could be a viable solution.

The router

I bought an used router DLink DIR-615, already equipped with DD-WRT. The router was quite cheap; but was without its power supply; at the end the cost of the power supply was greater than the cost of the (used) router :).

The DD-WRT distribution, is already equipped with some WOL facilities; via the administration web interface it is possible to send the WOL packet. Unfortunately this is impossible if the pc is switched-off. It is a typical chicken-egg problem :)

This kinds of routers are normally equipped with a button called SES-Button. This button is used to help the key exchange for wireless connection. But with the DD-WRT it is possible to associate other actions to the button.

When I received the router I faced with two problems:
1) The official way to handle the button event didn't work
2) It was not so simple to store the configuration changes

Using the SES-Button to generate the WOL packet

DD-WRT allows the user to handle the SES-Button event. If a script called <something>.sesbutton exists [3], it is called when the button is pressed. Unfortunately some tests quickly pointed out that on my router this didn't happen (see also this post [4])
I think that the real GPIO assignment is different from the one implemented in my firmware. With some testing I discovered that the SES Button is linked to the GPIO pin #0.

All my tests were performed with the stock gpio utility provided by the DD-WRT firmware. This utility has also the poll mode. So I tried to use it in a shell script to handle the SES Button event (GPIO #0). Quickly I discovered two problem of this program, which was not created to be used in a shell script:
1) the polling is continuously, this means that 100% CPU is used
2) this tool doesn't flush the buffer, so when it is used in a pipe the pipeline stalled.

The solution was to pick the source (which was gpl) and modify it. I downloaded the toolchains [5], then the program source [6]. The change was quite easy: removed all the unused code (I leaved only the code related to my hardware and the polling routines), add the missing fflush(2) call, add a call to usleep(3) in order to performs only 10 polls for second.

With these changes it was trivial to make a script which polls the button status, and when the button is pressed emit a WOL packet. Of course I added some led effects, when the button is pressed, as feedback.

Here [7] you can find all the source.

Storing the change

What was more difficult was to store these change. After a bit of test I discovered that not all routers have a writeable file-system. The only way to store something is to use the nvram (Non Volatile RAM). Fortunately the DD-WRT firmware during the boot looks at the nvram variable rc_startup, and it executes its value via the shell interpreter (ash - busybox). To make more complex the things, I have also a binary executable to run.
The solution was to build a script which deploys and runs all the other scripts/executable. A common technique is to append the files ( as uu-encoded tar archive) at the end of the script and to untar it in the filesystem.
The DD-WRT firmware didn't provide a "uudecode" utility. With a quick search on Internet I found a bash code capable to uu-decode [6]. I made a bit of hacking to support the syntax of ash.
Then I wrote a script (called build.sh) which joins this uu-decoder with the tar archive. The results is a script (called prg.sh) able to extract the self-contained archive; after the deploy, this script try also to execute the setup.sh command if present in the archive.

Below an excerpt of the resulted script, where I omitted the uudecode function, and the final blob:

#!/bin/ash


DEST=/tmp/

# this function is based on the one found on the following pages
# http://www.weeklywhinge.com/?p=108&cpage=1
# wringler should be the author
uudecode(){
[.....]
}

main(){

cat "$0" | (
skip=1
while read line; do
if [ "$line" = "# PUT THE UUENCODE TAR BELOW" ]; then
skip=0
break
fi
done
cat
) | uudecode | tar xz -C "$DEST"

[ -x $DEST/setup.sh ] && $DEST/setup.sh

}

main
exit

# PUT THE UUENCODE TAR BELOW
begin 644 file.tar.gz
M'XL(`)"T-T\``^U:76P;QQ$>_DB6J5BB_Q+&5IJEPM@R&I](_3JVU5)_D97*
MDJ`H3=(Z.%'D26)`D0<>Y3A%BA*.4_O!,`44!9P@0)7`3E-`J-WDI0].:Z`O
[....]


Summarizing:
- During the boot the DD-WRT firmware reads the value of the rc_startup from the nvram and executes this.
- this script extracts a self contained tar archive which contains:
1) the gpio_read executable, which is able to poll (10 times per seconds in order to avoid to use 100% CPU) a GPIO pin
2) the button_daemon shell script which emits the WOL packet when the SES Button is pressed
3) the setup.sh script aimed to make some further setup.
- After the unpacking the daemon is started by setup.sh

The main settings are stored in nvram. The following variable is used to configure the daemon:
- button_daemon_mac is the mac address of the host to power-on


Install

Below is show how install this software. As usual the following disclaimer is valid:
- it is assumed that the user is enough skilled for this kind of operations
- pay attention: there is always the risk to brick your router: I am not responsible if you brick your router, nor if you dog drink your beer
- this post is related to the DLINK DIR-615 equipped with the DD-WRT firmware (DD-WRT v24-sp2). It is very unlikely that you can use this script as is for other routers.

First you have to download the program:

root@DD-WRT:~# cd /tmp/
root@DD-WRT:/tmp# scp ghigo@192.168.7.27:/home/ghigo/ddwrt/gpio/tmp/prg.sh .

prg.sh is the deploy-er script, which can be found in [7].

Check the md5sum; it is not important that the your value is equal to the one of this post.
root@DD-WRT:/tmp# md5sum prg.sh 
6f11f31d2e469e87ece1872efdedba6f prg.sh

Install the software and check the checksum:
root@DD-WRT:/tmp# nvram set rc_startup="$( cat prg.sh )"
root@DD-WRT:/tmp# nvram get rc_startup | md5sum
6f11f31d2e469e87ece1872efdedba6f -

You must check that the two md5sum match. The values could be different from the one showed in this post, but the two ones calculated by you have to match.

Configure the mac address:
root@DD-WRT:/tmp# nvram set button_daemon_mac=00:08:74:08:cc:ce

00:08:74:08:cc:ce is the mac address of the target computer.

Finally perform a commit
root@DD-WRT:/tmp# nvram commit

Now you can reboot the router and test the system pressing the SES Button.

Conclusion

At the end I got an small object capable to start a laptop without opening it. In any case my father needed a router, so I didn't add other object on its desk.




Dir-615 GPIO connections

The table below show how some GPIO pins are connected:

GPIO Pin Function
#0 SES Button (0 pressed, 1 released)
#8 Orange main led (0 on, 1 off)
#9 Green main led (0 on, 1 off)
#11 Blue button led (0 on, 1 off)
#13 Red button led (0 on, 1 off)


References

[1] Wikipedia: WAKE ON LAN
[2] DD-WRT
[4] Forum on D-Link DIR-615 D3
[5] Development info - DD-WRT - Toolchain download
[6] gpio source
[6] uuencode implemented entirely in bash
[7] The archive with the scripts

My little patches...

Below a list of my patches spread on different projects: Linux kernel [all] 2018-02-01 iversion: Rename make inode_cmp_iversion{+raw}...