Archive for August, 2007

Successfully tested

Sunday, August 19th, 2007

Since last post were added  loading of RAM-drive, using httpfs precaching and some other small updates and bugfixes, most of time was spent  for testing.

Now rootpath option is used to specify location of root on web server. Also, I’ve found that bootp() doesn’t sets nameip (it was not usually needed for NFS loader, it’s enough to specify IP of server and resolving is not required. But in case of web0servers, which may use virtual servers for different domains for same IP – it’s needed to know domain name, and nameserver option is needed) and thus gives problems for code to work, so by default own DHCP client is used. If  not domain name but IP is specified in rootpath, then bootp() may be used.

Loading of RAM-drive is optional, but was needed to test correct working of code. It’s done simply by adding few  lines to loader.rc script. Actually, it’s possible use for example NFS after loading as root file system (by setting appropriate environment variables), but as long as pxe_http library code must be a little bit independent – best choice was to use RAM-drive (or additionaly to write kernel module for support httpfs. Last variant needed more time and knowledge, so it temporarily was thrown away).

RAM drive image was got from boot floppy image (mfsroot.gz). It contains needed for system installation files. So, after loading by pxe_http kernel+RAM drive  it’s possible to install FreeBSD by choosing ftp as media source.

While testing code in LAN I’ve found that loader trys to get file by small portions (maximum 4096 bytes per request). It is not very optimal, so about month ago I was thinking about simple caching mechanism and now it works. For this purpose, httpfs need access to socket receive buffer (this is done to reduce memory usage, so cached information is actually stored in socket buffer, main problem is in removing from buffer header and watching when cached data is empty). Such ability breaks abstraction of code layers, but raises up connection speed and reduces load of http server. While testing it lowered downloading time from about one minute to 25-30 seconds. After setting lower waiting time in pxe_await.h, code response to incoming packets become higher, and downloading is performed in 15-25 seconds at LAN.

After caching was checked, I’ve started testing of code with remote server, that is really remote and situated in 12 hops from my home computer. Here was first surprise, TCP code was working really strange: some packets were never received. During debugging also bug with resending was fixed (segments were waiting to be acked bigger aequential number, then needed).

So, packets were not receiving and code not worked in WAN. I’ve spent many hours trying to make it work. Ed (my mentor) said simple idea – to tcpdump on server side. Well, I don’t know why this idea have not appeared in my mind itself. tcpdump showed, that all is ok – packets from me correctly are coming to server, and from server to me are also sent, however not acked. Debugging of pxe_core showed that they are not reaching my NIC. I’ve started comparing two dumps and found rather interesting thing: only packets with segment size fuly filled to 1460 (default TCP MSS in pxe_http) were not transmitted correctly. I’ve changed MSS to 1260 (10 hops * 20 bytes is about 200 bytes…) and code started working correctly. Packets were sent with Don’t fragment IP flag, so I think somewhere on the path to me router dropped them cause couldn’t sent it not fragmented. May be it’s just my ISP side problem.

After getting code working correctly in Internet, I’ve tested it on two different web servers (first uses Apache, second one – nginx), both worked correctly. And to make sure code is really working, I’ve managed my friend to try it from his home through ADSL modem (at my home, Internet is got through VPN via ISP Ethernet based network, so router at my home is second computer with pppoe client). It also worked.

Last check was to test again NFS loader after some changes in pxe_open() function. It also worked, so finally code seems ready to be tested widely and tarball of current version of code will be sent as result of GSoC.

Finally, I’ve wrote simple documentation,  that may be useful for setting pxe_http to work or use it API. Well, it’s not covers every function of pxe_http code, but gives main information, needed to work with it.

Next two weeks I’m planning get more information about ipv6, spend summer time and do nothing usefull at all, except fixing any simple bugs found in code. Later I’ll  try to add ipv6 support for pxe_http and may be implement some other ideas.


Wednesday, August 1st, 2007

Finally I’ve booted kernel on real machine without unexpected rebooting it in process of booting.

While testing code, I’ve tried smaller buffers (16, 8, 4, and 2Kbytes were used) and tests figured out, that code with 4 and 2 kbytes is able to boot kernel on real machine, bigger buffers are working on virtual machines. Keeping in mind fact that memory allocation/freeing is done most probably correctly (it’s done by libstand functions, that are trusted) it was difficult to understand why smaller usage of memory for buffers gave such strange results.

First investigation showed, that loader’s heap is allocated over PXE data (in base memory higher 8d000h). That was fixed (higher bound of heap was limited to address from which PXE data starts), but hadn’t solved the problem. Yeah, sad moment, I had faith that problem is in heap allocation. Next suggestion was that pxeboot is rather big binary. In fact, PXE specs recommend to make download of boot images in two steps: remote.0 and remote.1. Remote.0 placed at well-known 7c00h:0000 and must not exceed 32Kbytes (cause “32K size limit provides the advantage of being able to clean up and fail gracefully” (c) PXE Specification). remote.0 determines if system is has adequate resources and downloads remote.1 (in specs to in extended memory at e98000h).  Monolithic pxeboot is about 200K, with pxe_http above 240Kb. I don’t know if 32K size is strict requirement or not, pxelinux for example is about 12Kb.

Well, “NFS loader works” I’ve thought and then reduced binary size by removing all pxe_http testing code and code related to support filesystems such as dos. It gave me nearely same size as usual for pxeboot. So, now http loaded kernel boots on my home machine.

While working with DHCP client, I’ve found that I’m doing same work, that was performed in bootp() call in libstand. I was not giving much attention to it, cause my code was working, but after binary reducing thought, that it’s good idea to use already available function. This function mainly depends on udpread/udpsend functions, that were earlier situated in libi386/pxe.c but commented by me while rewriting it’s code a little bit (commented cause it was using pxe_call and etc, that was removed by me form this file).

It was not big deal to rewrite udpread/udpwrite to use pxe_http functions to read/send data, but for this purpose was done “default socket” mechanism for UDP. It’s done with aim not use sockets while calling udpread/udpsend, cause code, which uses them, don’t know about pxe_http sockets. To be more accurate, sending of udp packets doesn’t require socket, it’s enough to call pxe_udp_send(), but incoming packets are coming through filters mechanism and are delivered to appropriate socket buffer. Previously, all data that was filtered out – was going to trash bin, now it goes to “default socket”. and it’s possible to read from it as usual, using pxe_udp_read() function.

So, bootp() began working good (except moment with updating of gateway/nameserver information, that I’ll fix in next few days). The main problem with it was, that bootp() may work only after opening of device, cause after that NIC MAC is known by code, that generates BOOTP packet. Normally, pxe_dhcp_query() – which is now may be wrapper for bootp() – is started from pxe_core_init(), and already knows own MAC. Well, I’ve returned pxe_dhcp_query() to pxe_open() in case if not own DHCP client used (here was bootp() originally). And all began work correctly.

NFS code also uses udpread/udpsend functions, so it was rather logical to try NFS loader. It also works. It’s started working without any doubts. so it’ uninteresting to tell anything about it.

This made me possible to make conclusion that compatibility with old working PXE related code in libi386 is big enough.

So, formally main goal of project is achieved. But there is more work to do:

1. load image of RAM drive and mount root to it, so booting process via http will be fully performed. Now it stopson stage of mounting of root file system.

2. Understand where is problem with memory when I’ve used bigger buffers with big pxeboot binary. It’s rather interesting moment, cause I’ve no idea what might cause rebooting of prefectly loaded kernel in case of bigger buffer size for sockets earlier.

3. Test all and update some code details (well, one of them is that root path is not used by http loader now).

4. write finally whole documentation related to project. I’ve started to write it few times, but it wasn’t finished yet.

5. choose notebook to buy.

These are my tasks for next weeks.