Skip to content

Commit

Permalink
fw_fcp: record history of node ID for transaction peer
Browse files Browse the repository at this point in the history
Hinawa.FwFcp is responsible for delivery of transaction response from
peer node to application by Hinawa.FwFcp::responded signal. However,
when several nodes connecting to the same 1394 OHCI hardware, current
implementation allows application to dispatch the signal in the response
from non-peer node. The check of generation and src_node_id parameters
of response is useful to the delivery, but the change of bus topology
is concerned since Linux FireWire subsystem has no guarantee the order
of events for bus-reset and asynchronous request for transaction
response. It is possible to dispatch the request event before handling
the bus-reset event

As a workaround, this commit adds history of node ID for transaction
peer to filter out the transaction response from non-peer node. As a
result, the signal is emitted exactly for the response transaction from
the peer node.

Signed-off-by: Takashi Sakamoto <[email protected]>
  • Loading branch information
takaswie committed Aug 16, 2023
1 parent 8ffa5a4 commit ac54f76
Showing 1 changed file with 149 additions and 3 deletions.
152 changes: 149 additions & 3 deletions src/fw_fcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,19 @@ enum avc_status {
AVC_STATUS_INTERIM = 0x0f,
};

#define NODE_HISTORY_RECORD_COUNT 10

struct node_record {
guint generation;
guint src_node_id;
};

struct node_history {
GMutex record_mutex;
struct node_record records[NODE_HISTORY_RECORD_COUNT];
struct node_record *cursor;
};

struct waiter;
LIST_HEAD(waiter_entries, waiter);

Expand All @@ -74,7 +87,10 @@ typedef struct {

struct waiter_entries transactions;
GMutex transactions_mutex;

struct node_history history;
} HinawaFwFcpPrivate;

G_DEFINE_TYPE_WITH_PRIVATE(HinawaFwFcp, hinawa_fw_fcp, HINAWA_TYPE_FW_RESP)

/* This object has one property. */
Expand Down Expand Up @@ -180,11 +196,14 @@ static void hinawa_fw_fcp_class_init(HinawaFwFcpClass *klass)
3, G_TYPE_UINT, G_TYPE_POINTER, G_TYPE_UINT);
}

static void node_history_init(struct node_history *history);

static void hinawa_fw_fcp_init(HinawaFwFcp *self)
{
HinawaFwFcpPrivate *priv = hinawa_fw_fcp_get_instance_private(self);

g_mutex_init(&priv->transactions_mutex);
node_history_init(&priv->history);
}

/**
Expand Down Expand Up @@ -486,6 +505,86 @@ gboolean hinawa_fw_fcp_avc_transaction(HinawaFwFcp *self, const guint8 *cmd, gsi
tstamp, timeout_ms, error);
}

struct node_record_iterator {
const struct node_record *records;
int cursor;
};

static void node_record_iterator_init(struct node_record_iterator *iter,
const struct node_history *history)
{
iter->records = history->records;
iter->cursor = 0;
}

static const struct node_record *node_record_iterator_next(struct node_record_iterator *iter)
{
const struct node_record *record;

if (iter->cursor >= NODE_HISTORY_RECORD_COUNT)
return NULL;

record = iter->records + iter->cursor;
++iter->cursor;

return record;
}

static void node_history_init(struct node_history *history)
{
g_mutex_init(&history->record_mutex);
}

static void node_history_reset(struct node_history *history)
{
memset(history->records, 0, sizeof(history->records));

history->cursor = history->records;
}

static void node_history_lock(struct node_history *history)
{
g_mutex_lock(&history->record_mutex);
}

static void node_history_unlock(struct node_history *history)
{
g_mutex_unlock(&history->record_mutex);
}

static const struct node_record *node_history_find_record(const struct node_history *history,
const struct node_record *target)
{
struct node_record_iterator iter;
const struct node_record *record;

node_record_iterator_init(&iter, history);
while ((record = node_record_iterator_next(&iter))) {
if (!memcmp(record, target, sizeof(*record)))
return record;
}

return NULL;
}

static void node_history_insert_record(struct node_history *history,
const struct node_record *record)
{
if (node_history_find_record(history, record) == NULL) {
*history->cursor = *record;

++history->cursor;
if (history->cursor >= history->records + NODE_HISTORY_RECORD_COUNT)
history->cursor = history->records;
}
}

static gboolean node_history_detect_record(struct node_history *history,
const struct node_record *record)
{
return node_history_find_record(history, record) != NULL;
}

static HinawaFwRcode handle_requested_signal(HinawaFwResp *resp, HinawaFwTcode tcode, guint64 offset,
guint src_node_id, guint dst_node_id, guint card_id,
guint generation, guint tstamp,
Expand All @@ -495,8 +594,36 @@ static HinawaFwRcode handle_requested_signal(HinawaFwResp *resp, HinawaFwTcode t
HinawaFwFcpPrivate *priv = hinawa_fw_fcp_get_instance_private(self);

if (offset == FCP_RESPOND_ADDR && tcode == HINAWA_FW_TCODE_WRITE_BLOCK_REQUEST &&
card_id == priv->card_id)
g_signal_emit(self, fw_fcp_sigs[FW_FCP_SIG_TYPE_RESPONDED], 0, tstamp, frame, length);
card_id == priv->card_id) {
const struct node_record record = {
.generation = generation,
.src_node_id = src_node_id,
};
gboolean recorded = FALSE;

node_history_lock(&priv->history);
recorded = node_history_detect_record(&priv->history, &record);
if (!recorded) {
struct node_record current;

// NOTE: for the case that the event of bus update is not handled yet.
g_object_get(priv->node,
"generation", &current.generation,
"node-id", &current.src_node_id,
NULL);
if (current.generation != record.generation)
node_history_insert_record(&priv->history, &current);

recorded = node_history_detect_record(&priv->history, &record);
}
node_history_unlock(&priv->history);

// Emit the event only when the source node is the target node.
if (recorded) {
g_signal_emit(self, fw_fcp_sigs[FW_FCP_SIG_TYPE_RESPONDED], 0,
tstamp, frame, length);
}
}

// MEMO: Linux firewire subsystem already send response subaction to finish the transaction,
// thus the rcode is just ignored.
Expand All @@ -508,6 +635,7 @@ static void handle_bus_update_signal(HinawaFwNode *node, HinawaFwFcp *self)
{
HinawaFwFcpPrivate *priv = hinawa_fw_fcp_get_instance_private(self);
struct waiter *w;
struct node_record record;

g_mutex_lock(&priv->transactions_mutex);

Expand All @@ -519,6 +647,12 @@ static void handle_bus_update_signal(HinawaFwNode *node, HinawaFwFcp *self)
}

g_mutex_unlock(&priv->transactions_mutex);

g_object_get(node, "generation", &record.generation, "node-id", &record.src_node_id, NULL);

node_history_lock(&priv->history);
node_history_insert_record(&priv->history, &record);
node_history_unlock(&priv->history);
}

/**
Expand All @@ -544,14 +678,26 @@ gboolean hinawa_fw_fcp_bind(HinawaFwFcp *self, HinawaFwNode *node, GError **erro
priv = hinawa_fw_fcp_get_instance_private(self);

if (priv->node == NULL) {
struct node_record record;

g_mutex_lock(&priv->transactions_mutex);
LIST_INIT(&priv->transactions);
g_mutex_unlock(&priv->transactions_mutex);

if (!hinawa_fw_resp_reserve(HINAWA_FW_RESP(self), node, FCP_RESPOND_ADDR,
FCP_MAXIMUM_FRAME_BYTES, error))
return FALSE;
g_object_get(node, "card-id", &priv->card_id, NULL);
g_object_get(node,
"card-id", &priv->card_id,
"generation", &record.generation,
"node-id", &record.src_node_id,
NULL);

node_history_lock(&priv->history);
node_history_reset(&priv->history);
node_history_insert_record(&priv->history, &record);
node_history_unlock(&priv->history);

priv->bus_update_handler_id =
g_signal_connect(node, "bus-update",
G_CALLBACK(handle_bus_update_signal), self);
Expand Down

0 comments on commit ac54f76

Please sign in to comment.