On 7/29/06, Shem Multinymous <multinymous@xxxxxxxxx> wrote:
There's a pattern here. Maybe what we need is a generic scheme for publishing continuous readouts, that will work for both hdaps and batteries (despite a X100 difference in typical time magnitudes). Doubtless there are other uses for such a scheme. We want it to support clients with different desired rates, data sources with different update frequencies, use minimum CPU and avoid unnecesary timer interrupts on tickless kernels. Here's one approach: use a syscall (e.g., ioctl) saying "block until there's new data on this fd, or N milliseconds have passed, whichever is *later*". This way each client declares the update rate it wants and can change it on the fly. The driver sees all the requests and can perform the minimum hardware quering -- for example, it won't query the hardware at all if no client has submitted a request with parameter N more than N milliseconds go. And there's no excessive work or interrupts. Some (simple) kernel code infrastructure is needed to help drivers manage the pending requests.
Here's a rough sketch for the userspace side of a continuous function sampling interface. It handles the blocking a bit better than the above proposal, in that it lets you easily handle multiple readouts. It's agnostic about /dev vs. /sys. As always, we have a file handing out readouts obtained from the hardware - either a device file or a sysfs attribute. The file has the following properties: 1. A new ioctl, SYSFS_DELAYED_UPDATE, meaning: "I want an a fresh readout in N milliseconds, or whenver it's ready, whichever is later. If I poll this FD with POLLIN, wait until the new readout is ready. Likewise for select." 2. When the file is opened in O_NONBLOCKing mode, read() always return the latest cached readout rather than querying the hardware (unless the latter has negligible cost). 3. When the file is opened in normal (blocking) mode, read() blocks until a fresh readout is available and returns this readout; see below. To illustrte, here's an example of a proper polling loop (sans error checking). This app wants updates at most every second, but obviously not more frequently than the hardware has to offer. If no readout ca be obtained for 20 seconds, it warns the user that the data is stale. -------------------------- /* Open attribute file with O_NONBLOCK so that all reads will * return cached values instead of blocking: */ int fd = open("/whatever/voltage", O_NONBLOCK|O_RDONLY); /* Read and process latest cached attribute value: */ read(fd, ...); ... while (1) { struct pollfd ufds = { .fd=fd, events=POLLIN }; /* Tell driver we want a fresh readout but no sooner than 1sec * from now: */ ioctl(fd, SYSFS_DELAYED_UPDATE, 1000); /* Wait for an update for at most 5 seconds. Nominally this will * block for at least 1 second, because of the above ioct. If we * were reading multiple attributes, we could poll them all * simultaneously. */ poll(&ufds, 1, 20000); ... warn user if timed out ... /* Read and process latest cached attribute value: */ read(fd, ...); ... handle readout ... } -------------------------- Note that the above does not ensure minimal separation between readouts: you're guaranteed a fresh readout at every iteration, but it might have been taken at the beginning of the >1sec poll period. If you need to ensure that all readouts are at least (700ms apart, add a sleep(700) at the end of the loop and decrease 1000 to 300 in the ioctl().I think most apps won't need this. Legacy and lightweight applications (e.g., cat from shell) will open the file in blocking mode. In this case, a "read(fd, ...)" has semantics equivalent to the following O_NONBLOCK-mode code: -------------------------- /* Tell driver we want an immediate update: */ ioctl(fd, ATTR_DELAY_UPDATE, 0); /* Wait indefinitely for an update: */ poll(&ufds, 1, -1); /* Read the latest cached attribute value: */ read(fd, ...); -------------------------- Similarly, select() and poll() on non-blocking files acts like you did "ioctl(fd, ATTR_DELAY_UPDATE, 0);" and then wait for a refresh or timeout. If we want to penalize people not using nonblocking mode and delayed updates, we can replace two "0" above with a sufficiently nasty (driver-dependent?) constant. How does this look? I'm not getting into the kernel side for now; it's doable, and with proper infrastructure (e.g., at the sysfs level) can be elegant and efficient. Shem - To unsubscribe from this list: send the line "unsubscribe linux-acpi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html