libcamera: control_serializer: Separate the handles space
Two independent instances of the ControlSerializer class are in use at the IPC boundaries, one in the Proxy class that serializes data from the pipeline handler to the IPA, and one in the ProxyWorker which serializes data in the opposite direction. Each instance operates autonomously, without any centralized point of control, and each one assigns a numerical handle to each ControlInfoMap it serializes. This creates a risk of potential collision on the handle values, as both instances will use the same numerical space and are not aware of what handles has been already used by the instance "on the other side". To fix that, partition the handles numerical space by initializing the control serializer with a seed according to the role of the component that creates the serializer and increment the handle number by 2, to avoid any collision risk. While this is temporary and rather hacky solution, it solves an issue with isolated IPA modules without too much complexity added. Signed-off-by: Jacopo Mondi <jacopo@jmondi.org> Reviewed-by: Paul Elder <paul.elder@ideasonboard.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
This commit is contained in:
parent
23c2b8a9ca
commit
11e9d19288
6 changed files with 75 additions and 13 deletions
|
@ -20,7 +20,12 @@ class ByteStreamBuffer;
|
||||||
class ControlSerializer
|
class ControlSerializer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ControlSerializer();
|
enum class Role {
|
||||||
|
Proxy,
|
||||||
|
Worker
|
||||||
|
};
|
||||||
|
|
||||||
|
ControlSerializer(Role role);
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
|
@ -47,6 +52,7 @@ private:
|
||||||
ControlInfo loadControlInfo(ControlType type, ByteStreamBuffer &buffer);
|
ControlInfo loadControlInfo(ControlType type, ByteStreamBuffer &buffer);
|
||||||
|
|
||||||
unsigned int serial_;
|
unsigned int serial_;
|
||||||
|
unsigned int serialSeed_;
|
||||||
std::vector<std::unique_ptr<ControlId>> controlIds_;
|
std::vector<std::unique_ptr<ControlId>> controlIds_;
|
||||||
std::vector<std::unique_ptr<ControlIdMap>> controlIdMaps_;
|
std::vector<std::unique_ptr<ControlIdMap>> controlIdMaps_;
|
||||||
std::map<unsigned int, ControlInfoMap> infoMaps_;
|
std::map<unsigned int, ControlInfoMap> infoMaps_;
|
||||||
|
|
|
@ -62,6 +62,14 @@ LOG_DEFINE_CATEGORY(Serializer)
|
||||||
* corresponding ControlInfoMap handle in the binary data, and when
|
* corresponding ControlInfoMap handle in the binary data, and when
|
||||||
* deserializing to retrieve the corresponding ControlInfoMap.
|
* deserializing to retrieve the corresponding ControlInfoMap.
|
||||||
*
|
*
|
||||||
|
* As independent ControlSerializer instances are used on both sides of the IPC
|
||||||
|
* boundary, and the two instances operate without a shared point of control,
|
||||||
|
* there is a potential risk of collision of the numerical handles assigned to
|
||||||
|
* each serialized ControlInfoMap. For this reason the control serializer is
|
||||||
|
* initialized with a seed and the handle is incremented by 2, so that instances
|
||||||
|
* initialized with a different seed operate on a separate numerical space,
|
||||||
|
* avoiding any collision risk.
|
||||||
|
*
|
||||||
* In order to perform those tasks, the serializer keeps an internal state that
|
* In order to perform those tasks, the serializer keeps an internal state that
|
||||||
* needs to be properly populated. This mechanism requires the ControlInfoMap
|
* needs to be properly populated. This mechanism requires the ControlInfoMap
|
||||||
* corresponding to a ControlList to have been serialized or deserialized
|
* corresponding to a ControlList to have been serialized or deserialized
|
||||||
|
@ -77,9 +85,45 @@ LOG_DEFINE_CATEGORY(Serializer)
|
||||||
* proceed with care to avoid stale references.
|
* proceed with care to avoid stale references.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
ControlSerializer::ControlSerializer()
|
/**
|
||||||
: serial_(0)
|
* \enum ControlSerializer::Role
|
||||||
|
* \brief Define the role of the IPC component using the control serializer
|
||||||
|
*
|
||||||
|
* The role of the component that creates the serializer is used to initialize
|
||||||
|
* the handles numerical space.
|
||||||
|
*
|
||||||
|
* \var ControlSerializer::Role::Proxy
|
||||||
|
* \brief The control serializer is used by the IPC Proxy classes
|
||||||
|
*
|
||||||
|
* \var ControlSerializer::Role::Worker
|
||||||
|
* \brief The control serializer is used by the IPC ProxyWorker classes
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Construct a new ControlSerializer
|
||||||
|
* \param[in] role The role of the IPC component using the serializer
|
||||||
|
*/
|
||||||
|
ControlSerializer::ControlSerializer(Role role)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* Initialize the handle numerical space using the role of the
|
||||||
|
* component that created the instance.
|
||||||
|
*
|
||||||
|
* Instances initialized for a different role will use a different
|
||||||
|
* numerical handle space, avoiding any collision risk when, in example,
|
||||||
|
* two instances of the ControlSerializer class are used at the IPC
|
||||||
|
* boundaries.
|
||||||
|
*
|
||||||
|
* Start counting handles from '1' as '0' is a special value used as
|
||||||
|
* place holder when serializing lists that do not have a ControlInfoMap
|
||||||
|
* associated (in example list of libcamera controls::controls).
|
||||||
|
*
|
||||||
|
* \todo This is a temporary hack and should probably be better
|
||||||
|
* engineered, but for the time being it avoids collisions on the handle
|
||||||
|
* value when using IPC.
|
||||||
|
*/
|
||||||
|
serialSeed_ = role == Role::Proxy ? 1 : 2;
|
||||||
|
serial_ = serialSeed_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -90,7 +134,7 @@ ControlSerializer::ControlSerializer()
|
||||||
*/
|
*/
|
||||||
void ControlSerializer::reset()
|
void ControlSerializer::reset()
|
||||||
{
|
{
|
||||||
serial_ = 0;
|
serial_ = serialSeed_;
|
||||||
|
|
||||||
infoMapHandles_.clear();
|
infoMapHandles_.clear();
|
||||||
infoMaps_.clear();
|
infoMaps_.clear();
|
||||||
|
@ -200,10 +244,10 @@ int ControlSerializer::serialize(const ControlInfoMap &infoMap,
|
||||||
else
|
else
|
||||||
idMapType = IPA_CONTROL_ID_MAP_V4L2;
|
idMapType = IPA_CONTROL_ID_MAP_V4L2;
|
||||||
|
|
||||||
/* Prepare the packet header, assign a handle to the ControlInfoMap. */
|
/* Prepare the packet header. */
|
||||||
struct ipa_controls_header hdr;
|
struct ipa_controls_header hdr;
|
||||||
hdr.version = IPA_CONTROLS_FORMAT_VERSION;
|
hdr.version = IPA_CONTROLS_FORMAT_VERSION;
|
||||||
hdr.handle = ++serial_;
|
hdr.handle = serial_;
|
||||||
hdr.entries = infoMap.size();
|
hdr.entries = infoMap.size();
|
||||||
hdr.size = sizeof(hdr) + entriesSize + valuesSize;
|
hdr.size = sizeof(hdr) + entriesSize + valuesSize;
|
||||||
hdr.data_offset = sizeof(hdr) + entriesSize;
|
hdr.data_offset = sizeof(hdr) + entriesSize;
|
||||||
|
@ -211,6 +255,15 @@ int ControlSerializer::serialize(const ControlInfoMap &infoMap,
|
||||||
|
|
||||||
buffer.write(&hdr);
|
buffer.write(&hdr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Increment the handle for the ControlInfoMap by 2 to keep the handles
|
||||||
|
* numerical space partitioned between instances initialized for a
|
||||||
|
* different role.
|
||||||
|
*
|
||||||
|
* \sa ControlSerializer::Role
|
||||||
|
*/
|
||||||
|
serial_ += 2;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Serialize all entries.
|
* Serialize all entries.
|
||||||
* \todo Serialize the control name too
|
* \todo Serialize the control name too
|
||||||
|
|
|
@ -30,8 +30,8 @@ protected:
|
||||||
|
|
||||||
int run() override
|
int run() override
|
||||||
{
|
{
|
||||||
ControlSerializer serializer;
|
ControlSerializer serializer(ControlSerializer::Role::Proxy);
|
||||||
ControlSerializer deserializer;
|
ControlSerializer deserializer(ControlSerializer::Role::Worker);
|
||||||
|
|
||||||
std::vector<uint8_t> infoData;
|
std::vector<uint8_t> infoData;
|
||||||
std::vector<uint8_t> listData;
|
std::vector<uint8_t> listData;
|
||||||
|
|
|
@ -162,7 +162,7 @@ private:
|
||||||
|
|
||||||
int testControls()
|
int testControls()
|
||||||
{
|
{
|
||||||
ControlSerializer cs;
|
ControlSerializer cs(ControlSerializer::Role::Proxy);
|
||||||
|
|
||||||
const ControlInfoMap &infoMap = camera_->controls();
|
const ControlInfoMap &infoMap = camera_->controls();
|
||||||
ControlList list = generateControlList(infoMap);
|
ControlList list = generateControlList(infoMap);
|
||||||
|
@ -195,7 +195,7 @@ private:
|
||||||
|
|
||||||
int testVector()
|
int testVector()
|
||||||
{
|
{
|
||||||
ControlSerializer cs;
|
ControlSerializer cs(ControlSerializer::Role::Proxy);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We don't test FileDescriptor serdes because it dup()s, so we
|
* We don't test FileDescriptor serdes because it dup()s, so we
|
||||||
|
@ -265,7 +265,7 @@ private:
|
||||||
|
|
||||||
int testMap()
|
int testMap()
|
||||||
{
|
{
|
||||||
ControlSerializer cs;
|
ControlSerializer cs(ControlSerializer::Role::Proxy);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Realistically, only string and integral keys.
|
* Realistically, only string and integral keys.
|
||||||
|
|
|
@ -46,7 +46,8 @@ namespace {{ns}} {
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
|
||||||
{{proxy_name}}::{{proxy_name}}(IPAModule *ipam, bool isolate)
|
{{proxy_name}}::{{proxy_name}}(IPAModule *ipam, bool isolate)
|
||||||
: IPAProxy(ipam), isolate_(isolate), seq_(0)
|
: IPAProxy(ipam), isolate_(isolate),
|
||||||
|
controlSerializer_(ControlSerializer::Role::Proxy), seq_(0)
|
||||||
{
|
{
|
||||||
LOG(IPAProxy, Debug)
|
LOG(IPAProxy, Debug)
|
||||||
<< "initializing {{module_name}} proxy: loading IPA from "
|
<< "initializing {{module_name}} proxy: loading IPA from "
|
||||||
|
|
|
@ -53,7 +53,9 @@ class {{proxy_worker_name}}
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
{{proxy_worker_name}}()
|
{{proxy_worker_name}}()
|
||||||
: ipa_(nullptr), exit_(false) {}
|
: ipa_(nullptr),
|
||||||
|
controlSerializer_(ControlSerializer::Role::Worker),
|
||||||
|
exit_(false) {}
|
||||||
|
|
||||||
~{{proxy_worker_name}}() {}
|
~{{proxy_worker_name}}() {}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue