summaryrefslogtreecommitdiff
path: root/net/smc/smc_llc.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-08-27 13:53:43 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-08-27 13:53:43 -0700
commit1b78070aaef63512688aebfbc82365ef9d6660f1 (patch)
tree691c0aeaa3d92278ceeb6ace56bc8cd56a7f2ae8 /net/smc/smc_llc.c
parent3ba13f5e7180c034b0a1ef7e052fb780856b134e (diff)
parent4a9d62a8774f130a5b8de26ca9f415e6050a9d51 (diff)
downloadlinux-next-stable.tar.gz
linux-next-stable.zip
Merge tag 'net-7.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/netstable
Pull networking fixes from Jakub Kicinski: "Including fixes from Bluetooth, IPSec and Netfilter. Current release - fix to a fix: - netfilter: ipset: remove need to allocate memory on delete operations Current release - regressions: - macb: drop CONFIG_OF #if block, fix build Previous releases - always broken: - stream of fixes for SCTP continues - inet: frags: strip GSO state from fragments before reassembly - virtio-net: ensure that TCP packets don't overflow gso_segs - tcp-ao: fix use-after-free of current_key on reconnect to another peer - page_pool: remove zone/policy GFP flags when allocating XArray entries - Bluetooth: L2CAP: reject accept queue add unless BT_LISTEN - tls: device: fix out-of-bounds write in tls_append_frag() - eth: bnxt: - ring the doorbell when SW USO exits early, avoid packets stuck in Tx - gate TPH enablement behind BNXT_SUPPORTS_QUEUE_API check, avoid users of older NICs seeing non-actionable warning messages - eth: qede: fix NULL pointer dereference in TPA fragment processing" * tag 'net-7.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net: (216 commits) inet: frags: strip GSO state from fragments before reassembly net/sched: sch_htb: limit htb_classify inner-class filter hops selftests/net: packetdrill: add tcp_urg_ptr_retransmit tcp: fix corruption of urgent data on multi-segment retransmit usb: atm: usbatm: fix invalid ci_range initialization net: fec: only stop PTP if it was initialized slip: remove slip_hangup() to fix use-after-free in slip_receive_buf() net: bridge: mcast: fix use-after-free of a master VLAN's multicast context net/sched: bound qdisc_pkt_len to prevent qdisc soft lockup net: dsa: mxl862xx: enable assisted learning on CPU port net: stmmac: restore NET_IP_ALIGN in the RX DMA offset net: stmmac: drop gso_enabled_types and rely on netdev features net: stmmac: selftests: Don't test flow control for small rx fifos net: stmmac: selftests: Account for the UC filter list for filtering tests net: stmmac: dwxgmac: Account for the primary MAC address for UC filtering net: stmmac: dwmac4: Account for the primary MAC address for UC filtering net: stmmac: dwmac1000: Account for the primary MAC address for UC filtering net: stmmac: selftests: Check multiple MMC counters selftests: net: Fix slow configurations in big_tcp_tunnels.sh selftests: net: Lower threshold with csum offload off in big_tcp_tunnels.sh ...
Diffstat (limited to 'net/smc/smc_llc.c')
-rw-r--r--net/smc/smc_llc.c125
1 files changed, 98 insertions, 27 deletions
diff --git a/net/smc/smc_llc.c b/net/smc/smc_llc.c
index aa6d83af55ed..c9ccd8480369 100644
--- a/net/smc/smc_llc.c
+++ b/net/smc/smc_llc.c
@@ -157,6 +157,7 @@ struct smc_llc_msg_confirm_rkey { /* type 0x06 */
};
#define SMC_LLC_DEL_RKEY_MAX 8
+#define SMC_LLC_DEL_RKEY_V2_INLINE 9
#define SMC_LLC_FLAG_RKEY_RETRY 0x10
#define SMC_LLC_FLAG_RKEY_NEG 0x20
@@ -177,6 +178,15 @@ struct smc_llc_msg_delete_rkey_v2 { /* type 0x29 */
__be32 rkey[];
};
+/* the leading rkeys of a DELETE_RKEY_V2 fit into union smc_llc_msg */
+struct smc_llc_msg_delete_rkey_v2_inline { /* type 0x29 */
+ struct smc_llc_hdr hd;
+ u8 num_rkeys;
+ u8 num_inval_rkeys;
+ u8 reserved[2];
+ __be32 rkey[SMC_LLC_DEL_RKEY_V2_INLINE];
+};
+
union smc_llc_msg {
struct smc_llc_msg_confirm_link confirm_link;
struct smc_llc_msg_add_link add_link;
@@ -186,6 +196,7 @@ union smc_llc_msg {
struct smc_llc_msg_confirm_rkey confirm_rkey;
struct smc_llc_msg_delete_rkey delete_rkey;
+ struct smc_llc_msg_delete_rkey_v2_inline delete_rkey_v2;
struct smc_llc_msg_test_link test_link;
struct {
@@ -194,15 +205,25 @@ union smc_llc_msg {
} raw;
};
+static_assert(SMC_LLC_DEL_RKEY_V2_INLINE ==
+ (sizeof(union smc_llc_msg) -
+ offsetof(struct smc_llc_msg_delete_rkey_v2, rkey)) /
+ sizeof(__be32));
+static_assert(offsetof(struct smc_llc_msg_delete_rkey_v2_inline, rkey) ==
+ offsetof(struct smc_llc_msg_delete_rkey_v2, rkey));
+
#define SMC_LLC_FLAG_RESP 0x80
struct smc_llc_qentry {
struct list_head list;
struct smc_link *link;
+ u16 body_len;
union smc_llc_msg msg;
+ u8 body[] __counted_by(body_len);
};
-static void smc_llc_enqueue(struct smc_link *link, union smc_llc_msg *llc);
+static void smc_llc_enqueue(struct smc_link *link, union smc_llc_msg *llc,
+ u32 byte_len);
struct smc_llc_qentry *smc_llc_flow_qentry_clr(struct smc_llc_flow *flow)
{
@@ -320,6 +341,7 @@ again:
void smc_llc_flow_stop(struct smc_link_group *lgr, struct smc_llc_flow *flow)
{
spin_lock_bh(&lgr->llc_flow_lock);
+ smc_llc_flow_qentry_del(flow);
memset(flow, 0, sizeof(*flow));
flow->type = SMC_LLC_FLOW_NONE;
spin_unlock_bh(&lgr->llc_flow_lock);
@@ -998,15 +1020,20 @@ static int smc_llc_cli_conf_link(struct smc_link *link,
static void smc_llc_save_add_link_rkeys(struct smc_link *link,
struct smc_link *link_new,
- u8 *llc_msg)
+ struct smc_llc_qentry *qentry)
{
+ const u32 rt_off = offsetof(struct smc_llc_msg_add_link_v2_ext, rt);
struct smc_llc_msg_add_link_v2_ext *ext;
struct smc_link_group *lgr = link->lgr;
int max, i;
- ext = (struct smc_llc_msg_add_link_v2_ext *)(llc_msg +
- SMC_WR_TX_SIZE);
+ /* the rkey count itself is only there if enough bytes arrived */
+ if (qentry->body_len < rt_off)
+ return;
+ ext = (struct smc_llc_msg_add_link_v2_ext *)qentry->body;
max = min_t(u8, ext->num_rkeys, SMC_LLC_RKEYS_PER_MSG_V2);
+ max = min_t(u32, max, (qentry->body_len - rt_off) /
+ sizeof(ext->rt[0]));
down_write(&lgr->rmbs_lock);
for (i = 0; i < max; i++) {
smc_rtoken_set(lgr, link->link_idx, link_new->link_idx,
@@ -1099,9 +1126,7 @@ int smc_llc_cli_add_link(struct smc_link *link, struct smc_llc_qentry *qentry)
if (rc)
goto out_clear_lnk;
if (lgr->smc_version == SMC_V2) {
- u8 *llc_msg = smc_link_shared_v2_rxbuf(link) ?
- (u8 *)lgr->wr_rx_buf_v2 : (u8 *)llc;
- smc_llc_save_add_link_rkeys(link, lnk_new, llc_msg);
+ smc_llc_save_add_link_rkeys(link, lnk_new, qentry);
} else {
rc = smc_llc_cli_rkey_exchange(link, lnk_new);
if (rc) {
@@ -1481,7 +1506,7 @@ int smc_llc_srv_add_link(struct smc_link *link,
}
add_llc = &qentry->msg.add_link;
if (add_llc->hd.flags & SMC_LLC_FLAG_ADD_LNK_REJ) {
- smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
+ smc_llc_flow_qentry_clr(&lgr->llc_flow_lcl);
rc = -ENOLINK;
goto out_err;
}
@@ -1492,7 +1517,8 @@ int smc_llc_srv_add_link(struct smc_link *link,
lgr_new_t = SMC_LGR_ASYMMETRIC_PEER;
}
smc_llc_save_add_link_info(link_new, add_llc);
- smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
+ /* add_llc still points into qentry, so only detach it here */
+ smc_llc_flow_qentry_clr(&lgr->llc_flow_lcl);
rc = smc_ib_ready_link(link_new);
if (rc)
@@ -1501,9 +1527,7 @@ int smc_llc_srv_add_link(struct smc_link *link,
if (rc)
goto out_err;
if (lgr->smc_version == SMC_V2) {
- u8 *llc_msg = smc_link_shared_v2_rxbuf(link) ?
- (u8 *)lgr->wr_rx_buf_v2 : (u8 *)add_llc;
- smc_llc_save_add_link_rkeys(link, link_new, llc_msg);
+ smc_llc_save_add_link_rkeys(link, link_new, qentry);
} else {
rc = smc_llc_srv_rkey_exchange(link, link_new);
if (rc)
@@ -1512,14 +1536,14 @@ int smc_llc_srv_add_link(struct smc_link *link,
rc = smc_llc_srv_conf_link(link, link_new, lgr_new_t);
if (rc)
goto out_err;
- kfree(ini);
- return 0;
+ goto out;
out_err:
if (link_new) {
link_new->state = SMC_LNK_INACTIVE;
smcr_link_clear(link_new, false);
}
out:
+ kfree(qentry);
kfree(ini);
if (send_req_add_link_resp)
smc_llc_send_req_add_link_response(req_qentry);
@@ -1552,7 +1576,8 @@ void smc_llc_add_link_local(struct smc_link *link)
add_llc.hd.common.llc_type = SMC_LLC_ADD_LINK;
smc_llc_init_msg_hdr(&add_llc.hd, link->lgr, sizeof(add_llc));
/* no dev and port needed */
- smc_llc_enqueue(link, (union smc_llc_msg *)&add_llc);
+ smc_llc_enqueue(link, (union smc_llc_msg *)&add_llc,
+ sizeof(union smc_llc_msg));
}
/* worker to process an add link message */
@@ -1588,7 +1613,8 @@ void smc_llc_srv_delete_link_local(struct smc_link *link, u8 del_link_id)
del_llc.link_num = del_link_id;
del_llc.reason = htonl(SMC_LLC_DEL_LOST_PATH);
del_llc.hd.flags |= SMC_LLC_FLAG_DEL_LINK_ORDERLY;
- smc_llc_enqueue(link, (union smc_llc_msg *)&del_llc);
+ smc_llc_enqueue(link, (union smc_llc_msg *)&del_llc,
+ sizeof(union smc_llc_msg));
}
static void smc_llc_process_cli_delete_link(struct smc_link_group *lgr)
@@ -1810,19 +1836,28 @@ static void smc_llc_rmt_delete_rkey(struct smc_link_group *lgr)
link = qentry->link;
if (lgr->smc_version == SMC_V2) {
- struct smc_llc_msg_delete_rkey_v2 *llcv2;
+ struct smc_llc_msg_delete_rkey_v2_inline *llcv2;
- if (smc_link_shared_v2_rxbuf(link)) {
- memcpy(lgr->wr_rx_buf_v2, llc, sizeof(*llc));
- llcv2 = (struct smc_llc_msg_delete_rkey_v2 *)lgr->wr_rx_buf_v2;
- } else {
- llcv2 = (struct smc_llc_msg_delete_rkey_v2 *)llc;
- }
+ /* The leading SMC_LLC_DEL_RKEY_V2_INLINE rkeys are declared in
+ * the message itself, any further ones were received into
+ * qentry->body.
+ */
+ llcv2 = &qentry->msg.delete_rkey_v2;
llcv2->num_inval_rkeys = 0;
max = min_t(u8, llcv2->num_rkeys, SMC_LLC_RKEYS_PER_MSG_V2);
+ max = min_t(u32, max, SMC_LLC_DEL_RKEY_V2_INLINE +
+ qentry->body_len / sizeof(__be32));
for (i = 0; i < max; i++) {
- if (smc_rtoken_delete(link, llcv2->rkey[i]))
+ __be32 rkey;
+
+ if (i < SMC_LLC_DEL_RKEY_V2_INLINE)
+ rkey = llcv2->rkey[i];
+ else
+ memcpy(&rkey, qentry->body +
+ (i - SMC_LLC_DEL_RKEY_V2_INLINE) *
+ sizeof(rkey), sizeof(rkey));
+ if (smc_rtoken_delete(link, rkey))
llcv2->num_inval_rkeys++;
}
memset(&llc->rkey[0], 0, sizeof(llc->rkey));
@@ -1911,6 +1946,8 @@ static void smc_llc_event_handler(struct smc_llc_qentry *qentry)
if (lgr->llc_flow_lcl.type ==
SMC_LLC_FLOW_REQ_ADD_LINK) {
/* server started add_link processing */
+ /* free any qentry stashed in REQ_ADD_LINK state */
+ smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
lgr->llc_flow_lcl.type = SMC_LLC_FLOW_ADD_LINK;
smc_llc_flow_qentry_set(&lgr->llc_flow_lcl,
qentry);
@@ -2064,18 +2101,52 @@ assign:
wake_up(&link->lgr->llc_msg_waiter);
}
-static void smc_llc_enqueue(struct smc_link *link, union smc_llc_msg *llc)
+/* the longest tail either reader of qentry->body can use */
+static u32 smc_llc_max_body_len(union smc_llc_msg *llc)
+{
+ switch (llc->raw.hdr.common.llc_type) {
+ case SMC_LLC_ADD_LINK:
+ return offsetof(struct smc_llc_msg_add_link_v2_ext, rt) +
+ SMC_LLC_RKEYS_PER_MSG_V2 *
+ sizeof(struct smc_llc_msg_add_link_cont_rt);
+ case SMC_LLC_DELETE_RKEY:
+ return (SMC_LLC_RKEYS_PER_MSG_V2 -
+ SMC_LLC_DEL_RKEY_V2_INLINE) * sizeof(__be32);
+ default:
+ return 0;
+ }
+}
+
+static void smc_llc_enqueue(struct smc_link *link, union smc_llc_msg *llc,
+ u32 byte_len)
{
struct smc_link_group *lgr = link->lgr;
struct smc_llc_qentry *qentry;
unsigned long flags;
+ u16 body_len = 0;
+
+ /* V2 messages can be longer than the inline union smc_llc_msg. Carry
+ * the remainder in the qentry itself, so that its lifetime and its
+ * length match the message the peer actually sent.
+ */
+ if (lgr->smc_version == SMC_V2 && byte_len > SMC_WR_TX_SIZE)
+ body_len = min_t(u32, byte_len, SMC_WR_BUF_V2_SIZE) -
+ SMC_WR_TX_SIZE;
+ body_len = min_t(u32, body_len, smc_llc_max_body_len(llc));
- qentry = kmalloc_obj(*qentry, GFP_ATOMIC);
+ qentry = kmalloc_flex(*qentry, body, body_len, GFP_ATOMIC);
if (!qentry)
return;
+ qentry->body_len = body_len;
qentry->link = link;
INIT_LIST_HEAD(&qentry->list);
memcpy(&qentry->msg, llc, sizeof(union smc_llc_msg));
+ if (body_len) {
+ u8 *src = smc_link_shared_v2_rxbuf(link) ?
+ (u8 *)lgr->wr_rx_buf_v2 : (u8 *)llc;
+
+ memcpy(qentry->body, src + SMC_WR_TX_SIZE, body_len);
+ }
/* process responses immediately */
if ((llc->raw.hdr.flags & SMC_LLC_FLAG_RESP) &&
@@ -2107,7 +2178,7 @@ static void smc_llc_rx_handler(struct ib_wc *wc, void *buf)
return; /* invalid message */
}
- smc_llc_enqueue(link, llc);
+ smc_llc_enqueue(link, llc, wc->byte_len);
}
/***************************** worker, utils *********************************/