* single-consumer dequeue * use where dequeue is protected by a lock * e.g. a network driver's tx queue lock */
| 157 | * e.g. a network driver's tx queue lock |
| 158 | */ |
| 159 | static __inline void * |
| 160 | buf_ring_dequeue_sc(struct buf_ring *br) |
| 161 | { |
| 162 | uint32_t cons_head, cons_next; |
| 163 | #ifdef PREFETCH_DEFINED |
| 164 | uint32_t cons_next_next; |
| 165 | #endif |
| 166 | uint32_t prod_tail; |
| 167 | void *buf; |
| 168 | |
| 169 | /* |
| 170 | * This is a workaround to allow using buf_ring on ARM and ARM64. |
| 171 | * ARM64TODO: Fix buf_ring in a generic way. |
| 172 | * REMARKS: It is suspected that br_cons_head does not require |
| 173 | * load_acq operation, but this change was extensively tested |
| 174 | * and confirmed it's working. To be reviewed once again in |
| 175 | * FreeBSD-12. |
| 176 | * |
| 177 | * Preventing following situation: |
| 178 | |
| 179 | * Core(0) - buf_ring_enqueue() Core(1) - buf_ring_dequeue_sc() |
| 180 | * ----------------------------------------- ---------------------------------------------- |
| 181 | * |
| 182 | * cons_head = br->br_cons_head; |
| 183 | * atomic_cmpset_acq_32(&br->br_prod_head, ...)); |
| 184 | * buf = br->br_ring[cons_head]; <see <1>> |
| 185 | * br->br_ring[prod_head] = buf; |
| 186 | * atomic_store_rel_32(&br->br_prod_tail, ...); |
| 187 | * prod_tail = br->br_prod_tail; |
| 188 | * if (cons_head == prod_tail) |
| 189 | * return (NULL); |
| 190 | * <condition is false and code uses invalid(old) buf>` |
| 191 | * |
| 192 | * <1> Load (on core 1) from br->br_ring[cons_head] can be reordered (speculative readed) by CPU. |
| 193 | */ |
| 194 | #if defined(__arm__) || defined(__aarch64__) |
| 195 | cons_head = atomic_load_acq_32(&br->br_cons_head); |
| 196 | #else |
| 197 | cons_head = br->br_cons_head; |
| 198 | #endif |
| 199 | prod_tail = atomic_load_acq_32(&br->br_prod_tail); |
| 200 | |
| 201 | cons_next = (cons_head + 1) & br->br_cons_mask; |
| 202 | #ifdef PREFETCH_DEFINED |
| 203 | cons_next_next = (cons_head + 2) & br->br_cons_mask; |
| 204 | #endif |
| 205 | |
| 206 | if (cons_head == prod_tail) |
| 207 | return (NULL); |
| 208 | |
| 209 | #ifdef PREFETCH_DEFINED |
| 210 | if (cons_next != prod_tail) { |
| 211 | prefetch(br->br_ring[cons_next]); |
| 212 | if (cons_next_next != prod_tail) |
| 213 | prefetch(br->br_ring[cons_next_next]); |
| 214 | } |
| 215 | #endif |
| 216 | br->br_cons_head = cons_next; |
no test coverage detected