mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-10-29 05:40:23 -04:00
core: Support memfd transport; bump protocol version
Now that all layers in the stack support memfd blocks, add memfd support for the daemon's global core mempool. Also introduce "enable-memfd=" daemon argument and configuration option. For now, memfd support is an opt-in feature to be activated only when daemon's enable-memfd= is set to yes. Signed-off-by: Ahmed S. Darwish <darwish.07@gmail.com>
This commit is contained in:
parent
b1d47d60fc
commit
d2a6afcab3
13 changed files with 122 additions and 24 deletions
37
PROTOCOL
37
PROTOCOL
|
|
@ -371,6 +371,43 @@ PA_COMMAND_DISABLE_SRBCHANNEL
|
|||
Tells the client to stop listening on the additional SHM ringbuffer channel.
|
||||
Acked by client by sending PA_COMMAND_DISABLE_SRBCHANNEL back.
|
||||
|
||||
## v31, implemented by >= 9.0
|
||||
|
||||
Memfd shared-memory support is now added to PulseAudio as an opt-in feature.
|
||||
Add 'enable-memfd=yes' to daemon's configuration to use memfds, instead of
|
||||
POSIX shm, by default.
|
||||
|
||||
Memfd is a simple memory sharing mechanism, added by the systemd/kdbus
|
||||
developers, to share pages between processes in an anonymous, no global
|
||||
registry needed, no mount-point required, relatively secure, manner.
|
||||
|
||||
PulseAudio memfd support builds the necessary (but not yet sufficient)
|
||||
groundwork for a better integration with per-app containers (e.g. xdg-app)
|
||||
|
||||
For further details on memfds in general, please check:
|
||||
|
||||
https://dvdhrm.wordpress.com/2014/06/10/memfd_create2/
|
||||
Archived at: http://www.webcitation.org/6gnHTy9Kr
|
||||
|
||||
Moreover, for both client and server, the second most-significant bit of
|
||||
the version tag is now used to flag memfd SHM support. On the way forward,
|
||||
the two most-significant _bytes_ of the version tag are now also reserved
|
||||
for flags.
|
||||
|
||||
PA_COMMAND_REGISTER_MEMFD_SHMID
|
||||
New command that can be sent both ways, from client to server and vice versa.
|
||||
This is needed to transfer a memfd pool's blocks without passing its fd every
|
||||
time, thus minimizing overhead and avoiding fd leaks.
|
||||
|
||||
The registration command above sends a packet with the pool's memfd fd as
|
||||
ancillary data. Such packet has an ID that uniquely identifies the pool's
|
||||
memfd memory area. Upon arrival, the other end (client or server) creates a
|
||||
permanent ID<->memfd mapping.
|
||||
|
||||
By doing so, there's need to reference the pool's memfd file descriptor any
|
||||
further -- just its ID. Thus both endpoints can then quickly and safely
|
||||
close their memfd file descriptors.
|
||||
|
||||
#### If you just changed the protocol, read this
|
||||
## module-tunnel depends on the sink/source/sink-input/source-input protocol
|
||||
## internals, so if you changed these, you might have broken module-tunnel.
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ AC_SUBST(PA_MINOR, pa_minor)
|
|||
AC_SUBST(PA_MAJORMINOR, pa_major.pa_minor)
|
||||
|
||||
AC_SUBST(PA_API_VERSION, 12)
|
||||
AC_SUBST(PA_PROTOCOL_VERSION, 30)
|
||||
AC_SUBST(PA_PROTOCOL_VERSION, 31)
|
||||
|
||||
# The stable ABI for client applications, for the version info x:y:z
|
||||
# always will hold y=z
|
||||
|
|
|
|||
|
|
@ -189,11 +189,16 @@ License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
|
|||
|
||||
<option>
|
||||
<p><opt>enable-shm=</opt> Enable data transfer via POSIX
|
||||
shared memory. Takes a boolean argument, defaults to
|
||||
or memfd shared memory. Takes a boolean argument, defaults to
|
||||
<opt>yes</opt>. The <opt>--disable-shm</opt> command line
|
||||
argument takes precedence.</p>
|
||||
</option>
|
||||
|
||||
<option>
|
||||
<p><opt>enable-memfd=</opt>. Enable memfd shared memory. Takes
|
||||
a boolean argument, defaults to <opt>no</opt>.</p>
|
||||
</option>
|
||||
|
||||
<option>
|
||||
<p><opt>shm-size-bytes=</opt> Sets the shared memory segment
|
||||
size for the daemon, in bytes. If left unspecified or is set to 0
|
||||
|
|
|
|||
|
|
@ -292,13 +292,22 @@ License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
|
|||
<p><opt>--disable-shm</opt><arg>[=BOOL]</arg></p>
|
||||
|
||||
<optdesc><p>PulseAudio clients and the server can exchange audio
|
||||
data via POSIX shared memory segments (on systems that support
|
||||
this). If disabled PulseAudio will communicate exclusively over
|
||||
sockets. Please note that data transfer via shared memory
|
||||
data via POSIX or memfd shared memory segments (on systems that
|
||||
support this). If disabled PulseAudio will communicate exclusively
|
||||
over sockets. Please note that data transfer via shared memory
|
||||
segments is always disabled when PulseAudio is running with
|
||||
<opt>--system</opt> enabled (see above).</p></optdesc>
|
||||
</option>
|
||||
|
||||
<option>
|
||||
<p><opt>--enable-memfd</opt><arg>[=BOOL]</arg></p>
|
||||
|
||||
<optdesc><p>PulseAudio clients and the server can exchange audio
|
||||
data via memfds - the anonymous Linux Kernel shared memory mechanism
|
||||
(on kernels that support this). If disabled PulseAudio will
|
||||
communicate via POSIX shared memory.</p></optdesc>
|
||||
</option>
|
||||
|
||||
<option>
|
||||
<p><opt>-L | --load</opt><arg>="MODULE ARGUMENTS"</arg></p>
|
||||
|
||||
|
|
|
|||
|
|
@ -500,13 +500,13 @@ _pulseaudio()
|
|||
--realtime= --disallow-module-loading= --disallow-exit= --exit-idle-time=
|
||||
--scache-idle-time= --log-level= -v --log-target= --log-meta= --log-time=
|
||||
--log-backtrace= -p --dl-search-path= --resample-method= --use-pit-file=
|
||||
--no-cpu-limit= --disable-shm= -L --load= -F --file= -C -n'
|
||||
--no-cpu-limit= --disable-shm= --enable-memfd= -L --load= -F --file= -C -n'
|
||||
_init_completion -n = || return
|
||||
|
||||
case $cur in
|
||||
--system=*|--daemonize=*|--fail=*|--high-priority=*|--realtime=*| \
|
||||
--disallow-*=*|--log-meta=*|--log-time=*|--use-pid-file=*| \
|
||||
--no-cpu-limit=*|--disable-shm=*)
|
||||
--no-cpu-limit=*|--disable-shm=*|--enable-memfd=*)
|
||||
cur=${cur#*=}
|
||||
COMPREPLY=($(compgen -W 'true false' -- "$cur"))
|
||||
;;
|
||||
|
|
|
|||
|
|
@ -690,6 +690,7 @@ _pulseaudio_completion() {
|
|||
'--use-pid-file=[create a PID file]:bool:(true false)' \
|
||||
'--no-cpu-limit=[do not install CPU load limiter]:bool:(true false)' \
|
||||
'--disable-shm=[disable shared memory support]:bool:(true false)' \
|
||||
'--enable-memfd=[enable memfd shared memory support]:bool:(true false)' \
|
||||
{-L,--load=}'[load the specified module]:modules:_all_modules' \
|
||||
{-F,--file=}'[run the specified script]:file:_files' \
|
||||
'-C[open a command line on the running tty]' \
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ enum {
|
|||
ARG_CHECK,
|
||||
ARG_NO_CPU_LIMIT,
|
||||
ARG_DISABLE_SHM,
|
||||
ARG_ENABLE_MEMFD,
|
||||
ARG_DUMP_RESAMPLE_METHODS,
|
||||
ARG_SYSTEM,
|
||||
ARG_CLEANUP_SHM,
|
||||
|
|
@ -100,6 +101,7 @@ static const struct option long_options[] = {
|
|||
{"system", 2, 0, ARG_SYSTEM},
|
||||
{"no-cpu-limit", 2, 0, ARG_NO_CPU_LIMIT},
|
||||
{"disable-shm", 2, 0, ARG_DISABLE_SHM},
|
||||
{"enable-memfd", 2, 0, ARG_ENABLE_MEMFD},
|
||||
{"dump-resample-methods", 2, 0, ARG_DUMP_RESAMPLE_METHODS},
|
||||
{"cleanup-shm", 2, 0, ARG_CLEANUP_SHM},
|
||||
{NULL, 0, 0, 0}
|
||||
|
|
@ -152,7 +154,8 @@ void pa_cmdline_help(const char *argv0) {
|
|||
" --use-pid-file[=BOOL] Create a PID file\n"
|
||||
" --no-cpu-limit[=BOOL] Do not install CPU load limiter on\n"
|
||||
" platforms that support it.\n"
|
||||
" --disable-shm[=BOOL] Disable shared memory support.\n\n"
|
||||
" --disable-shm[=BOOL] Disable shared memory support.\n"
|
||||
" --enable-memfd[=BOOL] Enable memfd shared memory support.\n\n"
|
||||
|
||||
"STARTUP SCRIPT:\n"
|
||||
" -L, --load=\"MODULE ARGUMENTS\" Load the specified plugin module with\n"
|
||||
|
|
@ -389,6 +392,14 @@ int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d
|
|||
conf->disable_shm = !!b;
|
||||
break;
|
||||
|
||||
case ARG_ENABLE_MEMFD:
|
||||
if ((b = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
|
||||
pa_log(_("--enable-memfd expects boolean argument"));
|
||||
goto fail;
|
||||
}
|
||||
conf->disable_memfd = !b;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ static const pa_daemon_conf default_conf = {
|
|||
#endif
|
||||
.no_cpu_limit = true,
|
||||
.disable_shm = false,
|
||||
.disable_memfd = true,
|
||||
.lock_memory = false,
|
||||
.deferred_volume = true,
|
||||
.default_n_fragments = 4,
|
||||
|
|
@ -526,6 +527,7 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
|
|||
{ "cpu-limit", pa_config_parse_not_bool, &c->no_cpu_limit, NULL },
|
||||
{ "disable-shm", pa_config_parse_bool, &c->disable_shm, NULL },
|
||||
{ "enable-shm", pa_config_parse_not_bool, &c->disable_shm, NULL },
|
||||
{ "enable-memfd", pa_config_parse_not_bool, &c->disable_memfd, NULL },
|
||||
{ "flat-volumes", pa_config_parse_bool, &c->flat_volumes, NULL },
|
||||
{ "lock-memory", pa_config_parse_bool, &c->lock_memory, NULL },
|
||||
{ "enable-deferred-volume", pa_config_parse_bool, &c->deferred_volume, NULL },
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ typedef struct pa_daemon_conf {
|
|||
system_instance,
|
||||
no_cpu_limit,
|
||||
disable_shm,
|
||||
disable_memfd,
|
||||
disable_remixing,
|
||||
disable_lfe_remixing,
|
||||
load_default_script_file,
|
||||
|
|
|
|||
|
|
@ -1017,7 +1017,9 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
pa_assert_se(mainloop = pa_mainloop_new());
|
||||
|
||||
if (!(c = pa_core_new(pa_mainloop_get_api(mainloop), !conf->disable_shm, conf->shm_size))) {
|
||||
if (!(c = pa_core_new(pa_mainloop_get_api(mainloop), !conf->disable_shm,
|
||||
!conf->disable_shm && !conf->disable_memfd && pa_memfd_is_locally_supported(),
|
||||
conf->shm_size))) {
|
||||
pa_log(_("pa_core_new() failed."));
|
||||
goto finish;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,16 +61,19 @@ static int core_process_msg(pa_msgobject *o, int code, void *userdata, int64_t o
|
|||
|
||||
static void core_free(pa_object *o);
|
||||
|
||||
pa_core* pa_core_new(pa_mainloop_api *m, bool shared, size_t shm_size) {
|
||||
pa_core* pa_core_new(pa_mainloop_api *m, bool shared, bool enable_memfd, size_t shm_size) {
|
||||
pa_core* c;
|
||||
pa_mempool *pool;
|
||||
pa_mem_type_t type;
|
||||
int j;
|
||||
|
||||
pa_assert(m);
|
||||
|
||||
if (shared) {
|
||||
if (!(pool = pa_mempool_new(PA_MEM_TYPE_SHARED_POSIX, shm_size, false))) {
|
||||
pa_log_warn("Failed to allocate shared memory pool. Falling back to a normal memory pool.");
|
||||
type = (enable_memfd) ? PA_MEM_TYPE_SHARED_MEMFD : PA_MEM_TYPE_SHARED_POSIX;
|
||||
if (!(pool = pa_mempool_new(type, shm_size, false))) {
|
||||
pa_log_warn("Failed to allocate %s memory pool. Falling back to a normal memory pool.",
|
||||
pa_mem_type_to_string(type));
|
||||
shared = false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -218,7 +218,7 @@ enum {
|
|||
PA_CORE_MESSAGE_MAX
|
||||
};
|
||||
|
||||
pa_core* pa_core_new(pa_mainloop_api *m, bool shared, size_t shm_size);
|
||||
pa_core* pa_core_new(pa_mainloop_api *m, bool shared, bool enable_memfd, size_t shm_size);
|
||||
|
||||
/* Check whether no one is connected to this core */
|
||||
void pa_core_check_idle(pa_core *c);
|
||||
|
|
|
|||
|
|
@ -2598,7 +2598,7 @@ static void command_exit(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_ta
|
|||
pa_pstream_send_simple_ack(c->pstream, tag); /* nonsense */
|
||||
}
|
||||
|
||||
static void setup_srbchannel(pa_native_connection *c) {
|
||||
static void setup_srbchannel(pa_native_connection *c, pa_mem_type_t shm_type) {
|
||||
pa_srbchannel_template srbt;
|
||||
pa_srbchannel *srb;
|
||||
pa_memchunk mc;
|
||||
|
|
@ -2631,20 +2631,25 @@ static void setup_srbchannel(pa_native_connection *c) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!(c->rw_mempool = pa_mempool_new(PA_MEM_TYPE_SHARED_POSIX, c->protocol->core->shm_size, true))) {
|
||||
if (!(c->rw_mempool = pa_mempool_new(shm_type, c->protocol->core->shm_size, true))) {
|
||||
pa_log_warn("Disabling srbchannel, reason: Failed to allocate shared "
|
||||
"writable memory pool.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (shm_type == PA_MEM_TYPE_SHARED_MEMFD) {
|
||||
const char *reason;
|
||||
if (pa_pstream_register_memfd_mempool(c->pstream, c->rw_mempool, &reason)) {
|
||||
pa_log_warn("Disabling srbchannel, reason: Failed to register memfd mempool: %s", reason);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
pa_mempool_set_is_remote_writable(c->rw_mempool, true);
|
||||
|
||||
srb = pa_srbchannel_new(c->protocol->core->mainloop, c->rw_mempool);
|
||||
if (!srb) {
|
||||
pa_log_debug("Failed to create srbchannel");
|
||||
|
||||
pa_mempool_unref(c->rw_mempool);
|
||||
c->rw_mempool = NULL;
|
||||
return;
|
||||
goto fail;
|
||||
}
|
||||
pa_log_debug("Enabling srbchannel...");
|
||||
pa_srbchannel_export(srb, &srbt);
|
||||
|
|
@ -2664,6 +2669,13 @@ static void setup_srbchannel(pa_native_connection *c) {
|
|||
pa_pstream_send_memblock(c->pstream, 0, 0, 0, &mc);
|
||||
|
||||
c->srbpending = srb;
|
||||
return;
|
||||
|
||||
fail:
|
||||
if (c->rw_mempool) {
|
||||
pa_mempool_unref(c->rw_mempool);
|
||||
c->rw_mempool = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void command_enable_srbchannel(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
||||
|
|
@ -2682,7 +2694,7 @@ static void command_enable_srbchannel(pa_pdispatch *pd, uint32_t command, uint32
|
|||
static void command_auth(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
||||
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
||||
const void*cookie;
|
||||
bool memfd_on_remote = false;
|
||||
bool memfd_on_remote = false, do_memfd = false;
|
||||
pa_tagstruct *reply;
|
||||
pa_mem_type_t shm_type;
|
||||
bool shm_on_remote = false, do_shm;
|
||||
|
|
@ -2777,7 +2789,7 @@ static void command_auth(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_ta
|
|||
}
|
||||
}
|
||||
|
||||
/* Enable shared memory support if possible */
|
||||
/* Enable shared memory and memfd support if possible */
|
||||
do_shm =
|
||||
pa_mempool_is_shared(c->protocol->core->mempool) &&
|
||||
c->is_local;
|
||||
|
|
@ -2803,9 +2815,12 @@ static void command_auth(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_ta
|
|||
pa_log_debug("Negotiated SHM: %s", pa_yes_no(do_shm));
|
||||
pa_pstream_enable_shm(c->pstream, do_shm);
|
||||
|
||||
do_memfd =
|
||||
do_shm && pa_mempool_is_memfd_backed(c->protocol->core->mempool);
|
||||
|
||||
shm_type = PA_MEM_TYPE_PRIVATE;
|
||||
if (do_shm) {
|
||||
if (c->version >= 31 && memfd_on_remote && pa_memfd_is_locally_supported()) {
|
||||
if (c->version >= 31 && memfd_on_remote && do_memfd) {
|
||||
pa_pstream_enable_memfd(c->pstream);
|
||||
shm_type = PA_MEM_TYPE_SHARED_MEMFD;
|
||||
} else
|
||||
|
|
@ -2817,7 +2832,7 @@ static void command_auth(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_ta
|
|||
|
||||
reply = reply_new(tag);
|
||||
pa_tagstruct_putu32(reply, PA_PROTOCOL_VERSION | (do_shm ? 0x80000000 : 0) |
|
||||
(pa_memfd_is_locally_supported() ? 0x40000000 : 0));
|
||||
(do_memfd ? 0x40000000 : 0));
|
||||
|
||||
#ifdef HAVE_CREDS
|
||||
{
|
||||
|
|
@ -2834,7 +2849,19 @@ static void command_auth(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_ta
|
|||
pa_pstream_send_tagstruct(c->pstream, reply);
|
||||
#endif
|
||||
|
||||
setup_srbchannel(c);
|
||||
/* The client enables memfd transport on its pstream only after
|
||||
* inspecting our version flags to see if we support memfds too.
|
||||
*
|
||||
* Thus register any pools after sending the server's version
|
||||
* flags and _never_ before it. */
|
||||
if (shm_type == PA_MEM_TYPE_SHARED_MEMFD) {
|
||||
const char *reason;
|
||||
|
||||
if (pa_pstream_register_memfd_mempool(c->pstream, c->protocol->core->mempool, &reason))
|
||||
pa_log("Failed to register memfd mempool. Reason: %s", reason);
|
||||
}
|
||||
|
||||
setup_srbchannel(c, shm_type);
|
||||
}
|
||||
|
||||
static void command_register_memfd_shmid(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue