Page 1 of 2

Mattise IPC Specification

Posted: Fri Sep 07, 2007 10:18 pm
by pcmattman
I've just drafted up the initial version of the Mattise IPC Specification. It's really basic and hasn't been implemented yet, but I think it's worth putting it up here for you to look at and give constructive criticism about.

It's here.

(the conversion from the Word document into a webpage didn't work quite as nicely as I'd hoped, but it's good enough)

Posted: Sat Sep 08, 2007 8:16 am
by Combuster
SYSCALL_RECVMSG

This system call takes the first message off the message queue and fills a passed buffer with its data. This is a non-blocking call.

Arguments

EAX: 28d
EDI: the address of a local buffer in which to place the new message.
EDX: the maximum count of octets that can be placed into the local buffer.

Return Values
EBX: 0
Under the assumption that this is indeed a non-blocking call, I miss what would happen if there were no messages? (the buffer isn't filled?), and how to determine that the message received is the same as the one queried by a previous call to message size.
In either case, I'd return a length of the message (or -1 if none), so that we can pop messages non-blocking and with enough information to allow concurrent receivers.

A second issue is one that has to do when the passed buffer is too small to hold the entire message - will the remainder of the message get discarded, is it kept for subsequent read calls, or will it provide an error code to tell the user that the buffer is too small?
int mattiseRecvMessage( struct RecvMsgData* dat )

Header
#include <mmsg.h>

Parameters
dat: a pointer to a buffer to receive an information structure about the received message

Return Value
The total size of the message data.
I have to assume that the caller allocated *dat, and that the callee allocates and fills dat->msgbuff.
To ease the work on the programmer, a set of helpers should be added, a bit like the following:

Code: Select all

int matisseRecvMessage(RecvMsgData * dat);
RecvMsgData * matisseAllocMessage();
void matisseFreeMessage(RecvMsgData * dat);
char * mattisseGetMessageContents(RecvMsgData * dat);
Also, a set of blocking calls are useful to have been provided to the software developer rather than needing to be written manually.

Posted: Sat Sep 08, 2007 6:46 pm
by pcmattman
Combuster wrote:Under the assumption that this is indeed a non-blocking call, I miss what would happen if there were no messages? (the buffer isn't filled?), and how to determine that the message received is the same as the one queried by a previous call to message size.
The system call does not return a failure code if no messages. I might have to revise that so that I don't try to pop an invalid message off the list.
In either case, I'd return a length of the message (or -1 if none), so that we can pop messages non-blocking and with enough information to allow concurrent receivers.
Sounds like a good idea, I was thinking that message receives would be facilitated through the mattiseRecvMessage which performs all this automatically.
A second issue is one that has to do when the passed buffer is too small to hold the entire message - will the remainder of the message get discarded, is it kept for subsequent read calls, or will it provide an error code to tell the user that the buffer is too small?
The system call will put -1 into EBX (this was revised during implementation and I have not updated the spec yet) if the message buffer is too small.
I have to assume that the caller allocated *dat, and that the callee allocates and fills dat->msgbuff.
Again, this is about to be re-implemented (will have to revise the spec) - I probably will have functionality to allocate the message for you.
Also, a set of blocking calls are useful to have been provided to the software developer rather than needing to be written manually.
I'll look into implementing a set of blocking calls. The only reason I haven't used them is because all system calls are via an interrupt (that isn't a trap gate) and doing a while loop with an interrupt call each time doesn't really work too well.

The reason I don't use trap gates is to give a rudimentary form of mutual exclusion - for example only one process can write to the console at once.

Posted: Sat Sep 08, 2007 9:03 pm
by pcmattman
OK, I've just revised the spec and uploaded the changes to that same location.

Posted: Mon Sep 10, 2007 1:54 am
by Combuster
I have one remark left:
SYSCALL_RECVMSG
(...)
EBX: -1 if an error was encountered
Wouldn't it be useful to be able to determine wether the error was due to no message present, or that the message was too long?

Posted: Mon Sep 10, 2007 2:36 am
by JamesM
Combuster: you could just set errno with ETOOLONG, EEXIST etc?

Posted: Mon Sep 10, 2007 2:59 am
by Combuster
JamesM wrote:Combuster: you could just set errno with ETOOLONG, EEXIST etc?
These are kernel calls, errno is a userland (libc) thing.

Posted: Mon Sep 10, 2007 4:32 pm
by pcmattman
Wouldn't it be useful to be able to determine wether the error was due to no message present, or that the message was too long?
I'll change the return codes to have different values for different errors, instead of just "-1" (ie. -1 = message too small, -2 = no messages, etc...).

Posted: Tue Sep 11, 2007 6:38 am
by JamesM
Combuster: my crt0 tells the kernel where errno is. So the kernel can set errno itself. It takes a lot of complexity out of the libc wrapper I find.

Posted: Tue Sep 11, 2007 10:03 am
by Combuster
my crt0 tells the kernel where errno is. So the kernel can set errno itself.
:shock: You sure you don't need a check on your specifications? That's about the ugliest hack I've heard of in a long time... :shock:

Posted: Tue Sep 11, 2007 3:17 pm
by JamesM
Heh, doesn't seem that ugly to me. Matter of personal opinion I suppose - I use syscall/sysret and my stubs only support the returning of one value (so I can't stick the errno in another register for example).

Posted: Tue Sep 11, 2007 4:05 pm
by pcmattman
Combuster wrote:
my crt0 tells the kernel where errno is. So the kernel can set errno itself.
:shock: You sure you don't need a check on your specifications? That's about the ugliest hack I've heard of in a long time... :shock:
Agreed - I don't see why a system call needs to set errno... You check errno after finding out a function call just failed, if you're coding assembly why would you need to worry about errno?

Posted: Wed Sep 12, 2007 4:50 am
by JamesM
Well, errno needs to be set with the appropriate value when a syscall fails, *somehow*. As far as I can see, there are only 3 ways of doing this.

1. Setting it in the kernel, when the function call fails.
2. Returning the value errno should take (ETOOLONG, etc) as well as the normal function "failed" return value (which differs depending on the function, if you're going by POSIX standards)
3. Returning the new value of errno encoded into the return value (negated or something) and hoping that it doesn't clash with a valid return value.

Many people opt for 2. I couldnt, so I opted for 1.

JamesM

Posted: Wed Sep 12, 2007 1:11 pm
by Combuster
errno needs to be set with the appropriate value when a syscall fails
It shouldn't. As I said before, errno is part of the C library, NOT of the kernel. if you want to report error codes from the kernel, you should use a kernel-specific function.
Even the C99 standard suggests that errno is restricted for use by the C library. Interfering kernel calls might therefore break C99 compliance which is a Bad Thing.

Posted: Wed Sep 12, 2007 3:52 pm
by pcmattman
A system call is called, for example, like this:

Code: Select all

uint32_t ebx;
asm( "int $0x80" : "=b" (ebx) : "a" (27) /*args...*/ );

// ebx = error code on failure, 0 if success
You don't use errno because then assembly language programs (assuming the programmer does not link in system libraries) would not be able to detect reasons for failure...

On the other hand, my libc functionality for message passing can set errno, based on what it reads from the return value of the syscall.

It is your choice how you make it all work though...