Hello all,

I want to change struct pollfd in include/sys/poll.h to the POSIX
layout. This is a breaking change, so I ask for your comments before
the pull request leaves the draft state.

1. The problem

Today struct pollfd has 32-bit events and revents and three internal
fields (arg, cb and priv). It is 40 bytes on a 64-bit target and 24
bytes on a 32-bit target. POSIX gives it three fields: int fd, short
events and short revents (8 bytes).

- A program or a library that builds its own array with the standard
  layout and calls poll() gets its neighbouring entries overwritten.
  Language runtimes, for example the Rust libc crate, already use the
  8-byte layout for NuttX.
- In flat and protected builds, poll() uses the caller's array
  directly. The kernel writes a semaphore address and a callback
  pointer into it, and poll_notify() calls that callback, often from an
  interrupt handler. In a protected build the array is user memory, so
  the kernel keeps a callback in memory that user code can change.
- Two threads cannot poll the same array at the same time (see the
  REVISIT comment in poll.h).

2. The change

- struct pollfd gets the POSIX layout: int fd; short events;
  short revents.
- A new kernel header, include/nuttx/poll.h, holds the kernel type
  struct pollfd_s (fd, 32-bit events and revents, arg, cb, priv),
  pollevent_t, pollcb_t, POLLALWAYS, poll_notify() and
  poll_default_cb(). sys/poll.h keeps only the POSIX part.
- poll() copies the fd and events of each entry into a struct pollfd_s
  array and copies revents back. Up to 4 entries go on the stack, more
  go on the fs heap. The kernel build already copied the array on every
  call.
- The poll() method of drivers and sockets, file_poll(), psock_poll()
  and poll_notify() take struct pollfd_s. This is a mechanical rename
  in about 150 files.
- select(), ppoll(), pselect(), epoll and the system call interface do
  not change. epoll already keeps its own kernel entry for each file
  descriptor.

3. Cost

On qemu-armv8a the code grows by 168 bytes (flat build) and 48 bytes
(kernel build). The stack of poll() grows by 160 bytes on a 64-bit
target and by 96 bytes on a 32-bit target.

4. Effect on users

- Applications: rebuild. Source code that uses only fd, events and
  revents does not change. Code that prints events or revents with
  PRIx32 must use %x (one nuttx-apps pull request fixes the five
  programs in nuttx-apps).
- Out-of-tree drivers: rename struct pollfd to struct pollfd_s in the
  poll methods, the waiter arrays and the poll callbacks. Include
  <nuttx/poll.h> if the file does not include <nuttx/fs/fs.h>. The
  compiler reports every place that is not renamed.

5. Pull requests

- One pull request with three commits: the rename (no change in
  behaviour), the layout change (marked with "!" and "BREAKING
  CHANGE"), and the documentation.
- Two nuttx-apps pull requests: the PRIx32 formats, and the rpmsgdev
  test driver.

Tests: a test program checks the size of struct pollfd and polls an
array with the standard layout (pipes, ppoll(), select(), epoll, two
threads on one array). ostest passes on qemu-armv8a:knsh and
qemu-armv8a:nsh. Help with tests on real hardware is welcome.

Please comment here before I open the draft pull request, or in
the draft pull request, that comes after the whitespace and
formatting preparation PRs.

Regards,
Marco Casaroli

Reply via email to