[oe] Kernel Headers Quality Issue

Phil Blundell philb at gnu.org
Fri May 14 10:25:54 UTC 2010


On Fri, 2010-05-14 at 11:59 +0200, Thilo Fromm wrote:
> I have no means of detecting a level of compatibility at runtime. How 
> could that work, anyway? I get ENOSYS when calling inotify_init1(), so, 
> well I *think* this interface is missing (although configure reported it 
> being available). Then I would have to dynamically fall back to 
> inotify_init() + fcntl().

Yes, precisely.  ENOSYS is indeed diagnostic of an unimplemented system
call, and what you have described is exactly what glibc does internally
for this kind of situation.  See for example the backwards compatibility
code for mmap2 vs mmap:

	DO_CALL (mmap2, 0)
	cmn	r0, $4096
# ifdef __ASSUME_MMAP2_SYSCALL
	ldr	r4, [sp], #4
	ldr	r5, [sp], #4
	RETINSTR(cc, lr)	
	b	PLTJMP(syscall_error)
# else
	ldrcc	r4, [sp], #4
	ldrcc	r5, [sp], #4
	RETINSTR(cc, lr)
	cmn	r0, $ENOSYS
	bne	.Lerror
	/* The current kernel does not support mmap2.  Fall back to plain
	   mmap if the offset is small enough.  */
	ldr	r5, [sp, $16]
	mov	r0, ip			@ first arg was clobbered
	teq	r5, $0
	ldreq	r4, [sp], #4
	ldreq	r5, [sp], #4
	beq	PLTJMP(__mmap)
#endif

> What if that returns ENOSYS? Fall back to dnotify?

If you wish to support kernels that don't have inotify at all, yes.
Obviously it's up to you how far back you want to go in terms of
compatibility, and I suspect most people would just give up at this
point.

> And repeat this dance every time I need to call inotify_init1()?

Yes.  Or, if this is performance critical code, you can cache the result
(since the kernel is unlikely to suddenly gain support for a syscall
that it didn't have last time) and go directly to the implementation
that works.

> Compatibility code would have to fumble for the interfaces 
> *really* available at runtime by using trial-and-error. I would not 
> expect anyone to provide this kind of compatibility.

Clearly you don't like the idea very much but yes, this is basically how
backwards compatibility code is done, and it is more or less an
inescapable consequence of the rather fluid nature of the Linux syscall
ABI.

Of course, you always have the option of simply writing to the lowest
common denominator and using inotify_init() directly rather than
inotify_init1().  Or, in the case of inotify, you have the option of
using a library which will hide this complexity for you and provide an
abstract userspace API which remains the same no matter what the
underlying implementation is doing.

This is not the only situation in which robust applications will adapt
themselves to the runtime environment in which they find themselves.  In
socket-based programs, a fairly common paradigm is something along the
lines of:

	fd = socket (PF_INET6, ...);
	if (fd < 0 && errno == EPFNOSUPPORT) {
		// IPv6 support is missing.  Use IPv4 instead.
		fd = socket (PF_INET, ...);
	}

in order that the application doesn't cease to work altogether for
people who have forgotten to load the ipv6 module.

p.






More information about the Openembedded-devel mailing list