Date: Fri, 27 Oct 1995 13:09:46 -0700 (MST) From: Terry Lambert <terry@lambert.org> To: bde@zeta.org.au (Bruce Evans) Cc: bde@zeta.org.au, terry@lambert.org, CVS-commiters@freefall.freebsd.org, bde@freefall.freebsd.org, cvs-sys@freefall.freebsd.org, hackers@freebsd.org, swallace@ece.uci.edu Subject: Re: SYSCALL IDEAS [Was: cvs commit: src/sys/kern sysv_msg.c sysv_sem.c sysv_shm.c] Message-ID: <199510272009.NAA23809@phaeton.artisoft.com> In-Reply-To: <199510271903.FAA24658@godzilla.zeta.org.au> from "Bruce Evans" at Oct 28, 95 05:03:48 am
next in thread | previous in thread | raw e-mail | index | archive | help
> >What wall time differentials do you expect from the conversion, and
> >why?
>
> Small. The current unportable code is close to optimal. I wouldn't
> make it slower. The machine generated code for the current FreeBSD
> i386 ABI might even be exactly the same as now because all conversions
> can be reduced to no-ops (this must be possible because the unportable
> code actually works).
The reason for my asking was to determine if you believed this to be
a speed optimization (which I might be able to get behind) or if it's
simply an interface change (which I suspected it was, and so it's
now an issue of whether it's gratuitious or if ti really buys something).
[ ... reordered for coherency ... ]
> >#pragma. If we are talking about compiler modifications, packing #pragma's
> >are much more generally useful than register call wrappings and the
> >internal code you have suggested for varargs.
>
> All #pragmas are unportable.
All packing assumptions are unportable. I argue that a #pragma beats
the __attrib crap because a compiler may ignore it.
> Conceptually it's a different layer but it shouldn't be implemented as
> a separate layer. My idea is to introduce such a conceptual layer
> somewhere between the syscall entry point (Xsyscall()) and the syscall
> implementing functions (e.g., read()) and then merge the layers as
> much as possible using machine-generated code.
[ ...]
> >What is your wrapper for open( const char *, int, ...)?
>
> On the user side (so that the open syscall doesn't have a variable number
> of args:
>
> int open(char const *path, int flags, ...)
Whoah. Right there we have a problem. I'll be happy to give you my
varrarg macros to make them ANSI-C and K&R C independent. Usign varrargs
in an ANSI dependent fashion reduces portability to non-ANIC-C envirnments.
The things belong in ctypes.h anyway.
[ ... ]
> int open(char const *path, int flags, ...)
> {
> va_list argp;
> int retval;
>
> va_start(argp, flags);
> if (flags & O_CREAT) {
> promoted_mode_t mode;
> registered_mode_t rmode;
>
> mode = va_arg(argp, int);
> rmode = = (registered_mode_t)mode;
> retval = three_arg_open_syscall(path, flags, rmode);
> } else
> retval = two_arg_open_syscall(path, flags);
> va_end(argp);
> return retval;
> }
I think that the system call itself could make the decision based on the
O_CREAT itself. I'd suggest changing:
> retval = three_arg_open_syscall(path, flags, rmode);
> } else
> retval = two_arg_open_syscall(path, flags);
to:
> retval = three_arg_open_syscall(path, flags, rmode);
> } else
> retval = three_arg_open_syscall(path, flags, 0);
[ ... ]
> On the kernel side: I can't write it without knowing the target machine.
> Assuming a simple case where the user side stored the args in a uniform
> way on the stack or in registers and the lower level kernel code has
> copied the args from to memory:
[ ... ]
> path = *(char const *)((char *)argp + PATH_OFFSET);
> flags = *(int *)((char *)argp + FLAGS_OFFSET);
> mode = *(promoted_mode_t *)((char *)argp + MODE_OFFSET);
Forgive me... but this is butt-ugly. I mean really, *really* butt-ugly.
I find this horribly obfucated. This is exactly what I feared when I
objected. 8-(.
BTW: the 'path' line is broken... "*(const char **)", maybe?
Since the stack variable assigns are based on an entry argument, then
the assignments can be done at declaration time. Therefore something
like:
#define ARG(argtype,arg) \
argtype arg = *(__CONCAT(argtype,*))((char *)argp + \
__CONCAT(arg,_OFFSET));
Then:
> int syscall_entry_open(struct proc *p, void *argp, int *retval)
> {
> ARG( char const *, PATH);
> ARG( int, FLAGS);
> ARG( promoted_mode_t, MODE);
>
> if (flags & O_CREAT) {
> return open(p, retval, PATH, FLAGS, MODE);
> }
> return open(p, retval, PATH, FLAGS);
> }
Would be more readable.
I don't understand why passing a bogus mode to open when O_CREAT wasn't
present would be a problem???
That is, why not:
> int syscall_entry_open(struct proc *p, void *argp, int *retval)
> {
> ARG( char const *, PATH);
> ARG( int, FLAGS);
> ARG( promoted_mode_t, MODE);
>
> return open(p, retval, PATH, FLAGS, MODE);
> }
???
Hell:
#define SYSCALL_ENTRY(function) \
int __CONCAT(syscall_entry_,function) \
(struct proc *p, void *argp, int *retval)
> SYSCALL_ENTRY(open)
> {
> ARG( char const *, PATH);
> ARG( int, FLAGS);
> ARG( promoted_mode_t, MODE);
>
> return open(p, retval, PATH, FLAGS, MODE);
> }
> >What about non-native architecture ABI support?
>
> It requires more code to generate the conversion code. In particular,
> sizeof() and `struct' can't be used, because the non-native compiler
> may have completely different behaviour (of course, it's not practical
> to emulate a machine with a larger address space or ints larger than
> your quads).
I agree with the practicality argument: after all, that's exactly what
we are doing already with lseek, et al. 8-).
I know the XDR aspects re structure packing are an issue, though since
you seem to be assuming the same compiler source everywhere (GCC),
I don't see where a packing paragma would be more of an issue than
any other approach requiring compiler modifications.
> >By going to a register passing mechanism, you destroy this, and complicate
> >the non-native ABI's at the same time... don't you see this as well?
>
> The register passing mechanism for syscalls is a side issue. I only
> mentioned it as an example of an ABI that we have to emulate (for Linux)
> and which is better so we should use it. There would be no new
> complications (except for improvements) because we already support the
> Linux ABI.
I'm glad to see this. I think, though, the rationale for the changes
still hasn't been clarified.
> >??? If it's not required, then either you *aren't* talking about passing
> >arguments to system calls in registers *or* you expect the compiler to
> >"do the right thing" magically.
>
> I'm talking about the requirement in C to pass parameters of the correct
> type (if a prototype is in scope, then there are more correct types).
OK. I buy this too.
But this implies that if there is not a prototype in scope, a function
that uses a type that is not correct without a prototype in scope should
not generate a bogus call reference.
This was my point on the quad crap and reverting the default interfaces
to POSIX compliance (and double doesn't count unless you can use it
without a prototype in scope).
> >Right now I can write code that makes system calls in assembly. With
> >register passing conventions, I will need to either use library or
> >machine generated external routine calls (unacceptable), or I will have
> >to have documentation of what the compiler has done so that I can do
> >the same thing (non-portable as hell -- ever use M4 to write machine
> >independent assembly code?).
>
> It's always been necessary to know what what the compiler does if you
> write glue functions in assembler. (gcc) inline assembler has many
> advantages here. You don't need to know what the compiler does (you
> just tell it how to load syscall args), and one layer of glue can be
> avoided.
But *lord*! Having to happen to know what register the argument goes
into before the call!
> >> >Note that prototypes of system calls screw up your ability to properly
> >> >utilize the syscall(2) call gate mechanism the way it was intended. A
> >>
> >> The use of syscall() in general requires handling all the messy conversion
> >> issues that we have been discussing in your own code.
>
> >Why is that? A push is a push. A trap is a trap.
>
> >The only "messy conversion" is in pushing the arguments on the stack,
> >and I still fail to see the benefit of playing "keep up with Linux"
> >in this regard.
>
> syscall() is written in C, so the compiler can pass args to it anywhere it
> wants. syscall() then has the task of putting the args where the kernel
> expects them. In general, it would have to know about all syscalls and
> do the inverse of the conversions that I'm talking about doing in the
> kernel.
Bletch. How does Linux deal with it (my linux box is running Win95
right now)?
> Consider a 68K compiler that passes the first 2 pointer args (if any)
> in a0-a1 and the first 2 integer args (if any) in d0-d1 and the other
> args on the stack. How are you going to push the args in syscall()?
> Hint: you'll need to know the arg types of all syscalls.
That's an evil way to pass arguments, then. 8-(.
> >> (2) choose a different violation of the ANSI C
> >> >and POSIX standards -- interface extension -- instead of passing quad's
> >>
> >> POSIX allows most reasonable extensions.
>
> >Yeah, well using quad for off_t isn't one of them.
>
> Would you prefer double? Allowing it seems to be a bug in POSIX.1-1990.
> More things would break.
No. I'd prefer int/long.
> It's worse than that. Linux doesn't have quads. Applications have to use
> pairs of longs and do their own (equivalent to quad) arithmetic on them.
> So ifdefs like the above all through the code might be necessary.
Linux has GCC. Linux has quads.
> We're in violent agreement :-). I'd like to hide the current
> unportabilities and scattered casts in a conceptually simple layer.
I agree.
> This requires a complicated and unportable implementation for the
> layer.
I don't agree. 8-).
Terry Lambert
terry@lambert.org
---
Any opinions in this posting are my own and not those of my present
or previous employers.
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199510272009.NAA23809>
