Hi, The behavior is intentional, not inconsistent.
Looking at the actual source code, the driver handles three cases differently:
No data at all → goto retry (must wait, can't return 0 which means EOF)
Buffer exhausted → goto retry (same reason)
Some data available → return it immediately, prefetch the rest
Why no goto retry in case 3?
Returning 0 from read() signals EOF to userspace
When there's no data, the driver must wait—otherwise it would falsely indicate EOF
When there is data, returning a short read is standard POSIX behavior
The prefetch (skel_do_read_io) is an optimization for the next read call
Standard Unix read() semantics:
read() may return fewer bytes than requested—this is normal
Userspace is expected to loop if it needs exactly N bytes
This applies to sockets, pipes, and character devices alike
Adding goto retry would work but would change the driver from "return data as soon as available" to "block until buffer is full"—which increases latency unnecessarily.
Because it would need to wait for full buffer.
//Userspace is expected to handle short reads:
// Standard pattern - userspace loops, not the driver
ssize_t read_full(int fd, void *buf, size_t count)
{
size_t total = 0;
while (total < count) {
ssize_t ret = read(fd, buf + total, count - total);
if (ret < 0) return ret; // error
if (ret == 0) break; // EOF
total += ret;
}
return total;
}
The problem:
User calls: read(fd, buf, 100)
Buffer has: 30 bytes
First iteration:
- Copy 30 bytes to buf[0..29]
- rv = 30
- goto retry...
Second iteration (after new data arrives):
- Copy to 'buffer' again (buf[0..??]) ← OVERWRITES first 30 bytes!
- rv = new_chunk ← loses the original 30
The userspace buffer pointer is never advanced. So goto retry would overwrite data already copied.
copy_to_user(buffer, ...); // First copy: buf[0]
goto retry;
copy_to_user(buffer, ...); // Second copy: buf[0] again! Data corrupted.
Even if you fixed the buffer pointer issue (by advancing it on each iteration), the modified behavior would still be undesirable because it changes the driver's semantics from "return data as available" to "block until full"