Skip to content

portal_devfs: blocking poll for plugin-backed char devices (fix #77)#78

Merged
lacraig2 merged 1 commit into
mainfrom
fix/77-pseudofile-poll-wait
Jun 17, 2026
Merged

portal_devfs: blocking poll for plugin-backed char devices (fix #77)#78
lacraig2 merged 1 commit into
mainfrom
fix/77-pseudofile-poll-wait

Conversation

@lacraig2

Copy link
Copy Markdown
Contributor

Summary

Fixes #77. A from_plugin-backed /dev char device could not block a
poll()/select() reader. igloo_convert_ops_to_fops() wired the devfs poll fop
straight to the Python-modeled poll (out->poll = ops->poll) and the driver never
called poll_wait(), so there was no wait queue for a "not ready" poll to sleep on:

  • a data-aware poll reporting "not ready" had nothing to block on, and
  • the legacy always-ready poll made poll()->read() readers busy-loop on 0-byte
    reads — pinning a core under TCG.

(Companion to rehosting/penguin#843, which adds the data-aware poll on the Python side.
With both, a from_plugin serial-style device delivers responses to a blocked reader
with no idle spin.)

Change

src/portal/portal_devfs.c:

  • struct portal_devfs_entry gains wait_queue_head_t poll_wq plus python_poll /
    python_write (the Python-modeled fops, kept so the proxies can forward to them and
    the block path can call them directly).
  • igloo_devfs_proxy_poll() — recovers the entry via container_of(inode->i_cdev),
    calls poll_wait(file, &pe->poll_wq, pt), then defers the readiness mask to the
    Python poll. Falls back to the legacy always-ready mask when no Python poll is modeled,
    so existing always-ready nodes are unchanged.
  • igloo_devfs_proxy_write() — forwards to the Python write, then
    wake_up_interruptible(&pe->poll_wq). A write commonly queues a response on the host
    side (request/response serial devices), so a reader blocked in poll() is woken
    promptly instead of waiting out a poll timeout.
  • out->poll/out->write are routed through these proxies; the entry is created with
    init_waitqueue_head(&pe->poll_wq) and the captured Python fops.
  • The block queue_rq path calls python_write directly (it has no struct file),
    bypassing the char-only write proxy.

Behavior

  • No Python poll modeled → always-ready mask (unchanged from today).
  • Data-aware Python poll (returns POLLIN only when data is queued) → a reader blocks in
    poll() when idle (no spin) and is woken by the next write that queues a reply.

Testing notes

Not yet built in CI from this branch / not runtime-tested in the combo with penguin#843
(no kernel-devel in my working tree). The change mirrors the existing proxy_release
pattern (same container_of/forwarding shape and the same unsigned int poll signature
that out->poll = ops->poll already used). Reviewers: please confirm the CI module build,
and the end-to-end behavior with penguin#843's data-aware poll.

Follow-ups

  • write_iter is not proxied (no wake) — add if a backing models it.
  • An explicit host-initiated wake (a portal op the backing can call after queuing
    unsolicited data, e.g. modem URCs) would cover the async case; wake-on-write covers
    the request/response pattern.

The devfs poll fop was wired straight to the Python-modeled poll (out->poll =
ops->poll) and the driver never called poll_wait(). So a from_plugin char
device could not block a poll()/select() reader: a data-aware poll reporting
'not ready' had no wait queue to sleep on, and the legacy always-ready poll
made poll()->read() readers busy-loop on 0-byte reads (one core pegged under
TCG).

Add a per-entry wait queue and route poll/write through proxies:
- struct portal_devfs_entry gains poll_wq + python_poll/python_write.
- igloo_devfs_proxy_poll() calls poll_wait(file, &pe->poll_wq, pt) then defers
  the readiness mask to the Python poll (falls back to the legacy always-ready
  mask if none is modeled).
- igloo_devfs_proxy_write() forwards to the Python write then
  wake_up_interruptible(&pe->poll_wq): a write commonly queues a response on
  the host side, so a reader blocked in poll() is woken promptly instead of
  waiting out a poll timeout.
- The block queue_rq path calls python_write directly (NULL file), bypassing
  the char-only write proxy.

Pairs with rehosting/penguin#843 (data-aware poll): with both, a from_plugin
serial-style device delivers responses to a blocked reader with no idle spin.

Note: write_iter is not yet proxied (no wake); add if a backing models it.
@lacraig2 lacraig2 merged commit d4a361c into main Jun 17, 2026
1 check passed
@lacraig2 lacraig2 deleted the fix/77-pseudofile-poll-wait branch June 17, 2026 18:17
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.

portal_devfs: support blocking poll for plugin-backed char devices (poll_wait + wake on data)

1 participant