| 0/0 |
2026/04/04 19:30 |
flow |
assessment-kcsan |
33m
Results: map[Benign:false Confident:true Explanation:The KCSAN data race report identifies a race between `bcm_can_tx()` (reading)
and `bcm_tx_setup()` (writing) on the CAN frame data within a `struct bcm_op`.
### Race Summary
- **Function A (`bcm_can_tx`)**: Reads a CAN frame from `op->frames` to copy it
into an `sk_buff` for transmission.
- **Function B (`bcm_tx_setup`)**: Updates the CAN frames in `op->frames` using
`memcpy_from_msg()` when a user sends a `TX_SETUP` command to update an existing
broadcast manager (BCM) operation.
- **Variable**: `op->frames` (specifically the content of a `struct canfd_frame`
or `struct can_frame` at a specific index).
### Synchronization Context
- `bcm_tx_setup()` is called from `bcm_sendmsg()`, which holds the socket lock
(`lock_sock(sk)`). This protects against concurrent `sendmsg()` calls on the
same socket but does not protect against the hrtimer that triggers
`bcm_can_tx()`.
- `bcm_can_tx()` is called from `bcm_tx_timeout_handler()` (an hrtimer handler)
or directly from `bcm_tx_setup()`. When called from the timer, it runs in
softirq context.
- Although `bcm_can_tx()` takes `op->bcm_tx_lock` to safely calculate the frame
pointer (`cf`), it releases the lock before calling `skb_put_data(skb, cf,
op->cfsiz)`.
- `bcm_tx_setup()` does **not** hold `op->bcm_tx_lock` while calling
`memcpy_from_msg()` to update the frame content.
### Final Classification: **HARMFUL**
### Failure Reasoning
The race leads to **data corruption of the transmitted CAN frame**. Since CAN FD
frames can be up to 72 bytes (`struct canfd_frame`), a concurrent `memcpy` (via
`memcpy_from_msg` and `skb_put_data`) is not atomic. This results in a "torn"
update where the transmitted frame contains a mixture of old and new data (e.g.,
a new `can_id` with old payload data).
In safety-critical environments where CAN is typically used (automotive,
industrial), sending inconsistent or "phantom" frames that never existed in a
valid state is unacceptable. Furthermore, `bcm_tx_setup()` validates the frame
length (`cf->len`) **after** the copy, meaning an invalid length could be
temporarily visible to the transmitter.
### Failure Sequencing Example
| CPU 0 (`bcm_tx_setup`) | CPU 1 (`bcm_can_tx`) |
| :--- | :--- |
| | `spin_lock_bh(&op->bcm_tx_lock)` |
| | `cf = op->frames + ...` |
| | `spin_unlock_bh(&op->bcm_tx_lock)` |
| `memcpy_from_msg(cf, ...)` | |
| (Starts copying 72 bytes) | |
| (Copies first 32 bytes, e.g., `can_id`) | |
| | `skb_put_data(skb, cf, 72)` |
| | (Copies 72 bytes from `cf` to `skb`) |
| | (Sees **new** `can_id` but **old** payload) |
| (Finishes copying remaining 40 bytes) | |
| | `can_send(skb)` |
| | **-> Corrupted frame sent on CAN bus!** |
### Recommended Fix
To ensure atomicity, `bcm_tx_setup()` should copy the user data to a temporary
kernel buffer first, then perform the update to `op->frames` while holding
`op->bcm_tx_lock`. Correspondingly, `bcm_can_tx()` must hold `op->bcm_tx_lock`
while copying the frame data into the `skb`.]
|
| 1/1 |
2026/04/04 19:30 |
action |
kernel-checkouter |
1m
Results: map[KernelSrc:/app/workdir/cache/src/6a060876177b1263c80b858b91e54133c87c3866]
|
| 2/1 |
2026/04/04 19:31 |
action |
kernel-builder |
16m
Results: map[KernelObj:/app/workdir/cache/build/e6f66ab2439f15e4b93d5dccebe0338317e19294]
|
| 3/1 |
2026/04/04 19:47 |
action |
codesearch-prepare |
14m
Results: map[Index:codesearch-index]
|
| 4/1 |
2026/04/04 20:02 |
agent |
expert |
1m
Model:
Results: map[Benign:false Confident:true]
Instruction: # KCSAN Data Race Severity Analysis Protocol
You are an expert Linux kernel concurrency engineer analyzing a Kernel
Concurrency Sanitizer (KCSAN) data race report to determine if it is
**BENIGN** or **HARMFUL**.
## 1. CLASSIFICATIONS
### **BENIGN (Truly Benign)**
The logic is sound and structurally tolerant to compiler optimizations or
stale/torn reads.
- **Diagnostics/Stats:** Reads used only for '/proc', '/sys', counters, or
'pr_info'.
- **Heuristic Hints:** A "hint" flag where an old value only causes a
slightly delayed update or a sub-optimal but safe fast-path.
- **Single-Writer Flag Updates:** A single writer updating flags where the
concurrent read is a simple bitwise check (e.g., 'flags & MASK'). These are
historically tolerated, assuming neither "Fused Accesses" nor "Ordering
Violations" are relevant in this context.
- **Marked Reloads:** A load feeding into a 'cmpxchg()' loop or checked
against a later 'READ_ONCE()' reload.
- **Safe Overwrites:** Writing the same value already present.
### **HARMFUL (Logic Bug or Marking Required)**
The race causes incorrect behavior due to a synchronization failure or
because missing annotations allow the compiler to break the algorithm.
**Marking Required for Correctness:**
The algorithm is logically sound but requires annotations ('READ_ONCE()',
'WRITE_ONCE()', 'smp_load_acquire()', 'smp_store_release()', etc.) to be safe.
- **Fused Accesses:** The compiler might merge accesses or hoist a load out
of a loop, breaking polling/wait loops (livelocks).
- **Torn Accesses:** A large access (e.g., 64-bit on 32-bit arch) might be
split into multiple non-atomic accesses. Note that 'READ_ONCE()' does **not**
guarantee atomicity for 64-bit variables on 32-bit architectures.
- **Ordering Violations:** The race breaks a "happens-before" relationship
(requires primitives with implied or explicit memory barriers).
**Logic Bugs:**
A fundamental synchronization failure. Marking accesses will **not** fix it;
the logic itself must change.
- **Pointers/Lifecycle:** The racing variable is a pointer being dereferenced
or a refcount governing object lifecycle (Use-After-Free risk).
- **Control Flow:** The variable guards a critical section, memory allocation,
or hardware command.
- **Bitfields:** Concurrent writes to different bits in the same word.
Compilers often use non-atomic read-modify-write sequences, meaning a
write to 'bit_A' can "clobber" a concurrent write to 'bit_B'. However,
do not blindly assume all bitfield accesses are harmful; you must prove
that a concurrent write actually clobbers another in a way that breaks
logic.
- **Complex Structures:** Races on shared lists, trees, or hashmaps.
- **Lossy Updates:** Concurrent plain RMW operations (e.g., 'var++') on
non-diagnostic variables where every increment must be preserved.
- **State Machines:** Races allowing a state machine to bypass transitions
or enter an invalid state.
- **Adjacent Unsynchronized Operations:** Consider races happening at the
same time. For example, if both threads execute 'struct->has_elements = true;
list_add(node, &struct->list);', the race on 'has_elements' implies an
adjacent race on 'list_head', which is HARMFUL.
## 2. RESEARCH & ANALYSIS WORKFLOW
1. **Locate the Race:** Find the exact variables and functions in the stack
traces. **Use codesearch tools to read the actual source code and
confirm all assumptions.** Do not speculate about hypothetical compiler
behaviors or theoretical dangers (e.g., dismissing something as
"fundamentally unsafe") without tracing the actual data flow to a crash.
2. **Contextualize:** Identify held locks, RCU sections, or interrupt
contexts. Explain why the race is possible (e.g., "Thread A holds 'lock',
but Thread B is a lockless reader").
3. **Data Flow:** Follow the racing variable into its subsequent uses. If
the reader sees a stale or torn value, what branch is taken? Does it lead
to a pointer dereference or 'BUG_ON()'? **You must prove harm via this
data flow.**
4. **Failure Sequencing (if HARMFUL):** Construct a two-column execution trace
demonstrating the failure.
*Example format:*
CPU0 CPU1
function_a()
// lockless read
ptr = global_ptr
<PREEMPT>
function_b()
lock(&my_lock)
global_ptr = NULL
unlock(&my_lock)
kfree(ptr)
if (ptr)
*ptr = 1; // -> Use-After-Free!
## 3. OUTPUT FORMAT
- **Race Summary:** '[Function A]' vs '[Function B]' on '[Variable/Field]'.
- **Synchronization context:** List held locks and explain the lack of mutual
exclusion.
- **Final Classification:** **BENIGN** or **HARMFUL**.
**If BENIGN:**
- **Reasoning:** Briefly explain why the race is structurally tolerant to stale
or torn reads.
- **Recommended Annotations:** Suggest appropriate annotations (e.g.,
'READ_ONCE()', 'data_race()').
**If HARMFUL:**
- **Failure Reasoning:** Explain and provide a two-column interleaving showing
exactly how the race leads to a failure such as corruption or kernel crash.
- **Recommended Fix (ONLY if fix is trivial):** Suggest a structural fix (e.g.,
"Hold 'mapping->i_pages' lock", "Convert to 'atomic_t'") or required memory
ordering annotations (e.g., "Wrap in 'READ_ONCE()'", "Use
'smp_load_acquire()'").
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt:
The data race report is:
==================================================================
BUG: KCSAN: data-race in _copy_from_iter / bcm_can_tx
read to 0xffff88810cf825f8 of 72 bytes by interrupt on cpu 1:
skb_put_data include/linux/skbuff.h:2800 [inline]
bcm_can_tx+0x1b3/0x4c0 net/can/bcm.c:327
bcm_tx_timeout_handler+0xe5/0x290 net/can/bcm.c:478
__run_hrtimer kernel/time/hrtimer.c:1785 [inline]
__hrtimer_run_queues+0x218/0x4f0 kernel/time/hrtimer.c:1849
hrtimer_run_softirq+0xe0/0x2c0 kernel/time/hrtimer.c:1866
handle_softirqs+0xb9/0x2a0 kernel/softirq.c:622
__do_softirq kernel/softirq.c:656 [inline]
invoke_softirq kernel/softirq.c:496 [inline]
__irq_exit_rcu+0x39/0xc0 kernel/softirq.c:723
instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1056 [inline]
sysvec_apic_timer_interrupt+0x37/0x80 arch/x86/kernel/apic/apic.c:1056
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:697
write to 0xffff88810cf825f8 of 72 bytes by task 5344 on cpu 0:
instrument_copy_from_user_before include/linux/instrumented.h:147 [inline]
copy_from_user_iter lib/iov_iter.c:66 [inline]
iterate_iovec include/linux/iov_iter.h:52 [inline]
iterate_and_advance2 include/linux/iov_iter.h:304 [inline]
iterate_and_advance include/linux/iov_iter.h:330 [inline]
__copy_from_iter lib/iov_iter.c:261 [inline]
_copy_from_iter+0x258/0xea0 lib/iov_iter.c:272
copy_from_iter include/linux/uio.h:228 [inline]
copy_from_iter_full include/linux/uio.h:245 [inline]
memcpy_from_msg include/linux/skbuff.h:4273 [inline]
bcm_tx_setup+0xae5/0xdd0 net/can/bcm.c:966
bcm_sendmsg+0x357/0x4a0 net/can/bcm.c:1434
sock_sendmsg_nosec net/socket.c:727 [inline]
__sock_sendmsg net/socket.c:742 [inline]
____sys_sendmsg+0x563/0x5b0 net/socket.c:2592
___sys_sendmsg+0x195/0x1e0 net/socket.c:2646
__sys_sendmsg net/socket.c:2678 [inline]
__do_sys_sendmsg net/socket.c:2683 [inline]
__se_sys_sendmsg net/socket.c:2681 [inline]
__x64_sys_sendmsg+0xd4/0x160 net/socket.c:2681
x64_sys_call+0x194c/0x3020 arch/x86/include/generated/asm/syscalls_64.h:47
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x12c/0x370 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Reported by Kernel Concurrency Sanitizer on:
CPU: 0 UID: 0 PID: 5344 Comm: syz.3.483 Not tainted syzkaller #0 PREEMPT(full)
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 02/12/2026
==================================================================
Reply: The KCSAN data race report identifies a race between `bcm_can_tx()` (reading) and `bcm_tx_setup()` (writing) on the CAN frame data within a `struct bcm_op`.
### Race Summary
- **Function A (`bcm_can_tx`)**: Reads a CAN frame from `op->frames` to copy it into an `sk_buff` for transmission.
- **Function B (`bcm_tx_setup`)**: Updates the CAN frames in `op->frames` using `memcpy_from_msg()` when a user sends a `TX_SETUP` command to update an existing broadcast manager (BCM) operation.
- **Variable**: `op->frames` (specifically the content of a `struct canfd_frame` or `struct can_frame` at a specific index).
### Synchronization Context
- `bcm_tx_setup()` is called from `bcm_sendmsg()`, which holds the socket lock (`lock_sock(sk)`). This protects against concurrent `sendmsg()` calls on the same socket but does not protect against the hrtimer that triggers `bcm_can_tx()`.
- `bcm_can_tx()` is called from `bcm_tx_timeout_handler()` (an hrtimer handler) or directly from `bcm_tx_setup()`. When called from the timer, it runs in softirq context.
- Although `bcm_can_tx()` takes `op->bcm_tx_lock` to safely calculate the frame pointer (`cf`), it releases the lock before calling `skb_put_data(skb, cf, op->cfsiz)`.
- `bcm_tx_setup()` does **not** hold `op->bcm_tx_lock` while calling `memcpy_from_msg()` to update the frame content.
### Final Classification: **HARMFUL**
### Failure Reasoning
The race leads to **data corruption of the transmitted CAN frame**. Since CAN FD frames can be up to 72 bytes (`struct canfd_frame`), a concurrent `memcpy` (via `memcpy_from_msg` and `skb_put_data`) is not atomic. This results in a "torn" update where the transmitted frame contains a mixture of old and new data (e.g., a new `can_id` with old payload data).
In safety-critical environments where CAN is typically used (automotive, industrial), sending inconsistent or "phantom" frames that never existed in a valid state is unacceptable. Furthermore, `bcm_tx_setup()` validates the frame length (`cf->len`) **after** the copy, meaning an invalid length could be temporarily visible to the transmitter.
### Failure Sequencing Example
| CPU 0 (`bcm_tx_setup`) | CPU 1 (`bcm_can_tx`) |
| :--- | :--- |
| | `spin_lock_bh(&op->bcm_tx_lock)` |
| | `cf = op->frames + ...` |
| | `spin_unlock_bh(&op->bcm_tx_lock)` |
| `memcpy_from_msg(cf, ...)` | |
| (Starts copying 72 bytes) | |
| (Copies first 32 bytes, e.g., `can_id`) | |
| | `skb_put_data(skb, cf, 72)` |
| | (Copies 72 bytes from `cf` to `skb`) |
| | (Sees **new** `can_id` but **old** payload) |
| (Finishes copying remaining 40 bytes) | |
| | `can_send(skb)` |
| | **-> Corrupted frame sent on CAN bus!** |
### Recommended Fix
To ensure atomicity, `bcm_tx_setup()` should copy the user data to a temporary kernel buffer first, then perform the update to `op->frames` while holding `op->bcm_tx_lock`. Correspondingly, `bcm_can_tx()` must hold `op->bcm_tx_lock` while copying the frame data into the `skb`.
|
| 5/2 |
2026/04/04 20:02 |
llm |
expert |
0m
Model:
Tokens:
input: 5157
output: 86
thoughts: 173
Thoughts: **Analyzing a Data Race in the CAN Bus Driver**
Okay, so the report flags a nasty data race and my instincts are telling me this could be a tricky one. Apparently, we have a conflict between `bcm_can_tx` and `_copy_from_iter` on that memory address: `0xffff88810cf825f8`. The access size is 72 bytes, which isn't huge, but still significant enough to cause real problems.
The key thing is *when* these functions are called. `bcm_can_tx` is called from `bcm_tx_timeout_handler`, which as a hrtimer handler runs within a softirq context. This is something I always keep an eye out for. Softirqs and interrupt contexts are prime suspects in data races. Meanwhile, `_copy_from_iter` gets called from `bcm_tx_setup`, which is itself called from `bcm_sendmsg`. So, we've got a send path and a timeout handler going head-to-head on the same memory region.
My next step is clear: I'm going to dig into the source code, specifically `net/can/bcm.c`. I need to understand the exact access patterns of these functions. I'll start with `bcm_can_tx` and `bcm_tx_setup` to understand how they are interacting. I need to figure out *what* exactly is being read and written to that memory address and under what conditions. Understanding the data flow will be crucial to identifying the root cause of this race and determining the appropriate fix.
|
| 6/2 |
2026/04/04 20:02 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/can/bcm.c IncludeLines:true Name:bcm_can_tx]
Results: map[SourceCode: 288: /*
289: * bcm_can_tx - send the (next) CAN frame to the appropriate CAN interface
290: * of the given bcm tx op
291: */
292: static void bcm_can_tx(struct bcm_op *op)
293: {
294: struct sk_buff *skb;
295: struct can_skb_ext *csx;
296: struct net_device *dev;
297: struct canfd_frame *cf;
298: int err;
299:
300: /* no target device? => exit */
301: if (!op->ifindex)
302: return;
303:
304: /* read currframe under lock protection */
305: spin_lock_bh(&op->bcm_tx_lock);
306: cf = op->frames + op->cfsiz * op->currframe;
307: spin_unlock_bh(&op->bcm_tx_lock);
308:
309: dev = dev_get_by_index(sock_net(op->sk), op->ifindex);
310: if (!dev) {
311: /* RFC: should this bcm_op remove itself here? */
312: return;
313: }
314:
315: skb = alloc_skb(op->cfsiz, gfp_any());
316: if (!skb)
317: goto out;
318:
319: csx = can_skb_ext_add(skb);
320: if (!csx) {
321: kfree_skb(skb);
322: goto out;
323: }
324:
325: csx->can_iif = dev->ifindex;
326:
327: skb_put_data(skb, cf, op->cfsiz);
328:
329: /* send with loopback */
330: skb->dev = dev;
331: can_skb_set_owner(skb, op->sk);
332: err = can_send(skb, 1);
333:
334: /* update currframe and count under lock protection */
335: spin_lock_bh(&op->bcm_tx_lock);
336:
337: if (!err)
338: op->frames_abs++;
339:
340: op->currframe++;
341:
342: /* reached last frame? */
343: if (op->currframe >= op->nframes)
344: op->currframe = 0;
345:
346: if (op->count > 0)
347: op->count--;
348:
349: spin_unlock_bh(&op->bcm_tx_lock);
350: out:
351: dev_put(dev);
352: }
SourceFile:net/can/bcm.c]
|
| 7/2 |
2026/04/04 20:02 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/can/bcm.c IncludeLines:true Name:bcm_tx_setup]
Results: map[SourceCode: 925: /*
926: * bcm_tx_setup - create or update a bcm tx op (for bcm_sendmsg)
927: */
928: static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
929: int ifindex, struct sock *sk)
930: {
931: struct bcm_sock *bo = bcm_sk(sk);
932: struct bcm_op *op;
933: struct canfd_frame *cf;
934: unsigned int i;
935: int err;
936:
937: /* we need a real device to send frames */
938: if (!ifindex)
939: return -ENODEV;
940:
941: /* check nframes boundaries - we need at least one CAN frame */
942: if (msg_head->nframes < 1 || msg_head->nframes > MAX_NFRAMES)
943: return -EINVAL;
944:
945: /* check timeval limitations */
946: if ((msg_head->flags & SETTIMER) && bcm_is_invalid_tv(msg_head))
947: return -EINVAL;
948:
949: /* check the given can_id */
950: op = bcm_find_op(&bo->tx_ops, msg_head, ifindex);
951: if (op) {
952: /* update existing BCM operation */
953:
954: /*
955: * Do we need more space for the CAN frames than currently
956: * allocated? -> This is a _really_ unusual use-case and
957: * therefore (complexity / locking) it is not supported.
958: */
959: if (msg_head->nframes > op->nframes)
960: return -E2BIG;
961:
962: /* update CAN frames content */
963: for (i = 0; i < msg_head->nframes; i++) {
964:
965: cf = op->frames + op->cfsiz * i;
966: err = memcpy_from_msg((u8 *)cf, msg, op->cfsiz);
967:
968: if (op->flags & CAN_FD_FRAME) {
969: if (cf->len > 64)
970: err = -EINVAL;
971: } else {
972: if (cf->len > 8)
973: err = -EINVAL;
974: }
975:
976: if (err < 0)
977: return err;
978:
979: if (msg_head->flags & TX_CP_CAN_ID) {
980: /* copy can_id into frame */
981: cf->can_id = msg_head->can_id;
982: }
983: }
984: op->flags = msg_head->flags;
985:
986: /* only lock for unlikely count/nframes/currframe changes */
987: if (op->nframes != msg_head->nframes ||
988: op->flags & TX_RESET_MULTI_IDX ||
989: op->flags & SETTIMER) {
990:
991: spin_lock_bh(&op->bcm_tx_lock);
992:
993: if (op->nframes != msg_head->nframes ||
994: op->flags & TX_RESET_MULTI_IDX) {
995: /* potentially update changed nframes */
996: op->nframes = msg_head->nframes;
997: /* restart multiple frame transmission */
998: op->currframe = 0;
999: }
1000:
1001: if (op->flags & SETTIMER)
1002: op->count = msg_head->count;
1003:
1004: spin_unlock_bh(&op->bcm_tx_lock);
1005: }
1006:
1007: } else {
1008: /* insert new BCM operation for the given can_id */
1009:
1010: op = kzalloc(OPSIZ, GFP_KERNEL);
1011: if (!op)
1012: return -ENOMEM;
1013:
1014: spin_lock_init(&op->bcm_tx_lock);
1015: op->can_id = msg_head->can_id;
1016: op->cfsiz = CFSIZ(msg_head->flags);
1017: op->flags = msg_head->flags;
1018: op->nframes = msg_head->nframes;
1019:
1020: if (op->flags & SETTIMER)
1021: op->count = msg_head->count;
1022:
1023: /* create array for CAN frames and copy the data */
1024: if (msg_head->nframes > 1) {
1025: op->frames = kmalloc_array(msg_head->nframes,
1026: op->cfsiz,
1027: GFP_KERNEL);
1028: if (!op->frames) {
1029: kfree(op);
1030: return -ENOMEM;
1031: }
1032: } else
1033: op->frames = &op->sframe;
1034:
1035: for (i = 0; i < msg_head->nframes; i++) {
1036:
1037: cf = op->frames + op->cfsiz * i;
1038: err = memcpy_from_msg((u8 *)cf, msg, op->cfsiz);
1039: if (err < 0)
1040: goto free_op;
1041:
1042: if (op->flags & CAN_FD_FRAME) {
1043: if (cf->len > 64)
1044: err = -EINVAL;
1045: } else {
1046: if (cf->len > 8)
1047: err = -EINVAL;
1048: }
1049:
1050: if (err < 0)
1051: goto free_op;
1052:
1053: if (msg_head->flags & TX_CP_CAN_ID) {
1054: /* copy can_id into frame */
1055: cf->can_id = msg_head->can_id;
1056: }
1057: }
1058:
1059: /* tx_ops never compare with previous received messages */
1060: op->last_frames = NULL;
1061:
1062: /* bcm_can_tx / bcm_tx_timeout_handler needs this */
1063: op->sk = sk;
1064: op->ifindex = ifindex;
1065:
1066: /* initialize uninitialized (kzalloc) structure */
1067: hrtimer_setup(&op->timer, bcm_tx_timeout_handler, CLOCK_MONOTONIC,
1068: HRTIMER_MODE_REL_SOFT);
1069:
1070: /* currently unused in tx_ops */
1071: hrtimer_setup(&op->thrtimer, hrtimer_dummy_timeout, CLOCK_MONOTONIC,
1072: HRTIMER_MODE_REL_SOFT);
1073:
1074: /* add this bcm_op to the list of the tx_ops */
1075: list_add(&op->list, &bo->tx_ops);
1076:
1077: } /* if ((op = bcm_find_op(&bo->tx_ops, msg_head->can_id, ifindex))) */
1078:
1079: if (op->flags & SETTIMER) {
1080: /* set timer values */
1081: op->ival1 = msg_head->ival1;
1082: op->ival2 = msg_head->ival2;
1083: op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1);
1084: op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2);
1085:
1086: /* disable an active timer due to zero values? */
1087: if (!op->kt_ival1 && !op->kt_ival2)
1088: hrtimer_cancel(&op->timer);
1089: }
1090:
1091: if (op->flags & STARTTIMER) {
1092: hrtimer_cancel(&op->timer);
1093: /* spec: send CAN frame when starting timer */
1094: op->flags |= TX_ANNOUNCE;
1095: }
1096:
1097: if (op->flags & TX_ANNOUNCE)
1098: bcm_can_tx(op);
1099:
1100: if (op->flags & STARTTIMER)
1101: bcm_tx_start_timer(op);
1102:
1103: return msg_head->nframes * op->cfsiz + MHSIZ;
1104:
1105: free_op:
1106: if (op->frames != &op->sframe)
1107: kfree(op->frames);
1108: kfree(op);
1109: return err;
1110: }
SourceFile:net/can/bcm.c]
|
| 8/2 |
2026/04/04 20:02 |
llm |
expert |
0m
Model:
Tokens:
input: 9491
output: 41
thoughts: 4351
Thoughts: **Data Race in BCM CAN TX Path: A Deep Dive**
Okay, so I've been digging into this potential data race in the BCM (Broadcast CAN Manager) code, and it's looking pretty significant. The core issue centers around concurrent access to CAN frames during transmission. Let me break down my thinking:
1. **The Contenders:** We have two main players here: `bcm_can_tx` and `bcm_tx_setup`. `bcm_can_tx` is responsible for grabbing a CAN frame and putting its data into an SKB (socket buffer) for transmission via `skb_put_data`. `bcm_tx_setup`, on the other hand, updates an *existing* BCM operation. This update involves copying new frame data from user space using `memcpy_from_msg`. The report specifically says `bcm_can_tx` is called from `bcm_tx_timeout_handler`, meaning it's running on an hrtimer.
2. **The Race Condition:** The data race arises because `bcm_tx_setup` *doesn't* hold the spinlock (`op->bcm_tx_lock`) while copying frame data from user space. `bcm_can_tx`, however, *does* grab the lock just to compute the address of the frame (`cf`). After calculating `cf`, which is the pointer to the data, it *releases* the lock and calls `skb_put_data`, which reads the CAN frame. This means `bcm_tx_setup` could be in the middle of writing a frame while `bcm_can_tx` is trying to read the *same* frame. This can occur if `bcm_tx_timeout_handler` fires while `bcm_tx_setup` is running.
3. **The Implications: Torn Reads and Inconsistent Data:** This creates a classic data race. Since `skb_put_data` just copies the bytes, if the write happens during a read, we will get a partially updated (torn) frame. This means the CAN frame sent on the wire could have inconsistent data. The question then becomes, how harmful is this?
4. **Is it BENIGN?** In the networking world, a "corrupted" packet is sometimes tolerated if the protocol can handle it or if there is eventual consistency. But, CAN frame content *should* be atomic, and this is a clear violation of that. It's not just that we might get an old or new frame, but a mix, making it a "logic bug."
5. **Is it HARMFUL?** The core problem is that `cf->len` is used. If `cf->len` is inconsistent with the data, it might cause issues in the driver. But it is verified in `bcm_tx_setup` *after* the `memcpy_from_msg` call. If `bcm_tx_setup` returns an error, the frame might already be partially updated. Even if the frame is invalid, the timer might have fired and sent it.
6. **Other protections:** I considered the socket locking and the potential for multiple processes, but the timer is independent of `lock_sock`. If a user updates the frames with `sendmsg`, they probably expect atomicity with the periodic transmission via the timer. They would want to get either the old or new frames, not a mix of the two.
7. **The Worst-Case Scenario:** Let's say `can_send` checks `cf->len`. If an invalid frame gets through the `bcm_tx_setup` checks, `can_send` might be called with an invalid frame, which could be problematic.
**In summary,** this is a definite data race, and it is almost certainly harmful. While a partially updated frame might not crash the kernel immediately, it violates the expected semantics of atomic frame updates and could lead to issues further down the CAN stack, especially if the `len` field is inconsistent with the data. It is a bug that needs to be addressed.
|
| 9/2 |
2026/04/04 20:02 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/can/af_can.c IncludeLines:true Name:can_send]
Results: map[SourceCode: 186: /**
187: * can_send - transmit a CAN frame (optional with local loopback)
188: * @skb: pointer to socket buffer with CAN frame in data section
189: * @loop: loopback for listeners on local CAN sockets (recommended default!)
190: *
191: * Due to the loopback this routine must not be called from hardirq context.
192: *
193: * Return:
194: * 0 on success
195: * -ENETDOWN when the selected interface is down
196: * -ENOBUFS on full driver queue (see net_xmit_errno())
197: * -ENOMEM when local loopback failed at calling skb_clone()
198: * -EPERM when trying to send on a non-CAN interface
199: * -EMSGSIZE CAN frame size is bigger than CAN interface MTU
200: * -EINVAL when the skb->data does not contain a valid CAN frame
201: */
202: int can_send(struct sk_buff *skb, int loop)
203: {
204: struct sk_buff *newskb = NULL;
205: struct can_pkg_stats *pkg_stats = dev_net(skb->dev)->can.pkg_stats;
206: int err = -EINVAL;
207:
208: if (can_is_canxl_skb(skb)) {
209: skb->protocol = htons(ETH_P_CANXL);
210: } else if (can_is_can_skb(skb)) {
211: skb->protocol = htons(ETH_P_CAN);
212: } else if (can_is_canfd_skb(skb)) {
213: struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
214:
215: skb->protocol = htons(ETH_P_CANFD);
216:
217: /* set CAN FD flag for CAN FD frames by default */
218: cfd->flags |= CANFD_FDF;
219: } else {
220: goto inval_skb;
221: }
222:
223: /* Make sure the CAN frame can pass the selected CAN netdevice. */
224: if (unlikely(skb->len > READ_ONCE(skb->dev->mtu))) {
225: err = -EMSGSIZE;
226: goto inval_skb;
227: }
228:
229: if (unlikely(skb->dev->type != ARPHRD_CAN)) {
230: err = -EPERM;
231: goto inval_skb;
232: }
233:
234: if (unlikely(!(skb->dev->flags & IFF_UP))) {
235: err = -ENETDOWN;
236: goto inval_skb;
237: }
238:
239: skb->ip_summed = CHECKSUM_UNNECESSARY;
240:
241: skb_reset_mac_header(skb);
242: skb_reset_network_header(skb);
243: skb_reset_transport_header(skb);
244:
245: if (loop) {
246: /* local loopback of sent CAN frames */
247:
248: /* indication for the CAN driver: do loopback */
249: skb->pkt_type = PACKET_LOOPBACK;
250:
251: /* The reference to the originating sock may be required
252: * by the receiving socket to check whether the frame is
253: * its own. Example: can_raw sockopt CAN_RAW_RECV_OWN_MSGS
254: * Therefore we have to ensure that skb->sk remains the
255: * reference to the originating sock by restoring skb->sk
256: * after each skb_clone() or skb_orphan() usage.
257: */
258:
259: if (!(skb->dev->flags & IFF_ECHO)) {
260: /* If the interface is not capable to do loopback
261: * itself, we do it here.
262: */
263: newskb = skb_clone(skb, GFP_ATOMIC);
264: if (!newskb) {
265: kfree_skb(skb);
266: return -ENOMEM;
267: }
268:
269: can_skb_set_owner(newskb, skb->sk);
270: newskb->ip_summed = CHECKSUM_UNNECESSARY;
271: newskb->pkt_type = PACKET_BROADCAST;
272: }
273: } else {
274: /* indication for the CAN driver: no loopback required */
275: skb->pkt_type = PACKET_HOST;
276: }
277:
278: /* send to netdevice */
279: err = dev_queue_xmit(skb);
280: if (err > 0)
281: err = net_xmit_errno(err);
282:
283: if (err) {
284: kfree_skb(newskb);
285: return err;
286: }
287:
288: if (newskb)
289: netif_rx(newskb);
290:
291: /* update statistics */
292: atomic_long_inc(&pkg_stats->tx_frames);
293: atomic_long_inc(&pkg_stats->tx_frames_delta);
294:
295: return 0;
296:
297: inval_skb:
298: kfree_skb(skb);
299: return err;
300: }
SourceFile:net/can/af_can.c]
|
| 10/2 |
2026/04/04 20:02 |
llm |
expert |
0m
Model:
Tokens:
input: 16556
output: 48
thoughts: 30
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience, presented in the first person:
**Analyzing the Transmission Path**
Right, so `can_send` is the entry point, and it's calling a few helper functions – `can_is_canfd_skb(skb)` and presumably others. My immediate thought is, "Let's dig into those helpers." I need to understand how this code is handling the CAN frame classification before transmission. Are we differentiating CAN FD frames correctly at this stage? Are there any opportunities to optimize the frame checks for speed? I want to see if this part of the path has any bottlenecks or potential areas for improvement, like unnecessarily redundant checks or missed optimizations for the target architecture. Understanding exactly *how* these helpers are used is critical for a full assessment of the CAN transmission workflow.
|
| 11/2 |
2026/04/04 20:02 |
llm |
expert |
0m
Model:
Tokens:
input: 29599
output: 19
thoughts: 6952
Thoughts: **Analysis of CAN BCM Driver Race Condition**
Okay, so I've been digging into this KCSAN report, and it seems like we have a genuine data race in the CAN BCM driver. `can_send` itself doesn't validate the frame content beyond the SKB length check against the MTU, which is okay, but it does rely on the driver to use the `can_id` and `len` fields. The problem is that `memcpy_from_msg` in `bcm_tx_setup` isn’t synchronized correctly with the `bcm_can_tx` function, which is reading this very data to build the CAN frame. The current check in `bcm_tx_setup` for the frame length occurs *after* `memcpy_from_msg`, which doesn't protect against inconsistencies.
Essentially, `bcm_tx_setup` can update the frame data while `bcm_can_tx` is in the process of reading it for transmission, a classic read-write data race. While the byte-sized `len` field might not be torn catastrophically, the `can_id` and especially the data payload can be mixed. Now, it's not a kernel-crashing event, but sending corrupted packets is definitely a bug. KCSAN flags this, and it’s correct because it represents an unsynchronized access.
Looking into `skb_put_data`, which actually copies the data to the network buffer, uses `op->cfsiz`, and, yes, `op->cfsiz` is determined when `op` is created, and doesn't change during updates, which guarantees that `skb_put_data` will always copy the expected number of bytes. The problem is with the *content* of those bytes, which might be a mix of old and new data.
I initially thought we could simply use `op->bcm_tx_lock` during `bcm_tx_setup`, but `memcpy_from_msg` can sleep, rendering `spin_lock_bh` ineffective. The more elegant solution is to copy the data from user space to a temporary buffer and then use a spinlock to copy from this buffer to `op->frames`. I did realize that for the new operation setup, something similar happens, and this must be the way.
Upon closer inspection of the code, in `bcm_tx_setup` where frames are updated, it uses a loop to copy them:
```c
for (i = 0; i < msg_head->nframes; i++) {
cf = op->frames + op->cfsiz * i;
err = memcpy_from_msg((u8 *)cf, msg, op->cfsiz);
}
```
The fix, therefore, involves protecting the copy process. Since `memcpy_from_msg` can sleep, and we have a single frame, the solution is,
1. Copy from the user space to a temporary buffer.
2. Lock `op->bcm_tx_lock`.
3. Copy from the temporary buffer to `op->frames`.
4. Unlock.
This must be done for *each* frame, inside the loop. The `op->frames` can be an array of up to 256 frames, but it is small enough to be done under a spinlock. But we must be careful with the context.
I then realized this race isn't limited to transmission. The same race condition exists in `bcm_rx_setup` which is also updating `op->frames` via `memcpy_from_msg` and `bcm_rx_handler` which reads `op->frames` without a lock.
```c
err = memcpy_from_msg(op->frames, msg, msg_head->nframes * op->cfsiz);
```
This is a clear race. If the data is being updated during the multiplex comparison, incorrect filtering could happen.
The relevant stack trace from the KCSAN report (which might have slightly different line numbers) shows `bcm_can_tx` called from the timer, i.e., in softirq context, and that this softirq is reading the content of `op->frames`. `skb_put_data`, an inline function for `memcpy`, reads the data at the same time it's being updated. This is where the race happens. `bcm_can_tx` calls `spin_lock_bh` just to find the pointer to the data and does *not* protect the `skb_put_data` or the data.
I did verify that `memcpy_from_msg` can sleep, so no `spin_lock_bh` within the memcpy itself.
The KCSAN report
|
| 12/2 |
2026/04/04 20:03 |
tool |
set-results |
0m
Args: map[Benign:false Confident:true]
Results: map[Benign:false Confident:true]
|
| 13/2 |
2026/04/04 20:03 |
llm |
expert |
0m
Model:
Tokens:
input: 37612
output: 885
thoughts: 824
Thoughts: **Analysis of the bcm_can_tx Race Condition**
Okay, the analysis is done. I'm ready to provide the final assessment. But before I do, let me just double-check something. Hang on, is `bcm_can_tx` also called from `bcm_rx_handler`? Ah, yes, it is. The code shows it responding to RTR requests:
```c
if (op->flags & RX_RTR_FRAME) {
/* send reply for RTR-request (placed in op->frames[0]) */
bcm_can_tx(op);
return;
}
```
And, I see the potential for a race here with `bcm_rx_setup` which is also updating `op->frames`. I'm going to focus on the KCSAN report mentioning the race between `bcm_can_tx` and `bcm_tx_setup`.
This race is *definitely* harmful. Let me examine the details. `bcm_tx_setup` holds `lock_sock(sk)`, but `bcm_can_tx` via the hrtimer and `bcm_rx_handler` do not. So, there's no mutual exclusion.
The immediate fix would be to use a temporary buffer and hold `op->bcm_tx_lock` during the copy. But, now I'm thinking, `bcm_can_tx` also needs to hold the lock during `skb_put_data`, right? Let me see... yes, `bcm_can_tx` *is* already holding `op->bcm_tx_lock` for `currframe` and `count`. So, all that's needed is to extend that critical section to include `skb_put_data`. Let's clarify:
```c
/* read currframe under lock protection */
spin_lock_bh(&op->bcm_tx_lock);
cf = op->frames + op->cfsiz * op->currframe;
skb_put_data(skb, cf, op->cfsiz);
spin_unlock_bh(&op->bcm_tx_lock);
```
Then, in `bcm_tx_setup`:
```c
// for each frame i:
// copy from msg to temp_frame
spin_lock_bh(&op->bcm_tx_lock);
memcpy(op->frames + i * op->cfsiz, temp_frame, op->cfsiz);
spin_unlock_bh(&op->bcm_tx_lock);
```
That should fix it.
But... wait a second. `bcm_tx_setup` can update *multiple* frames in a single call. Here's the loop:
```c
for (i = 0; i < msg_head->nframes; i++) {
cf = op->frames + op->cfsiz * i;
err = memcpy_from_msg((u8 *)cf, msg, op->cfsiz);
...
}
```
If we want to avoid the race, we need to do the *entire* loop under the lock, however, we can't do that because `memcpy_from_msg` has to happen outside the lock. So, we'd need to copy all frames to a temporary buffer first.
Actually, given the current implementation, it's definitely *not* safe.
Final verdict: HARMFUL.
|
| 14/1 |
2026/04/04 20:03 |
action |
format-explanation |
0m
Results: map[Explanation:The KCSAN report identifies a data race between `bcm_can_tx()` (reading) and
`bcm_tx_setup()` (writing) on a `struct canfd_frame` (72 bytes).
### Race Summary
- **Function A (`bcm_can_tx`):** Triggered by an `hrtimer` (softirq context) or
a received CAN frame (`bcm_rx_handler`), this function reads a CAN frame from
the `op->frames` array and copies it into a socket buffer (`skb`) using
`skb_put_data()`.
- **Function B (`bcm_tx_setup`):** Triggered by a `sendmsg()` syscall (task
context), this function updates the content of the `op->frames` array using
`memcpy_from_msg()`.
- **Variable:** A `struct canfd_frame` within the `op->frames` array.
### Synchronization Context
`bcm_tx_setup()` holds the `lock_sock(sk)` mutex, but `bcm_can_tx()` (running in
softirq context) does not and cannot acquire it. While `bcm_can_tx()` uses a
spinlock `op->bcm_tx_lock` to protect the `currframe` index and `count` state,
it releases the lock **before** reading the frame data via `skb_put_data()`.
Conversely, `bcm_tx_setup()` performs the update via `memcpy_from_msg()` without
holding `op->bcm_tx_lock`, as `memcpy_from_msg()` can sleep.
### Final Classification: **HARMFUL**
### Failure Reasoning
The race allows `bcm_can_tx()` to read a CAN frame while it is being partially
updated by `bcm_tx_setup()`. This leads to **torn reads**, where the resulting
`skb` contains a mixture of old and new data (e.g., a new `can_id` with old
payload data, or a corrupted `len` field). Sending such inconsistent frames to
the CAN network is a logic bug that breaks the integrity of the communication
protocol.
#### Two-Column Execution Trace:
| CPU 0 (`bcm_tx_setup`) | CPU 1 (`bcm_can_tx` via hrtimer) |
| :--- | :--- |
| `for (i=0; i<nframes; i++)` | |
| `cf = op->frames + ...` | |
| `memcpy_from_msg(cf, msg, 72)` | |
| `// Writes first 4 bytes (can_id)` | |
| `cf->can_id = 0x123; // New ID` | |
| | `spin_lock_bh(&op->bcm_tx_lock)` |
| | `cf = op->frames + ...` |
| | `spin_unlock_bh(&op->bcm_tx_lock)` |
| | `skb_put_data(skb, cf, 72)` |
| | **// TORN READ:** sees `can_id=0x123` but old `data` |
| `// Writes remaining 64 bytes (data)` | |
| `memcpy(cf->data, new_data, 64);` | |
| | `can_send(skb, 1)` |
| | **// Result: Corrupted frame sent to network!** |
### Recommended Fix
To ensure atomicity, `bcm_tx_setup()` should copy the user data into a temporary
kernel buffer first, then acquire `op->bcm_tx_lock` and perform a `memcpy()` to
the shared `op->frames` array. Correspondingly, `bcm_can_tx()` must hold
`op->bcm_tx_lock` while executing `skb_put_data()`. Since the maximum size of a
CAN FD frame is small (72 bytes), performing the copy under a spinlock is
acceptable.]
|
| 15/2 |
2026/04/04 20:03 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/can/bcm.c IncludeLines:true Name:bcm_rx_handler]
Results: map[SourceCode: 697: /*
698: * bcm_rx_handler - handle a CAN frame reception
699: */
700: static void bcm_rx_handler(struct sk_buff *skb, void *data)
701: {
702: struct bcm_op *op = (struct bcm_op *)data;
703: const struct canfd_frame *rxframe = (struct canfd_frame *)skb->data;
704: unsigned int i;
705: unsigned char traffic_flags;
706:
707: if (op->can_id != rxframe->can_id)
708: return;
709:
710: /* make sure to handle the correct frame type (CAN / CAN FD) */
711: if (op->flags & CAN_FD_FRAME) {
712: if (!can_is_canfd_skb(skb))
713: return;
714: } else {
715: if (!can_is_can_skb(skb))
716: return;
717: }
718:
719: /* disable timeout */
720: hrtimer_cancel(&op->timer);
721:
722: /* save rx timestamp */
723: op->rx_stamp = skb->tstamp;
724: /* save originator for recvfrom() */
725: op->rx_ifindex = skb->dev->ifindex;
726: /* update statistics */
727: op->frames_abs++;
728:
729: if (op->flags & RX_RTR_FRAME) {
730: /* send reply for RTR-request (placed in op->frames[0]) */
731: bcm_can_tx(op);
732: return;
733: }
734:
735: /* compute flags to distinguish between own/local/remote CAN traffic */
736: traffic_flags = 0;
737: if (skb->sk) {
738: traffic_flags |= RX_LOCAL;
739: if (skb->sk == op->sk)
740: traffic_flags |= RX_OWN;
741: }
742:
743: if (op->flags & RX_FILTER_ID) {
744: /* the easiest case */
745: bcm_rx_update_and_send(op, op->last_frames, rxframe,
746: traffic_flags);
747: goto rx_starttimer;
748: }
749:
750: if (op->nframes == 1) {
751: /* simple compare with index 0 */
752: bcm_rx_cmp_to_index(op, 0, rxframe, traffic_flags);
753: goto rx_starttimer;
754: }
755:
756: if (op->nframes > 1) {
757: /*
758: * multiplex compare
759: *
760: * find the first multiplex mask that fits.
761: * Remark: The MUX-mask is stored in index 0 - but only the
762: * first 64 bits of the frame data[] are relevant (CAN FD)
763: */
764:
765: for (i = 1; i < op->nframes; i++) {
766: if ((get_u64(op->frames, 0) & get_u64(rxframe, 0)) ==
767: (get_u64(op->frames, 0) &
768: get_u64(op->frames + op->cfsiz * i, 0))) {
769: bcm_rx_cmp_to_index(op, i, rxframe,
770: traffic_flags);
771: break;
772: }
773: }
774: }
775:
776: rx_starttimer:
777: bcm_rx_starttimer(op);
778: }
SourceFile:net/can/bcm.c]
|
| 16/2 |
2026/04/04 20:03 |
llm |
expert |
0m
Model:
Tokens:
input: 24661
output: 41
thoughts: 1816
Thoughts: **Data Race Analysis in BCM's CAN Implementation**
Alright, let's break this down. It seems the code has a potential race condition. Specifically, `bcm_rx_handler` reads from `op->frames` while `bcm_rx_setup` or even the `hrtimer` or CAN receive handler, in softirq context, *might* be writing to it. I see this data race as potentially harmful.
The BCM code appears to rely on `lock_sock(sk)` to protect `memcpy_from_msg` to `op->frames`. However, this lock is only effective within the same socket, not against concurrent access from softirqs like the receive handler or the hrtimer, and certainly not the CAN receive handler.
Let me think… the `bcm_tx_lock` on `bcm_op` *does* protect `op->currframe`, `op->frames_abs`, and `op->count` in `bcm_can_tx`. But it *doesn't* protect the frame *data* itself. Looking at how `bcm_can_tx` grabs `cf` within the lock, the lock only prevents reallocation of the `op->frames` structure. Importantly, there's a comment in `bcm_tx_setup` about the complexity of allocating more space which suggests they're aware of locking considerations. The structure of `op` is protected, but the *contents* of `op->frames` are not.
Okay, let's ensure there are no out-of-bounds accesses. `op->frames` is an array of `op->nframes` elements, and `op->currframe` is always less than `op->nframes`, and `bcm_tx_setup` ensures `msg_head->nframes <= op->nframes` . So that's safe.
The *only* problem, as I see it, is the data race on the frame *content*. Now, in many parts of the kernel, data races leading to minor data corruption might be considered "benign" – things like a slightly inaccurate counter or a garbled diagnostic string. But this is *not* that. This is the actual CAN frame data, the data being transmitted or used for filtering. If I were using BCM and saw my critical CAN frames getting corrupted due to an update, I'd be *furious*.
There's even a comment that suggests the author knew of the locking complexity: "*Do we need more space for the CAN frames than currently allocated? -> This is a _really_ unusual use-case and therefore (complexity / locking) it is not supported*".
I'll quickly check if `op->frames` can be null. No, that's not a problem.
If this is HARMFUL, I need to explain *why*. The harm is that `bcm_can_tx` can send a frame with a mixture of old and new data due to the concurrent execution of `memcpy_from_msg` in `bcm_tx_setup` and `skb_put_data` (which is a `memcpy`) in `bcm_can_tx`, because CAN frames can be up to 72 bytes.
Consider a simple example. Suppose we have a CAN frame of `can_id=0x123` and `data=[0,0,0,0,0,0,0,0]`. The user wants to update it to `can_id=0x456` and `data=[1,1,1,1,1,1,1,1]`.
CPU0 (bcm_tx_setup) copies the can_id. CPU1 copies the partially updated frame (old can_id with new data.)
This is a "torn" update. The receiver sees a frame that *never* existed in a consistent state. While some applications might tolerate this, most will not. In the context of KCSAN, this is a *data race*. And the BCM logic is *not* structurally tolerant to "torn reads" of CAN frames, meaning this is a harmful problem.
What about `bcm_rx_handler`? A torn read of `op->frames` there could lead to the wrong match in the multiplexer, because mask data might get updated.
So, I classify this as **HARMFUL**. It's a "Logic Bug" or at least requires "Marking Required for Correctness". Simple `READ_ONCE`/`WRITE_ONCE` won't solve this. Proper synchronization is needed to fix it.
Could I get away with `data_race()`? Maybe. But as a Linux kernel concurrency expert, I have to say this is HARMFUL. It violates atomicity. Let's see if
|
| 17/2 |
2026/04/04 20:03 |
tool |
grepper |
0m
Args: map[Expression:READ_ONCE|WRITE_ONCE|data_race|smp_load_acquire|smp_store_release]
Results: map[Output:
Full output is too long, showing 500 out of 61895 lines.
Use more precise expression if possible.
[Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst=78=lock-acquisition and lock-release functions::
--
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-84- 5 raw_spin_lock_rcu_node(rnp);
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst:85: 6 WRITE_ONCE(x, 1);
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst:86: 7 r1 = READ_ONCE(y);
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-87- 8 raw_spin_unlock_rcu_node(rnp);
--
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-92- 13 raw_spin_lock_rcu_node(rnp);
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst:93: 14 WRITE_ONCE(y, 1);
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst:94: 15 r2 = READ_ONCE(z);
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-95- 16 raw_spin_unlock_rcu_node(rnp);
--
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-99- 20 {
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst:100: 21 WRITE_ONCE(z, 1);
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-101- 22 smp_mb();
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst:102: 23 r3 = READ_ONCE(x);
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-103- 24 }
--
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst=112=The ``smp_mb__after_unlock_lock()`` invocations prevent this
--
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-131-| ---- ---- |
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst:132:| WRITE_ONCE(X, 1) WRITE_ONCE(Y, 1) |
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-133-| g = get_state_synchronize_rcu() smp_mb() |
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst:134:| while (!poll_state_synchronize_rcu(g)) r1 = READ_ONCE(X) |
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-135-| continue; |
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst:136:| r0 = READ_ONCE(Y) |
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-137-| |
--
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst=198=newly arrived RCU callbacks against future grace periods:
--
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-213- 12 /* Handle nohz enablement switches conservatively. */
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst:214: 13 tne = READ_ONCE(tick_nohz_active);
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-215- 14 if (tne != rdp->tick_nohz_enabled_snap) {
--
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst=342=counter, as shown below:
--
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-345-
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst:346:The actual increment is carried out using ``smp_store_release()``, which
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-347-helps reject false-positive RCU CPU stall detection. Note that only the
--
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-145- x="112.04738"
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg:146: y="268.18076">WRITE_ONCE(a, 1);</tspan></text>
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-147- <text
--
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-156- x="112.04738"
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg:157: y="439.13766">WRITE_ONCE(b, 1);</tspan></text>
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-158- <text
--
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-167- x="255.60869"
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg:168: y="309.29346">r1 = READ_ONCE(a);</tspan></text>
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-169- <text
--
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-178- x="255.14423"
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg:179: y="520.61786">WRITE_ONCE(c, 1);</tspan></text>
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-180- <text
--
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-189- x="396.10254"
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg:190: y="384.71124">r2 = READ_ONCE(b);</tspan></text>
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-191- <text
--
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-200- x="396.10254"
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg:201: y="582.13617">r3 = READ_ONCE(c);</tspan></text>
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-202- <text
--
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-173- x="112.04738"
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg:174: y="268.18076">WRITE_ONCE(a, 1);</tspan></text>
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-175- <text
--
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-184- x="112.04738"
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg:185: y="487.13766">WRITE_ONCE(b, 1);</tspan></text>
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-186- <text
--
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-195- x="255.60869"
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg:196: y="297.29346">r1 = READ_ONCE(a);</tspan></text>
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-197- <text
--
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-206- x="255.14423"
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg:207: y="554.61786">WRITE_ONCE(c, 1);</tspan></text>
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-208- <text
--
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-217- x="396.10254"
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg:218: y="370.71124">WRITE_ONCE(d, 1);</tspan></text>
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-219- <text
--
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-228- x="396.10254"
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg:229: y="572.13617">r2 = READ_ONCE(c);</tspan></text>
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-230- <text
--
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-463- x="541.70508"
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg:464: y="387.6217">r3 = READ_ONCE(d);</tspan></text>
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-465- <text
--
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-474- x="541.2406"
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg:475: y="646.94611">WRITE_ONCE(e, 1);</tspan></text>
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-476- <path
--
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-509- x="686.27747"
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg:510: y="461.83929">r4 = READ_ONCE(b);</tspan></text>
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-511- <text
--
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-520- x="686.27747"
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg:521: y="669.26422">r5 = READ_ONCE(e);</tspan></text>
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-522- <text
--
Documentation/RCU/Design/Requirements/Requirements.rst=84=overhead to readers, for example:
--
Documentation/RCU/Design/Requirements/Requirements.rst-92- 5 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:93: 6 r1 = READ_ONCE(x);
Documentation/RCU/Design/Requirements/Requirements.rst:94: 7 r2 = READ_ONCE(y);
Documentation/RCU/Design/Requirements/Requirements.rst-95- 8 rcu_read_unlock();
--
Documentation/RCU/Design/Requirements/Requirements.rst-99- 12 {
Documentation/RCU/Design/Requirements/Requirements.rst:100: 13 WRITE_ONCE(x, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-101- 14 synchronize_rcu();
Documentation/RCU/Design/Requirements/Requirements.rst:102: 15 WRITE_ONCE(y, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-103- 16 }
--
Documentation/RCU/Design/Requirements/Requirements.rst=138=recovery from node failure, more or less as follows:
--
Documentation/RCU/Design/Requirements/Requirements.rst-153- 12 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:154: 13 state_snap = READ_ONCE(state);
Documentation/RCU/Design/Requirements/Requirements.rst-155- 14 if (state_snap == STATE_NORMAL)
--
Documentation/RCU/Design/Requirements/Requirements.rst-163- 22 {
Documentation/RCU/Design/Requirements/Requirements.rst:164: 23 WRITE_ONCE(state, STATE_WANT_RECOVERY);
Documentation/RCU/Design/Requirements/Requirements.rst-165- 24 synchronize_rcu();
Documentation/RCU/Design/Requirements/Requirements.rst:166: 25 WRITE_ONCE(state, STATE_RECOVERING);
Documentation/RCU/Design/Requirements/Requirements.rst-167- 26 recovery();
Documentation/RCU/Design/Requirements/Requirements.rst:168: 27 WRITE_ONCE(state, STATE_WANT_NORMAL);
Documentation/RCU/Design/Requirements/Requirements.rst-169- 28 synchronize_rcu();
Documentation/RCU/Design/Requirements/Requirements.rst:170: 29 WRITE_ONCE(state, STATE_NORMAL);
Documentation/RCU/Design/Requirements/Requirements.rst-171- 30 }
--
Documentation/RCU/Design/Requirements/Requirements.rst=467=resembling the dependency-ordering barrier that was later subsumed
Documentation/RCU/Design/Requirements/Requirements.rst:468:into rcu_dereference() and later still into READ_ONCE(). The
Documentation/RCU/Design/Requirements/Requirements.rst-469-need for these operations made itself known quite suddenly at a
--
Documentation/RCU/Design/Requirements/Requirements.rst=702=threads:
--
Documentation/RCU/Design/Requirements/Requirements.rst-708- 3 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:709: 4 WRITE_ONCE(x, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-710- 5 rcu_read_unlock();
Documentation/RCU/Design/Requirements/Requirements.rst-711- 6 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:712: 7 WRITE_ONCE(y, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-713- 8 rcu_read_unlock();
--
Documentation/RCU/Design/Requirements/Requirements.rst-718- 13 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:719: 14 r1 = READ_ONCE(y);
Documentation/RCU/Design/Requirements/Requirements.rst-720- 15 rcu_read_unlock();
Documentation/RCU/Design/Requirements/Requirements.rst-721- 16 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:722: 17 r2 = READ_ONCE(x);
Documentation/RCU/Design/Requirements/Requirements.rst-723- 18 rcu_read_unlock();
--
Documentation/RCU/Design/Requirements/Requirements.rst=737=significant ordering constraints would slow down these fast-path APIs.
--
Documentation/RCU/Design/Requirements/Requirements.rst-745-+-----------------------------------------------------------------------+
Documentation/RCU/Design/Requirements/Requirements.rst:746:| No, the volatile casts in READ_ONCE() and WRITE_ONCE() |
Documentation/RCU/Design/Requirements/Requirements.rst-747-| prevent the compiler from reordering in this particular case. |
--
Documentation/RCU/Design/Requirements/Requirements.rst=755=example illustrates this:
--
Documentation/RCU/Design/Requirements/Requirements.rst-761- 3 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:762: 4 r1 = READ_ONCE(y);
Documentation/RCU/Design/Requirements/Requirements.rst-763- 5 if (r1) {
Documentation/RCU/Design/Requirements/Requirements.rst-764- 6 do_something_with_nonzero_x();
Documentation/RCU/Design/Requirements/Requirements.rst:765: 7 r2 = READ_ONCE(x);
Documentation/RCU/Design/Requirements/Requirements.rst-766- 8 WARN_ON(!r2); /* BUG!!! */
--
Documentation/RCU/Design/Requirements/Requirements.rst-773- 15 spin_lock(&my_lock);
Documentation/RCU/Design/Requirements/Requirements.rst:774: 16 WRITE_ONCE(x, 1);
Documentation/RCU/Design/Requirements/Requirements.rst:775: 17 WRITE_ONCE(y, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-776- 18 spin_unlock(&my_lock);
--
Documentation/RCU/Design/Requirements/Requirements.rst=819=are initially all zero:
--
Documentation/RCU/Design/Requirements/Requirements.rst-825- 3 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:826: 4 WRITE_ONCE(a, 1);
Documentation/RCU/Design/Requirements/Requirements.rst:827: 5 WRITE_ONCE(b, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-828- 6 rcu_read_unlock();
--
Documentation/RCU/Design/Requirements/Requirements.rst-832- 10 {
Documentation/RCU/Design/Requirements/Requirements.rst:833: 11 r1 = READ_ONCE(a);
Documentation/RCU/Design/Requirements/Requirements.rst-834- 12 synchronize_rcu();
Documentation/RCU/Design/Requirements/Requirements.rst:835: 13 WRITE_ONCE(c, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-836- 14 }
--
Documentation/RCU/Design/Requirements/Requirements.rst-840- 18 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:841: 19 r2 = READ_ONCE(b);
Documentation/RCU/Design/Requirements/Requirements.rst:842: 20 r3 = READ_ONCE(c);
Documentation/RCU/Design/Requirements/Requirements.rst-843- 21 rcu_read_unlock();
--
Documentation/RCU/Design/Requirements/Requirements.rst=862=period is known to end before the second grace period starts:
--
Documentation/RCU/Design/Requirements/Requirements.rst-868- 3 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:869: 4 WRITE_ONCE(a, 1);
Documentation/RCU/Design/Requirements/Requirements.rst:870: 5 WRITE_ONCE(b, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-871- 6 rcu_read_unlock();
--
Documentation/RCU/Design/Requirements/Requirements.rst-875- 10 {
Documentation/RCU/Design/Requirements/Requirements.rst:876: 11 r1 = READ_ONCE(a);
Documentation/RCU/Design/Requirements/Requirements.rst-877- 12 synchronize_rcu();
Documentation/RCU/Design/Requirements/Requirements.rst:878: 13 WRITE_ONCE(c, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-879- 14 }
--
Documentation/RCU/Design/Requirements/Requirements.rst-882- 17 {
Documentation/RCU/Design/Requirements/Requirements.rst:883: 18 r2 = READ_ONCE(c);
Documentation/RCU/Design/Requirements/Requirements.rst-884- 19 synchronize_rcu();
Documentation/RCU/Design/Requirements/Requirements.rst:885: 20 WRITE_ONCE(d, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-886- 21 }
--
Documentation/RCU/Design/Requirements/Requirements.rst-890- 25 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:891: 26 r3 = READ_ONCE(b);
Documentation/RCU/Design/Requirements/Requirements.rst:892: 27 r4 = READ_ONCE(d);
Documentation/RCU/Design/Requirements/Requirements.rst-893- 28 rcu_read_unlock();
--
Documentation/RCU/Design/Requirements/Requirements.rst=920=illustrated by the following, with all variables initially zero:
--
Documentation/RCU/Design/Requirements/Requirements.rst-926- 3 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:927: 4 WRITE_ONCE(a, 1);
Documentation/RCU/Design/Requirements/Requirements.rst:928: 5 WRITE_ONCE(b, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-929- 6 rcu_read_unlock();
--
Documentation/RCU/Design/Requirements/Requirements.rst-933- 10 {
Documentation/RCU/Design/Requirements/Requirements.rst:934: 11 r1 = READ_ONCE(a);
Documentation/RCU/Design/Requirements/Requirements.rst-935- 12 synchronize_rcu();
Documentation/RCU/Design/Requirements/Requirements.rst:936: 13 WRITE_ONCE(c, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-937- 14 }
--
Documentation/RCU/Design/Requirements/Requirements.rst-941- 18 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:942: 19 WRITE_ONCE(d, 1);
Documentation/RCU/Design/Requirements/Requirements.rst:943: 20 r2 = READ_ONCE(c);
Documentation/RCU/Design/Requirements/Requirements.rst-944- 21 rcu_read_unlock();
--
Documentation/RCU/Design/Requirements/Requirements.rst-948- 25 {
Documentation/RCU/Design/Requirements/Requirements.rst:949: 26 r3 = READ_ONCE(d);
Documentation/RCU/Design/Requirements/Requirements.rst-950- 27 synchronize_rcu();
Documentation/RCU/Design/Requirements/Requirements.rst:951: 28 WRITE_ONCE(e, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-952- 29 }
--
Documentation/RCU/Design/Requirements/Requirements.rst-956- 33 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:957: 34 r4 = READ_ONCE(b);
Documentation/RCU/Design/Requirements/Requirements.rst:958: 35 r5 = READ_ONCE(e);
Documentation/RCU/Design/Requirements/Requirements.rst-959- 36 rcu_read_unlock();
--
Documentation/RCU/checklist.rst=12=over a rather long period of time, but improvements are always welcome!
--
Documentation/RCU/checklist.rst-131- to test. Where it works, it is better to use things
Documentation/RCU/checklist.rst:132: like smp_store_release() and smp_load_acquire(), but in
Documentation/RCU/checklist.rst-133- some cases the smp_mb() full memory barrier is required.
--
Documentation/RCU/checklist.rst-363- time that readers might be accessing that structure. In such
Documentation/RCU/checklist.rst:364: cases, READ_ONCE() may be used in place of rcu_dereference()
Documentation/RCU/checklist.rst-365- and the read-side markers (rcu_read_lock() and rcu_read_unlock(),
--
Documentation/RCU/listRCU.rst=145=has become list_for_each_entry_rcu(). The **_rcu()** list-traversal
Documentation/RCU/listRCU.rst:146:primitives add READ_ONCE() and diagnostic checks for incorrect use
Documentation/RCU/listRCU.rst-147-outside of an RCU read-side critical section.
--
Documentation/RCU/rcu_dereference.rst=25=readers working properly:
--
Documentation/RCU/rcu_dereference.rst-35- return data preceding initialization that preceded the store
Documentation/RCU/rcu_dereference.rst:36: of the pointer. (As noted later, in recent kernels READ_ONCE()
Documentation/RCU/rcu_dereference.rst-37- also prevents DEC Alpha from playing these tricks.)
--
Documentation/RCU/rcu_dereference.rst-45-- In the special case where data is added but is never removed
Documentation/RCU/rcu_dereference.rst:46: while readers are accessing the structure, READ_ONCE() may be used
Documentation/RCU/rcu_dereference.rst:47: instead of rcu_dereference(). In this case, use of READ_ONCE()
Documentation/RCU/rcu_dereference.rst-48- takes on the role of the lockless_dereference() primitive that
--
Documentation/RCU/whatisRCU.rst=687=don't forget about them when submitting patches making use of RCU!]::
--
Documentation/RCU/whatisRCU.rst-690- ({ \
Documentation/RCU/whatisRCU.rst:691: smp_store_release(&(p), (v)); \
Documentation/RCU/whatisRCU.rst-692- })
--
Documentation/RCU/whatisRCU.rst-695- ({ \
Documentation/RCU/whatisRCU.rst:696: typeof(p) _________p1 = READ_ONCE(p); \
Documentation/RCU/whatisRCU.rst-697- (_________p1); \
--
Documentation/RCU/whatisRCU.rst=936=but with RCU the typical approach is to perform reads with SMP-aware
Documentation/RCU/whatisRCU.rst:937:operations such as smp_load_acquire(), to perform updates with atomic
Documentation/RCU/whatisRCU.rst-938-read-modify-write operations, and to provide the necessary ordering.
--
Documentation/atomic_t.txt=82=The non-RMW ops are (typically) regular LOADs and STOREs and are canonically
Documentation/atomic_t.txt:83:implemented using READ_ONCE(), WRITE_ONCE(), smp_load_acquire() and
Documentation/atomic_t.txt:84:smp_store_release() respectively. Therefore, if you find yourself only using
Documentation/atomic_t.txt-85-the Non-RMW operations of atomic_t, you do not in fact need atomic_t at all
--
Documentation/atomic_t.txt=119=with a lock:
--
Documentation/atomic_t.txt-124- lock();
Documentation/atomic_t.txt:125: ret = READ_ONCE(v->counter); // == 1
Documentation/atomic_t.txt-126- atomic_set(v, 0);
Documentation/atomic_t.txt:127: if (ret != u) WRITE_ONCE(v->counter, 0);
Documentation/atomic_t.txt:128: WRITE_ONCE(v->counter, ret + 1);
Documentation/atomic_t.txt-129- unlock();
--
Documentation/atomic_t.txt=234=strictly stronger than ACQUIRE. As illustrated:
--
Documentation/atomic_t.txt-242- {
Documentation/atomic_t.txt:243: r0 = READ_ONCE(*x);
Documentation/atomic_t.txt-244- smp_rmb();
--
Documentation/atomic_t.txt-251- smp_mb__after_atomic();
Documentation/atomic_t.txt:252: WRITE_ONCE(*x, 1);
Documentation/atomic_t.txt-253- }
--
Documentation/atomic_t.txt=260=because it would not order the W part of the RMW against the following
Documentation/atomic_t.txt:261:WRITE_ONCE. Thus:
Documentation/atomic_t.txt-262-
--
Documentation/core-api/circular-buffers.rst=154=The producer will look something like this::
--
Documentation/core-api/circular-buffers.rst-159- /* The spin_unlock() and next spin_lock() provide needed ordering. */
Documentation/core-api/circular-buffers.rst:160: unsigned long tail = READ_ONCE(buffer->tail);
Documentation/core-api/circular-buffers.rst-161-
--
Documentation/core-api/circular-buffers.rst-167-
Documentation/core-api/circular-buffers.rst:168: smp_store_release(buffer->head,
Documentation/core-api/circular-buffers.rst-169- (head + 1) & (buffer->size - 1));
--
Documentation/core-api/circular-buffers.rst=195=The consumer will look something like this::
--
Documentation/core-api/circular-buffers.rst-199- /* Read index before reading contents at that index. */
Documentation/core-api/circular-buffers.rst:200: unsigned long head = smp_load_acquire(buffer->head);
Documentation/core-api/circular-buffers.rst-201- unsigned long tail = buffer->tail;
--
Documentation/core-api/circular-buffers.rst-210- /* Finish reading descriptor before incrementing tail. */
Documentation/core-api/circular-buffers.rst:211: smp_store_release(buffer->tail,
Documentation/core-api/circular-buffers.rst-212- (tail + 1) & (buffer->size - 1));
--
Documentation/core-api/circular-buffers.rst=219=before it writes the new tail pointer, which will erase the item.
Documentation/core-api/circular-buffers.rst-220-
Documentation/core-api/circular-buffers.rst:221:Note the use of READ_ONCE() and smp_load_acquire() to read the
Documentation/core-api/circular-buffers.rst-222-opposition index. This prevents the compiler from discarding and
--
Documentation/core-api/circular-buffers.rst=224=be sure that the opposition index will _only_ be used the once.
Documentation/core-api/circular-buffers.rst:225:The smp_load_acquire() additionally forces the CPU to order against
Documentation/core-api/circular-buffers.rst:226:subsequent memory references. Similarly, smp_store_release() is used
Documentation/core-api/circular-buffers.rst-227-in both algorithms to write the thread's index. This documents the
--
Documentation/core-api/errseq.rst=144=errseq_check_and_advance after taking the lock. e.g.::
Documentation/core-api/errseq.rst-145-
Documentation/core-api/errseq.rst:146: if (errseq_check(&wd.wd_err, READ_ONCE(su.s_wd_err)) {
Documentation/core-api/errseq.rst-147- /* su.s_wd_err is protected by s_wd_err_lock */
--
Documentation/core-api/refcount-vs-atomic.rst=37=are executed in program order on a single CPU.
Documentation/core-api/refcount-vs-atomic.rst:38:This is implemented using READ_ONCE()/WRITE_ONCE() and
Documentation/core-api/refcount-vs-atomic.rst-39-compare-and-swap primitives.
--
Documentation/core-api/refcount-vs-atomic.rst=53=must propagate to all other CPUs before the release operation
Documentation/core-api/refcount-vs-atomic.rst-54-(A-cumulative property). This is implemented using
Documentation/core-api/refcount-vs-atomic.rst:55:smp_store_release().
Documentation/core-api/refcount-vs-atomic.rst-56-
--
Documentation/dev-tools/checkpatch.rst=456=Comments
--
Documentation/dev-tools/checkpatch.rst-476- **DATA_RACE**
Documentation/dev-tools/checkpatch.rst:477: Applications of data_race() should have a comment so as to document the
Documentation/dev-tools/checkpatch.rst-478- reasoning behind why it was deemed safe.
--
Documentation/dev-tools/kcsan.rst=87=the below options are available:
Documentation/dev-tools/kcsan.rst-88-
Documentation/dev-tools/kcsan.rst:89:* KCSAN understands the ``data_race(expr)`` annotation, which tells KCSAN that
Documentation/dev-tools/kcsan.rst-90- any data races due to accesses in ``expr`` should be ignored and resulting
--
Documentation/dev-tools/kcsan.rst-93-
Documentation/dev-tools/kcsan.rst:94:* Similar to ``data_race(...)``, the type qualifier ``__data_racy`` can be used
Documentation/dev-tools/kcsan.rst-95- to document that all data races due to accesses to a variable are intended
--
Documentation/dev-tools/kcsan.rst=214=and if that code is free from data races.
Documentation/dev-tools/kcsan.rst-215-
Documentation/dev-tools/kcsan.rst:216:KCSAN is aware of *marked atomic operations* (``READ_ONCE``, ``WRITE_ONCE``,
Documentation/dev-tools/kcsan.rst-217-``atomic_*``, etc.), and a subset of ordering guarantees implied by memory
--
Documentation/dev-tools/kcsan.rst=219=buffering, and can detect missing ``smp_mb()``, ``smp_wmb()``, ``smp_rmb()``,
Documentation/dev-tools/kcsan.rst:220:``smp_store_release()``, and all ``atomic_*`` operations with equivalent
Documentation/dev-tools/kcsan.rst-221-implied barriers.
--
Documentation/dev-tools/kcsan.rst=297=barrier. Consider the example::
--
Documentation/dev-tools/kcsan.rst-302- x = 1; // data race!
Documentation/dev-tools/kcsan.rst:303: WRITE_ONCE(flag, 1); // correct: smp_store_release(&flag, 1)
Documentation/dev-tools/kcsan.rst-304- }
--
Documentation/dev-tools/kcsan.rst-306- {
Documentation/dev-tools/kcsan.rst:307: while (!READ_ONCE(flag)); // correct: smp_load_acquire(&flag)
Documentation/dev-tools/kcsan.rst-308- ... = x; // data race!
--
Documentation/driver-api/surface_aggregator/internal.rst=270=submission, i.e. cancellation, can not rely on the ``ptl`` reference to be
Documentation/driver-api/surface_aggregator/internal.rst:271:set. Access to it in these functions is guarded by ``READ_ONCE()``, whereas
Documentation/driver-api/surface_aggregator/internal.rst:272:setting ``ptl`` is equally guarded with ``WRITE_ONCE()`` for symmetry.
Documentation/driver-api/surface_aggregator/internal.rst-273-
--
Documentation/driver-api/surface_aggregator/internal.rst=275=them, specifically priority and state for tracing. In those cases, proper
Documentation/driver-api/surface_aggregator/internal.rst:276:access is ensured by employing ``WRITE_ONCE()`` and ``READ_ONCE()``. Such
Documentation/driver-api/surface_aggregator/internal.rst-277-read-only access is only allowed when stale values are not critical.
--
Documentation/driver-api/surface_aggregator/internal.rst=451=them, specifically the state for tracing. In those cases, proper access is
Documentation/driver-api/surface_aggregator/internal.rst:452:ensured by employing ``WRITE_ONCE()`` and ``READ_ONCE()``. Such read-only
Documentation/driver-api/surface_aggregator/internal.rst-453-access is only allowed when stale values are not critical.
--
Documentation/driver-api/surface_aggregator/internal.rst=572=invalid usages, but rather aim to help catch them. In those cases, proper
Documentation/driver-api/surface_aggregator/internal.rst:573:variable access is ensured by employing ``WRITE_ONCE()`` and ``READ_ONCE()``.
Documentation/driver-api/surface_aggregator/internal.rst-574-
--
Documentation/filesystems/path-lookup.rst=896=similar.
Documentation/filesystems/path-lookup.rst-897-
Documentation/filesystems/path-lookup.rst:898:.. _READ_ONCE: https://lwn.net/Articles/624126/
Documentation/filesystems/path-lookup.rst-899-
--
Documentation/filesystems/path-lookup.rst=904=when accessing fields in the dentry. This "extra care" typically
Documentation/filesystems/path-lookup.rst:905:involves using `READ_ONCE() <READ_ONCE_>`_ to access fields, and verifying the
Documentation/filesystems/path-lookup.rst-906-result is not NULL before using it. This pattern can be seen in
--
Documentation/gpu/todo.rst=932=struct drm_sched_rq is read at many places without any locks, not even with a
Documentation/gpu/todo.rst:933:READ_ONCE. At XDC 2025 no one could really tell why that is the case, whether
Documentation/gpu/todo.rst-934-locks are needed and whether they could be added. (But for real, that should
--
Documentation/litmus-tests/README=48=DCL-fixed.litmus
Documentation/litmus-tests/README-49- Demonstrates corrected double-checked locking that uses
Documentation/litmus-tests/README:50: smp_store_release() and smp_load_acquire() in addition to the
Documentation/litmus-tests/README-51- obvious lock acquisitions and releases.
--
Documentation/litmus-tests/atomic/Atomic-RMW+mb__after_atomic-is-stronger-than-acquire.litmus=14=P0(int *x, atomic_t *y)
--
Documentation/litmus-tests/atomic/Atomic-RMW+mb__after_atomic-is-stronger-than-acquire.litmus-18-
Documentation/litmus-tests/atomic/Atomic-RMW+mb__after_atomic-is-stronger-than-acquire.litmus:19: r0 = READ_ONCE(*x);
Documentation/litmus-tests/atomic/Atomic-RMW+mb__after_atomic-is-stronger-than-acquire.litmus-20- smp_rmb();
--
Documentation/litmus-tests/atomic/Atomic-RMW+mb__after_atomic-is-stronger-than-acquire.litmus=24=P1(int *x, atomic_t *y)
--
Documentation/litmus-tests/atomic/Atomic-RMW+mb__after_atomic-is-stronger-than-acquire.litmus-27- smp_mb__after_atomic();
Documentation/litmus-tests/atomic/Atomic-RMW+mb__after_atomic-is-stronger-than-acquire.litmus:28: WRITE_ONCE(*x, 1);
Documentation/litmus-tests/atomic/Atomic-RMW+mb__after_atomic-is-stronger-than-acquire.litmus-29-}
--
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus=12=P0(int *x, int *y, int *z)
--
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus-16-
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus:17: WRITE_ONCE(*x, 1);
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus-18- r1 = cmpxchg(z, 1, 0);
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus-19- smp_mb__after_atomic();
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus:20: r0 = READ_ONCE(*y);
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus-21-}
--
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus=23=P1(int *x, int *y, int *z)
--
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus-27-
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus:28: WRITE_ONCE(*y, 1);
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus-29- r1 = cmpxchg(z, 1, 0);
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus-30- smp_mb__after_atomic();
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus:31: r0 = READ_ONCE(*x);
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus-32-}
--
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-2.litmus=12=P0(int *x, int *y)
--
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-2.litmus-15-
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-2.litmus:16: WRITE_ONCE(*x, 1);
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-2.litmus-17- r1 = cmpxchg(y, 0, 1);
--
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-2.litmus=20=P1(int *x, int *y)
--
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-2.litmus-26- smp_mb__after_atomic();
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-2.litmus:27: r2 = READ_ONCE(*x);
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-2.litmus-28-}
--
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus=13=P0(int *x, int *y, int *z)
--
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus-17-
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus:18: WRITE_ONCE(*x, 1);
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus-19- r1 = cmpxchg(z, 1, 0);
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus:20: r0 = READ_ONCE(*y);
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus-21-}
--
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus=23=P1(int *x, int *y, int *z)
--
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus-27-
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus:28: WRITE_ONCE(*y, 1);
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus-29- r1 = cmpxchg(z, 1, 0);
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus:30: r0 = READ_ONCE(*x);
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus-31-}
--
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-2.litmus=13=P0(int *x, int *y)
--
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-2.litmus-16-
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-2.litmus:17: WRITE_ONCE(*x, 1);
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-2.litmus-18- r1 = cmpxchg(y, 0, 1);
--
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-2.litmus=21=P1(int *x, int *y)
--
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-2.litmus-26- r1 = cmpxchg(y, 0, 1);
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-2.litmus:27: r2 = READ_ONCE(*x);
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-2.litmus-28-}
--
Documentation/litmus-tests/locking/DCL-broken.litmus=15=P0(int *flag, int *data, spinlock_t *lck)
--
Documentation/litmus-tests/locking/DCL-broken.litmus-20-
Documentation/litmus-tests/locking/DCL-broken.litmus:21: r0 = READ_ONCE(*flag);
Documentation/litmus-tests/locking/DCL-broken.litmus-22- if (r0 == 0) {
Documentation/litmus-tests/locking/DCL-broken.litmus-23- spin_lock(lck);
]
]
|
| 18/2 |
2026/04/04 20:03 |
llm |
expert |
0m
Model:
Tokens:
input: 39817
output: 49
thoughts: 26
Thoughts: Here's my thought process, summarized as you requested:
**Refining the Search Scope**
Okay, the initial `grepper` output is just too darn noisy. It's giving me a firehose of information, and I need to narrow it down. I need to be more surgical in my approach. The broader search is yielding too many unrelated results, and I'm wasting valuable time sifting through irrelevant material. I need to focus on the core area of interest to efficiently pinpoint what I'm looking for. That means I'm going to restrict the search to a very specific file: `net/can/bcm.c`. This will give me a much more manageable and focused dataset to work with.
|
| 19/2 |
2026/04/04 20:03 |
tool |
grepper |
0m
Args: map[Expression:READ_ONCE|WRITE_ONCE|data_race|smp_load_acquire|smp_store_release net/can/bcm.c]
Results: map[Output:
Full output is too long, showing 500 out of 59992 lines.
Use more precise expression if possible.
[Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst=78=lock-acquisition and lock-release functions::
--
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-84- 5 raw_spin_lock_rcu_node(rnp);
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst:85: 6 WRITE_ONCE(x, 1);
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst:86: 7 r1 = READ_ONCE(y);
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-87- 8 raw_spin_unlock_rcu_node(rnp);
--
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-92- 13 raw_spin_lock_rcu_node(rnp);
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst:93: 14 WRITE_ONCE(y, 1);
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst:94: 15 r2 = READ_ONCE(z);
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-95- 16 raw_spin_unlock_rcu_node(rnp);
--
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-99- 20 {
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst:100: 21 WRITE_ONCE(z, 1);
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-101- 22 smp_mb();
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst:102: 23 r3 = READ_ONCE(x);
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-103- 24 }
--
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst=112=The ``smp_mb__after_unlock_lock()`` invocations prevent this
--
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-131-| ---- ---- |
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst:132:| WRITE_ONCE(X, 1) WRITE_ONCE(Y, 1) |
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-133-| g = get_state_synchronize_rcu() smp_mb() |
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst:134:| while (!poll_state_synchronize_rcu(g)) r1 = READ_ONCE(X) |
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-135-| continue; |
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst:136:| r0 = READ_ONCE(Y) |
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-137-| |
--
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst=198=newly arrived RCU callbacks against future grace periods:
--
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-213- 12 /* Handle nohz enablement switches conservatively. */
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst:214: 13 tne = READ_ONCE(tick_nohz_active);
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-215- 14 if (tne != rdp->tick_nohz_enabled_snap) {
--
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-145- x="112.04738"
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg:146: y="268.18076">WRITE_ONCE(a, 1);</tspan></text>
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-147- <text
--
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-156- x="112.04738"
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg:157: y="439.13766">WRITE_ONCE(b, 1);</tspan></text>
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-158- <text
--
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-167- x="255.60869"
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg:168: y="309.29346">r1 = READ_ONCE(a);</tspan></text>
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-169- <text
--
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-178- x="255.14423"
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg:179: y="520.61786">WRITE_ONCE(c, 1);</tspan></text>
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-180- <text
--
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-189- x="396.10254"
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg:190: y="384.71124">r2 = READ_ONCE(b);</tspan></text>
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-191- <text
--
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-200- x="396.10254"
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg:201: y="582.13617">r3 = READ_ONCE(c);</tspan></text>
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-202- <text
--
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-173- x="112.04738"
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg:174: y="268.18076">WRITE_ONCE(a, 1);</tspan></text>
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-175- <text
--
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-184- x="112.04738"
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg:185: y="487.13766">WRITE_ONCE(b, 1);</tspan></text>
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-186- <text
--
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-195- x="255.60869"
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg:196: y="297.29346">r1 = READ_ONCE(a);</tspan></text>
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-197- <text
--
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-206- x="255.14423"
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg:207: y="554.61786">WRITE_ONCE(c, 1);</tspan></text>
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-208- <text
--
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-217- x="396.10254"
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg:218: y="370.71124">WRITE_ONCE(d, 1);</tspan></text>
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-219- <text
--
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-228- x="396.10254"
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg:229: y="572.13617">r2 = READ_ONCE(c);</tspan></text>
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-230- <text
--
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-463- x="541.70508"
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg:464: y="387.6217">r3 = READ_ONCE(d);</tspan></text>
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-465- <text
--
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-474- x="541.2406"
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg:475: y="646.94611">WRITE_ONCE(e, 1);</tspan></text>
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-476- <path
--
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-509- x="686.27747"
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg:510: y="461.83929">r4 = READ_ONCE(b);</tspan></text>
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-511- <text
--
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-520- x="686.27747"
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg:521: y="669.26422">r5 = READ_ONCE(e);</tspan></text>
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-522- <text
--
Documentation/RCU/Design/Requirements/Requirements.rst=84=overhead to readers, for example:
--
Documentation/RCU/Design/Requirements/Requirements.rst-92- 5 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:93: 6 r1 = READ_ONCE(x);
Documentation/RCU/Design/Requirements/Requirements.rst:94: 7 r2 = READ_ONCE(y);
Documentation/RCU/Design/Requirements/Requirements.rst-95- 8 rcu_read_unlock();
--
Documentation/RCU/Design/Requirements/Requirements.rst-99- 12 {
Documentation/RCU/Design/Requirements/Requirements.rst:100: 13 WRITE_ONCE(x, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-101- 14 synchronize_rcu();
Documentation/RCU/Design/Requirements/Requirements.rst:102: 15 WRITE_ONCE(y, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-103- 16 }
--
Documentation/RCU/Design/Requirements/Requirements.rst=138=recovery from node failure, more or less as follows:
--
Documentation/RCU/Design/Requirements/Requirements.rst-153- 12 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:154: 13 state_snap = READ_ONCE(state);
Documentation/RCU/Design/Requirements/Requirements.rst-155- 14 if (state_snap == STATE_NORMAL)
--
Documentation/RCU/Design/Requirements/Requirements.rst-163- 22 {
Documentation/RCU/Design/Requirements/Requirements.rst:164: 23 WRITE_ONCE(state, STATE_WANT_RECOVERY);
Documentation/RCU/Design/Requirements/Requirements.rst-165- 24 synchronize_rcu();
Documentation/RCU/Design/Requirements/Requirements.rst:166: 25 WRITE_ONCE(state, STATE_RECOVERING);
Documentation/RCU/Design/Requirements/Requirements.rst-167- 26 recovery();
Documentation/RCU/Design/Requirements/Requirements.rst:168: 27 WRITE_ONCE(state, STATE_WANT_NORMAL);
Documentation/RCU/Design/Requirements/Requirements.rst-169- 28 synchronize_rcu();
Documentation/RCU/Design/Requirements/Requirements.rst:170: 29 WRITE_ONCE(state, STATE_NORMAL);
Documentation/RCU/Design/Requirements/Requirements.rst-171- 30 }
--
Documentation/RCU/Design/Requirements/Requirements.rst=467=resembling the dependency-ordering barrier that was later subsumed
Documentation/RCU/Design/Requirements/Requirements.rst:468:into rcu_dereference() and later still into READ_ONCE(). The
Documentation/RCU/Design/Requirements/Requirements.rst-469-need for these operations made itself known quite suddenly at a
--
Documentation/RCU/Design/Requirements/Requirements.rst=702=threads:
--
Documentation/RCU/Design/Requirements/Requirements.rst-708- 3 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:709: 4 WRITE_ONCE(x, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-710- 5 rcu_read_unlock();
Documentation/RCU/Design/Requirements/Requirements.rst-711- 6 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:712: 7 WRITE_ONCE(y, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-713- 8 rcu_read_unlock();
--
Documentation/RCU/Design/Requirements/Requirements.rst-718- 13 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:719: 14 r1 = READ_ONCE(y);
Documentation/RCU/Design/Requirements/Requirements.rst-720- 15 rcu_read_unlock();
Documentation/RCU/Design/Requirements/Requirements.rst-721- 16 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:722: 17 r2 = READ_ONCE(x);
Documentation/RCU/Design/Requirements/Requirements.rst-723- 18 rcu_read_unlock();
--
Documentation/RCU/Design/Requirements/Requirements.rst=737=significant ordering constraints would slow down these fast-path APIs.
--
Documentation/RCU/Design/Requirements/Requirements.rst-745-+-----------------------------------------------------------------------+
Documentation/RCU/Design/Requirements/Requirements.rst:746:| No, the volatile casts in READ_ONCE() and WRITE_ONCE() |
Documentation/RCU/Design/Requirements/Requirements.rst-747-| prevent the compiler from reordering in this particular case. |
--
Documentation/RCU/Design/Requirements/Requirements.rst=755=example illustrates this:
--
Documentation/RCU/Design/Requirements/Requirements.rst-761- 3 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:762: 4 r1 = READ_ONCE(y);
Documentation/RCU/Design/Requirements/Requirements.rst-763- 5 if (r1) {
Documentation/RCU/Design/Requirements/Requirements.rst-764- 6 do_something_with_nonzero_x();
Documentation/RCU/Design/Requirements/Requirements.rst:765: 7 r2 = READ_ONCE(x);
Documentation/RCU/Design/Requirements/Requirements.rst-766- 8 WARN_ON(!r2); /* BUG!!! */
--
Documentation/RCU/Design/Requirements/Requirements.rst-773- 15 spin_lock(&my_lock);
Documentation/RCU/Design/Requirements/Requirements.rst:774: 16 WRITE_ONCE(x, 1);
Documentation/RCU/Design/Requirements/Requirements.rst:775: 17 WRITE_ONCE(y, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-776- 18 spin_unlock(&my_lock);
--
Documentation/RCU/Design/Requirements/Requirements.rst=819=are initially all zero:
--
Documentation/RCU/Design/Requirements/Requirements.rst-825- 3 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:826: 4 WRITE_ONCE(a, 1);
Documentation/RCU/Design/Requirements/Requirements.rst:827: 5 WRITE_ONCE(b, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-828- 6 rcu_read_unlock();
--
Documentation/RCU/Design/Requirements/Requirements.rst-832- 10 {
Documentation/RCU/Design/Requirements/Requirements.rst:833: 11 r1 = READ_ONCE(a);
Documentation/RCU/Design/Requirements/Requirements.rst-834- 12 synchronize_rcu();
Documentation/RCU/Design/Requirements/Requirements.rst:835: 13 WRITE_ONCE(c, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-836- 14 }
--
Documentation/RCU/Design/Requirements/Requirements.rst-840- 18 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:841: 19 r2 = READ_ONCE(b);
Documentation/RCU/Design/Requirements/Requirements.rst:842: 20 r3 = READ_ONCE(c);
Documentation/RCU/Design/Requirements/Requirements.rst-843- 21 rcu_read_unlock();
--
Documentation/RCU/Design/Requirements/Requirements.rst=862=period is known to end before the second grace period starts:
--
Documentation/RCU/Design/Requirements/Requirements.rst-868- 3 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:869: 4 WRITE_ONCE(a, 1);
Documentation/RCU/Design/Requirements/Requirements.rst:870: 5 WRITE_ONCE(b, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-871- 6 rcu_read_unlock();
--
Documentation/RCU/Design/Requirements/Requirements.rst-875- 10 {
Documentation/RCU/Design/Requirements/Requirements.rst:876: 11 r1 = READ_ONCE(a);
Documentation/RCU/Design/Requirements/Requirements.rst-877- 12 synchronize_rcu();
Documentation/RCU/Design/Requirements/Requirements.rst:878: 13 WRITE_ONCE(c, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-879- 14 }
--
Documentation/RCU/Design/Requirements/Requirements.rst-882- 17 {
Documentation/RCU/Design/Requirements/Requirements.rst:883: 18 r2 = READ_ONCE(c);
Documentation/RCU/Design/Requirements/Requirements.rst-884- 19 synchronize_rcu();
Documentation/RCU/Design/Requirements/Requirements.rst:885: 20 WRITE_ONCE(d, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-886- 21 }
--
Documentation/RCU/Design/Requirements/Requirements.rst-890- 25 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:891: 26 r3 = READ_ONCE(b);
Documentation/RCU/Design/Requirements/Requirements.rst:892: 27 r4 = READ_ONCE(d);
Documentation/RCU/Design/Requirements/Requirements.rst-893- 28 rcu_read_unlock();
--
Documentation/RCU/Design/Requirements/Requirements.rst=920=illustrated by the following, with all variables initially zero:
--
Documentation/RCU/Design/Requirements/Requirements.rst-926- 3 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:927: 4 WRITE_ONCE(a, 1);
Documentation/RCU/Design/Requirements/Requirements.rst:928: 5 WRITE_ONCE(b, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-929- 6 rcu_read_unlock();
--
Documentation/RCU/Design/Requirements/Requirements.rst-933- 10 {
Documentation/RCU/Design/Requirements/Requirements.rst:934: 11 r1 = READ_ONCE(a);
Documentation/RCU/Design/Requirements/Requirements.rst-935- 12 synchronize_rcu();
Documentation/RCU/Design/Requirements/Requirements.rst:936: 13 WRITE_ONCE(c, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-937- 14 }
--
Documentation/RCU/Design/Requirements/Requirements.rst-941- 18 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:942: 19 WRITE_ONCE(d, 1);
Documentation/RCU/Design/Requirements/Requirements.rst:943: 20 r2 = READ_ONCE(c);
Documentation/RCU/Design/Requirements/Requirements.rst-944- 21 rcu_read_unlock();
--
Documentation/RCU/Design/Requirements/Requirements.rst-948- 25 {
Documentation/RCU/Design/Requirements/Requirements.rst:949: 26 r3 = READ_ONCE(d);
Documentation/RCU/Design/Requirements/Requirements.rst-950- 27 synchronize_rcu();
Documentation/RCU/Design/Requirements/Requirements.rst:951: 28 WRITE_ONCE(e, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-952- 29 }
--
Documentation/RCU/Design/Requirements/Requirements.rst-956- 33 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:957: 34 r4 = READ_ONCE(b);
Documentation/RCU/Design/Requirements/Requirements.rst:958: 35 r5 = READ_ONCE(e);
Documentation/RCU/Design/Requirements/Requirements.rst-959- 36 rcu_read_unlock();
--
Documentation/RCU/checklist.rst=12=over a rather long period of time, but improvements are always welcome!
--
Documentation/RCU/checklist.rst-131- to test. Where it works, it is better to use things
Documentation/RCU/checklist.rst:132: like smp_store_release() and smp_load_acquire(), but in
Documentation/RCU/checklist.rst-133- some cases the smp_mb() full memory barrier is required.
--
Documentation/RCU/checklist.rst-363- time that readers might be accessing that structure. In such
Documentation/RCU/checklist.rst:364: cases, READ_ONCE() may be used in place of rcu_dereference()
Documentation/RCU/checklist.rst-365- and the read-side markers (rcu_read_lock() and rcu_read_unlock(),
--
Documentation/RCU/listRCU.rst=145=has become list_for_each_entry_rcu(). The **_rcu()** list-traversal
Documentation/RCU/listRCU.rst:146:primitives add READ_ONCE() and diagnostic checks for incorrect use
Documentation/RCU/listRCU.rst-147-outside of an RCU read-side critical section.
--
Documentation/RCU/rcu_dereference.rst=25=readers working properly:
--
Documentation/RCU/rcu_dereference.rst-35- return data preceding initialization that preceded the store
Documentation/RCU/rcu_dereference.rst:36: of the pointer. (As noted later, in recent kernels READ_ONCE()
Documentation/RCU/rcu_dereference.rst-37- also prevents DEC Alpha from playing these tricks.)
--
Documentation/RCU/rcu_dereference.rst-45-- In the special case where data is added but is never removed
Documentation/RCU/rcu_dereference.rst:46: while readers are accessing the structure, READ_ONCE() may be used
Documentation/RCU/rcu_dereference.rst:47: instead of rcu_dereference(). In this case, use of READ_ONCE()
Documentation/RCU/rcu_dereference.rst-48- takes on the role of the lockless_dereference() primitive that
--
Documentation/RCU/whatisRCU.rst=687=don't forget about them when submitting patches making use of RCU!]::
--
Documentation/RCU/whatisRCU.rst-695- ({ \
Documentation/RCU/whatisRCU.rst:696: typeof(p) _________p1 = READ_ONCE(p); \
Documentation/RCU/whatisRCU.rst-697- (_________p1); \
--
Documentation/RCU/whatisRCU.rst=936=but with RCU the typical approach is to perform reads with SMP-aware
Documentation/RCU/whatisRCU.rst:937:operations such as smp_load_acquire(), to perform updates with atomic
Documentation/RCU/whatisRCU.rst-938-read-modify-write operations, and to provide the necessary ordering.
--
Documentation/atomic_t.txt=82=The non-RMW ops are (typically) regular LOADs and STOREs and are canonically
Documentation/atomic_t.txt:83:implemented using READ_ONCE(), WRITE_ONCE(), smp_load_acquire() and
Documentation/atomic_t.txt-84-smp_store_release() respectively. Therefore, if you find yourself only using
--
Documentation/atomic_t.txt=119=with a lock:
--
Documentation/atomic_t.txt-124- lock();
Documentation/atomic_t.txt:125: ret = READ_ONCE(v->counter); // == 1
Documentation/atomic_t.txt-126- atomic_set(v, 0);
Documentation/atomic_t.txt:127: if (ret != u) WRITE_ONCE(v->counter, 0);
Documentation/atomic_t.txt:128: WRITE_ONCE(v->counter, ret + 1);
Documentation/atomic_t.txt-129- unlock();
--
Documentation/atomic_t.txt=234=strictly stronger than ACQUIRE. As illustrated:
--
Documentation/atomic_t.txt-242- {
Documentation/atomic_t.txt:243: r0 = READ_ONCE(*x);
Documentation/atomic_t.txt-244- smp_rmb();
--
Documentation/atomic_t.txt-251- smp_mb__after_atomic();
Documentation/atomic_t.txt:252: WRITE_ONCE(*x, 1);
Documentation/atomic_t.txt-253- }
--
Documentation/atomic_t.txt=260=because it would not order the W part of the RMW against the following
Documentation/atomic_t.txt:261:WRITE_ONCE. Thus:
Documentation/atomic_t.txt-262-
--
Documentation/core-api/circular-buffers.rst=154=The producer will look something like this::
--
Documentation/core-api/circular-buffers.rst-159- /* The spin_unlock() and next spin_lock() provide needed ordering. */
Documentation/core-api/circular-buffers.rst:160: unsigned long tail = READ_ONCE(buffer->tail);
Documentation/core-api/circular-buffers.rst-161-
--
Documentation/core-api/circular-buffers.rst=195=The consumer will look something like this::
--
Documentation/core-api/circular-buffers.rst-199- /* Read index before reading contents at that index. */
Documentation/core-api/circular-buffers.rst:200: unsigned long head = smp_load_acquire(buffer->head);
Documentation/core-api/circular-buffers.rst-201- unsigned long tail = buffer->tail;
--
Documentation/core-api/circular-buffers.rst=219=before it writes the new tail pointer, which will erase the item.
Documentation/core-api/circular-buffers.rst-220-
Documentation/core-api/circular-buffers.rst:221:Note the use of READ_ONCE() and smp_load_acquire() to read the
Documentation/core-api/circular-buffers.rst-222-opposition index. This prevents the compiler from discarding and
--
Documentation/core-api/circular-buffers.rst=224=be sure that the opposition index will _only_ be used the once.
Documentation/core-api/circular-buffers.rst:225:The smp_load_acquire() additionally forces the CPU to order against
Documentation/core-api/circular-buffers.rst-226-subsequent memory references. Similarly, smp_store_release() is used
--
Documentation/core-api/errseq.rst=144=errseq_check_and_advance after taking the lock. e.g.::
Documentation/core-api/errseq.rst-145-
Documentation/core-api/errseq.rst:146: if (errseq_check(&wd.wd_err, READ_ONCE(su.s_wd_err)) {
Documentation/core-api/errseq.rst-147- /* su.s_wd_err is protected by s_wd_err_lock */
--
Documentation/core-api/refcount-vs-atomic.rst=37=are executed in program order on a single CPU.
Documentation/core-api/refcount-vs-atomic.rst:38:This is implemented using READ_ONCE()/WRITE_ONCE() and
Documentation/core-api/refcount-vs-atomic.rst-39-compare-and-swap primitives.
--
Documentation/dev-tools/checkpatch.rst=456=Comments
--
Documentation/dev-tools/checkpatch.rst-476- **DATA_RACE**
Documentation/dev-tools/checkpatch.rst:477: Applications of data_race() should have a comment so as to document the
Documentation/dev-tools/checkpatch.rst-478- reasoning behind why it was deemed safe.
--
Documentation/dev-tools/kcsan.rst=87=the below options are available:
Documentation/dev-tools/kcsan.rst-88-
Documentation/dev-tools/kcsan.rst:89:* KCSAN understands the ``data_race(expr)`` annotation, which tells KCSAN that
Documentation/dev-tools/kcsan.rst-90- any data races due to accesses in ``expr`` should be ignored and resulting
--
Documentation/dev-tools/kcsan.rst-93-
Documentation/dev-tools/kcsan.rst:94:* Similar to ``data_race(...)``, the type qualifier ``__data_racy`` can be used
Documentation/dev-tools/kcsan.rst-95- to document that all data races due to accesses to a variable are intended
--
Documentation/dev-tools/kcsan.rst=214=and if that code is free from data races.
Documentation/dev-tools/kcsan.rst-215-
Documentation/dev-tools/kcsan.rst:216:KCSAN is aware of *marked atomic operations* (``READ_ONCE``, ``WRITE_ONCE``,
Documentation/dev-tools/kcsan.rst-217-``atomic_*``, etc.), and a subset of ordering guarantees implied by memory
--
Documentation/dev-tools/kcsan.rst=297=barrier. Consider the example::
--
Documentation/dev-tools/kcsan.rst-302- x = 1; // data race!
Documentation/dev-tools/kcsan.rst:303: WRITE_ONCE(flag, 1); // correct: smp_store_release(&flag, 1)
Documentation/dev-tools/kcsan.rst-304- }
--
Documentation/dev-tools/kcsan.rst-306- {
Documentation/dev-tools/kcsan.rst:307: while (!READ_ONCE(flag)); // correct: smp_load_acquire(&flag)
Documentation/dev-tools/kcsan.rst-308- ... = x; // data race!
--
Documentation/driver-api/surface_aggregator/internal.rst=270=submission, i.e. cancellation, can not rely on the ``ptl`` reference to be
Documentation/driver-api/surface_aggregator/internal.rst:271:set. Access to it in these functions is guarded by ``READ_ONCE()``, whereas
Documentation/driver-api/surface_aggregator/internal.rst:272:setting ``ptl`` is equally guarded with ``WRITE_ONCE()`` for symmetry.
Documentation/driver-api/surface_aggregator/internal.rst-273-
--
Documentation/driver-api/surface_aggregator/internal.rst=275=them, specifically priority and state for tracing. In those cases, proper
Documentation/driver-api/surface_aggregator/internal.rst:276:access is ensured by employing ``WRITE_ONCE()`` and ``READ_ONCE()``. Such
Documentation/driver-api/surface_aggregator/internal.rst-277-read-only access is only allowed when stale values are not critical.
--
Documentation/driver-api/surface_aggregator/internal.rst=451=them, specifically the state for tracing. In those cases, proper access is
Documentation/driver-api/surface_aggregator/internal.rst:452:ensured by employing ``WRITE_ONCE()`` and ``READ_ONCE()``. Such read-only
Documentation/driver-api/surface_aggregator/internal.rst-453-access is only allowed when stale values are not critical.
--
Documentation/driver-api/surface_aggregator/internal.rst=572=invalid usages, but rather aim to help catch them. In those cases, proper
Documentation/driver-api/surface_aggregator/internal.rst:573:variable access is ensured by employing ``WRITE_ONCE()`` and ``READ_ONCE()``.
Documentation/driver-api/surface_aggregator/internal.rst-574-
--
Documentation/filesystems/path-lookup.rst=896=similar.
Documentation/filesystems/path-lookup.rst-897-
Documentation/filesystems/path-lookup.rst:898:.. _READ_ONCE: https://lwn.net/Articles/624126/
Documentation/filesystems/path-lookup.rst-899-
--
Documentation/filesystems/path-lookup.rst=904=when accessing fields in the dentry. This "extra care" typically
Documentation/filesystems/path-lookup.rst:905:involves using `READ_ONCE() <READ_ONCE_>`_ to access fields, and verifying the
Documentation/filesystems/path-lookup.rst-906-result is not NULL before using it. This pattern can be seen in
--
Documentation/gpu/todo.rst=932=struct drm_sched_rq is read at many places without any locks, not even with a
Documentation/gpu/todo.rst:933:READ_ONCE. At XDC 2025 no one could really tell why that is the case, whether
Documentation/gpu/todo.rst-934-locks are needed and whether they could be added. (But for real, that should
--
Documentation/litmus-tests/README=48=DCL-fixed.litmus
Documentation/litmus-tests/README-49- Demonstrates corrected double-checked locking that uses
Documentation/litmus-tests/README:50: smp_store_release() and smp_load_acquire() in addition to the
Documentation/litmus-tests/README-51- obvious lock acquisitions and releases.
--
Documentation/litmus-tests/atomic/Atomic-RMW+mb__after_atomic-is-stronger-than-acquire.litmus=14=P0(int *x, atomic_t *y)
--
Documentation/litmus-tests/atomic/Atomic-RMW+mb__after_atomic-is-stronger-than-acquire.litmus-18-
Documentation/litmus-tests/atomic/Atomic-RMW+mb__after_atomic-is-stronger-than-acquire.litmus:19: r0 = READ_ONCE(*x);
Documentation/litmus-tests/atomic/Atomic-RMW+mb__after_atomic-is-stronger-than-acquire.litmus-20- smp_rmb();
--
Documentation/litmus-tests/atomic/Atomic-RMW+mb__after_atomic-is-stronger-than-acquire.litmus=24=P1(int *x, atomic_t *y)
--
Documentation/litmus-tests/atomic/Atomic-RMW+mb__after_atomic-is-stronger-than-acquire.litmus-27- smp_mb__after_atomic();
Documentation/litmus-tests/atomic/Atomic-RMW+mb__after_atomic-is-stronger-than-acquire.litmus:28: WRITE_ONCE(*x, 1);
Documentation/litmus-tests/atomic/Atomic-RMW+mb__after_atomic-is-stronger-than-acquire.litmus-29-}
--
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus=12=P0(int *x, int *y, int *z)
--
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus-16-
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus:17: WRITE_ONCE(*x, 1);
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus-18- r1 = cmpxchg(z, 1, 0);
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus-19- smp_mb__after_atomic();
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus:20: r0 = READ_ONCE(*y);
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus-21-}
--
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus=23=P1(int *x, int *y, int *z)
--
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus-27-
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus:28: WRITE_ONCE(*y, 1);
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus-29- r1 = cmpxchg(z, 1, 0);
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus-30- smp_mb__after_atomic();
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus:31: r0 = READ_ONCE(*x);
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus-32-}
--
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-2.litmus=12=P0(int *x, int *y)
--
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-2.litmus-15-
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-2.litmus:16: WRITE_ONCE(*x, 1);
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-2.litmus-17- r1 = cmpxchg(y, 0, 1);
--
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-2.litmus=20=P1(int *x, int *y)
--
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-2.litmus-26- smp_mb__after_atomic();
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-2.litmus:27: r2 = READ_ONCE(*x);
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-2.litmus-28-}
--
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus=13=P0(int *x, int *y, int *z)
--
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus-17-
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus:18: WRITE_ONCE(*x, 1);
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus-19- r1 = cmpxchg(z, 1, 0);
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus:20: r0 = READ_ONCE(*y);
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus-21-}
--
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus=23=P1(int *x, int *y, int *z)
--
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus-27-
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus:28: WRITE_ONCE(*y, 1);
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus-29- r1 = cmpxchg(z, 1, 0);
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus:30: r0 = READ_ONCE(*x);
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus-31-}
--
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-2.litmus=13=P0(int *x, int *y)
--
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-2.litmus-16-
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-2.litmus:17: WRITE_ONCE(*x, 1);
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-2.litmus-18- r1 = cmpxchg(y, 0, 1);
--
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-2.litmus=21=P1(int *x, int *y)
--
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-2.litmus-26- r1 = cmpxchg(y, 0, 1);
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-2.litmus:27: r2 = READ_ONCE(*x);
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-2.litmus-28-}
--
Documentation/litmus-tests/locking/DCL-broken.litmus=15=P0(int *flag, int *data, spinlock_t *lck)
--
Documentation/litmus-tests/locking/DCL-broken.litmus-20-
Documentation/litmus-tests/locking/DCL-broken.litmus:21: r0 = READ_ONCE(*flag);
Documentation/litmus-tests/locking/DCL-broken.litmus-22- if (r0 == 0) {
Documentation/litmus-tests/locking/DCL-broken.litmus-23- spin_lock(lck);
Documentation/litmus-tests/locking/DCL-broken.litmus:24: r1 = READ_ONCE(*flag);
Documentation/litmus-tests/locking/DCL-broken.litmus-25- if (r1 == 0) {
Documentation/litmus-tests/locking/DCL-broken.litmus:26: WRITE_ONCE(*data, 1);
Documentation/litmus-tests/locking/DCL-broken.litmus:27: WRITE_ONCE(*flag, 1);
Documentation/litmus-tests/locking/DCL-broken.litmus-28- }
--
Documentation/litmus-tests/locking/DCL-broken.litmus-30- }
Documentation/litmus-tests/locking/DCL-broken.litmus:31: r2 = READ_ONCE(*data);
Documentation/litmus-tests/locking/DCL-broken.litmus-32-}
--
Documentation/litmus-tests/locking/DCL-broken.litmus=34=P1(int *flag, int *data, spinlock_t *lck)
--
Documentation/litmus-tests/locking/DCL-broken.litmus-39-
Documentation/litmus-tests/locking/DCL-broken.litmus:40: r0 = READ_ONCE(*flag);
Documentation/litmus-tests/locking/DCL-broken.litmus-41- if (r0 == 0) {
Documentation/litmus-tests/locking/DCL-broken.litmus-42- spin_lock(lck);
Documentation/litmus-tests/locking/DCL-broken.litmus:43: r1 = READ_ONCE(*flag);
Documentation/litmus-tests/locking/DCL-broken.litmus-44- if (r1 == 0) {
Documentation/litmus-tests/locking/DCL-broken.litmus:45: WRITE_ONCE(*data, 1);
Documentation/litmus-tests/locking/DCL-broken.litmus:46: WRITE_ONCE(*flag, 1);
Documentation/litmus-tests/locking/DCL-broken.litmus-47- }
--
Documentation/litmus-tests/locking/DCL-broken.litmus-49- }
Documentation/litmus-tests/locking/DCL-broken.litmus:50: r2 = READ_ONCE(*data);
Documentation/litmus-tests/locking/DCL-broken.litmus-51-}
--
Documentation/litmus-tests/locking/DCL-fixed.litmus=1=C DCL-fixed
--
Documentation/litmus-tests/locking/DCL-fixed.litmus-6- * This litmus test demonstrates that double-checked locking can be
]
]
|
| 20/2 |
2026/04/04 20:03 |
llm |
expert |
0m
Model:
Tokens:
input: 52313
output: 49
thoughts: 13
Thoughts: Okay, here's my summary, in the first person, as if these are my thoughts, keeping in mind I'm an expert in my field:
**Direct & Efficient: `grep`**
Alright, let's get down to brass tacks. I've got a task at hand, and honestly, the simplest, most direct, and usually quickest solution is staring me right in the face: I'll just reach for `grep`. I'm going to leverage my familiarity with the command line and its powerful utilities. There's probably no need to overcomplicate things with scripts or more elaborate processing. A quick `grep` will get the job done efficiently.
|
| 21/2 |
2026/04/04 20:03 |
tool |
grepper |
0m
Args: map[Expression:READ_ONCE|WRITE_ONCE|data_race|smp_load_acquire|smp_store_release net/can/bcm.c]
Results: map[Output:
Full output is too long, showing 500 out of 59992 lines.
Use more precise expression if possible.
[Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst=78=lock-acquisition and lock-release functions::
--
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-84- 5 raw_spin_lock_rcu_node(rnp);
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst:85: 6 WRITE_ONCE(x, 1);
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst:86: 7 r1 = READ_ONCE(y);
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-87- 8 raw_spin_unlock_rcu_node(rnp);
--
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-92- 13 raw_spin_lock_rcu_node(rnp);
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst:93: 14 WRITE_ONCE(y, 1);
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst:94: 15 r2 = READ_ONCE(z);
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-95- 16 raw_spin_unlock_rcu_node(rnp);
--
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-99- 20 {
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst:100: 21 WRITE_ONCE(z, 1);
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-101- 22 smp_mb();
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst:102: 23 r3 = READ_ONCE(x);
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-103- 24 }
--
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst=112=The ``smp_mb__after_unlock_lock()`` invocations prevent this
--
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-131-| ---- ---- |
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst:132:| WRITE_ONCE(X, 1) WRITE_ONCE(Y, 1) |
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-133-| g = get_state_synchronize_rcu() smp_mb() |
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst:134:| while (!poll_state_synchronize_rcu(g)) r1 = READ_ONCE(X) |
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-135-| continue; |
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst:136:| r0 = READ_ONCE(Y) |
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-137-| |
--
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst=198=newly arrived RCU callbacks against future grace periods:
--
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-213- 12 /* Handle nohz enablement switches conservatively. */
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst:214: 13 tne = READ_ONCE(tick_nohz_active);
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-215- 14 if (tne != rdp->tick_nohz_enabled_snap) {
--
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-145- x="112.04738"
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg:146: y="268.18076">WRITE_ONCE(a, 1);</tspan></text>
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-147- <text
--
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-156- x="112.04738"
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg:157: y="439.13766">WRITE_ONCE(b, 1);</tspan></text>
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-158- <text
--
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-167- x="255.60869"
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg:168: y="309.29346">r1 = READ_ONCE(a);</tspan></text>
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-169- <text
--
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-178- x="255.14423"
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg:179: y="520.61786">WRITE_ONCE(c, 1);</tspan></text>
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-180- <text
--
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-189- x="396.10254"
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg:190: y="384.71124">r2 = READ_ONCE(b);</tspan></text>
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-191- <text
--
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-200- x="396.10254"
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg:201: y="582.13617">r3 = READ_ONCE(c);</tspan></text>
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-202- <text
--
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-173- x="112.04738"
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg:174: y="268.18076">WRITE_ONCE(a, 1);</tspan></text>
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-175- <text
--
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-184- x="112.04738"
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg:185: y="487.13766">WRITE_ONCE(b, 1);</tspan></text>
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-186- <text
--
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-195- x="255.60869"
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg:196: y="297.29346">r1 = READ_ONCE(a);</tspan></text>
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-197- <text
--
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-206- x="255.14423"
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg:207: y="554.61786">WRITE_ONCE(c, 1);</tspan></text>
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-208- <text
--
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-217- x="396.10254"
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg:218: y="370.71124">WRITE_ONCE(d, 1);</tspan></text>
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-219- <text
--
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-228- x="396.10254"
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg:229: y="572.13617">r2 = READ_ONCE(c);</tspan></text>
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-230- <text
--
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-463- x="541.70508"
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg:464: y="387.6217">r3 = READ_ONCE(d);</tspan></text>
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-465- <text
--
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-474- x="541.2406"
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg:475: y="646.94611">WRITE_ONCE(e, 1);</tspan></text>
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-476- <path
--
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-509- x="686.27747"
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg:510: y="461.83929">r4 = READ_ONCE(b);</tspan></text>
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-511- <text
--
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-520- x="686.27747"
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg:521: y="669.26422">r5 = READ_ONCE(e);</tspan></text>
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-522- <text
--
Documentation/RCU/Design/Requirements/Requirements.rst=84=overhead to readers, for example:
--
Documentation/RCU/Design/Requirements/Requirements.rst-92- 5 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:93: 6 r1 = READ_ONCE(x);
Documentation/RCU/Design/Requirements/Requirements.rst:94: 7 r2 = READ_ONCE(y);
Documentation/RCU/Design/Requirements/Requirements.rst-95- 8 rcu_read_unlock();
--
Documentation/RCU/Design/Requirements/Requirements.rst-99- 12 {
Documentation/RCU/Design/Requirements/Requirements.rst:100: 13 WRITE_ONCE(x, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-101- 14 synchronize_rcu();
Documentation/RCU/Design/Requirements/Requirements.rst:102: 15 WRITE_ONCE(y, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-103- 16 }
--
Documentation/RCU/Design/Requirements/Requirements.rst=138=recovery from node failure, more or less as follows:
--
Documentation/RCU/Design/Requirements/Requirements.rst-153- 12 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:154: 13 state_snap = READ_ONCE(state);
Documentation/RCU/Design/Requirements/Requirements.rst-155- 14 if (state_snap == STATE_NORMAL)
--
Documentation/RCU/Design/Requirements/Requirements.rst-163- 22 {
Documentation/RCU/Design/Requirements/Requirements.rst:164: 23 WRITE_ONCE(state, STATE_WANT_RECOVERY);
Documentation/RCU/Design/Requirements/Requirements.rst-165- 24 synchronize_rcu();
Documentation/RCU/Design/Requirements/Requirements.rst:166: 25 WRITE_ONCE(state, STATE_RECOVERING);
Documentation/RCU/Design/Requirements/Requirements.rst-167- 26 recovery();
Documentation/RCU/Design/Requirements/Requirements.rst:168: 27 WRITE_ONCE(state, STATE_WANT_NORMAL);
Documentation/RCU/Design/Requirements/Requirements.rst-169- 28 synchronize_rcu();
Documentation/RCU/Design/Requirements/Requirements.rst:170: 29 WRITE_ONCE(state, STATE_NORMAL);
Documentation/RCU/Design/Requirements/Requirements.rst-171- 30 }
--
Documentation/RCU/Design/Requirements/Requirements.rst=467=resembling the dependency-ordering barrier that was later subsumed
Documentation/RCU/Design/Requirements/Requirements.rst:468:into rcu_dereference() and later still into READ_ONCE(). The
Documentation/RCU/Design/Requirements/Requirements.rst-469-need for these operations made itself known quite suddenly at a
--
Documentation/RCU/Design/Requirements/Requirements.rst=702=threads:
--
Documentation/RCU/Design/Requirements/Requirements.rst-708- 3 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:709: 4 WRITE_ONCE(x, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-710- 5 rcu_read_unlock();
Documentation/RCU/Design/Requirements/Requirements.rst-711- 6 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:712: 7 WRITE_ONCE(y, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-713- 8 rcu_read_unlock();
--
Documentation/RCU/Design/Requirements/Requirements.rst-718- 13 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:719: 14 r1 = READ_ONCE(y);
Documentation/RCU/Design/Requirements/Requirements.rst-720- 15 rcu_read_unlock();
Documentation/RCU/Design/Requirements/Requirements.rst-721- 16 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:722: 17 r2 = READ_ONCE(x);
Documentation/RCU/Design/Requirements/Requirements.rst-723- 18 rcu_read_unlock();
--
Documentation/RCU/Design/Requirements/Requirements.rst=737=significant ordering constraints would slow down these fast-path APIs.
--
Documentation/RCU/Design/Requirements/Requirements.rst-745-+-----------------------------------------------------------------------+
Documentation/RCU/Design/Requirements/Requirements.rst:746:| No, the volatile casts in READ_ONCE() and WRITE_ONCE() |
Documentation/RCU/Design/Requirements/Requirements.rst-747-| prevent the compiler from reordering in this particular case. |
--
Documentation/RCU/Design/Requirements/Requirements.rst=755=example illustrates this:
--
Documentation/RCU/Design/Requirements/Requirements.rst-761- 3 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:762: 4 r1 = READ_ONCE(y);
Documentation/RCU/Design/Requirements/Requirements.rst-763- 5 if (r1) {
Documentation/RCU/Design/Requirements/Requirements.rst-764- 6 do_something_with_nonzero_x();
Documentation/RCU/Design/Requirements/Requirements.rst:765: 7 r2 = READ_ONCE(x);
Documentation/RCU/Design/Requirements/Requirements.rst-766- 8 WARN_ON(!r2); /* BUG!!! */
--
Documentation/RCU/Design/Requirements/Requirements.rst-773- 15 spin_lock(&my_lock);
Documentation/RCU/Design/Requirements/Requirements.rst:774: 16 WRITE_ONCE(x, 1);
Documentation/RCU/Design/Requirements/Requirements.rst:775: 17 WRITE_ONCE(y, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-776- 18 spin_unlock(&my_lock);
--
Documentation/RCU/Design/Requirements/Requirements.rst=819=are initially all zero:
--
Documentation/RCU/Design/Requirements/Requirements.rst-825- 3 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:826: 4 WRITE_ONCE(a, 1);
Documentation/RCU/Design/Requirements/Requirements.rst:827: 5 WRITE_ONCE(b, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-828- 6 rcu_read_unlock();
--
Documentation/RCU/Design/Requirements/Requirements.rst-832- 10 {
Documentation/RCU/Design/Requirements/Requirements.rst:833: 11 r1 = READ_ONCE(a);
Documentation/RCU/Design/Requirements/Requirements.rst-834- 12 synchronize_rcu();
Documentation/RCU/Design/Requirements/Requirements.rst:835: 13 WRITE_ONCE(c, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-836- 14 }
--
Documentation/RCU/Design/Requirements/Requirements.rst-840- 18 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:841: 19 r2 = READ_ONCE(b);
Documentation/RCU/Design/Requirements/Requirements.rst:842: 20 r3 = READ_ONCE(c);
Documentation/RCU/Design/Requirements/Requirements.rst-843- 21 rcu_read_unlock();
--
Documentation/RCU/Design/Requirements/Requirements.rst=862=period is known to end before the second grace period starts:
--
Documentation/RCU/Design/Requirements/Requirements.rst-868- 3 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:869: 4 WRITE_ONCE(a, 1);
Documentation/RCU/Design/Requirements/Requirements.rst:870: 5 WRITE_ONCE(b, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-871- 6 rcu_read_unlock();
--
Documentation/RCU/Design/Requirements/Requirements.rst-875- 10 {
Documentation/RCU/Design/Requirements/Requirements.rst:876: 11 r1 = READ_ONCE(a);
Documentation/RCU/Design/Requirements/Requirements.rst-877- 12 synchronize_rcu();
Documentation/RCU/Design/Requirements/Requirements.rst:878: 13 WRITE_ONCE(c, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-879- 14 }
--
Documentation/RCU/Design/Requirements/Requirements.rst-882- 17 {
Documentation/RCU/Design/Requirements/Requirements.rst:883: 18 r2 = READ_ONCE(c);
Documentation/RCU/Design/Requirements/Requirements.rst-884- 19 synchronize_rcu();
Documentation/RCU/Design/Requirements/Requirements.rst:885: 20 WRITE_ONCE(d, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-886- 21 }
--
Documentation/RCU/Design/Requirements/Requirements.rst-890- 25 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:891: 26 r3 = READ_ONCE(b);
Documentation/RCU/Design/Requirements/Requirements.rst:892: 27 r4 = READ_ONCE(d);
Documentation/RCU/Design/Requirements/Requirements.rst-893- 28 rcu_read_unlock();
--
Documentation/RCU/Design/Requirements/Requirements.rst=920=illustrated by the following, with all variables initially zero:
--
Documentation/RCU/Design/Requirements/Requirements.rst-926- 3 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:927: 4 WRITE_ONCE(a, 1);
Documentation/RCU/Design/Requirements/Requirements.rst:928: 5 WRITE_ONCE(b, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-929- 6 rcu_read_unlock();
--
Documentation/RCU/Design/Requirements/Requirements.rst-933- 10 {
Documentation/RCU/Design/Requirements/Requirements.rst:934: 11 r1 = READ_ONCE(a);
Documentation/RCU/Design/Requirements/Requirements.rst-935- 12 synchronize_rcu();
Documentation/RCU/Design/Requirements/Requirements.rst:936: 13 WRITE_ONCE(c, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-937- 14 }
--
Documentation/RCU/Design/Requirements/Requirements.rst-941- 18 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:942: 19 WRITE_ONCE(d, 1);
Documentation/RCU/Design/Requirements/Requirements.rst:943: 20 r2 = READ_ONCE(c);
Documentation/RCU/Design/Requirements/Requirements.rst-944- 21 rcu_read_unlock();
--
Documentation/RCU/Design/Requirements/Requirements.rst-948- 25 {
Documentation/RCU/Design/Requirements/Requirements.rst:949: 26 r3 = READ_ONCE(d);
Documentation/RCU/Design/Requirements/Requirements.rst-950- 27 synchronize_rcu();
Documentation/RCU/Design/Requirements/Requirements.rst:951: 28 WRITE_ONCE(e, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-952- 29 }
--
Documentation/RCU/Design/Requirements/Requirements.rst-956- 33 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:957: 34 r4 = READ_ONCE(b);
Documentation/RCU/Design/Requirements/Requirements.rst:958: 35 r5 = READ_ONCE(e);
Documentation/RCU/Design/Requirements/Requirements.rst-959- 36 rcu_read_unlock();
--
Documentation/RCU/checklist.rst=12=over a rather long period of time, but improvements are always welcome!
--
Documentation/RCU/checklist.rst-131- to test. Where it works, it is better to use things
Documentation/RCU/checklist.rst:132: like smp_store_release() and smp_load_acquire(), but in
Documentation/RCU/checklist.rst-133- some cases the smp_mb() full memory barrier is required.
--
Documentation/RCU/checklist.rst-363- time that readers might be accessing that structure. In such
Documentation/RCU/checklist.rst:364: cases, READ_ONCE() may be used in place of rcu_dereference()
Documentation/RCU/checklist.rst-365- and the read-side markers (rcu_read_lock() and rcu_read_unlock(),
--
Documentation/RCU/listRCU.rst=145=has become list_for_each_entry_rcu(). The **_rcu()** list-traversal
Documentation/RCU/listRCU.rst:146:primitives add READ_ONCE() and diagnostic checks for incorrect use
Documentation/RCU/listRCU.rst-147-outside of an RCU read-side critical section.
--
Documentation/RCU/rcu_dereference.rst=25=readers working properly:
--
Documentation/RCU/rcu_dereference.rst-35- return data preceding initialization that preceded the store
Documentation/RCU/rcu_dereference.rst:36: of the pointer. (As noted later, in recent kernels READ_ONCE()
Documentation/RCU/rcu_dereference.rst-37- also prevents DEC Alpha from playing these tricks.)
--
Documentation/RCU/rcu_dereference.rst-45-- In the special case where data is added but is never removed
Documentation/RCU/rcu_dereference.rst:46: while readers are accessing the structure, READ_ONCE() may be used
Documentation/RCU/rcu_dereference.rst:47: instead of rcu_dereference(). In this case, use of READ_ONCE()
Documentation/RCU/rcu_dereference.rst-48- takes on the role of the lockless_dereference() primitive that
--
Documentation/RCU/whatisRCU.rst=687=don't forget about them when submitting patches making use of RCU!]::
--
Documentation/RCU/whatisRCU.rst-695- ({ \
Documentation/RCU/whatisRCU.rst:696: typeof(p) _________p1 = READ_ONCE(p); \
Documentation/RCU/whatisRCU.rst-697- (_________p1); \
--
Documentation/RCU/whatisRCU.rst=936=but with RCU the typical approach is to perform reads with SMP-aware
Documentation/RCU/whatisRCU.rst:937:operations such as smp_load_acquire(), to perform updates with atomic
Documentation/RCU/whatisRCU.rst-938-read-modify-write operations, and to provide the necessary ordering.
--
Documentation/atomic_t.txt=82=The non-RMW ops are (typically) regular LOADs and STOREs and are canonically
Documentation/atomic_t.txt:83:implemented using READ_ONCE(), WRITE_ONCE(), smp_load_acquire() and
Documentation/atomic_t.txt-84-smp_store_release() respectively. Therefore, if you find yourself only using
--
Documentation/atomic_t.txt=119=with a lock:
--
Documentation/atomic_t.txt-124- lock();
Documentation/atomic_t.txt:125: ret = READ_ONCE(v->counter); // == 1
Documentation/atomic_t.txt-126- atomic_set(v, 0);
Documentation/atomic_t.txt:127: if (ret != u) WRITE_ONCE(v->counter, 0);
Documentation/atomic_t.txt:128: WRITE_ONCE(v->counter, ret + 1);
Documentation/atomic_t.txt-129- unlock();
--
Documentation/atomic_t.txt=234=strictly stronger than ACQUIRE. As illustrated:
--
Documentation/atomic_t.txt-242- {
Documentation/atomic_t.txt:243: r0 = READ_ONCE(*x);
Documentation/atomic_t.txt-244- smp_rmb();
--
Documentation/atomic_t.txt-251- smp_mb__after_atomic();
Documentation/atomic_t.txt:252: WRITE_ONCE(*x, 1);
Documentation/atomic_t.txt-253- }
--
Documentation/atomic_t.txt=260=because it would not order the W part of the RMW against the following
Documentation/atomic_t.txt:261:WRITE_ONCE. Thus:
Documentation/atomic_t.txt-262-
--
Documentation/core-api/circular-buffers.rst=154=The producer will look something like this::
--
Documentation/core-api/circular-buffers.rst-159- /* The spin_unlock() and next spin_lock() provide needed ordering. */
Documentation/core-api/circular-buffers.rst:160: unsigned long tail = READ_ONCE(buffer->tail);
Documentation/core-api/circular-buffers.rst-161-
--
Documentation/core-api/circular-buffers.rst=195=The consumer will look something like this::
--
Documentation/core-api/circular-buffers.rst-199- /* Read index before reading contents at that index. */
Documentation/core-api/circular-buffers.rst:200: unsigned long head = smp_load_acquire(buffer->head);
Documentation/core-api/circular-buffers.rst-201- unsigned long tail = buffer->tail;
--
Documentation/core-api/circular-buffers.rst=219=before it writes the new tail pointer, which will erase the item.
Documentation/core-api/circular-buffers.rst-220-
Documentation/core-api/circular-buffers.rst:221:Note the use of READ_ONCE() and smp_load_acquire() to read the
Documentation/core-api/circular-buffers.rst-222-opposition index. This prevents the compiler from discarding and
--
Documentation/core-api/circular-buffers.rst=224=be sure that the opposition index will _only_ be used the once.
Documentation/core-api/circular-buffers.rst:225:The smp_load_acquire() additionally forces the CPU to order against
Documentation/core-api/circular-buffers.rst-226-subsequent memory references. Similarly, smp_store_release() is used
--
Documentation/core-api/errseq.rst=144=errseq_check_and_advance after taking the lock. e.g.::
Documentation/core-api/errseq.rst-145-
Documentation/core-api/errseq.rst:146: if (errseq_check(&wd.wd_err, READ_ONCE(su.s_wd_err)) {
Documentation/core-api/errseq.rst-147- /* su.s_wd_err is protected by s_wd_err_lock */
--
Documentation/core-api/refcount-vs-atomic.rst=37=are executed in program order on a single CPU.
Documentation/core-api/refcount-vs-atomic.rst:38:This is implemented using READ_ONCE()/WRITE_ONCE() and
Documentation/core-api/refcount-vs-atomic.rst-39-compare-and-swap primitives.
--
Documentation/dev-tools/checkpatch.rst=456=Comments
--
Documentation/dev-tools/checkpatch.rst-476- **DATA_RACE**
Documentation/dev-tools/checkpatch.rst:477: Applications of data_race() should have a comment so as to document the
Documentation/dev-tools/checkpatch.rst-478- reasoning behind why it was deemed safe.
--
Documentation/dev-tools/kcsan.rst=87=the below options are available:
Documentation/dev-tools/kcsan.rst-88-
Documentation/dev-tools/kcsan.rst:89:* KCSAN understands the ``data_race(expr)`` annotation, which tells KCSAN that
Documentation/dev-tools/kcsan.rst-90- any data races due to accesses in ``expr`` should be ignored and resulting
--
Documentation/dev-tools/kcsan.rst-93-
Documentation/dev-tools/kcsan.rst:94:* Similar to ``data_race(...)``, the type qualifier ``__data_racy`` can be used
Documentation/dev-tools/kcsan.rst-95- to document that all data races due to accesses to a variable are intended
--
Documentation/dev-tools/kcsan.rst=214=and if that code is free from data races.
Documentation/dev-tools/kcsan.rst-215-
Documentation/dev-tools/kcsan.rst:216:KCSAN is aware of *marked atomic operations* (``READ_ONCE``, ``WRITE_ONCE``,
Documentation/dev-tools/kcsan.rst-217-``atomic_*``, etc.), and a subset of ordering guarantees implied by memory
--
Documentation/dev-tools/kcsan.rst=297=barrier. Consider the example::
--
Documentation/dev-tools/kcsan.rst-302- x = 1; // data race!
Documentation/dev-tools/kcsan.rst:303: WRITE_ONCE(flag, 1); // correct: smp_store_release(&flag, 1)
Documentation/dev-tools/kcsan.rst-304- }
--
Documentation/dev-tools/kcsan.rst-306- {
Documentation/dev-tools/kcsan.rst:307: while (!READ_ONCE(flag)); // correct: smp_load_acquire(&flag)
Documentation/dev-tools/kcsan.rst-308- ... = x; // data race!
--
Documentation/driver-api/surface_aggregator/internal.rst=270=submission, i.e. cancellation, can not rely on the ``ptl`` reference to be
Documentation/driver-api/surface_aggregator/internal.rst:271:set. Access to it in these functions is guarded by ``READ_ONCE()``, whereas
Documentation/driver-api/surface_aggregator/internal.rst:272:setting ``ptl`` is equally guarded with ``WRITE_ONCE()`` for symmetry.
Documentation/driver-api/surface_aggregator/internal.rst-273-
--
Documentation/driver-api/surface_aggregator/internal.rst=275=them, specifically priority and state for tracing. In those cases, proper
Documentation/driver-api/surface_aggregator/internal.rst:276:access is ensured by employing ``WRITE_ONCE()`` and ``READ_ONCE()``. Such
Documentation/driver-api/surface_aggregator/internal.rst-277-read-only access is only allowed when stale values are not critical.
--
Documentation/driver-api/surface_aggregator/internal.rst=451=them, specifically the state for tracing. In those cases, proper access is
Documentation/driver-api/surface_aggregator/internal.rst:452:ensured by employing ``WRITE_ONCE()`` and ``READ_ONCE()``. Such read-only
Documentation/driver-api/surface_aggregator/internal.rst-453-access is only allowed when stale values are not critical.
--
Documentation/driver-api/surface_aggregator/internal.rst=572=invalid usages, but rather aim to help catch them. In those cases, proper
Documentation/driver-api/surface_aggregator/internal.rst:573:variable access is ensured by employing ``WRITE_ONCE()`` and ``READ_ONCE()``.
Documentation/driver-api/surface_aggregator/internal.rst-574-
--
Documentation/filesystems/path-lookup.rst=896=similar.
Documentation/filesystems/path-lookup.rst-897-
Documentation/filesystems/path-lookup.rst:898:.. _READ_ONCE: https://lwn.net/Articles/624126/
Documentation/filesystems/path-lookup.rst-899-
--
Documentation/filesystems/path-lookup.rst=904=when accessing fields in the dentry. This "extra care" typically
Documentation/filesystems/path-lookup.rst:905:involves using `READ_ONCE() <READ_ONCE_>`_ to access fields, and verifying the
Documentation/filesystems/path-lookup.rst-906-result is not NULL before using it. This pattern can be seen in
--
Documentation/gpu/todo.rst=932=struct drm_sched_rq is read at many places without any locks, not even with a
Documentation/gpu/todo.rst:933:READ_ONCE. At XDC 2025 no one could really tell why that is the case, whether
Documentation/gpu/todo.rst-934-locks are needed and whether they could be added. (But for real, that should
--
Documentation/litmus-tests/README=48=DCL-fixed.litmus
Documentation/litmus-tests/README-49- Demonstrates corrected double-checked locking that uses
Documentation/litmus-tests/README:50: smp_store_release() and smp_load_acquire() in addition to the
Documentation/litmus-tests/README-51- obvious lock acquisitions and releases.
--
Documentation/litmus-tests/atomic/Atomic-RMW+mb__after_atomic-is-stronger-than-acquire.litmus=14=P0(int *x, atomic_t *y)
--
Documentation/litmus-tests/atomic/Atomic-RMW+mb__after_atomic-is-stronger-than-acquire.litmus-18-
Documentation/litmus-tests/atomic/Atomic-RMW+mb__after_atomic-is-stronger-than-acquire.litmus:19: r0 = READ_ONCE(*x);
Documentation/litmus-tests/atomic/Atomic-RMW+mb__after_atomic-is-stronger-than-acquire.litmus-20- smp_rmb();
--
Documentation/litmus-tests/atomic/Atomic-RMW+mb__after_atomic-is-stronger-than-acquire.litmus=24=P1(int *x, atomic_t *y)
--
Documentation/litmus-tests/atomic/Atomic-RMW+mb__after_atomic-is-stronger-than-acquire.litmus-27- smp_mb__after_atomic();
Documentation/litmus-tests/atomic/Atomic-RMW+mb__after_atomic-is-stronger-than-acquire.litmus:28: WRITE_ONCE(*x, 1);
Documentation/litmus-tests/atomic/Atomic-RMW+mb__after_atomic-is-stronger-than-acquire.litmus-29-}
--
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus=12=P0(int *x, int *y, int *z)
--
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus-16-
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus:17: WRITE_ONCE(*x, 1);
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus-18- r1 = cmpxchg(z, 1, 0);
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus-19- smp_mb__after_atomic();
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus:20: r0 = READ_ONCE(*y);
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus-21-}
--
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus=23=P1(int *x, int *y, int *z)
--
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus-27-
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus:28: WRITE_ONCE(*y, 1);
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus-29- r1 = cmpxchg(z, 1, 0);
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus-30- smp_mb__after_atomic();
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus:31: r0 = READ_ONCE(*x);
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus-32-}
--
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-2.litmus=12=P0(int *x, int *y)
--
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-2.litmus-15-
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-2.litmus:16: WRITE_ONCE(*x, 1);
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-2.litmus-17- r1 = cmpxchg(y, 0, 1);
--
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-2.litmus=20=P1(int *x, int *y)
--
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-2.litmus-26- smp_mb__after_atomic();
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-2.litmus:27: r2 = READ_ONCE(*x);
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-2.litmus-28-}
--
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus=13=P0(int *x, int *y, int *z)
--
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus-17-
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus:18: WRITE_ONCE(*x, 1);
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus-19- r1 = cmpxchg(z, 1, 0);
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus:20: r0 = READ_ONCE(*y);
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus-21-}
--
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus=23=P1(int *x, int *y, int *z)
--
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus-27-
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus:28: WRITE_ONCE(*y, 1);
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus-29- r1 = cmpxchg(z, 1, 0);
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus:30: r0 = READ_ONCE(*x);
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus-31-}
--
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-2.litmus=13=P0(int *x, int *y)
--
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-2.litmus-16-
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-2.litmus:17: WRITE_ONCE(*x, 1);
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-2.litmus-18- r1 = cmpxchg(y, 0, 1);
--
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-2.litmus=21=P1(int *x, int *y)
--
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-2.litmus-26- r1 = cmpxchg(y, 0, 1);
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-2.litmus:27: r2 = READ_ONCE(*x);
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-2.litmus-28-}
--
Documentation/litmus-tests/locking/DCL-broken.litmus=15=P0(int *flag, int *data, spinlock_t *lck)
--
Documentation/litmus-tests/locking/DCL-broken.litmus-20-
Documentation/litmus-tests/locking/DCL-broken.litmus:21: r0 = READ_ONCE(*flag);
Documentation/litmus-tests/locking/DCL-broken.litmus-22- if (r0 == 0) {
Documentation/litmus-tests/locking/DCL-broken.litmus-23- spin_lock(lck);
Documentation/litmus-tests/locking/DCL-broken.litmus:24: r1 = READ_ONCE(*flag);
Documentation/litmus-tests/locking/DCL-broken.litmus-25- if (r1 == 0) {
Documentation/litmus-tests/locking/DCL-broken.litmus:26: WRITE_ONCE(*data, 1);
Documentation/litmus-tests/locking/DCL-broken.litmus:27: WRITE_ONCE(*flag, 1);
Documentation/litmus-tests/locking/DCL-broken.litmus-28- }
--
Documentation/litmus-tests/locking/DCL-broken.litmus-30- }
Documentation/litmus-tests/locking/DCL-broken.litmus:31: r2 = READ_ONCE(*data);
Documentation/litmus-tests/locking/DCL-broken.litmus-32-}
--
Documentation/litmus-tests/locking/DCL-broken.litmus=34=P1(int *flag, int *data, spinlock_t *lck)
--
Documentation/litmus-tests/locking/DCL-broken.litmus-39-
Documentation/litmus-tests/locking/DCL-broken.litmus:40: r0 = READ_ONCE(*flag);
Documentation/litmus-tests/locking/DCL-broken.litmus-41- if (r0 == 0) {
Documentation/litmus-tests/locking/DCL-broken.litmus-42- spin_lock(lck);
Documentation/litmus-tests/locking/DCL-broken.litmus:43: r1 = READ_ONCE(*flag);
Documentation/litmus-tests/locking/DCL-broken.litmus-44- if (r1 == 0) {
Documentation/litmus-tests/locking/DCL-broken.litmus:45: WRITE_ONCE(*data, 1);
Documentation/litmus-tests/locking/DCL-broken.litmus:46: WRITE_ONCE(*flag, 1);
Documentation/litmus-tests/locking/DCL-broken.litmus-47- }
--
Documentation/litmus-tests/locking/DCL-broken.litmus-49- }
Documentation/litmus-tests/locking/DCL-broken.litmus:50: r2 = READ_ONCE(*data);
Documentation/litmus-tests/locking/DCL-broken.litmus-51-}
--
Documentation/litmus-tests/locking/DCL-fixed.litmus=1=C DCL-fixed
--
Documentation/litmus-tests/locking/DCL-fixed.litmus-6- * This litmus test demonstrates that double-checked locking can be
]
]
|
| 22/2 |
2026/04/04 20:03 |
llm |
expert |
0m
Model:
Tokens:
input: 64788
output: 49
thoughts: 12
Thoughts: Okay, here's my summary, presented as a direct thought process:
**Direct Approach: Grep It!**
Right, let's get down to brass tacks. I'm dealing with a file and need to quickly pull out some information. No need to overcomplicate things with scripts or parsing libraries (at least not initially!). The most efficient path forward, in this case, is likely to just use `grep` directly on the file. It's the Unix way, quick and dirty (in a good way!), and usually does the trick for a simple text search. It's a pragmatic and targeted solution - focusing on the immediate objective. I can always refine the approach later if this doesn't fully satisfy the need, but I'll start with the simplest and most direct approach.
|
| 23/2 |
2026/04/04 20:03 |
tool |
grepper |
0m
Args: map[Expression:READ_ONCE|WRITE_ONCE|data_race|smp_load_acquire|smp_store_release net/can/bcm.c]
Results: map[Output:
Full output is too long, showing 500 out of 59992 lines.
Use more precise expression if possible.
[Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst=78=lock-acquisition and lock-release functions::
--
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-84- 5 raw_spin_lock_rcu_node(rnp);
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst:85: 6 WRITE_ONCE(x, 1);
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst:86: 7 r1 = READ_ONCE(y);
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-87- 8 raw_spin_unlock_rcu_node(rnp);
--
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-92- 13 raw_spin_lock_rcu_node(rnp);
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst:93: 14 WRITE_ONCE(y, 1);
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst:94: 15 r2 = READ_ONCE(z);
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-95- 16 raw_spin_unlock_rcu_node(rnp);
--
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-99- 20 {
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst:100: 21 WRITE_ONCE(z, 1);
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-101- 22 smp_mb();
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst:102: 23 r3 = READ_ONCE(x);
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-103- 24 }
--
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst=112=The ``smp_mb__after_unlock_lock()`` invocations prevent this
--
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-131-| ---- ---- |
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst:132:| WRITE_ONCE(X, 1) WRITE_ONCE(Y, 1) |
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-133-| g = get_state_synchronize_rcu() smp_mb() |
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst:134:| while (!poll_state_synchronize_rcu(g)) r1 = READ_ONCE(X) |
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-135-| continue; |
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst:136:| r0 = READ_ONCE(Y) |
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-137-| |
--
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst=198=newly arrived RCU callbacks against future grace periods:
--
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-213- 12 /* Handle nohz enablement switches conservatively. */
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst:214: 13 tne = READ_ONCE(tick_nohz_active);
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst-215- 14 if (tne != rdp->tick_nohz_enabled_snap) {
--
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-145- x="112.04738"
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg:146: y="268.18076">WRITE_ONCE(a, 1);</tspan></text>
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-147- <text
--
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-156- x="112.04738"
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg:157: y="439.13766">WRITE_ONCE(b, 1);</tspan></text>
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-158- <text
--
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-167- x="255.60869"
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg:168: y="309.29346">r1 = READ_ONCE(a);</tspan></text>
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-169- <text
--
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-178- x="255.14423"
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg:179: y="520.61786">WRITE_ONCE(c, 1);</tspan></text>
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-180- <text
--
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-189- x="396.10254"
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg:190: y="384.71124">r2 = READ_ONCE(b);</tspan></text>
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-191- <text
--
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-200- x="396.10254"
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg:201: y="582.13617">r3 = READ_ONCE(c);</tspan></text>
Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg-202- <text
--
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-173- x="112.04738"
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg:174: y="268.18076">WRITE_ONCE(a, 1);</tspan></text>
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-175- <text
--
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-184- x="112.04738"
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg:185: y="487.13766">WRITE_ONCE(b, 1);</tspan></text>
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-186- <text
--
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-195- x="255.60869"
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg:196: y="297.29346">r1 = READ_ONCE(a);</tspan></text>
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-197- <text
--
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-206- x="255.14423"
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg:207: y="554.61786">WRITE_ONCE(c, 1);</tspan></text>
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-208- <text
--
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-217- x="396.10254"
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg:218: y="370.71124">WRITE_ONCE(d, 1);</tspan></text>
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-219- <text
--
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-228- x="396.10254"
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg:229: y="572.13617">r2 = READ_ONCE(c);</tspan></text>
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-230- <text
--
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-463- x="541.70508"
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg:464: y="387.6217">r3 = READ_ONCE(d);</tspan></text>
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-465- <text
--
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-474- x="541.2406"
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg:475: y="646.94611">WRITE_ONCE(e, 1);</tspan></text>
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-476- <path
--
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-509- x="686.27747"
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg:510: y="461.83929">r4 = READ_ONCE(b);</tspan></text>
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-511- <text
--
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-520- x="686.27747"
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg:521: y="669.26422">r5 = READ_ONCE(e);</tspan></text>
Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg-522- <text
--
Documentation/RCU/Design/Requirements/Requirements.rst=84=overhead to readers, for example:
--
Documentation/RCU/Design/Requirements/Requirements.rst-92- 5 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:93: 6 r1 = READ_ONCE(x);
Documentation/RCU/Design/Requirements/Requirements.rst:94: 7 r2 = READ_ONCE(y);
Documentation/RCU/Design/Requirements/Requirements.rst-95- 8 rcu_read_unlock();
--
Documentation/RCU/Design/Requirements/Requirements.rst-99- 12 {
Documentation/RCU/Design/Requirements/Requirements.rst:100: 13 WRITE_ONCE(x, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-101- 14 synchronize_rcu();
Documentation/RCU/Design/Requirements/Requirements.rst:102: 15 WRITE_ONCE(y, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-103- 16 }
--
Documentation/RCU/Design/Requirements/Requirements.rst=138=recovery from node failure, more or less as follows:
--
Documentation/RCU/Design/Requirements/Requirements.rst-153- 12 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:154: 13 state_snap = READ_ONCE(state);
Documentation/RCU/Design/Requirements/Requirements.rst-155- 14 if (state_snap == STATE_NORMAL)
--
Documentation/RCU/Design/Requirements/Requirements.rst-163- 22 {
Documentation/RCU/Design/Requirements/Requirements.rst:164: 23 WRITE_ONCE(state, STATE_WANT_RECOVERY);
Documentation/RCU/Design/Requirements/Requirements.rst-165- 24 synchronize_rcu();
Documentation/RCU/Design/Requirements/Requirements.rst:166: 25 WRITE_ONCE(state, STATE_RECOVERING);
Documentation/RCU/Design/Requirements/Requirements.rst-167- 26 recovery();
Documentation/RCU/Design/Requirements/Requirements.rst:168: 27 WRITE_ONCE(state, STATE_WANT_NORMAL);
Documentation/RCU/Design/Requirements/Requirements.rst-169- 28 synchronize_rcu();
Documentation/RCU/Design/Requirements/Requirements.rst:170: 29 WRITE_ONCE(state, STATE_NORMAL);
Documentation/RCU/Design/Requirements/Requirements.rst-171- 30 }
--
Documentation/RCU/Design/Requirements/Requirements.rst=467=resembling the dependency-ordering barrier that was later subsumed
Documentation/RCU/Design/Requirements/Requirements.rst:468:into rcu_dereference() and later still into READ_ONCE(). The
Documentation/RCU/Design/Requirements/Requirements.rst-469-need for these operations made itself known quite suddenly at a
--
Documentation/RCU/Design/Requirements/Requirements.rst=702=threads:
--
Documentation/RCU/Design/Requirements/Requirements.rst-708- 3 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:709: 4 WRITE_ONCE(x, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-710- 5 rcu_read_unlock();
Documentation/RCU/Design/Requirements/Requirements.rst-711- 6 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:712: 7 WRITE_ONCE(y, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-713- 8 rcu_read_unlock();
--
Documentation/RCU/Design/Requirements/Requirements.rst-718- 13 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:719: 14 r1 = READ_ONCE(y);
Documentation/RCU/Design/Requirements/Requirements.rst-720- 15 rcu_read_unlock();
Documentation/RCU/Design/Requirements/Requirements.rst-721- 16 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:722: 17 r2 = READ_ONCE(x);
Documentation/RCU/Design/Requirements/Requirements.rst-723- 18 rcu_read_unlock();
--
Documentation/RCU/Design/Requirements/Requirements.rst=737=significant ordering constraints would slow down these fast-path APIs.
--
Documentation/RCU/Design/Requirements/Requirements.rst-745-+-----------------------------------------------------------------------+
Documentation/RCU/Design/Requirements/Requirements.rst:746:| No, the volatile casts in READ_ONCE() and WRITE_ONCE() |
Documentation/RCU/Design/Requirements/Requirements.rst-747-| prevent the compiler from reordering in this particular case. |
--
Documentation/RCU/Design/Requirements/Requirements.rst=755=example illustrates this:
--
Documentation/RCU/Design/Requirements/Requirements.rst-761- 3 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:762: 4 r1 = READ_ONCE(y);
Documentation/RCU/Design/Requirements/Requirements.rst-763- 5 if (r1) {
Documentation/RCU/Design/Requirements/Requirements.rst-764- 6 do_something_with_nonzero_x();
Documentation/RCU/Design/Requirements/Requirements.rst:765: 7 r2 = READ_ONCE(x);
Documentation/RCU/Design/Requirements/Requirements.rst-766- 8 WARN_ON(!r2); /* BUG!!! */
--
Documentation/RCU/Design/Requirements/Requirements.rst-773- 15 spin_lock(&my_lock);
Documentation/RCU/Design/Requirements/Requirements.rst:774: 16 WRITE_ONCE(x, 1);
Documentation/RCU/Design/Requirements/Requirements.rst:775: 17 WRITE_ONCE(y, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-776- 18 spin_unlock(&my_lock);
--
Documentation/RCU/Design/Requirements/Requirements.rst=819=are initially all zero:
--
Documentation/RCU/Design/Requirements/Requirements.rst-825- 3 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:826: 4 WRITE_ONCE(a, 1);
Documentation/RCU/Design/Requirements/Requirements.rst:827: 5 WRITE_ONCE(b, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-828- 6 rcu_read_unlock();
--
Documentation/RCU/Design/Requirements/Requirements.rst-832- 10 {
Documentation/RCU/Design/Requirements/Requirements.rst:833: 11 r1 = READ_ONCE(a);
Documentation/RCU/Design/Requirements/Requirements.rst-834- 12 synchronize_rcu();
Documentation/RCU/Design/Requirements/Requirements.rst:835: 13 WRITE_ONCE(c, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-836- 14 }
--
Documentation/RCU/Design/Requirements/Requirements.rst-840- 18 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:841: 19 r2 = READ_ONCE(b);
Documentation/RCU/Design/Requirements/Requirements.rst:842: 20 r3 = READ_ONCE(c);
Documentation/RCU/Design/Requirements/Requirements.rst-843- 21 rcu_read_unlock();
--
Documentation/RCU/Design/Requirements/Requirements.rst=862=period is known to end before the second grace period starts:
--
Documentation/RCU/Design/Requirements/Requirements.rst-868- 3 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:869: 4 WRITE_ONCE(a, 1);
Documentation/RCU/Design/Requirements/Requirements.rst:870: 5 WRITE_ONCE(b, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-871- 6 rcu_read_unlock();
--
Documentation/RCU/Design/Requirements/Requirements.rst-875- 10 {
Documentation/RCU/Design/Requirements/Requirements.rst:876: 11 r1 = READ_ONCE(a);
Documentation/RCU/Design/Requirements/Requirements.rst-877- 12 synchronize_rcu();
Documentation/RCU/Design/Requirements/Requirements.rst:878: 13 WRITE_ONCE(c, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-879- 14 }
--
Documentation/RCU/Design/Requirements/Requirements.rst-882- 17 {
Documentation/RCU/Design/Requirements/Requirements.rst:883: 18 r2 = READ_ONCE(c);
Documentation/RCU/Design/Requirements/Requirements.rst-884- 19 synchronize_rcu();
Documentation/RCU/Design/Requirements/Requirements.rst:885: 20 WRITE_ONCE(d, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-886- 21 }
--
Documentation/RCU/Design/Requirements/Requirements.rst-890- 25 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:891: 26 r3 = READ_ONCE(b);
Documentation/RCU/Design/Requirements/Requirements.rst:892: 27 r4 = READ_ONCE(d);
Documentation/RCU/Design/Requirements/Requirements.rst-893- 28 rcu_read_unlock();
--
Documentation/RCU/Design/Requirements/Requirements.rst=920=illustrated by the following, with all variables initially zero:
--
Documentation/RCU/Design/Requirements/Requirements.rst-926- 3 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:927: 4 WRITE_ONCE(a, 1);
Documentation/RCU/Design/Requirements/Requirements.rst:928: 5 WRITE_ONCE(b, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-929- 6 rcu_read_unlock();
--
Documentation/RCU/Design/Requirements/Requirements.rst-933- 10 {
Documentation/RCU/Design/Requirements/Requirements.rst:934: 11 r1 = READ_ONCE(a);
Documentation/RCU/Design/Requirements/Requirements.rst-935- 12 synchronize_rcu();
Documentation/RCU/Design/Requirements/Requirements.rst:936: 13 WRITE_ONCE(c, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-937- 14 }
--
Documentation/RCU/Design/Requirements/Requirements.rst-941- 18 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:942: 19 WRITE_ONCE(d, 1);
Documentation/RCU/Design/Requirements/Requirements.rst:943: 20 r2 = READ_ONCE(c);
Documentation/RCU/Design/Requirements/Requirements.rst-944- 21 rcu_read_unlock();
--
Documentation/RCU/Design/Requirements/Requirements.rst-948- 25 {
Documentation/RCU/Design/Requirements/Requirements.rst:949: 26 r3 = READ_ONCE(d);
Documentation/RCU/Design/Requirements/Requirements.rst-950- 27 synchronize_rcu();
Documentation/RCU/Design/Requirements/Requirements.rst:951: 28 WRITE_ONCE(e, 1);
Documentation/RCU/Design/Requirements/Requirements.rst-952- 29 }
--
Documentation/RCU/Design/Requirements/Requirements.rst-956- 33 rcu_read_lock();
Documentation/RCU/Design/Requirements/Requirements.rst:957: 34 r4 = READ_ONCE(b);
Documentation/RCU/Design/Requirements/Requirements.rst:958: 35 r5 = READ_ONCE(e);
Documentation/RCU/Design/Requirements/Requirements.rst-959- 36 rcu_read_unlock();
--
Documentation/RCU/checklist.rst=12=over a rather long period of time, but improvements are always welcome!
--
Documentation/RCU/checklist.rst-131- to test. Where it works, it is better to use things
Documentation/RCU/checklist.rst:132: like smp_store_release() and smp_load_acquire(), but in
Documentation/RCU/checklist.rst-133- some cases the smp_mb() full memory barrier is required.
--
Documentation/RCU/checklist.rst-363- time that readers might be accessing that structure. In such
Documentation/RCU/checklist.rst:364: cases, READ_ONCE() may be used in place of rcu_dereference()
Documentation/RCU/checklist.rst-365- and the read-side markers (rcu_read_lock() and rcu_read_unlock(),
--
Documentation/RCU/listRCU.rst=145=has become list_for_each_entry_rcu(). The **_rcu()** list-traversal
Documentation/RCU/listRCU.rst:146:primitives add READ_ONCE() and diagnostic checks for incorrect use
Documentation/RCU/listRCU.rst-147-outside of an RCU read-side critical section.
--
Documentation/RCU/rcu_dereference.rst=25=readers working properly:
--
Documentation/RCU/rcu_dereference.rst-35- return data preceding initialization that preceded the store
Documentation/RCU/rcu_dereference.rst:36: of the pointer. (As noted later, in recent kernels READ_ONCE()
Documentation/RCU/rcu_dereference.rst-37- also prevents DEC Alpha from playing these tricks.)
--
Documentation/RCU/rcu_dereference.rst-45-- In the special case where data is added but is never removed
Documentation/RCU/rcu_dereference.rst:46: while readers are accessing the structure, READ_ONCE() may be used
Documentation/RCU/rcu_dereference.rst:47: instead of rcu_dereference(). In this case, use of READ_ONCE()
Documentation/RCU/rcu_dereference.rst-48- takes on the role of the lockless_dereference() primitive that
--
Documentation/RCU/whatisRCU.rst=687=don't forget about them when submitting patches making use of RCU!]::
--
Documentation/RCU/whatisRCU.rst-695- ({ \
Documentation/RCU/whatisRCU.rst:696: typeof(p) _________p1 = READ_ONCE(p); \
Documentation/RCU/whatisRCU.rst-697- (_________p1); \
--
Documentation/RCU/whatisRCU.rst=936=but with RCU the typical approach is to perform reads with SMP-aware
Documentation/RCU/whatisRCU.rst:937:operations such as smp_load_acquire(), to perform updates with atomic
Documentation/RCU/whatisRCU.rst-938-read-modify-write operations, and to provide the necessary ordering.
--
Documentation/atomic_t.txt=82=The non-RMW ops are (typically) regular LOADs and STOREs and are canonically
Documentation/atomic_t.txt:83:implemented using READ_ONCE(), WRITE_ONCE(), smp_load_acquire() and
Documentation/atomic_t.txt-84-smp_store_release() respectively. Therefore, if you find yourself only using
--
Documentation/atomic_t.txt=119=with a lock:
--
Documentation/atomic_t.txt-124- lock();
Documentation/atomic_t.txt:125: ret = READ_ONCE(v->counter); // == 1
Documentation/atomic_t.txt-126- atomic_set(v, 0);
Documentation/atomic_t.txt:127: if (ret != u) WRITE_ONCE(v->counter, 0);
Documentation/atomic_t.txt:128: WRITE_ONCE(v->counter, ret + 1);
Documentation/atomic_t.txt-129- unlock();
--
Documentation/atomic_t.txt=234=strictly stronger than ACQUIRE. As illustrated:
--
Documentation/atomic_t.txt-242- {
Documentation/atomic_t.txt:243: r0 = READ_ONCE(*x);
Documentation/atomic_t.txt-244- smp_rmb();
--
Documentation/atomic_t.txt-251- smp_mb__after_atomic();
Documentation/atomic_t.txt:252: WRITE_ONCE(*x, 1);
Documentation/atomic_t.txt-253- }
--
Documentation/atomic_t.txt=260=because it would not order the W part of the RMW against the following
Documentation/atomic_t.txt:261:WRITE_ONCE. Thus:
Documentation/atomic_t.txt-262-
--
Documentation/core-api/circular-buffers.rst=154=The producer will look something like this::
--
Documentation/core-api/circular-buffers.rst-159- /* The spin_unlock() and next spin_lock() provide needed ordering. */
Documentation/core-api/circular-buffers.rst:160: unsigned long tail = READ_ONCE(buffer->tail);
Documentation/core-api/circular-buffers.rst-161-
--
Documentation/core-api/circular-buffers.rst=195=The consumer will look something like this::
--
Documentation/core-api/circular-buffers.rst-199- /* Read index before reading contents at that index. */
Documentation/core-api/circular-buffers.rst:200: unsigned long head = smp_load_acquire(buffer->head);
Documentation/core-api/circular-buffers.rst-201- unsigned long tail = buffer->tail;
--
Documentation/core-api/circular-buffers.rst=219=before it writes the new tail pointer, which will erase the item.
Documentation/core-api/circular-buffers.rst-220-
Documentation/core-api/circular-buffers.rst:221:Note the use of READ_ONCE() and smp_load_acquire() to read the
Documentation/core-api/circular-buffers.rst-222-opposition index. This prevents the compiler from discarding and
--
Documentation/core-api/circular-buffers.rst=224=be sure that the opposition index will _only_ be used the once.
Documentation/core-api/circular-buffers.rst:225:The smp_load_acquire() additionally forces the CPU to order against
Documentation/core-api/circular-buffers.rst-226-subsequent memory references. Similarly, smp_store_release() is used
--
Documentation/core-api/errseq.rst=144=errseq_check_and_advance after taking the lock. e.g.::
Documentation/core-api/errseq.rst-145-
Documentation/core-api/errseq.rst:146: if (errseq_check(&wd.wd_err, READ_ONCE(su.s_wd_err)) {
Documentation/core-api/errseq.rst-147- /* su.s_wd_err is protected by s_wd_err_lock */
--
Documentation/core-api/refcount-vs-atomic.rst=37=are executed in program order on a single CPU.
Documentation/core-api/refcount-vs-atomic.rst:38:This is implemented using READ_ONCE()/WRITE_ONCE() and
Documentation/core-api/refcount-vs-atomic.rst-39-compare-and-swap primitives.
--
Documentation/dev-tools/checkpatch.rst=456=Comments
--
Documentation/dev-tools/checkpatch.rst-476- **DATA_RACE**
Documentation/dev-tools/checkpatch.rst:477: Applications of data_race() should have a comment so as to document the
Documentation/dev-tools/checkpatch.rst-478- reasoning behind why it was deemed safe.
--
Documentation/dev-tools/kcsan.rst=87=the below options are available:
Documentation/dev-tools/kcsan.rst-88-
Documentation/dev-tools/kcsan.rst:89:* KCSAN understands the ``data_race(expr)`` annotation, which tells KCSAN that
Documentation/dev-tools/kcsan.rst-90- any data races due to accesses in ``expr`` should be ignored and resulting
--
Documentation/dev-tools/kcsan.rst-93-
Documentation/dev-tools/kcsan.rst:94:* Similar to ``data_race(...)``, the type qualifier ``__data_racy`` can be used
Documentation/dev-tools/kcsan.rst-95- to document that all data races due to accesses to a variable are intended
--
Documentation/dev-tools/kcsan.rst=214=and if that code is free from data races.
Documentation/dev-tools/kcsan.rst-215-
Documentation/dev-tools/kcsan.rst:216:KCSAN is aware of *marked atomic operations* (``READ_ONCE``, ``WRITE_ONCE``,
Documentation/dev-tools/kcsan.rst-217-``atomic_*``, etc.), and a subset of ordering guarantees implied by memory
--
Documentation/dev-tools/kcsan.rst=297=barrier. Consider the example::
--
Documentation/dev-tools/kcsan.rst-302- x = 1; // data race!
Documentation/dev-tools/kcsan.rst:303: WRITE_ONCE(flag, 1); // correct: smp_store_release(&flag, 1)
Documentation/dev-tools/kcsan.rst-304- }
--
Documentation/dev-tools/kcsan.rst-306- {
Documentation/dev-tools/kcsan.rst:307: while (!READ_ONCE(flag)); // correct: smp_load_acquire(&flag)
Documentation/dev-tools/kcsan.rst-308- ... = x; // data race!
--
Documentation/driver-api/surface_aggregator/internal.rst=270=submission, i.e. cancellation, can not rely on the ``ptl`` reference to be
Documentation/driver-api/surface_aggregator/internal.rst:271:set. Access to it in these functions is guarded by ``READ_ONCE()``, whereas
Documentation/driver-api/surface_aggregator/internal.rst:272:setting ``ptl`` is equally guarded with ``WRITE_ONCE()`` for symmetry.
Documentation/driver-api/surface_aggregator/internal.rst-273-
--
Documentation/driver-api/surface_aggregator/internal.rst=275=them, specifically priority and state for tracing. In those cases, proper
Documentation/driver-api/surface_aggregator/internal.rst:276:access is ensured by employing ``WRITE_ONCE()`` and ``READ_ONCE()``. Such
Documentation/driver-api/surface_aggregator/internal.rst-277-read-only access is only allowed when stale values are not critical.
--
Documentation/driver-api/surface_aggregator/internal.rst=451=them, specifically the state for tracing. In those cases, proper access is
Documentation/driver-api/surface_aggregator/internal.rst:452:ensured by employing ``WRITE_ONCE()`` and ``READ_ONCE()``. Such read-only
Documentation/driver-api/surface_aggregator/internal.rst-453-access is only allowed when stale values are not critical.
--
Documentation/driver-api/surface_aggregator/internal.rst=572=invalid usages, but rather aim to help catch them. In those cases, proper
Documentation/driver-api/surface_aggregator/internal.rst:573:variable access is ensured by employing ``WRITE_ONCE()`` and ``READ_ONCE()``.
Documentation/driver-api/surface_aggregator/internal.rst-574-
--
Documentation/filesystems/path-lookup.rst=896=similar.
Documentation/filesystems/path-lookup.rst-897-
Documentation/filesystems/path-lookup.rst:898:.. _READ_ONCE: https://lwn.net/Articles/624126/
Documentation/filesystems/path-lookup.rst-899-
--
Documentation/filesystems/path-lookup.rst=904=when accessing fields in the dentry. This "extra care" typically
Documentation/filesystems/path-lookup.rst:905:involves using `READ_ONCE() <READ_ONCE_>`_ to access fields, and verifying the
Documentation/filesystems/path-lookup.rst-906-result is not NULL before using it. This pattern can be seen in
--
Documentation/gpu/todo.rst=932=struct drm_sched_rq is read at many places without any locks, not even with a
Documentation/gpu/todo.rst:933:READ_ONCE. At XDC 2025 no one could really tell why that is the case, whether
Documentation/gpu/todo.rst-934-locks are needed and whether they could be added. (But for real, that should
--
Documentation/litmus-tests/README=48=DCL-fixed.litmus
Documentation/litmus-tests/README-49- Demonstrates corrected double-checked locking that uses
Documentation/litmus-tests/README:50: smp_store_release() and smp_load_acquire() in addition to the
Documentation/litmus-tests/README-51- obvious lock acquisitions and releases.
--
Documentation/litmus-tests/atomic/Atomic-RMW+mb__after_atomic-is-stronger-than-acquire.litmus=14=P0(int *x, atomic_t *y)
--
Documentation/litmus-tests/atomic/Atomic-RMW+mb__after_atomic-is-stronger-than-acquire.litmus-18-
Documentation/litmus-tests/atomic/Atomic-RMW+mb__after_atomic-is-stronger-than-acquire.litmus:19: r0 = READ_ONCE(*x);
Documentation/litmus-tests/atomic/Atomic-RMW+mb__after_atomic-is-stronger-than-acquire.litmus-20- smp_rmb();
--
Documentation/litmus-tests/atomic/Atomic-RMW+mb__after_atomic-is-stronger-than-acquire.litmus=24=P1(int *x, atomic_t *y)
--
Documentation/litmus-tests/atomic/Atomic-RMW+mb__after_atomic-is-stronger-than-acquire.litmus-27- smp_mb__after_atomic();
Documentation/litmus-tests/atomic/Atomic-RMW+mb__after_atomic-is-stronger-than-acquire.litmus:28: WRITE_ONCE(*x, 1);
Documentation/litmus-tests/atomic/Atomic-RMW+mb__after_atomic-is-stronger-than-acquire.litmus-29-}
--
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus=12=P0(int *x, int *y, int *z)
--
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus-16-
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus:17: WRITE_ONCE(*x, 1);
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus-18- r1 = cmpxchg(z, 1, 0);
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus-19- smp_mb__after_atomic();
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus:20: r0 = READ_ONCE(*y);
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus-21-}
--
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus=23=P1(int *x, int *y, int *z)
--
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus-27-
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus:28: WRITE_ONCE(*y, 1);
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus-29- r1 = cmpxchg(z, 1, 0);
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus-30- smp_mb__after_atomic();
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus:31: r0 = READ_ONCE(*x);
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-1.litmus-32-}
--
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-2.litmus=12=P0(int *x, int *y)
--
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-2.litmus-15-
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-2.litmus:16: WRITE_ONCE(*x, 1);
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-2.litmus-17- r1 = cmpxchg(y, 0, 1);
--
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-2.litmus=20=P1(int *x, int *y)
--
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-2.litmus-26- smp_mb__after_atomic();
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-2.litmus:27: r2 = READ_ONCE(*x);
Documentation/litmus-tests/atomic/cmpxchg-fail-ordered-2.litmus-28-}
--
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus=13=P0(int *x, int *y, int *z)
--
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus-17-
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus:18: WRITE_ONCE(*x, 1);
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus-19- r1 = cmpxchg(z, 1, 0);
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus:20: r0 = READ_ONCE(*y);
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus-21-}
--
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus=23=P1(int *x, int *y, int *z)
--
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus-27-
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus:28: WRITE_ONCE(*y, 1);
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus-29- r1 = cmpxchg(z, 1, 0);
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus:30: r0 = READ_ONCE(*x);
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-1.litmus-31-}
--
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-2.litmus=13=P0(int *x, int *y)
--
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-2.litmus-16-
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-2.litmus:17: WRITE_ONCE(*x, 1);
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-2.litmus-18- r1 = cmpxchg(y, 0, 1);
--
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-2.litmus=21=P1(int *x, int *y)
--
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-2.litmus-26- r1 = cmpxchg(y, 0, 1);
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-2.litmus:27: r2 = READ_ONCE(*x);
Documentation/litmus-tests/atomic/cmpxchg-fail-unordered-2.litmus-28-}
--
Documentation/litmus-tests/locking/DCL-broken.litmus=15=P0(int *flag, int *data, spinlock_t *lck)
--
Documentation/litmus-tests/locking/DCL-broken.litmus-20-
Documentation/litmus-tests/locking/DCL-broken.litmus:21: r0 = READ_ONCE(*flag);
Documentation/litmus-tests/locking/DCL-broken.litmus-22- if (r0 == 0) {
Documentation/litmus-tests/locking/DCL-broken.litmus-23- spin_lock(lck);
Documentation/litmus-tests/locking/DCL-broken.litmus:24: r1 = READ_ONCE(*flag);
Documentation/litmus-tests/locking/DCL-broken.litmus-25- if (r1 == 0) {
Documentation/litmus-tests/locking/DCL-broken.litmus:26: WRITE_ONCE(*data, 1);
Documentation/litmus-tests/locking/DCL-broken.litmus:27: WRITE_ONCE(*flag, 1);
Documentation/litmus-tests/locking/DCL-broken.litmus-28- }
--
Documentation/litmus-tests/locking/DCL-broken.litmus-30- }
Documentation/litmus-tests/locking/DCL-broken.litmus:31: r2 = READ_ONCE(*data);
Documentation/litmus-tests/locking/DCL-broken.litmus-32-}
--
Documentation/litmus-tests/locking/DCL-broken.litmus=34=P1(int *flag, int *data, spinlock_t *lck)
--
Documentation/litmus-tests/locking/DCL-broken.litmus-39-
Documentation/litmus-tests/locking/DCL-broken.litmus:40: r0 = READ_ONCE(*flag);
Documentation/litmus-tests/locking/DCL-broken.litmus-41- if (r0 == 0) {
Documentation/litmus-tests/locking/DCL-broken.litmus-42- spin_lock(lck);
Documentation/litmus-tests/locking/DCL-broken.litmus:43: r1 = READ_ONCE(*flag);
Documentation/litmus-tests/locking/DCL-broken.litmus-44- if (r1 == 0) {
Documentation/litmus-tests/locking/DCL-broken.litmus:45: WRITE_ONCE(*data, 1);
Documentation/litmus-tests/locking/DCL-broken.litmus:46: WRITE_ONCE(*flag, 1);
Documentation/litmus-tests/locking/DCL-broken.litmus-47- }
--
Documentation/litmus-tests/locking/DCL-broken.litmus-49- }
Documentation/litmus-tests/locking/DCL-broken.litmus:50: r2 = READ_ONCE(*data);
Documentation/litmus-tests/locking/DCL-broken.litmus-51-}
--
Documentation/litmus-tests/locking/DCL-fixed.litmus=1=C DCL-fixed
--
Documentation/litmus-tests/locking/DCL-fixed.litmus-6- * This litmus test demonstrates that double-checked locking can be
]
]
|
| 24/2 |
2026/04/04 20:03 |
llm |
expert |
0m
Model:
Tokens:
input: 77288
output: 23
thoughts: 35
Thoughts: Okay, here's my take on that:
**Manual Inspection is Necessary**
Right, so `grepper` is letting me down – not the first time that's happened, and certainly not the last. Guess I'll have to go the old-fashioned route. Time to pull up `net/can/bcm.c` using `read-file` and get my hands dirty, manually searching for what I need. Sometimes, you just have to bypass the automation and rely on good ol' fashioned code sleuthing. Let's see what secrets that source code is hiding.
|
| 25/2 |
2026/04/04 20:03 |
tool |
read-file |
0m
Args:
Results: map[Contents:// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
/*
* bcm.c - Broadcast Manager to filter/send (cyclic) CAN content
*
* Copyright (c) 2002-2017 Volkswagen Group Electronic Research
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Volkswagen nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* Alternatively, provided that this notice is retained in full, this
* software may be distributed under the terms of the GNU General
* Public License ("GPL") version 2, in which case the provisions of the
* GPL apply INSTEAD OF those given above.
*
* The provided data structures and external interfaces from this code
* are not restricted to be used by modules with a GPL compatible license.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/hrtimer.h>
#include <linux/list.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/uio.h>
#include <linux/net.h>
#include <linux/netdevice.h>
#include <linux/socket.h>
#include <linux/if_arp.h>
#include <linux/skbuff.h>
#include <linux/can.h>
#include <linux/can/core.h>
#include <linux/can/skb.h>
#include <linux/can/bcm.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <net/can.h>
#include <net/sock.h>
#include <net/net_namespace.h>
/*
* To send multiple CAN frame content within TX_SETUP or to filter
* CAN messages with multiplex index within RX_SETUP, the number of
* different filters is limited to 256 due to the one byte index value.
*/
#define MAX_NFRAMES 256
/* limit timers to 400 days for sending/timeouts */
#define BCM_TIMER_SEC_MAX (400 * 24 * 60 * 60)
/* use of last_frames[index].flags */
#define RX_LOCAL 0x10 /* frame was created on the local host */
#define RX_OWN 0x20 /* frame was sent via the socket it was received on */
#define RX_RECV 0x40 /* received data for this element */
#define RX_THR 0x80 /* element not been sent due to throttle feature */
#define BCM_CAN_FLAGS_MASK 0x0F /* to clean private flags after usage */
/* get best masking value for can_rx_register() for a given single can_id */
#define REGMASK(id) ((id & CAN_EFF_FLAG) ? \
(CAN_EFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG) : \
(CAN_SFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG))
MODULE_DESCRIPTION("PF_CAN broadcast manager protocol");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
MODULE_ALIAS("can-proto-2");
#define BCM_MIN_NAMELEN CAN_REQUIRED_SIZE(struct sockaddr_can, can_ifindex)
/*
* easy access to the first 64 bit of can(fd)_frame payload. cp->data is
* 64 bit aligned so the offset has to be multiples of 8 which is ensured
* by the only callers in bcm_rx_cmp_to_index() bcm_rx_handler().
*/
static inline u64 get_u64(const struct canfd_frame *cp, int offset)
{
return *(u64 *)(cp->data + offset);
}
struct bcm_op {
struct list_head list;
struct rcu_head rcu;
int ifindex;
canid_t can_id;
u32 flags;
unsigned long frames_abs, frames_filtered;
struct bcm_timeval ival1, ival2;
struct hrtimer timer, thrtimer;
ktime_t rx_stamp, kt_ival1, kt_ival2, kt_lastmsg;
int rx_ifindex;
int cfsiz;
u32 count;
u32 nframes;
u32 currframe;
/* void pointers to arrays of struct can[fd]_frame */
void *frames;
void *last_frames;
struct canfd_frame sframe;
struct canfd_frame last_sframe;
struct sock *sk;
struct net_device *rx_reg_dev;
spinlock_t bcm_tx_lock; /* protect currframe/count in runtime updates */
};
struct bcm_sock {
struct sock sk;
int bound;
int ifindex;
struct list_head notifier;
struct list_head rx_ops;
struct list_head tx_ops;
unsigned long dropped_usr_msgs;
struct proc_dir_entry *bcm_proc_read;
char procname [32]; /* inode number in decimal with \0 */
};
static LIST_HEAD(bcm_notifier_list);
static DEFINE_SPINLOCK(bcm_notifier_lock);
static struct bcm_sock *bcm_busy_notifier;
/* Return pointer to store the extra msg flags for bcm_recvmsg().
* We use the space of one unsigned int beyond the 'struct sockaddr_can'
* in skb->cb.
*/
static inline unsigned int *bcm_flags(struct sk_buff *skb)
{
/* return pointer after struct sockaddr_can */
return (unsigned int *)(&((struct sockaddr_can *)skb->cb)[1]);
}
static inline struct bcm_sock *bcm_sk(const struct sock *sk)
{
return (struct bcm_sock *)sk;
}
static inline ktime_t bcm_timeval_to_ktime(struct bcm_timeval tv)
{
return ktime_set(tv.tv_sec, tv.tv_usec * NSEC_PER_USEC);
}
/* check limitations for timeval provided by user */
static bool bcm_is_invalid_tv(struct bcm_msg_head *msg_head)
{
if ((msg_head->ival1.tv_sec < 0) ||
(msg_head->ival1.tv_sec > BCM_TIMER_SEC_MAX) ||
(msg_head->ival1.tv_usec < 0) ||
(msg_head->ival1.tv_usec >= USEC_PER_SEC) ||
(msg_head->ival2.tv_sec < 0) ||
(msg_head->ival2.tv_sec > BCM_TIMER_SEC_MAX) ||
(msg_head->ival2.tv_usec < 0) ||
(msg_head->ival2.tv_usec >= USEC_PER_SEC))
return true;
return false;
}
#define CFSIZ(flags) ((flags & CAN_FD_FRAME) ? CANFD_MTU : CAN_MTU)
#define OPSIZ sizeof(struct bcm_op)
#define MHSIZ sizeof(struct bcm_msg_head)
/*
* procfs functions
*/
#if IS_ENABLED(CONFIG_PROC_FS)
static char *bcm_proc_getifname(struct net *net, char *result, int ifindex)
{
struct net_device *dev;
if (!ifindex)
return "any";
rcu_read_lock();
dev = dev_get_by_index_rcu(net, ifindex);
if (dev)
strcpy(result, dev->name);
else
strcpy(result, "???");
rcu_read_unlock();
return result;
}
static int bcm_proc_show(struct seq_file *m, void *v)
{
char ifname[IFNAMSIZ];
struct net *net = m->private;
struct sock *sk = (struct sock *)pde_data(m->file->f_inode);
struct bcm_sock *bo = bcm_sk(sk);
struct bcm_op *op;
seq_printf(m, ">>> socket %pK", sk->sk_socket);
seq_printf(m, " / sk %pK", sk);
seq_printf(m, " / bo %pK", bo);
seq_printf(m, " / dropped %lu", bo->dropped_usr_msgs);
seq_printf(m, " / bound %s", bcm_proc_getifname(net, ifname, bo->ifindex));
seq_printf(m, " <<<\n");
rcu_read_lock();
list_for_each_entry_rcu(op, &bo->rx_ops, list) {
unsigned long reduction;
/* print only active entries & prevent division by zero */
if (!op->frames_abs)
continue;
seq_printf(m, "rx_op: %03X %-5s ", op->can_id,
bcm_proc_getifname(net, ifname, op->ifindex));
if (op->flags & CAN_FD_FRAME)
seq_printf(m, "(%u)", op->nframes);
else
seq_printf(m, "[%u]", op->nframes);
seq_printf(m, "%c ", (op->flags & RX_CHECK_DLC) ? 'd' : ' ');
if (op->kt_ival1)
seq_printf(m, "timeo=%lld ",
(long long)ktime_to_us(op->kt_ival1));
if (op->kt_ival2)
seq_printf(m, "thr=%lld ",
(long long)ktime_to_us(op->kt_ival2));
seq_printf(m, "# recv %ld (%ld) => reduction: ",
op->frames_filtered, op->frames_abs);
reduction = 100 - (op->frames_filtered * 100) / op->frames_abs;
seq_printf(m, "%s%ld%%\n",
(reduction == 100) ? "near " : "", reduction);
}
list_for_each_entry(op, &bo->tx_ops, list) {
seq_printf(m, "tx_op: %03X %s ", op->can_id,
bcm_proc_getifname(net, ifname, op->ifindex));
if (op->flags & CAN_FD_FRAME)
seq_printf(m, "(%u) ", op->nframes);
else
seq_printf(m, "[%u] ", op->nframes);
if (op->kt_ival1)
seq_printf(m, "t1=%lld ",
(long long)ktime_to_us(op->kt_ival1));
if (op->kt_ival2)
seq_printf(m, "t2=%lld ",
(long long)ktime_to_us(op->kt_ival2));
seq_printf(m, "# sent %ld\n", op->frames_abs);
}
seq_putc(m, '\n');
rcu_read_unlock();
return 0;
}
#endif /* CONFIG_PROC_FS */
/*
* bcm_can_tx - send the (next) CAN frame to the appropriate CAN interface
* of the given bcm tx op
*/
static void bcm_can_tx(struct bcm_op *op)
{
struct sk_buff *skb;
struct can_skb_ext *csx;
struct net_device *dev;
struct canfd_frame *cf;
int err;
/* no target device? => exit */
if (!op->ifindex)
return;
/* read currframe under lock protection */
spin_lock_bh(&op->bcm_tx_lock);
cf = op->frames + op->cfsiz * op->currframe;
spin_unlock_bh(&op->bcm_tx_lock);
dev = dev_get_by_index(sock_net(op->sk), op->ifindex);
if (!dev) {
/* RFC: should this bcm_op remove itself here? */
return;
}
skb = alloc_skb(op->cfsiz, gfp_any());
if (!skb)
goto out;
csx = can_skb_ext_add(skb);
if (!csx) {
kfree_skb(skb);
goto out;
}
csx->can_iif = dev->ifindex;
skb_put_data(skb, cf, op->cfsiz);
/* send with loopback */
skb->dev = dev;
can_skb_set_owner(skb, op->sk);
err = can_send(skb, 1);
/* update currframe and count under lock protection */
spin_lock_bh(&op->bcm_tx_lock);
if (!err)
op->frames_abs++;
op->currframe++;
/* reached last frame? */
if (op->currframe >= op->nframes)
op->currframe = 0;
if (op->count > 0)
op->count--;
spin_unlock_bh(&op->bcm_tx_lock);
out:
dev_put(dev);
}
/*
* bcm_send_to_user - send a BCM message to the userspace
* (consisting of bcm_msg_head + x CAN frames)
*/
static void bcm_send_to_user(struct bcm_op *op, struct bcm_msg_head *head,
struct canfd_frame *frames, int has_timestamp)
{
struct sk_buff *skb;
struct canfd_frame *firstframe;
struct sockaddr_can *addr;
struct sock *sk = op->sk;
unsigned int datalen = head->nframes * op->cfsiz;
int err;
unsigned int *pflags;
enum skb_drop_reason reason;
skb = alloc_skb(sizeof(*head) + datalen, gfp_any());
if (!skb)
return;
skb_put_data(skb, head, sizeof(*head));
/* ensure space for sockaddr_can and msg flags */
sock_skb_cb_check_size(sizeof(struct sockaddr_can) +
sizeof(unsigned int));
/* initialize msg flags */
pflags = bcm_flags(skb);
*pflags = 0;
if (head->nframes) {
/* CAN frames starting here */
firstframe = (struct canfd_frame *)skb_tail_pointer(skb);
skb_put_data(skb, frames, datalen);
/*
* the BCM uses the flags-element of the canfd_frame
* structure for internal purposes. This is only
* relevant for updates that are generated by the
* BCM, where nframes is 1
*/
if (head->nframes == 1) {
if (firstframe->flags & RX_LOCAL)
*pflags |= MSG_DONTROUTE;
if (firstframe->flags & RX_OWN)
*pflags |= MSG_CONFIRM;
firstframe->flags &= BCM_CAN_FLAGS_MASK;
}
}
if (has_timestamp) {
/* restore rx timestamp */
skb->tstamp = op->rx_stamp;
}
/*
* Put the datagram to the queue so that bcm_recvmsg() can
* get it from there. We need to pass the interface index to
* bcm_recvmsg(). We pass a whole struct sockaddr_can in skb->cb
* containing the interface index.
*/
addr = (struct sockaddr_can *)skb->cb;
memset(addr, 0, sizeof(*addr));
addr->can_family = AF_CAN;
addr->can_ifindex = op->rx_ifindex;
err = sock_queue_rcv_skb_reason(sk, skb, &reason);
if (err < 0) {
struct bcm_sock *bo = bcm_sk(sk);
sk_skb_reason_drop(sk, skb, reason);
/* don't care about overflows in this statistic */
bo->dropped_usr_msgs++;
}
}
static bool bcm_tx_set_expiry(struct bcm_op *op, struct hrtimer *hrt)
{
ktime_t ival;
if (op->kt_ival1 && op->count)
ival = op->kt_ival1;
else if (op->kt_ival2)
ival = op->kt_ival2;
else
return false;
hrtimer_set_expires(hrt, ktime_add(ktime_get(), ival));
return true;
}
static void bcm_tx_start_timer(struct bcm_op *op)
{
if (bcm_tx_set_expiry(op, &op->timer))
hrtimer_start_expires(&op->timer, HRTIMER_MODE_ABS_SOFT);
}
/* bcm_tx_timeout_handler - performs cyclic CAN frame transmissions */
static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer)
{
struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer);
struct bcm_msg_head msg_head;
if (op->kt_ival1 && (op->count > 0)) {
bcm_can_tx(op);
if (!op->count && (op->flags & TX_COUNTEVT)) {
/* create notification to user */
memset(&msg_head, 0, sizeof(msg_head));
msg_head.opcode = TX_EXPIRED;
msg_head.flags = op->flags;
msg_head.count = op->count;
msg_head.ival1 = op->ival1;
msg_head.ival2 = op->ival2;
msg_head.can_id = op->can_id;
msg_head.nframes = 0;
bcm_send_to_user(op, &msg_head, NULL, 0);
}
} else if (op->kt_ival2) {
bcm_can_tx(op);
}
return bcm_tx_set_expiry(op, &op->timer) ?
HRTIMER_RESTART : HRTIMER_NORESTART;
}
/*
* bcm_rx_changed - create a RX_CHANGED notification due to changed content
*/
static void bcm_rx_changed(struct bcm_op *op, struct canfd_frame *data)
{
struct bcm_msg_head head;
/* update statistics */
op->frames_filtered++;
/* prevent statistics overflow */
if (op->frames_filtered > ULONG_MAX/100)
op->frames_filtered = op->frames_abs = 0;
/* this element is not throttled anymore */
data->flags &= ~RX_THR;
memset(&head, 0, sizeof(head));
head.opcode = RX_CHANGED;
head.flags = op->flags;
head.count = op->count;
head.ival1 = op->ival1;
head.ival2 = op->ival2;
head.can_id = op->can_id;
head.nframes = 1;
bcm_send_to_user(op, &head, data, 1);
}
/*
* bcm_rx_update_and_send - process a detected relevant receive content change
* 1. update the last received data
* 2. send a notification to the user (if possible)
*/
static void bcm_rx_update_and_send(struct bcm_op *op,
struct canfd_frame *lastdata,
const struct canfd_frame *rxdata,
unsigned char traffic_flags)
{
memcpy(lastdata, rxdata, op->cfsiz);
/* mark as used and throttled by default */
lastdata->flags |= (RX_RECV|RX_THR);
/* add own/local/remote traffic flags */
lastdata->flags |= traffic_flags;
/* throttling mode inactive ? */
if (!op->kt_ival2) {
/* send RX_CHANGED to the user immediately */
bcm_rx_changed(op, lastdata);
return;
}
/* with active throttling timer we are just done here */
if (hrtimer_active(&op->thrtimer))
return;
/* first reception with enabled throttling mode */
if (!op->kt_lastmsg)
goto rx_changed_settime;
/* got a second frame inside a potential throttle period? */
if (ktime_us_delta(ktime_get(), op->kt_lastmsg) <
ktime_to_us(op->kt_ival2)) {
/* do not send the saved data - only start throttle timer */
hrtimer_start(&op->thrtimer,
ktime_add(op->kt_lastmsg, op->kt_ival2),
HRTIMER_MODE_ABS_SOFT);
return;
}
/* the gap was that big, that throttling was not needed here */
rx_changed_settime:
bcm_rx_changed(op, lastdata);
op->kt_lastmsg = ktime_get();
}
/*
* bcm_rx_cmp_to_index - (bit)compares the currently received data to formerly
* received data stored in op->last_frames[]
*/
static void bcm_rx_cmp_to_index(struct bcm_op *op, unsigned int index,
const struct canfd_frame *rxdata,
unsigned char traffic_flags)
{
struct canfd_frame *cf = op->frames + op->cfsiz * index;
struct canfd_frame *lcf = op->last_frames + op->cfsiz * index;
int i;
/*
* no one uses the MSBs of flags for comparison,
* so we use it here to detect the first time of reception
*/
if (!(lcf->flags & RX_RECV)) {
/* received data for the first time => send update to user */
bcm_rx_update_and_send(op, lcf, rxdata, traffic_flags);
return;
}
/* do a real check in CAN frame data section */
for (i = 0; i < rxdata->len; i += 8) {
if ((get_u64(cf, i) & get_u64(rxdata, i)) !=
(get_u64(cf, i) & get_u64(lcf, i))) {
bcm_rx_update_and_send(op, lcf, rxdata, traffic_flags);
return;
}
}
if (op->flags & RX_CHECK_DLC) {
/* do a real check in CAN frame length */
if (rxdata->len != lcf->len) {
bcm_rx_update_and_send(op, lcf, rxdata, traffic_flags);
return;
}
}
}
/*
* bcm_rx_starttimer - enable timeout monitoring for CAN frame reception
*/
static void bcm_rx_starttimer(struct bcm_op *op)
{
if (op->flags & RX_NO_AUTOTIMER)
return;
if (op->kt_ival1)
hrtimer_start(&op->timer, op->kt_ival1, HRTIMER_MODE_REL_SOFT);
}
/* bcm_rx_timeout_handler - when the (cyclic) CAN frame reception timed out */
static enum hrtimer_restart bcm_rx_timeout_handler(struct hrtimer *hrtimer)
{
struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer);
struct bcm_msg_head msg_head;
/* if user wants to be informed, when cyclic CAN-Messages come back */
if ((op->flags & RX_ANNOUNCE_RESUME) && op->last_frames) {
/* clear received CAN frames to indicate 'nothing received' */
memset(op->last_frames, 0, op->nframes * op->cfsiz);
}
/* create notification to user */
memset(&msg_head, 0, sizeof(msg_head));
msg_head.opcode = RX_TIMEOUT;
msg_head.flags = op->flags;
msg_head.count = op->count;
msg_head.ival1 = op->ival1;
msg_head.ival2 = op->ival2;
msg_head.can_id = op->can_id;
msg_head.nframes = 0;
bcm_send_to_user(op, &msg_head, NULL, 0);
return HRTIMER_NORESTART;
}
/*
* bcm_rx_do_flush - helper for bcm_rx_thr_flush
*/
static inline int bcm_rx_do_flush(struct bcm_op *op, unsigned int index)
{
struct canfd_frame *lcf = op->last_frames + op->cfsiz * index;
if ((op->last_frames) && (lcf->flags & RX_THR)) {
bcm_rx_changed(op, lcf);
return 1;
}
return 0;
}
/*
* bcm_rx_thr_flush - Check for throttled data and send it to the userspace
*/
static int bcm_rx_thr_flush(struct bcm_op *op)
{
int updated = 0;
if (op->nframes > 1) {
unsigned int i;
/* for MUX filter we start at index 1 */
for (i = 1; i < op->nframes; i++)
updated += bcm_rx_do_flush(op, i);
} else {
/* for RX_FILTER_ID and simple filter */
updated += bcm_rx_do_flush(op, 0);
}
return updated;
}
/*
* bcm_rx_thr_handler - the time for blocked content updates is over now:
* Check for throttled data and send it to the userspace
*/
static enum hrtimer_restart bcm_rx_thr_handler(struct hrtimer *hrtimer)
{
struct bcm_op *op = container_of(hrtimer, struct bcm_op, thrtimer);
if (bcm_rx_thr_flush(op)) {
hrtimer_forward_now(hrtimer, op->kt_ival2);
return HRTIMER_RESTART;
} else {
/* rearm throttle handling */
op->kt_lastmsg = 0;
return HRTIMER_NORESTART;
}
}
/*
* bcm_rx_handler - handle a CAN frame reception
*/
static void bcm_rx_handler(struct sk_buff *skb, void *data)
{
struct bcm_op *op = (struct bcm_op *)data;
const struct canfd_frame *rxframe = (struct canfd_frame *)skb->data;
unsigned int i;
unsigned char traffic_flags;
if (op->can_id != rxframe->can_id)
return;
/* make sure to handle the correct frame type (CAN / CAN FD) */
if (op->flags & CAN_FD_FRAME) {
if (!can_is_canfd_skb(skb))
return;
} else {
if (!can_is_can_skb(skb))
return;
}
/* disable timeout */
hrtimer_cancel(&op->timer);
/* save rx timestamp */
op->rx_stamp = skb->tstamp;
/* save originator for recvfrom() */
op->rx_ifindex = skb->dev->ifindex;
/* update statistics */
op->frames_abs++;
if (op->flags & RX_RTR_FRAME) {
/* send reply for RTR-request (placed in op->frames[0]) */
bcm_can_tx(op);
return;
}
/* compute flags to distinguish between own/local/remote CAN traffic */
traffic_flags = 0;
if (skb->sk) {
traffic_flags |= RX_LOCAL;
if (skb->sk == op->sk)
traffic_flags |= RX_OWN;
}
if (op->flags & RX_FILTER_ID) {
/* the easiest case */
bcm_rx_update_and_send(op, op->last_frames, rxframe,
traffic_flags);
goto rx_starttimer;
}
if (op->nframes == 1) {
/* simple compare with index 0 */
bcm_rx_cmp_to_index(op, 0, rxframe, traffic_flags);
goto rx_starttimer;
}
if (op->nframes > 1) {
/*
* multiplex compare
*
* find the first multiplex mask that fits.
* Remark: The MUX-mask is stored in index 0 - but only the
* first 64 bits of the frame data[] are relevant (CAN FD)
*/
for (i = 1; i < op->nframes; i++) {
if ((get_u64(op->frames, 0) & get_u64(rxframe, 0)) ==
(get_u64(op->frames, 0) &
get_u64(op->frames + op->cfsiz * i, 0))) {
bcm_rx_cmp_to_index(op, i, rxframe,
traffic_flags);
break;
}
}
}
rx_starttimer:
bcm_rx_starttimer(op);
}
/*
* helpers for bcm_op handling: find & delete bcm [rx|tx] op elements
*/
static struct bcm_op *bcm_find_op(struct list_head *ops,
struct bcm_msg_head *mh, int ifindex)
{
struct bcm_op *op;
list_for_each_entry(op, ops, list) {
if ((op->can_id == mh->can_id) && (op->ifindex == ifindex) &&
(op->flags & CAN_FD_FRAME) == (mh->flags & CAN_FD_FRAME))
return op;
}
return NULL;
}
static void bcm_free_op_rcu(struct rcu_head *rcu_head)
{
struct bcm_op *op = container_of(rcu_head, struct bcm_op, rcu);
if ((op->frames) && (op->frames != &op->sframe))
kfree(op->frames);
if ((op->last_frames) && (op->last_frames != &op->last_sframe))
kfree(op->last_frames);
kfree(op);
}
static void bcm_remove_op(struct bcm_op *op)
{
hrtimer_cancel(&op->timer);
hrtimer_cancel(&op->thrtimer);
call_rcu(&op->rcu, bcm_free_op_rcu);
}
static void bcm_rx_unreg(struct net_device *dev, struct bcm_op *op)
{
if (op->rx_reg_dev == dev) {
can_rx_unregister(dev_net(dev), dev, op->can_id,
REGMASK(op->can_id), bcm_rx_handler, op);
/* mark as removed subscription */
op->rx_reg_dev = NULL;
} else
printk(KERN_ERR "can-bcm: bcm_rx_unreg: registered device "
"mismatch %p %p\n", op->rx_reg_dev, dev);
}
/*
* bcm_delete_rx_op - find and remove a rx op (returns number of removed ops)
*/
static int bcm_delete_rx_op(struct list_head *ops, struct bcm_msg_head *mh,
int ifindex)
{
struct bcm_op *op, *n;
list_for_each_entry_safe(op, n, ops, list) {
if ((op->can_id == mh->can_id) && (op->ifindex == ifindex) &&
(op->flags & CAN_FD_FRAME) == (mh->flags & CAN_FD_FRAME)) {
/* disable automatic timer on frame reception */
op->flags |= RX_NO_AUTOTIMER;
/*
* Don't care if we're bound or not (due to netdev
* problems) can_rx_unregister() is always a save
* thing to do here.
*/
if (op->ifindex) {
/*
* Only remove subscriptions that had not
* been removed due to NETDEV_UNREGISTER
* in bcm_notifier()
*/
if (op->rx_reg_dev) {
struct net_device *dev;
dev = dev_get_by_index(sock_net(op->sk),
op->ifindex);
if (dev) {
bcm_rx_unreg(dev, op);
dev_put(dev);
}
}
} else
can_rx_unregister(sock_net(op->sk), NULL,
op->can_id,
REGMASK(op->can_id),
bcm_rx_handler, op);
list_del_rcu(&op->list);
bcm_remove_op(op);
return 1; /* done */
}
}
return 0; /* not found */
}
/*
* bcm_delete_tx_op - find and remove a tx op (returns number of removed ops)
*/
static int bcm_delete_tx_op(struct list_head *ops, struct bcm_msg_head *mh,
int ifindex)
{
struct bcm_op *op, *n;
list_for_each_entry_safe(op, n, ops, list) {
if ((op->can_id == mh->can_id) && (op->ifindex == ifindex) &&
(op->flags & CAN_FD_FRAME) == (mh->flags & CAN_FD_FRAME)) {
list_del_rcu(&op->list);
bcm_remove_op(op);
return 1; /* done */
}
}
return 0; /* not found */
}
/*
* bcm_read_op - read out a bcm_op and send it to the user (for bcm_sendmsg)
*/
static int bcm_read_op(struct list_head *ops, struct bcm_msg_head *msg_head,
int ifindex)
{
struct bcm_op *op = bcm_find_op(ops, msg_head, ifindex);
if (!op)
return -EINVAL;
/* put current values into msg_head */
msg_head->flags = op->flags;
msg_head->count = op->count;
msg_head->ival1 = op->ival1;
msg_head->ival2 = op->ival2;
msg_head->nframes = op->nframes;
bcm_send_to_user(op, msg_head, op->frames, 0);
return MHSIZ;
}
/*
* bcm_tx_setup - create or update a bcm tx op (for bcm_sendmsg)
*/
static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
int ifindex, struct sock *sk)
{
struct bcm_sock *bo = bcm_sk(sk);
struct bcm_op *op;
struct canfd_frame *cf;
unsigned int i;
int err;
/* we need a real device to send frames */
if (!ifindex)
return -ENODEV;
/* check nframes boundaries - we need at least one CAN frame */
if (msg_head->nframes < 1 || msg_head->nframes > MAX_NFRAMES)
return -EINVAL;
/* check timeval limitations */
if ((msg_head->flags & SETTIMER) && bcm_is_invalid_tv(msg_head))
return -EINVAL;
/* check the given can_id */
op = bcm_find_op(&bo->tx_ops, msg_head, ifindex);
if (op) {
/* update existing BCM operation */
/*
* Do we need more space for the CAN frames than currently
* allocated? -> This is a _really_ unusual use-case and
* therefore (complexity / locking) it is not supported.
*/
if (msg_head->nframes > op->nframes)
return -E2BIG;
/* update CAN frames content */
for (i = 0; i < msg_head->nframes; i++) {
cf = op->frames + op->cfsiz * i;
err = memcpy_from_msg((u8 *)cf, msg, op->cfsiz);
if (op->flags & CAN_FD_FRAME) {
if (cf->len > 64)
err = -EINVAL;
} else {
if (cf->len > 8)
err = -EINVAL;
}
if (err < 0)
return err;
if (msg_head->flags & TX_CP_CAN_ID) {
/* copy can_id into frame */
cf->can_id = msg_head->can_id;
}
}
op->flags = msg_head->flags;
/* only lock for unlikely count/nframes/currframe changes */
if (op->nframes != msg_head->nframes ||
op->flags & TX_RESET_MULTI_IDX ||
op->flags & SETTIMER) {
spin_lock_bh(&op->bcm_tx_lock);
if (op->nframes != msg_head->nframes ||
op->flags & TX_RESET_MULTI_IDX) {
/* potentially update changed nframes */
op->nframes = msg_head->nframes;
/* restart multiple frame transmission */
op->currframe = 0;
}
if (op->flags & SETTIMER)
op->count = msg_head->count;
spin_unlock_bh(&op->bcm_tx_lock);
}
} else {
/* insert new BCM operation for the given can_id */
op = kzalloc(OPSIZ, GFP_KERNEL);
if (!op)
return -ENOMEM;
spin_lock_init(&op->bcm_tx_lock);
op->can_id = msg_head->can_id;
op->cfsiz = CFSIZ(msg_head->flags);
op->flags = msg_head->flags;
op->nframes = msg_head->nframes;
if (op->flags & SETTIMER)
op->count = msg_head->count;
/* create array for CAN frames and copy the data */
if (msg_head->nframes > 1) {
op->frames = kmalloc_array(msg_head->nframes,
op->cfsiz,
GFP_KERNEL);
if (!op->frames) {
kfree(op);
return -ENOMEM;
}
} else
op->frames = &op->sframe;
for (i = 0; i < msg_head->nframes; i++) {
cf = op->frames + op->cfsiz * i;
err = memcpy_from_msg((u8 *)cf, msg, op->cfsiz);
if (err < 0)
goto free_op;
if (op->flags & CAN_FD_FRAME) {
if (cf->len > 64)
err = -EINVAL;
} else {
if (cf->len > 8)
err = -EINVAL;
}
if (err < 0)
goto free_op;
if (msg_head->flags & TX_CP_CAN_ID) {
/* copy can_id into frame */
cf->can_id = msg_head->can_id;
}
}
/* tx_ops never compare with previous received messages */
op->last_frames = NULL;
/* bcm_can_tx / bcm_tx_timeout_handler needs this */
op->sk = sk;
op->ifindex = ifindex;
/* initialize uninitialized (kzalloc) structure */
hrtimer_setup(&op->timer, bcm_tx_timeout_handler, CLOCK_MONOTONIC,
HRTIMER_MODE_REL_SOFT);
/* currently unused in tx_ops */
hrtimer_setup(&op->thrtimer, hrtimer_dummy_timeout, CLOCK_MONOTONIC,
HRTIMER_MODE_REL_SOFT);
/* add this bcm_op to the list of the tx_ops */
list_add(&op->list, &bo->tx_ops);
} /* if ((op = bcm_find_op(&bo->tx_ops, msg_head->can_id, ifindex))) */
if (op->flags & SETTIMER) {
/* set timer values */
op->ival1 = msg_head->ival1;
op->ival2 = msg_head->ival2;
op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1);
op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2);
/* disable an active timer due to zero values? */
if (!op->kt_ival1 && !op->kt_ival2)
hrtimer_cancel(&op->timer);
}
if (op->flags & STARTTIMER) {
hrtimer_cancel(&op->timer);
/* spec: send CAN frame when starting timer */
op->flags |= TX_ANNOUNCE;
}
if (op->flags & TX_ANNOUNCE)
bcm_can_tx(op);
if (op->flags & STARTTIMER)
bcm_tx_start_timer(op);
return msg_head->nframes * op->cfsiz + MHSIZ;
free_op:
if (op->frames != &op->sframe)
kfree(op->frames);
kfree(op);
return err;
}
/*
* bcm_rx_setup - create or update a bcm rx op (for bcm_sendmsg)
*/
static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
int ifindex, struct sock *sk)
{
struct bcm_sock *bo = bcm_sk(sk);
struct bcm_op *op;
int do_rx_register;
int err = 0;
if ((msg_head->flags & RX_FILTER_ID) || (!(msg_head->nframes))) {
/* be robust against wrong usage ... */
msg_head->flags |= RX_FILTER_ID;
/* ignore trailing garbage */
msg_head->nframes = 0;
}
/* the first element contains the mux-mask => MAX_NFRAMES + 1 */
if (msg_head->nframes > MAX_NFRAMES + 1)
return -EINVAL;
if ((msg_head->flags & RX_RTR_FRAME) &&
((msg_head->nframes != 1) ||
(!(msg_head->can_id & CAN_RTR_FLAG))))
return -EINVAL;
/* check timeval limitations */
if ((msg_head->flags & SETTIMER) && bcm_is_invalid_tv(msg_head))
return -EINVAL;
/* check the given can_id */
op = bcm_find_op(&bo->rx_ops, msg_head, ifindex);
if (op) {
/* update existing BCM operation */
/*
* Do we need more space for the CAN frames than currently
* allocated? -> This is a _really_ unusual use-case and
* therefore (complexity / locking) it is not supported.
*/
if (msg_head->nframes > op->nframes)
return -E2BIG;
if (msg_head->nframes) {
/* update CAN frames content */
err = memcpy_from_msg(op->frames, msg,
msg_head->nframes * op->cfsiz);
if (err < 0)
return err;
/* clear last_frames to indicate 'nothing received' */
memset(op->last_frames, 0, msg_head->nframes * op->cfsiz);
}
op->nframes = msg_head->nframes;
op->flags = msg_head->flags;
/* Only an update -> do not call can_rx_register() */
do_rx_register = 0;
} else {
/* insert new BCM operation for the given can_id */
op = kzalloc(OPSIZ, GFP_KERNEL);
if (!op)
return -ENOMEM;
spin_lock_init(&op->bcm_tx_lock);
op->can_id = msg_head->can_id;
op->nframes = msg_head->nframes;
op->cfsiz = CFSIZ(msg_head->flags);
op->flags = msg_head->flags;
if (msg_head->nframes > 1) {
/* create array for CAN frames and copy the data */
op->frames = kmalloc_array(msg_head->nframes,
op->cfsiz,
GFP_KERNEL);
if (!op->frames) {
kfree(op);
return -ENOMEM;
}
/* create and init array for received CAN frames */
op->last_frames = kcalloc(msg_head->nframes,
op->cfsiz,
GFP_KERNEL);
if (!op->last_frames) {
kfree(op->frames);
kfree(op);
return -ENOMEM;
}
} else {
op->frames = &op->sframe;
op->last_frames = &op->last_sframe;
}
if (msg_head->nframes) {
err = memcpy_from_msg(op->frames, msg,
msg_head->nframes * op->cfsiz);
if (err < 0) {
if (op->frames != &op->sframe)
kfree(op->frames);
if (op->last_frames != &op->last_sframe)
kfree(op->last_frames);
kfree(op);
return err;
}
}
/* bcm_can_tx / bcm_tx_timeout_handler needs this */
op->sk = sk;
op->ifindex = ifindex;
/* ifindex for timeout events w/o previous frame reception */
op->rx_ifindex = ifindex;
/* initialize uninitialized (kzalloc) structure */
hrtimer_setup(&op->timer, bcm_rx_timeout_handler, CLOCK_MONOTONIC,
HRTIMER_MODE_REL_SOFT);
hrtimer_setup(&op->thrtimer, bcm_rx_thr_handler, CLOCK_MONOTONIC,
HRTIMER_MODE_REL_SOFT);
/* add this bcm_op to the list of the rx_ops */
list_add(&op->list, &bo->rx_ops);
/* call can_rx_register() */
do_rx_register = 1;
} /* if ((op = bcm_find_op(&bo->rx_ops, msg_head->can_id, ifindex))) */
/* check flags */
if (op->flags & RX_RTR_FRAME) {
struct canfd_frame *frame0 = op->frames;
/* no timers in RTR-mode */
hrtimer_cancel(&op->thrtimer);
hrtimer_cancel(&op->timer);
/*
* funny feature in RX(!)_SETUP only for RTR-mode:
* copy can_id into frame BUT without RTR-flag to
* prevent a full-load-loopback-test ... ;-]
*/
if ((op->flags & TX_CP_CAN_ID) ||
(frame0->can_id == op->can_id))
frame0->can_id = op->can_id & ~CAN_RTR_FLAG;
} else {
if (op->flags & SETTIMER) {
/* set timer value */
op->ival1 = msg_head->ival1;
op->ival2 = msg_head->ival2;
op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1);
op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2);
/* disable an active timer due to zero value? */
if (!op->kt_ival1)
hrtimer_cancel(&op->timer);
/*
* In any case cancel the throttle timer, flush
* potentially blocked msgs and reset throttle handling
*/
op->kt_lastmsg = 0;
hrtimer_cancel(&op->thrtimer);
bcm_rx_thr_flush(op);
}
if ((op->flags & STARTTIMER) && op->kt_ival1)
hrtimer_start(&op->timer, op->kt_ival1,
HRTIMER_MODE_REL_SOFT);
}
/* now we can register for can_ids, if we added a new bcm_op */
if (do_rx_register) {
if (ifindex) {
struct net_device *dev;
dev = dev_get_by_index(sock_net(sk), ifindex);
if (dev) {
err = can_rx_register(sock_net(sk), dev,
op->can_id,
REGMASK(op->can_id),
bcm_rx_handler, op,
"bcm", sk);
op->rx_reg_dev = dev;
dev_put(dev);
}
} else
err = can_rx_register(sock_net(sk), NULL, op->can_id,
REGMASK(op->can_id),
bcm_rx_handler, op, "bcm", sk);
if (err) {
/* this bcm rx op is broken -> remove it */
list_del_rcu(&op->list);
bcm_remove_op(op);
return err;
}
}
return msg_head->nframes * op->cfsiz + MHSIZ;
}
/*
* bcm_tx_send - send a single CAN frame to the CAN interface (for bcm_sendmsg)
*/
static int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk,
int cfsiz)
{
struct sk_buff *skb;
struct can_skb_ext *csx;
struct net_device *dev;
int err;
/* we need a real device to send frames */
if (!ifindex)
return -ENODEV;
skb = alloc_skb(cfsiz, GFP_KERNEL);
if (!skb)
return -ENOMEM;
csx = can_skb_ext_add(skb);
if (!csx) {
kfree_skb(skb);
return -ENOMEM;
}
err = memcpy_from_msg(skb_put(skb, cfsiz), msg, cfsiz);
if (err < 0) {
kfree_skb(skb);
return err;
}
dev = dev_get_by_index(sock_net(sk), ifindex);
if (!dev) {
kfree_skb(skb);
return -ENODEV;
}
csx->can_iif = dev->ifindex;
skb->dev = dev;
can_skb_set_owner(skb, sk);
err = can_send(skb, 1); /* send with loopback */
dev_put(dev);
if (err)
return err;
return cfsiz + MHSIZ;
}
/*
* bcm_sendmsg - process BCM commands (opcodes) from the userspace
*/
static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
{
struct sock *sk = sock->sk;
struct bcm_sock *bo = bcm_sk(sk);
int ifindex = bo->ifindex; /* default ifindex for this bcm_op */
struct bcm_msg_head msg_head;
int cfsiz;
int ret; /* read bytes or error codes as return value */
if (!bo->bound)
return -ENOTCONN;
/* check for valid message length from userspace */
if (size < MHSIZ)
return -EINVAL;
/* read message head information */
ret = memcpy_from_msg((u8 *)&msg_head, msg, MHSIZ);
if (ret < 0)
return ret;
cfsiz = CFSIZ(msg_head.flags);
if ((size - MHSIZ) % cfsiz)
return -EINVAL;
/* check for alternative ifindex for this bcm_op */
if (!ifindex && msg->msg_name) {
/* no bound device as default => check msg_name */
DECLARE_SOCKADDR(struct sockaddr_can *, addr, msg->msg_name);
if (msg->msg_namelen < BCM_MIN_NAMELEN)
return -EINVAL;
if (addr->can_family != AF_CAN)
return -EINVAL;
/* ifindex from sendto() */
ifindex = addr->can_ifindex;
if (ifindex) {
struct net_device *dev;
dev = dev_get_by_index(sock_net(sk), ifindex);
if (!dev)
return -ENODEV;
if (dev->type != ARPHRD_CAN) {
dev_put(dev);
return -ENODEV;
}
dev_put(dev);
}
}
lock_sock(sk);
switch (msg_head.opcode) {
case TX_SETUP:
ret = bcm_tx_setup(&msg_head, msg, ifindex, sk);
break;
case RX_SETUP:
ret = bcm_rx_setup(&msg_head, msg, ifindex, sk);
break;
case TX_DELETE:
if (bcm_delete_tx_op(&bo->tx_ops, &msg_head, ifindex))
ret = MHSIZ;
else
ret = -EINVAL;
break;
case RX_DELETE:
if (bcm_delete_rx_op(&bo->rx_ops, &msg_head, ifindex))
ret = MHSIZ;
else
ret = -EINVAL;
break;
case TX_READ:
/* reuse msg_head for the reply to TX_READ */
msg_head.opcode = TX_STATUS;
ret = bcm_read_op(&bo->tx_ops, &msg_head, ifindex);
break;
case RX_READ:
/* reuse msg_head for the reply to RX_READ */
msg_head.opcode = RX_STATUS;
ret = bcm_read_op(&bo->rx_ops, &msg_head, ifindex);
break;
case TX_SEND:
/* we need exactly one CAN frame behind the msg head */
if ((msg_head.nframes != 1) || (size != cfsiz + MHSIZ))
ret = -EINVAL;
else
ret = bcm_tx_send(msg, ifindex, sk, cfsiz);
break;
default:
ret = -EINVAL;
break;
}
release_sock(sk);
return ret;
}
/*
* notification handler for netdevice status changes
*/
static void bcm_notify(struct bcm_sock *bo, unsigned long msg,
struct net_device *dev)
{
struct sock *sk = &bo->sk;
struct bcm_op *op;
int notify_enodev = 0;
if (!net_eq(dev_net(dev), sock_net(sk)))
return;
switch (msg) {
case NETDEV_UNREGISTER:
lock_sock(sk);
/* remove device specific receive entries */
list_for_each_entry(op, &bo->rx_ops, list)
if (op->rx_reg_dev == dev)
bcm_rx_unreg(dev, op);
/* remove device reference, if this is our bound device */
if (bo->bound && bo->ifindex == dev->ifindex) {
#if IS_ENABLED(CONFIG_PROC_FS)
if (sock_net(sk)->can.bcmproc_dir && bo->bcm_proc_read) {
remove_proc_entry(bo->procname, sock_net(sk)->can.bcmproc_dir);
bo->bcm_proc_read = NULL;
}
#endif
bo->bound = 0;
bo->ifindex = 0;
notify_enodev = 1;
}
release_sock(sk);
if (notify_enodev) {
sk->sk_err = ENODEV;
if (!sock_flag(sk, SOCK_DEAD))
sk_error_report(sk);
}
break;
case NETDEV_DOWN:
if (bo->bound && bo->ifindex == dev->ifindex) {
sk->sk_err = ENETDOWN;
if (!sock_flag(sk, SOCK_DEAD))
sk_error_report(sk);
}
}
}
static int bcm_notifier(struct notifier_block *nb, unsigned long msg,
void *ptr)
{
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
if (dev->type != ARPHRD_CAN)
return NOTIFY_DONE;
if (msg != NETDEV_UNREGISTER && msg != NETDEV_DOWN)
return NOTIFY_DONE;
if (unlikely(bcm_busy_notifier)) /* Check for reentrant bug. */
return NOTIFY_DONE;
spin_lock(&bcm_notifier_lock);
list_for_each_entry(bcm_busy_notifier, &bcm_notifier_list, notifier) {
spin_unlock(&bcm_notifier_lock);
bcm_notify(bcm_busy_notifier, msg, dev);
spin_lock(&bcm_notifier_lock);
}
bcm_busy_notifier = NULL;
spin_unlock(&bcm_notifier_lock);
return NOTIFY_DONE;
}
/*
* initial settings for all BCM sockets to be set at socket creation time
*/
static int bcm_init(struct sock *sk)
{
struct bcm_sock *bo = bcm_sk(sk);
bo->bound = 0;
bo->ifindex = 0;
bo->dropped_usr_msgs = 0;
bo->bcm_proc_read = NULL;
INIT_LIST_HEAD(&bo->tx_ops);
INIT_LIST_HEAD(&bo->rx_ops);
/* set notifier */
spin_lock(&bcm_notifier_lock);
list_add_tail(&bo->notifier, &bcm_notifier_list);
spin_unlock(&bcm_notifier_lock);
return 0;
}
/*
* standard socket functions
*/
static int bcm_release(struct socket *sock)
{
struct sock *sk = sock->sk;
struct net *net;
struct bcm_sock *bo;
struct bcm_op *op, *next;
if (!sk)
return 0;
net = sock_net(sk);
bo = bcm_sk(sk);
/* remove bcm_ops, timer, rx_unregister(), etc. */
spin_lock(&bcm_notifier_lock);
while (bcm_busy_notifier == bo) {
spin_unlock(&bcm_notifier_lock);
schedule_timeout_uninterruptible(1);
spin_lock(&bcm_notifier_lock);
}
list_del(&bo->notifier);
spin_unlock(&bcm_notifier_lock);
lock_sock(sk);
#if IS_ENABLED(CONFIG_PROC_FS)
/* remove procfs entry */
if (net->can.bcmproc_dir && bo->bcm_proc_read)
remove_proc_entry(bo->procname, net->can.bcmproc_dir);
#endif /* CONFIG_PROC_FS */
list_for_each_entry_safe(op, next, &bo->tx_ops, list)
bcm_remove_op(op);
list_for_each_entry_safe(op, next, &bo->rx_ops, list) {
/*
* Don't care if we're bound or not (due to netdev problems)
* can_rx_unregister() is always a save thing to do here.
*/
if (op->ifindex) {
/*
* Only remove subscriptions that had not
* been removed due to NETDEV_UNREGISTER
* in bcm_notifier()
*/
if (op->rx_reg_dev) {
struct net_device *dev;
dev = dev_get_by_index(net, op->ifindex);
if (dev) {
bcm_rx_unreg(dev, op);
dev_put(dev);
}
}
} else
can_rx_unregister(net, NULL, op->can_id,
REGMASK(op->can_id),
bcm_rx_handler, op);
}
synchronize_rcu();
list_for_each_entry_safe(op, next, &bo->rx_ops, list)
bcm_remove_op(op);
/* remove device reference */
if (bo->bound) {
bo->bound = 0;
bo->ifindex = 0;
}
sock_orphan(sk);
sock->sk = NULL;
release_sock(sk);
sock_prot_inuse_add(net, sk->sk_prot, -1);
sock_put(sk);
return 0;
}
static int bcm_connect(struct socket *sock, struct sockaddr_unsized *uaddr, int len,
int flags)
{
struct sockaddr_can *addr = (struct sockaddr_can *)uaddr;
struct sock *sk = sock->sk;
struct bcm_sock *bo = bcm_sk(sk);
struct net *net = sock_net(sk);
int ret = 0;
if (len < BCM_MIN_NAMELEN)
return -EINVAL;
lock_sock(sk);
if (bo->bound) {
ret = -EISCONN;
goto fail;
}
/* bind a device to this socket */
if (addr->can_ifindex) {
struct net_device *dev;
dev = dev_get_by_index(net, addr->can_ifindex);
if (!dev) {
ret = -ENODEV;
goto fail;
}
if (dev->type != ARPHRD_CAN) {
dev_put(dev);
ret = -ENODEV;
goto fail;
}
bo->ifindex = dev->ifindex;
dev_put(dev);
} else {
/* no interface reference for ifindex = 0 ('any' CAN device) */
bo->ifindex = 0;
}
#if IS_ENABLED(CONFIG_PROC_FS)
if (net->can.bcmproc_dir) {
/* unique socket address as filename */
sprintf(bo->procname, "%lu", sock_i_ino(sk));
bo->bcm_proc_read = proc_create_net_single(bo->procname, 0644,
net->can.bcmproc_dir,
bcm_proc_show, sk);
if (!bo->bcm_proc_read) {
ret = -ENOMEM;
goto fail;
}
}
#endif /* CONFIG_PROC_FS */
bo->bound = 1;
fail:
release_sock(sk);
return ret;
}
static int bcm_recvmsg(struct socket *sock, struct msghdr *msg, size_t size,
int flags)
{
struct sock *sk = sock->sk;
struct sk_buff *skb;
int error = 0;
int err;
skb = skb_recv_datagram(sk, flags, &error);
if (!skb)
return error;
if (skb->len < size)
size = skb->len;
err = memcpy_to_msg(msg, skb->data, size);
if (err < 0) {
skb_free_datagram(sk, skb);
return err;
}
sock_recv_cmsgs(msg, sk, skb);
if (msg->msg_name) {
__sockaddr_check_size(BCM_MIN_NAMELEN);
msg->msg_namelen = BCM_MIN_NAMELEN;
memcpy(msg->msg_name, skb->cb, msg->msg_namelen);
}
/* assign the flags that have been recorded in bcm_send_to_user() */
msg->msg_flags |= *(bcm_flags(skb));
skb_free_datagram(sk, skb);
return size;
}
static int bcm_sock_no_ioctlcmd(struct socket *sock, unsigned int cmd,
unsigned long arg)
{
/* no ioctls for socket layer -> hand it down to NIC layer */
return -ENOIOCTLCMD;
}
static const struct proto_ops bcm_ops = {
.family = PF_CAN,
.release = bcm_release,
.bind = sock_no_bind,
.connect = bcm_connect,
.socketpair = sock_no_socketpair,
.accept = sock_no_accept,
.getname = sock_no_getname,
.poll = datagram_poll,
.ioctl = bcm_sock_no_ioctlcmd,
.gettstamp = sock_gettstamp,
.listen = sock_no_listen,
.shutdown = sock_no_shutdown,
.sendmsg = bcm_sendmsg,
.recvmsg = bcm_recvmsg,
.mmap = sock_no_mmap,
};
static struct proto bcm_proto __read_mostly = {
.name = "CAN_BCM",
.owner = THIS_MODULE,
.obj_size = sizeof(struct bcm_sock),
.init = bcm_init,
};
static const struct can_proto bcm_can_proto = {
.type = SOCK_DGRAM,
.protocol = CAN_BCM,
.ops = &bcm_ops,
.prot = &bcm_proto,
};
static int canbcm_pernet_init(struct net *net)
{
#if IS_ENABLED(CONFIG_PROC_FS)
/* create /proc/net/can-bcm directory */
net->can.bcmproc_dir = proc_net_mkdir(net, "can-bcm", net->proc_net);
#endif /* CONFIG_PROC_FS */
return 0;
}
static void canbcm_pernet_exit(struct net *net)
{
#if IS_ENABLED(CONFIG_PROC_FS)
/* remove /proc/net/can-bcm directory */
if (net->can.bcmproc_dir)
remove_proc_entry("can-bcm", net->proc_net);
#endif /* CONFIG_PROC_FS */
}
static struct pernet_operations canbcm_pernet_ops __read_mostly = {
.init = canbcm_pernet_init,
.exit = canbcm_pernet_exit,
};
static struct notifier_block canbcm_notifier = {
.notifier_call = bcm_notifier
};
static int __init bcm_module_init(void)
{
int err;
pr_info("can: broadcast manager protocol\n");
err = register_pernet_subsys(&canbcm_pernet_ops);
if (err)
return err;
err = register_netdevice_notifier(&canbcm_notifier);
if (err)
goto register_notifier_failed;
err = can_proto_register(&bcm_can_proto);
if (err < 0) {
printk(KERN_ERR "can: registration of bcm protocol failed\n");
goto register_proto_failed;
}
return 0;
register_proto_failed:
unregister_netdevice_notifier(&canbcm_notifier);
register_notifier_failed:
unregister_pernet_subsys(&canbcm_pernet_ops);
return err;
}
static void __exit bcm_module_exit(void)
{
can_proto_unregister(&bcm_can_proto);
unregister_netdevice_notifier(&canbcm_notifier);
unregister_pernet_subsys(&canbcm_pernet_ops);
}
module_init(bcm_module_init);
module_exit(bcm_module_exit);
]
|
| 26/2 |
2026/04/04 20:03 |
llm |
expert |
0m
Model:
Tokens:
input: 95274
output: 19
thoughts: 1638
Thoughts: **Data Race Analysis: `bcm_can_tx` vs. `bcm_tx_setup` in CAN BCM**
Okay, I've dug into this code, and it's clear there's a serious data race issue in the CAN BCM implementation. The conflict is between `bcm_can_tx`, which is responsible for transmitting CAN frames, and `bcm_tx_setup`, which is responsible for setting up the frames to be transmitted. `bcm_can_tx` is running in a hrtimer/softirq context, while `bcm_tx_setup` is running in process context under `lock_sock(sk)`.
The heart of the problem lies within the in-place update of CAN frames by `bcm_tx_setup`. Specifically, the code does a `memcpy_from_msg` to populate the `op->frames[i]` buffer. Critically, this `memcpy` operation is *not* protected by `op->bcm_tx_lock`. `bcm_can_tx` grabs the lock, but only long enough to get a pointer to the current frame to be transmitted (`cf`), and then it releases the lock before passing that pointer to `skb_put_data` (which is itself, essentially, a `memcpy`).
This interleaving is the killer. Because `memcpy` isn't atomic for anything larger than a word, `skb_put_data` in `bcm_can_tx` can, and likely *will*, read a partially updated CAN frame. Imagine a 72-byte CAN frame. `memcpy_from_msg` starts copying, maybe it copies the CAN ID and some data, then the timer fires. `bcm_can_tx` runs, and grabs what's been written so far, and the rest from the old buffer content. This results in a corrupted frame being transmitted to the CAN bus. This isn't just a minor glitch; it's a fundamental violation of data integrity.
I checked the code to confirm my initial assessment. The `memcpy_from_msg` call happens at `net/can/bcm.c:966`, and the `skb_put_data` happens at `net/can/bcm.c:327`.
Initially, I wanted to double check what `op->bcm_tx_lock` was meant to protect. Based on the comments (line 101), the lock *specifically* protects `currframe` and `count`, not `frames` themselves. The author may have had some assumptions about "torn" reads being acceptable, or that users would take pains to stop the timer while updating the buffer content. But this seems wrong, and not compatible with cyclic transmission, which is what BCM is *designed* for.
Yes, `bcm_can_tx` grabs the lock at line 305 to ensure a consistent `cf` pointer, but the *contents* of the `op->frames` buffer are unprotected. It's almost as if the author missed the non-atomic nature of `memcpy` operations.
There's an additional risk related to frame validity. `bcm_tx_setup` checks the length *after* the `memcpy`, meaning an invalid length might briefly be visible to `bcm_can_tx` before it's caught, and while `can_send` might reject the frame, this is a bug.
The fix is straightforward: we need to either hold the spinlock during the entire frame update (i.e. inside the loop), which might be less desirable because `memcpy_from_msg` can sleep, or use a temporary buffer and perform a swap operation, or `memcpy`, under the lock. I'm noting that there is a spinlock later in the same function (`bcm_tx_setup`), but it is unrelated to the frame content.
I confirm that this is a classic data race, and confirm that this race also exists in `bcm_rx_setup` versus `bcm_rx_handler`.
Given that CAN data integrity is critical and that BCM is designed for continuous updates of cyclic buffers, this is a **HARMFUL** data race.
|
| 27/2 |
2026/04/04 20:03 |
tool |
set-results |
0m
Args: map[Benign:false Confident:true]
Results: map[Benign:false Confident:true]
|
| 28/2 |
2026/04/04 20:03 |
llm |
expert |
0m
Model:
Tokens:
input: 97851
output: 913
thoughts: 13
Thoughts: Okay, here's the summary, assuming I'm the expert and reflecting on the text's prompt:
**Finalizing the Response**
Alright, the heavy lifting's done. I've finished the analysis, carefully considered the implications, and have all the necessary information synthesized. Now, it's time to craft the final response. No need to belabor the point or reiterate foundational principles, just a concise and insightful summation of the key findings, presented in a way that's meaningful and useful. It's time to deliver the well-considered conclusion.
|
| 29/1 |
2026/04/04 20:04 |
action |
format-explanation |
0m
Results: map[Explanation:The KCSAN data race report identifies a race between `bcm_can_tx()` (reading)
and `bcm_tx_setup()` (writing) on the CAN frame data within a `struct bcm_op`.
### Race Summary
- **Function A (`bcm_can_tx`)**: Reads a CAN frame from `op->frames` to copy it
into an `sk_buff` for transmission.
- **Function B (`bcm_tx_setup`)**: Updates the CAN frames in `op->frames` using
`memcpy_from_msg()` when a user sends a `TX_SETUP` command to update an existing
broadcast manager (BCM) operation.
- **Variable**: `op->frames` (specifically the content of a `struct canfd_frame`
or `struct can_frame` at a specific index).
### Synchronization Context
- `bcm_tx_setup()` is called from `bcm_sendmsg()`, which holds the socket lock
(`lock_sock(sk)`). This protects against concurrent `sendmsg()` calls on the
same socket but does not protect against the hrtimer that triggers
`bcm_can_tx()`.
- `bcm_can_tx()` is called from `bcm_tx_timeout_handler()` (an hrtimer handler)
or directly from `bcm_tx_setup()`. When called from the timer, it runs in
softirq context.
- Although `bcm_can_tx()` takes `op->bcm_tx_lock` to safely calculate the frame
pointer (`cf`), it releases the lock before calling `skb_put_data(skb, cf,
op->cfsiz)`.
- `bcm_tx_setup()` does **not** hold `op->bcm_tx_lock` while calling
`memcpy_from_msg()` to update the frame content.
### Final Classification: **HARMFUL**
### Failure Reasoning
The race leads to **data corruption of the transmitted CAN frame**. Since CAN FD
frames can be up to 72 bytes (`struct canfd_frame`), a concurrent `memcpy` (via
`memcpy_from_msg` and `skb_put_data`) is not atomic. This results in a "torn"
update where the transmitted frame contains a mixture of old and new data (e.g.,
a new `can_id` with old payload data).
In safety-critical environments where CAN is typically used (automotive,
industrial), sending inconsistent or "phantom" frames that never existed in a
valid state is unacceptable. Furthermore, `bcm_tx_setup()` validates the frame
length (`cf->len`) **after** the copy, meaning an invalid length could be
temporarily visible to the transmitter.
### Failure Sequencing Example
| CPU 0 (`bcm_tx_setup`) | CPU 1 (`bcm_can_tx`) |
| :--- | :--- |
| | `spin_lock_bh(&op->bcm_tx_lock)` |
| | `cf = op->frames + ...` |
| | `spin_unlock_bh(&op->bcm_tx_lock)` |
| `memcpy_from_msg(cf, ...)` | |
| (Starts copying 72 bytes) | |
| (Copies first 32 bytes, e.g., `can_id`) | |
| | `skb_put_data(skb, cf, 72)` |
| | (Copies 72 bytes from `cf` to `skb`) |
| | (Sees **new** `can_id` but **old** payload) |
| (Finishes copying remaining 40 bytes) | |
| | `can_send(skb)` |
| | **-> Corrupted frame sent on CAN bus!** |
### Recommended Fix
To ensure atomicity, `bcm_tx_setup()` should copy the user data to a temporary
kernel buffer first, then perform the update to `op->frames` while holding
`op->bcm_tx_lock`. Correspondingly, `bcm_can_tx()` must hold `op->bcm_tx_lock`
while copying the frame data into the `skb`.]
|