Commit a389fcfd2cb5 ("Drivers: hv: vmbus: Fix signaling logic in hv_need_to_signal_on_read()") added the proper mb(), but removed the test "prev_write_sz < pending_sz" when making the signal decision. As a result, the guest can signal the host unnecessarily, and then the host can throttle the guest because the host thinks the guest is buggy or malicious; finally the user running stress test can perceive intermittent freeze of the guest. This patch brings back the test, and properly handles the in-place consumption APIs used by NetVSC (see get_next_pkt_raw(), put_pkt_raw() and commit_rd_index()). Fixes: a389fcfd2cb5 ("Drivers: hv: vmbus: Fix signaling logic in hv_need_to_signal_on_read()")
Showing the changes to include/linux (1 of 3 files). Show all
Unified · Split
| @@ -128,6 +128,7 @@ struct hv_ring_buffer_info { · 127 unmodified lines | ||
| 128 | 128 | u32 ring_data_startoffset; |
| 129 | 129 | u32 priv_write_index; |
| 130 | 130 | u32 priv_read_index; |
| 131 | + u32 cached_read_index; | |
| 131 | 132 | }; |
| 132 | 133 | |
| 133 | 134 | /* |
| @@ -180,6 +181,19 @@ static inline u32 hv_get_bytes_to_write(struct hv_ring_buffer_info *rbi) · 46 unmodified lines | ||
| 180 | 181 | return write; |
| 181 | 182 | } |
| 182 | 183 | |
| 184 | +static inline u32 hv_get_cached_bytes_to_write( | |
| 185 | + const struct hv_ring_buffer_info *rbi) | |
| 186 | +{ | |
| 187 | + u32 read_loc, write_loc, dsize, write; | |
| 188 | + | |
| 189 | + dsize = rbi->ring_datasize; | |
| 190 | + read_loc = rbi->cached_read_index; | |
| 191 | + write_loc = rbi->ring_buffer->write_index; | |
| 192 | + | |
| 193 | + write = write_loc >= read_loc ? dsize - (write_loc - read_loc) : | |
| 194 | + read_loc - write_loc; | |
| 195 | + return write; | |
| 196 | +} | |
| 183 | 197 | /* |
| 184 | 198 | * VMBUS version is 32 bit entity broken up into |
| 185 | 199 | * two 16 bit quantities: major_number. minor_number. |
| @@ -1488,7 +1502,7 @@ hv_get_ring_buffer(struct hv_ring_buffer_info *ring_info) · 1302 unmodified lines | ||
| 1488 | 1502 | |
| 1489 | 1503 | static inline void hv_signal_on_read(struct vmbus_channel *channel) |
| 1490 | 1504 | { |
| 1491 | − u32 cur_write_sz; | |
| 1505 | + u32 cur_write_sz, cached_write_sz; | |
| 1492 | 1506 | u32 pending_sz; |
| 1493 | 1507 | struct hv_ring_buffer_info *rbi = &channel->inbound; |
| 1494 | 1508 | |
| @@ -1512,12 +1526,24 @@ static inline void hv_signal_on_read(struct vmbus_channel *channel) · 17 unmodified lines | ||
| 1512 | 1526 | |
| 1513 | 1527 | cur_write_sz = hv_get_bytes_to_write(rbi); |
| 1514 | 1528 | |
| 1515 | − if (cur_write_sz >= pending_sz) | |
| 1529 | + if (cur_write_sz < pending_sz) | |
| 1530 | + return; | |
| 1531 | + | |
| 1532 | + cached_write_sz = hv_get_cached_bytes_to_write(rbi); | |
| 1533 | + if (cached_write_sz < pending_sz) | |
| 1516 | 1534 | vmbus_setevent(channel); |
| 1517 | 1535 | |
| 1518 | 1536 | return; |
| 1519 | 1537 | } |
| 1520 | 1538 | |
| 1539 | +static inline void | |
| 1540 | +init_cached_read_index(struct vmbus_channel *channel) | |
| 1541 | +{ | |
| 1542 | + struct hv_ring_buffer_info *rbi = &channel->inbound; | |
| 1543 | + | |
| 1544 | + rbi->cached_read_index = rbi->ring_buffer->read_index; | |
| 1545 | +} | |
| 1546 | + | |
| 1521 | 1547 | /* |
| 1522 | 1548 | * An API to support in-place processing of incoming VMBUS packets. |
| 1523 | 1549 | */ |
| @@ -1569,6 +1595,8 @@ static inline void put_pkt_raw(struct vmbus_channel *channel, · 45 unmodified lines | ||
| 1569 | 1595 | * This call commits the read index and potentially signals the host. |
| 1570 | 1596 | * Here is the pattern for using the "in-place" consumption APIs: |
| 1571 | 1597 | * |
| 1598 | + * init_cached_read_index(); | |
| 1599 | + * | |
| 1572 | 1600 | * while (get_next_pkt_raw() { |
| 1573 | 1601 | * process the packet "in-place"; |
| 1574 | 1602 | * put_pkt_raw(); |