mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	net/smc: fix sender_free computation
In some scenarios a separate consumer cursor update is necessary.
The decision is made in smc_tx_consumer_cursor_update(). The
sender_free computation could be wrong:
The rx confirmed cursor is always smaller than or equal to the
rx producer cursor. The parameters in the smc_curs_diff() call
have to be exchanged, otherwise sender_free might even be negative.
And if more data arrives local_rx_ctrl.prod might be updated, enabling
a cursor difference between local_rx_ctrl.prod and rx confirmed cursor
larger than the RMB size. This case is not covered by smc_curs_diff().
Thus function smc_curs_diff_large() is introduced here.
If a recvmsg() is processed in parallel, local_tx_ctrl.cons might
change during smc_cdc_msg_send. Make sure rx_curs_confirmed is updated
with the actually sent local_tx_ctrl.cons value.
Fixes: e82f2e31f5 ("net/smc: optimize consumer cursor updates")
Signed-off-by: Ursula Braun <ubraun@linux.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
			
			
This commit is contained in:
		
							parent
							
								
									ad6f317f72
								
							
						
					
					
						commit
						b8649efad8
					
				
					 3 changed files with 30 additions and 4 deletions
				
			
		| 
						 | 
					@ -91,6 +91,7 @@ int smc_cdc_msg_send(struct smc_connection *conn,
 | 
				
			||||||
		     struct smc_wr_buf *wr_buf,
 | 
							     struct smc_wr_buf *wr_buf,
 | 
				
			||||||
		     struct smc_cdc_tx_pend *pend)
 | 
							     struct smc_cdc_tx_pend *pend)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						union smc_host_cursor cfed;
 | 
				
			||||||
	struct smc_link *link;
 | 
						struct smc_link *link;
 | 
				
			||||||
	int rc;
 | 
						int rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -102,10 +103,10 @@ int smc_cdc_msg_send(struct smc_connection *conn,
 | 
				
			||||||
	conn->local_tx_ctrl.seqno = conn->tx_cdc_seq;
 | 
						conn->local_tx_ctrl.seqno = conn->tx_cdc_seq;
 | 
				
			||||||
	smc_host_msg_to_cdc((struct smc_cdc_msg *)wr_buf,
 | 
						smc_host_msg_to_cdc((struct smc_cdc_msg *)wr_buf,
 | 
				
			||||||
			    &conn->local_tx_ctrl, conn);
 | 
								    &conn->local_tx_ctrl, conn);
 | 
				
			||||||
 | 
						smc_curs_copy(&cfed, &((struct smc_host_cdc_msg *)wr_buf)->cons, conn);
 | 
				
			||||||
	rc = smc_wr_tx_send(link, (struct smc_wr_tx_pend_priv *)pend);
 | 
						rc = smc_wr_tx_send(link, (struct smc_wr_tx_pend_priv *)pend);
 | 
				
			||||||
	if (!rc)
 | 
						if (!rc)
 | 
				
			||||||
		smc_curs_copy(&conn->rx_curs_confirmed,
 | 
							smc_curs_copy(&conn->rx_curs_confirmed, &cfed, conn);
 | 
				
			||||||
			      &conn->local_tx_ctrl.cons, conn);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return rc;
 | 
						return rc;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -160,7 +160,9 @@ static inline void smcd_curs_copy(union smcd_cdc_cursor *tgt,
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* calculate cursor difference between old and new, where old <= new */
 | 
					/* calculate cursor difference between old and new, where old <= new and
 | 
				
			||||||
 | 
					 * difference cannot exceed size
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
static inline int smc_curs_diff(unsigned int size,
 | 
					static inline int smc_curs_diff(unsigned int size,
 | 
				
			||||||
				union smc_host_cursor *old,
 | 
									union smc_host_cursor *old,
 | 
				
			||||||
				union smc_host_cursor *new)
 | 
									union smc_host_cursor *new)
 | 
				
			||||||
| 
						 | 
					@ -185,6 +187,28 @@ static inline int smc_curs_comp(unsigned int size,
 | 
				
			||||||
	return smc_curs_diff(size, old, new);
 | 
						return smc_curs_diff(size, old, new);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* calculate cursor difference between old and new, where old <= new and
 | 
				
			||||||
 | 
					 * difference may exceed size
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static inline int smc_curs_diff_large(unsigned int size,
 | 
				
			||||||
 | 
									      union smc_host_cursor *old,
 | 
				
			||||||
 | 
									      union smc_host_cursor *new)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (old->wrap < new->wrap)
 | 
				
			||||||
 | 
							return min_t(int,
 | 
				
			||||||
 | 
								     (size - old->count) + new->count +
 | 
				
			||||||
 | 
								     (new->wrap - old->wrap - 1) * size,
 | 
				
			||||||
 | 
								     size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (old->wrap > new->wrap) /* wrap has switched from 0xffff to 0x0000 */
 | 
				
			||||||
 | 
							return min_t(int,
 | 
				
			||||||
 | 
								     (size - old->count) + new->count +
 | 
				
			||||||
 | 
								     (new->wrap + 0xffff - old->wrap) * size,
 | 
				
			||||||
 | 
								     size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return max_t(int, 0, (new->count - old->count));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline void smc_host_cursor_to_cdc(union smc_cdc_cursor *peer,
 | 
					static inline void smc_host_cursor_to_cdc(union smc_cdc_cursor *peer,
 | 
				
			||||||
					  union smc_host_cursor *local,
 | 
										  union smc_host_cursor *local,
 | 
				
			||||||
					  struct smc_connection *conn)
 | 
										  struct smc_connection *conn)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -595,7 +595,8 @@ void smc_tx_consumer_update(struct smc_connection *conn, bool force)
 | 
				
			||||||
	if (to_confirm > conn->rmbe_update_limit) {
 | 
						if (to_confirm > conn->rmbe_update_limit) {
 | 
				
			||||||
		smc_curs_copy(&prod, &conn->local_rx_ctrl.prod, conn);
 | 
							smc_curs_copy(&prod, &conn->local_rx_ctrl.prod, conn);
 | 
				
			||||||
		sender_free = conn->rmb_desc->len -
 | 
							sender_free = conn->rmb_desc->len -
 | 
				
			||||||
			      smc_curs_diff(conn->rmb_desc->len, &prod, &cfed);
 | 
								      smc_curs_diff_large(conn->rmb_desc->len,
 | 
				
			||||||
 | 
											  &cfed, &prod);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (conn->local_rx_ctrl.prod_flags.cons_curs_upd_req ||
 | 
						if (conn->local_rx_ctrl.prod_flags.cons_curs_upd_req ||
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue