On 2016-02-20 01:19, Ahmed S. Darwish wrote: > Hi! > > On Tue, Feb 16, 2016 at 03:58:02PM +0100, David Henningsson wrote: >> >> On 2016-02-12 01:19, Ahmed S. Darwish wrote: >>> Now that we have the necessary infrastructure for memexporting and >>> mempimporting a memfd memblock, extend that support higher up in the >>> chain with pstreams. >>> >>> A PulseAudio endpoint can now _transparently_ send a memfd memblock >>> to the other end by simply calling pa_pstream_send_memblock(). >> >> ...provided the memfds are first registered. >> > > Yup, will add that. > > ... >>> @@ -92,6 +96,9 @@ struct item_info { >>> >>> /* packet info */ >>> pa_packet *packet; >>> + bool shmid_to_memfd_packet:1; >> >> This seems a bit strange to me. When you set up a srbchannel, there is a >> special packet sent (PA_COMMAND_ENABLE_SRBCHANNEL) which has two fds as >> ancil data. >> >> Would it not make sense (and lead to less code changes, probably) if this >> followed a similar scheme, i e, add a new command (PA_COMMAND_SHMID_FD or >> something) that would have an memfd as ancil data and the ID as packet >> content? >> > > Hmm, I guess this should've been further commented. > > Let's first agree to the following: > > - to send a command like PA_COMMAND_ENABLE_SRBCHANNEL, a > tagstruct is built and sent over the pipe using > pa_pstream_send_tagstruct_with_fds() > > [ protocol_native.c::setup_srbchannel() ] > > - pa_pstream_send_tagstruct_with_fds() transform the tagstruct to > an opaque packet data using a simple memcpy(), then the packet > with its ancillary is sent over the domain socket. > > [ pstream-util.c::pa_pstream_send_tagstruct_with_fds() ==> > pstream-util.c::pa_pstream_send_tagstruct_with_ancil_data() > ==> pstream.c::pa_pstream_send_packet() /* event deferred */ > ==> pstream.c::do_write() /* packet write */ > ==> iochannel.c::pa_iochannel_write_with_fds() > ==> POSIX sendmsg()! ] > > So, _on the wire_, both srbchannel enablement and SHMID<->memfd > registration mechanisms are actually the same: the latter just > uses pa_pstream_send_packet() with extra little modifications. > > Given the above, why not create a PA_COMMAND_SHMID_FD and save us > some code? > > Well, the reason is that we want to close the memfd fd directly > after sending the packet and its ancillary using POSIX sendmsg(). > pa_pstream_send_tagstruct_**() does not offer us any ability to > do that :-( It _opaques the message_ then defers the write event > over the pstream. > > Due to such opaqueness, after a succesful pstream do_write() I > cannot just say: the packet written was a PA_COMMAND_SMID_FD, so > let's close its associated fds. It seems quite straight forward just to add another parameter "bool close_fds" to pa_pstream_send_tagstruct_with_fds and struct pa_cmsg_ancil_data, and in pstream.c::do_write, right after the call to pa_iochannel_write_with_fds, you close the fds if the parameter is set? That way you don't need any "cleanup hook" either. > An argument could then be stated: why not wait for the other PA > endpoint to reply/ACK the command and then close the fds? Well, > this would open us to malicous clients .. leading to an fd leak > per each malicious client connection, and thus bringing the whole > server down after a while. > > == > > Thus the patch, actually simple, solution was created. Let's mark > the SHM_ID<->memfd registration packet with a special color. Now > in pstream.c::do_write() I can see that this is a registration > packet and call up a simple cleanup hook after write(). The hook > does all the necessary validations and then close the fds. > > This way, the fds are always safely closed on the sending side. > > [ After writing the above, I've just discovered a bug! If sendmsg > fails, the cleanup hook should also be called but it is not. > Will fix that ] > >> >> As a side note, we could potentially optimise setting up an srbchannel by >> sending all three memfds in PA_COMMAND_ENABLE_SRBCHANNEL, it'll be one less >> package to send... >> > > I'm really not a big fan of this.. It will hardcode both the > number, _and nature_, of mempools in the Pulse communication > protocol itslef. Are you sure we want to do that? Hey, it was just an idea :-) Indeed that would mean some additional code, probably not worth it at this point. There are just these really quick clients, e g if you do "pactl list sinks" or something, and we're adding package after package just to set the connection up. But thinking about it, this would probably be better fixed if, e g, "pactl" could just disable SHM in the first place. > > So in the future, if we merge the per-client srbchannel mempool > with the envisioned per-client (earlier global) mempool, we will > have to change the protocol semantics. In the far future if we > reached the optimum case of a single mempool per client (not 3), > we'll also change the protocol semantics. > > And in the present, we will have to insert lots of if conditions > and such if the client does not support memfds. Moreover, the fd > sending will be bidirectional. Three fds from the server if memfd > are supported [1 srbchannel, 1 srbchannel mempool, 1 global > mempool], and one fd from the client [audio data mempool]. And if > memfds are not supported, then 1 fd from the server and zero from > the client. This really complicates the protocol _a lot_ :-( > > The current situation does not have this limitation at all.. Any > endpoint can create a shared mempool and send memblock references > to the other end. This preserves the dynamicism inherent in the > protocol and does not artificially limit it further. -- David Henningsson, Canonical Ltd. https://launchpad.net/~diwic