179 karma · joined August 15, 2013
The state of affairs you described is much less the case now than in the past.
> I guess one might be tempted to forgive a few warts in the interface layers
... well, yeah, that's exactly my sentiment about kqueue here. What you're talking about is basically a small wart that no one's bothered to address because it's inconsequential.
I don't really understand what argument you're making. Is io_uring also a bad design because it requires new file_operations?
The kernel doesn't magically know whether your device file has data available to read, your device file has to define what that means. That's all I'm referring to. Hooking that up to kqueue usually involves writing 5-10 lines of code.
For instance, FreeBSD (and I think other BSDs) also have EVFILT_PROC, which lets you monitor a PID (not an fd) for events. One such event is NOTE_FORK, i.e., the monitored process just forked. Can you wait for such events with epoll? I'm not sure.
More generally, suppose you wanted to automatically start watching all descendants of the process for events as well. If I was required to use a separate fd to monitor that child process, then upon getting the fork event I'd have to somehow obtain an fd for that child process and then tell epoll about it, and in that window I may have missed the child process forking off a grandchild.
I'm not sure how to solve this kind of problem in the epoll world. I guess you could introduce a new fd type which represents a process and all of its descendants, and define some semantics for how epoll reports events on that fd type. In FreeBSD we can just have a dedicated EVFILT_PROC filter, no need for a new fd type. I'm not really sure whether that's better or worse.
When adding a new descriptor type, one can define semantics for existing filters (e.g., EVFILT_READ) as one pleases.
To give an example, FreeBSD has EVFILT_PROCDESC to watch for events on process descriptors, which are basically analogous to pidfds. Right now, using that filter kevent() can tell you that the process referenced by a procdesc has exited. That could have been defined using the EVFILT_READ filter instead of or in addition to adding EVFILT_PROCDESC. There was no specific need to introduce EVFILT_PROCDESC, except that the implementor presumably wanted to leave space to add additional event types, and it seemed cleaner to introduce a new EVFILT_PROCDESC filter. Process descriptors don't implement EVFILT_READ today, but there's no reason they can't.
So if one wants to define a new event type using kevent(), one has the option of adding a new definition (new filter, new note type for an existing filter, etc.), or adding a new type of file descriptor which implements EVFILT_READ and other "standard" filters. kqueue doesn't really constrain you either way.
In FreeBSD, most of the filter types correspond to non-fd-based events. But nothing stops one from adding new fd types for similar purposes. For instance, we have both EVFILT_TIMER (a non-fd event filter) and timerfd (which implements EVFILT_READ and in particular didn't need a new filter). Both are roughly equivalent; the latter behaves more like a regular file descriptor from kqueue's perspective, which might be better, but it'd be nice to see an example illustrating how.
One could argue that the simultaneous existence of timerfds and EVFILT_TIMER is technical debt, but that's not really kqueue's fault. EVFILT_TIMER has been around forever, and timerfd was added to improve Linux compatibility.
So, I think the article is misguided. In particular, the claim that "any time you want kqueue to do something new, you have to add a new type of event filter" is just wrong. I'm not arguing that there isn't technical debt here, but it's not really because of kqueue's design.
78436 cat PFLT 0x6c71f99cda8 0x2<VM_PROT_WRITE> 78436 cat PRET KERN_SUCCESS 78436 cat PFLT 0x3c6efd36c280 0x2<VM_PROT_WRITE> 78436 cat PRET KERN_SUCCESS 78436 cat PFLT 0x3c6efd36e158 0x2<VM_PROT_WRITE> 78436 cat PRET KERN_SUCCESS ...
Obviously not nearly as flexible as ebpf though. For instance it'll log all page faults happening in the context of the process, and so includes page faults that happen in the kernel due to copyin()/copyout() etc. Sometimes it's helpful and other times confusing.
I'm not sure what happened in this case. The linked report does indeed have an associated reproducer.
It's not. We do a lot of code review, and it's done publicly. It's easy to look at the commit logs. I find it telling that the article doesn't spend even one word trying to delve into our code review practices. This case was an aberration.
The tests appear to compare ZFS and ext4 and clang and gcc as much as FreeBSD and Linux.
You don't really need a separate API. The kernel can implement munmap() lazily, it just needs to also ensure that the address range isn't reused until a TLB shootdown is completed. LATR is a system that leverages this to implement lazy TLB shootdowns for munmap(): http://www.cs.yale.edu/homes/abhishek/kumar-asplos18.pdf
It does a pretty good job of maintaining system responsiveness and latency when there's sustained memory pressure, at least much better than the simpler hysteresis loops that are commonly used for this sort of thing.