Skip to content

throttleRecord: write link-status flags for the correct link#29

Open
physwkim wants to merge 1 commit into
epics-modules:masterfrom
physwkim:fix/throttle-wrong-link-status
Open

throttleRecord: write link-status flags for the correct link#29
physwkim wants to merge 1 commit into
epics-modules:masterfrom
physwkim:fix/throttle-wrong-link-status

Conversation

@physwkim

Copy link
Copy Markdown
Contributor

throttleRecord: write link-status flags for the correct link

Summary

throttleRecord keeps two per-link diagnostic status fields — outLinkStat
for the output (OUT) link and sinpLinkStat for the sync-input (SINP)
link. Each holds NOT_CA_LINK / CA_LINK_OK / CA_LINK_NOT_OK and is meant
to reflect the connection state of its own link. init_record and
checkLink already select a per-link short *plinkStat (pointing at
outLinkStat or sinpLinkStat) and write through it. Two other places
violate the "each flag reflects its own link" invariant: one writes the wrong
field by name, the other reads a stale value carried over from the previous
loop iteration. This PR fixes both so every write targets the link actually
being processed.

These are wrong-variable / stale-variable writes into diagnostic
link-status flags. They are not a crash, memory-safety, or data-loss bug —
graded Low impact (see below).

Site 1 — special(), .OUT/.SINP change naming an off-IOC PV

When an operator changes OUT or SINP to a PV that is not on this IOC, the
"pv is not on this ioc" branch scheduled a connection-status callback and set:

prpvt->outLinkStat = CA_LINK_NOT_OK;   /* always OUT, even when SINP changed */

special() already dispatches plink/plinkValid on
fieldIndex == throttleRecordOUT vs throttleRecordSINP, but it had no
plinkStat in that dispatch, so the hardcoded write always marked the OUT
link — even for a .SINP change. Result: a .SINP change to an off-IOC PV
flagged outLinkStat (the wrong link) and never set sinpLinkStat.

Fix: add short *plinkStat to the existing field dispatch (pointing at
&prpvt->outLinkStat for OUT, &prpvt->sinpLinkStat for SINP, mirroring
init_record/checkLink) and write *plinkStat = CA_LINK_NOT_OK;.

Site 2 — checkLink(), stale caLink/caLinkNc across the link loop

checkLink loops for (i = 0; i < 2; i++) over the OUT (i==0) and SINP
(i==1) links, and at the end of each iteration writes that link's status:

if (caLinkNc)      *plinkStat = CA_LINK_NOT_OK;
else if (caLink)   *plinkStat = CA_LINK_OK;
else               *plinkStat = NOT_CA_LINK;

But caLink and caLinkNc were declared once, outside the loop and were
only ever assigned inside if (plink->type == CA_LINK). On the i==1
(SINP) pass, if SINP is not a CA link that block is skipped, so the status
write used the stale caLink/caLinkNc values left over from the i==0
(OUT) iteration. A disconnected CA OUT link (caLinkNc == 1) therefore forced
sinpLinkStat = CA_LINK_NOT_OK even when SINP was perfectly fine (e.g. a
constant or a local link).

Fix: move the caLink/caLinkNc declarations inside the loop body so each
iteration starts from a clean 0/0 by construction — no carryover is
representable. (Equivalent to resetting both at the top of each iteration, but
scoping them per-iteration makes the reset structural rather than a manual
step a future edit could drop.)

Impact — Low

Both bugs land only in the diagnostic link-status flags, not in the record's
value or output:

  • outLinkStat self-heals: the next checkLink pass (scheduled connection
    poll, or a process() on a CA OUT link) recomputes it from the real
    connection state.
  • Site 1: an operator who points .SINP at an off-IOC PV briefly sees the
    OUT link flagged not-connected instead of SINP, until the next
    checkLink corrects both — a misleading diagnostic, not a functional fault.
  • Site 2: a disconnected OUT link can make checkLink report sinpLinkStat
    as CA_LINK_NOT_OK even when SINP is connected or non-CA — again a wrong
    diagnostic value, corrected on the next pass once SINP's own state is
    evaluated cleanly.

No overstatement intended: neither bug corrupts the throttled value, drops a
callback, or affects link behaviour — only the reported per-link status.

Family check

Anchor: every outLinkStat / sinpLinkStat / plinkStat / caLink /
caLinkNc read and write in stdApp/src/throttleRecord.c.

  • Fixed (this finding, one commit, two sites):
    • special() — added plinkStat to the field dispatch; the off-IOC branch
      now writes *plinkStat instead of the hardcoded prpvt->outLinkStat.
    • checkLink()caLink/caLinkNc scoped per loop iteration.
  • Left unchanged because already correct:
    • init_record() — already selects plinkStat per link and writes
      *plinkStat (both the NOT_CA_LINK init and the off-IOC
      CA_LINK_NOT_OK); untouched.
    • Reads that gate control flow, not link-status writes: the
      init_record schedule check
      (outLinkStat == CA_LINK_NOT_OK || sinpLinkStat == CA_LINK_NOT_OK) and the
      process() check (outLinkStat != NOT_CA_LINKcheckLink) — reads of
      the correct field for their purpose; no wrong-link write.
    • The checkLink end-of-loop writes through *plinkStat were already
      per-link correct; only their caLink/caLinkNc inputs were stale.

No third site writes the wrong link.

Testing

Not compiled locally — no EPICS support tree / std build is configured in this
worktree. The change is a source-only edit relying on CI to compile. Expected
before/after behaviour:

  • caput <rec>.SINP <off-ioc-pv> now flags sinpLinkStat
    (was outLinkStat).
  • With a disconnected CA OUT link, checkLink no longer forces
    sinpLinkStat = CA_LINK_NOT_OK when SINP is connected or non-CA.
  • The OUT link's own status is unchanged (it was already correct on both
    paths).

special() marked outLinkStat unconditionally in the off-IOC branch even
for a .SINP change, and checkLink() carried caLink/caLinkNc across the
OUT/SINP loop so SINP's status could be written from OUT's connection
state. Select plinkStat per field in special(), and scope caLink/caLinkNc
per loop iteration so each link's status is computed only from its own
link. init_record was already correct and is left untouched.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant