Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[kernel] Add configurable size kernel file & inode structures #119

Merged
merged 4 commits into from
Dec 27, 2024
Merged

Conversation

Mellvik
Copy link
Owner

@Mellvik Mellvik commented Dec 21, 2024

The size of the kernel file and inode tables are now configurable via the new inodes= and files= parameters in /bootopts. Along with the formerly introduced tasks= parameter, this addition makes it easier to tune the system to actual usage and available resources (memory in particular).

When not present, the values default to the settings in linuxmt/config.h which traditionally have been 64 for files and 96 for inodes.

A minimal configuration for a system short on memory would be
tasks=6 bufs=8 cache=4 files=20 inodes=24

In order for a configuration this tight to complete startup properly, add n by itself to bootopts to prevent /etc/rc.sys from running.

Also included is a more detailed output from the boot process indicating the max # of tasks, files and inodes in the running system:
PC/AT class, cpu 7, syscaps 0xff, 639K base ram, 14 tasks, 64 files, 96 inodes

This improvement is lifted from ELKS: ghaerr/elks#1987 (comment) and ghaerr/elks#1840 (comment) -- thanks @ghaerr.

@Mellvik
Copy link
Owner Author

Mellvik commented Dec 23, 2024

The final 'amendment' to this PR adds memsize= and heap= to bootopts. Both take a decimal number which is read as k bytes. Like memsize=512.

While initially sceptical to the usefulness of a heap parameter, the increasing tuneability of kernel datastructures makes it useful, even important in some settings. Like when tuning the system for really tight RAM conditions in which a full 64k data segment makes no sense.

This is also what brought about the memsize= parameter - which immediately has 3 situations in which it becomes useful:

  1. Force low memory conditions for debugging/tuning
  2. Make available the upper 1k of memory which some bioses leave out when reporting memory size but which are rarely if ever used by the BIOS.
  3. Cases where the BIOS actually reports incorrect RAM size

@ghaerr
Copy link

ghaerr commented Dec 23, 2024

Glad you're liking the heap= option. Sounds like memsize= is also a great idea for debugging, may have to take that over to ELKS :)

A minimal configuration for a system short on memory would be
tasks=6 bufs=8 cache=4 files=20 inodes=24

I hadn't tried bufs=0 cache=12, but see you've changed that back to bufs=8, cache=4. Did you run into problems?

I had also tried lowering tasks= to 3 or 4, but got some kernel crashes as a result (even with no /bin/init). Funny thing is that the kernel checks for tasks reaching its limit, and I haven't yet figured out why the higher task setting seems necessary.

@Mellvik
Copy link
Owner Author

Mellvik commented Dec 23, 2024

Thanks @ghaerr - you know, one thing leads to another.

I decided to import the files= and inodes= settings, then started to play. I was sure I had bufs=0 working, then changed thing around and it stopped working. Then repeat. Now I cannot get it to work and decided to let it go for now.

Then I started pushing limits and memsize= became a requirement. Then heap= etc. It's interesting, and there is more to find out here. I haven't found a good reason for the 'flakyness' about bufs=0 but I'm admittedly curious. And I'm working up - for the heck of it - a configuration that boots with 256k. With the new toolset it's just too tempting. Running with 4 tasks too ...

@ghaerr
Copy link

ghaerr commented Dec 23, 2024

Keep me posted if you find out anything regarding bufs=0 or tasks=4 that actually works. It may appear to but I think there may be some problems lurking we haven't identified yet, because I tried that and couldn't get it to work. The proper failure mode should be "No tasks" displayed and fork() terminated, but instead I've seen system crashes which are not supposed to be possible.

I wasn't aware that bufs=0 would work, hopefully that doesn't try to pass a zero for the size to seg_alloc - not sure what happens in that case.

And I'm working up - for the heck of it - a configuration that boots with 256k.

ELKS has you beat there with configurations (notably ROM) that'll run in 128k RAM (running now on emu86 emulator). The variable heap size through config.h (then later heap=) was added to get the system to run in 128k at the time, a couple years ago.

@Mellvik
Copy link
Owner Author

Mellvik commented Dec 24, 2024

Keep me posted if you find out anything regarding bufs=0 or tasks=4 that actually works. It may appear to but I think there may be some problems lurking we haven't identified yet, because I tried that and couldn't get it to work. The proper failure mode should be "No tasks" displayed and fork() terminated, but instead I've seen system crashes which are not supposed to be possible.

Will do. bufs=0 turns out to work fine on a very narrowed down configuration, but I need to iterate into what and at what level something breaks. The same applies to tasks= where 5 works well, multiuser boot and 2 ttys.

PC/AT class, cpu 7, syscaps 0xff, 300K base ram, 5 tasks, 20 files, 30 inodes
TLVC 0.6.0 (51488 text, 0 ftext, 7536 data, 4768 bss, 20480 heap)
Kernel text 0xb0, data 0xd42, top 0x4b00, 214K free

Unsurprisingly not enough memory to run vi. I need to find a unix-like ed for occasions like this, sed is kinda frustrating although it works. And the minix ed, well, I guess I just don't want to learn yet another line editor.

I wasn't aware that bufs=0 would work, hopefully that doesn't try to pass a zero for the size to seg_alloc - not sure what happens in that case.

And I'm working up - for the heck of it - a configuration that boots with 256k.

ELKS has you beat there with configurations (notably ROM) that'll run in 128k RAM (running now on emu86 emulator). The variable heap size through config.h (then later heap=) was added to get the system to run in 128k at the time, a couple years ago.

Can't beat that one with a disk/floppy based system I suppose. Let's see how far down I can get. Happy holidays.

@Mellvik
Copy link
Owner Author

Mellvik commented Dec 25, 2024

This turned out to be an interesting exercise.

  • The system runs fine on 265k mem (I haven't tried to push it further, 200 is probably OK but the only lower level that is interesting is 128, and while it may boot it doesn't make for a useable system).
  • With 256k and some more tuning (below) - plus using /bin/sash, vi may be run (which exposed a bug in sash printenv, fix pending)
  • There is also an argv bug in init/main.c (fixed but not yet pushed) which prevents /bin/shfrom starting if there are single char parameters anywhere in bootopts.
  • tasks=3 is OK
  • bufs=0 is OK, but only of the kernel config does NOT have external buffers support. I haven't looked into where the bug is yet.

Here's a 'small system' bootopts that works:

## boot options max 1023 bytes, see wiki for details
#console=ttyS0,57600 3	# serial console and init-level (3)
debug=0
net=ne0			# start netw on boot w/this interface
#root=df0
#TZ=MDT7
LOCALIP=10.0.2.15
GATEWAY=10.0.2.1
HOSTNAME=tlvc15
ne0=12,0x300,,0x80
wd0=2,0x280,0xCC00,0x80
#3c0=11,0x330,,0x80
ee0=11,0x360,,0x80
comirq=,,7,10	# IRQ for com ports, up to 4
cache=8		# L1 cache
bufs=0		# L2 buffers
xmsbufs=0	# if supported
heap=18		# max heap (k bytes)
memsize=256	# system RAM size, k bytes
files=12 inodes=16
hdparms=306,4,17,128,614,4,17,-1 # CHSW values for 2 drives, overrides CMOS and drive values
#netbufs=2,0    # netw buffers, recv/trans, ne2k only
tasks=3		# default 16, max 20
sync=30		# autosync secs
#xtflpy=3,1	# meaningful for XT systems w/720k drive(s) (type3)
#fdcache=5	# floppy sector cache, for slow systems <= 286
#xtide=0x300,,,,,	# addr, IRQ, flgs for 2 XT-IDE drives
#init=/bin/init 3 n	# multiuser serial no /etc/rc.sys
init=/bin/sh	# singleuser shell
n

... delivering boot messages and meminfo:

Direct console, polling kbd 80x25 emulating ANSI (3 virtual consoles)
ttyS0 at 0x3f8, irq 4 is a 16550A
athd0: AT/IDE controller at 0x1f0
athd0d0: IDE CHS: 63/16/63, Multisector I/O, max 16 sects
athd0d1: IDE CHS: 614/4/17, Multisector I/O, max 16 sects
athd1: Controller not found at 0x170 (376)
athd: found 2 hard drives
df: Direct floppy driver, FDC 8272A @ irq 6, DMA 2
df0: 1.44M (type 4) [CMOS]
Partitions: dhda:(0,63504) no mbr (flat drive assumed)
 dhdb:(0,41752)  dhdb1:(17,41667) 
PC/AT class, cpu 7, syscaps 0xff, 256K base ram, 3 tasks, 12 files, 16 inodes
TLVC 0.6.0 (51520 text, 0 ftext, 7536 data, 4768 bss, 18432 heap)
Kernel text 0xb0, data 0xd44, top 0x4000, 172K free
MINIX: inodes 256 imap 1 zmap 1, first data 12
MINIX-fs: mounting unchecked file system 0x200, running fsck is recommended.
VFS: Mounted root 0x0200 (minix filesystem).
# meminfo
Only 1 task slots
  HEAP   TYPE  SIZE    SEG   STYPE   SSIZE CNT  NAME
  301E   MEM     16   14C6   CSEG   45936    1  /bin/sh 
  303A   TASK  2466   
  39E8   INOD  1152   
  3E74   FILE   168   
  3F28   CACH  8192   
  5F34   BUFH   160   
  5FE0   TTY   1024   
  63EC   TTY     80   
  6448   MEM     16   0090   free     512    0  
  6464   free  5038   
  2326   MEM     16   1FFD   DSEG   25184    1  /bin/sh 
  2342   MEM     16   2623   free   25184    0  
  235E   MEM     16   2C49   CSEG    6864    1  meminfo 
  237A   MEM     16   2DF6   DSEG    4112    1  meminfo 
  2396   MEM     16   2EF7   free   69776    0  
  23B2   free   872   
Heap/free   19456/5910, Total mem 177568
Memory:  173KB total,   80KB used,   93KB free

@ghaerr
Copy link

ghaerr commented Dec 25, 2024

Very interesting you've got tasks=3 running. I'll play with that and see what happens over here.

There is also an argv bug in init/main.c (fixed but not yet pushed) which prevents /bin/shfrom starting if there are single char parameters anywhere in bootopts.

Well, unknown keywords (that don't have '=' in them) are converted to arguments to bininit= in the order they're seen. So with bininit=/bin/sh the shell is started with those arguments and likely complains. /bin/sh has to start with -c in order to run its arguments and I don't think sash handles them at all. The mechanism was designed so that any program could be run, not just a shell. Passing 'n' to /bin/init, for instance, says don't run :sysinit: (/etc/rc.sys). Bootopts isn't checking argument lists against specific programs.

bufs=0 is OK, but only of the kernel config does NOT have external buffers support.

Since bufs= only specifies external (L2) buffers, with cache= specifying L1 buffers, bufs=0 is working since it is ignored.

I haven't looked into where the bug is yet.

When ext buffers is configured, the buffers (including 0) are passed to seg_alloc as I explained above earlier. The likely problem is that seg_alloc doesn't check for a 0 size either.

5FE0 TTY 1024

The large serial input buffer is only required for SLIP or fast incoming data. I've been thinking about ways to allocate that only when needed. Thoughts?

@Mellvik
Copy link
Owner Author

Mellvik commented Dec 25, 2024

There is also an argv bug in init/main.c (fixed but not yet pushed) which prevents /bin/shfrom starting if there are single char parameters anywhere in bootopts.

... Passing 'n' to /bin/init, for instance, says don't run :sysinit: (/etc/rc.sys). Bootopts isn't checking argument lists against specific programs.

yeah, I know :-), fixed a couple of days ago.

bufs=0 is OK, but only of the kernel config does NOT have external buffers support.

Since bufs= only specifies external (L2) buffers, with cache= specifying L1 buffers, bufs=0 is working since it is ignored.

I haven't looked into where the bug is yet.

When ext buffers is configured, the buffers (including 0) are passed to seg_alloc as I explained above earlier. The likely problem is that seg_alloc doesn't check for a 0 size either.

sounds like an easy fix, I'll take a look at that.

5FE0 TTY 1024

The large serial input buffer is only required for SLIP or fast incoming data. I've been thinking about ways to allocate that only when needed. Thoughts?

It has been on my list to ask you about that. Seems this is a good time to take closer look.

thanks!

@Mellvik
Copy link
Owner Author

Mellvik commented Dec 26, 2024

When ext buffers is configured, the buffers (including 0) are passed to seg_alloc as I explained above earlier. The likely problem is that seg_alloc doesn't check for a 0 size either.
sounds like an easy fix, I'll take a look at that.

Not so easy as it turns out. The buffer init routine is full of #ifdefs and the condition we're looking at is outside anything considered when the code was written. After spending some time with the code, I came to the conclusion that handling such an esoteric variant 'properly' (i.e. automagically) is too expensive codewise.

Instead I decided the smart thing to do was to print a warning on the console and set the buffer to 2 instead of 0. That way the system boots properly and the user can change configuration or bootopts more easily.

@ghaerr
Copy link

ghaerr commented Dec 27, 2024

It has been on my list to ask you about that. Seems this is a good time to take closer look.

The question is how should the system would know when to allocate a large or small buffer? It can't be done post-open (e.g with a program), since the open routine allocates the (large) buffer. Adding into /bootopts is yet another option which has to be changed to do SLIP or fast incoming serial I/O.

We could have a sysctl serial.inq=1024 or something, that could also possibly be done programmatically by ktcp in SLIP mode before the port open (unless the port is already opened by a getty or console)?

@Mellvik
Copy link
Owner Author

Mellvik commented Dec 27, 2024

We could have a sysctl serial.inq=1024 or something, that could also possibly be done programmatically by ktcp in SLIP mode before the port open (unless the port is already opened by a getty or console)?

I was thinking along the same lines, although not via sysctl, but rather adding an ioctl to the driver and let ktcp (and others, like minicom and rz if it ever gets ported, uucp etc.) use it when needed. I haven't looked at the code yet though.

@Mellvik Mellvik merged commit 929d331 into master Dec 27, 2024
@Mellvik
Copy link
Owner Author

Mellvik commented Dec 28, 2024

BTW @ghaerr, what's the reasoning behind having an inode table that is larger than the file table?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants