Proposal for kernel change

From: Abramo Bagnara (abramo@alsa-project.org)
Date: Sat Jan 22 2000 - 08:37:10 EST


After a preliminary discussion with Alan and his green light, I send you
our proposal for writev/readv callback insertion to file_operation
struct.

We are willing to submit you a patch following your guidelines (if any).

In approval case let us know if the patch that you want is against
latest 2.3 or 2.5.0.

-----------

Introduction:

Many sound cards may (or need) to receive and send data with voices non
interleaved (N samples for the left channel and N for the right channel
by example).

This is due to having separate ring buffers or for other hardware
dependant consideration.

ALSA current write interface can work in two different mode called block
and stream mode.

In block mode the ring buffers area is divided in N fragments and the
write is accepted only if count % frag_size == 0.

In stream mode the data are sent to or received from hardware as soon as
it's available. In this way we reduce the latency to the minimum
possible.

The block mode can be used easily with non interleaved data keeping safe
that frag_size is divided in V parts (where V is the number of voices).

Currently the stream mode is not usable with non interleaved data
because:
- we'd have to specify bytes per voice (or deduce it using count / V)
- we'd have to recompose the stream after a partial transfer
- the transferred bytes are not contiguous
etc.

We can for sure affirm that to use that with standard write interface is
a mess and not efficient.
-----------

Proposal:

To extend struct file_operation for readv and writev functions.

If the pointer is NULL the current behaviour is retained.

This permit also to not treat the sock case as an exception (exceptions
may be thought as bad design symptoms ;-)

The writev/readv semantic is untouched (this syscall seem to be born for
our needs).

I attach a sample program that show the efficiency level reachable.
The program read many mono stream and output them as a non interleaved
multiple voices stream, trying to keep the latency the minimum possible.

-- 
Abramo Bagnara                       mailto:abramo@alsa-project.org

Opera Unica Via Emilia Interna, 140 Phone: +39.0546.656023 48014 Castel Bolognese (RA) - Italy Fax: +39.0546.656023

ALSA project is http://www.alsa-project.org sponsored by SuSE Linux http://www.suse.com

It sounds good!

#include <errno.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/uio.h>

#define BUFSIZE 1024

/* Require: new writev semantics: each iov_base represent a different voice In this implementation write size is reduced by buffer boundary, but this can be avoided using voices*2 iovec elements

Without to need a kernel change we may use a special ioctl to do the writev stuff, but the writev solution is *far more* beautiful. */

int stream_with_new_writev(void) { int voices = 2; int pfd = 3; struct voice_buf { char *buf; int woff; int used; int free; } bufs[voices]; struct iovec iovec[voices]; int fd[voices]; int v, result; fd_set rfds, wfds; int writable; int fds = pfd; int roff = 0; for (v = 0; v < voices; ++v) { bufs[v].buf = malloc(BUFSIZE); bufs[v].woff = 0; bufs[v].used = 0; bufs[v].free = BUFSIZE; if (fd[v] > fds) fds = fd[v]; } fds++; writable = 0; while (1) { FD_ZERO(&rfds); for (v = 0; v < voices; ++v) { if (bufs[v].free > 0) FD_SET(fd[v], &rfds); } FD_ZERO(&wfds); if (writable > 0) FD_SET(pfd, &wfds); result = select(fds, &rfds, &wfds, NULL, NULL); if (FD_ISSET(pfd, &wfds)) { for (v = 0; v < voices; ++v) { iovec[v].iov_base = bufs[v].buf+roff; iovec[v].iov_len = writable; } result = writev(pfd, iovec, voices); if (result > 0) { int empty = 1; if (result % voices != 0) { printf("driver failure\n"); return -1; } result /= voices; roff += result; if (roff == BUFSIZE) roff = 0; for (v = 0; v < voices; ++v) { bufs[v].used -= result; bufs[v].free += result; if (bufs[v].used > 0) empty = 0; } if (empty && roff > 0) { for (v = 0; v < voices; ++v) bufs[v].woff = 0; roff = 0; } } else { printf("writev error\n"); return -1; } } writable = BUFSIZE - roff; for (v = 0; v < voices; ++v) { if (FD_ISSET(fd[v], &rfds)) { int readable = BUFSIZE - bufs[v].woff; if (readable > bufs[v].free) readable = bufs[v].free; result = read(fd[v], bufs[v].buf + bufs[v].woff, readable); if (result < 0) { printf("read error\n"); return -1; } bufs[v].woff += result; bufs[v].used += result; bufs[v].free -= result; if (bufs[v].woff == BUFSIZE) bufs[v].woff = 0; } if (bufs[v].used < writable) writable = bufs[v].used; } } return 0; }

- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Sun Jan 23 2000 - 21:00:27 EST