From 3f8ad41f42b6d6ca7e7115211c3beb5aa416921c Mon Sep 17 00:00:00 2001 From: Gokul Sivakumar Date: Mon, 13 Oct 2025 15:58:19 +0530 Subject: [PATCH 001/143] wifi: brcmfmac: fix crash while sending Action Frames in standalone AP Mode commit 3776c685ebe5f43e9060af06872661de55e80b9a upstream. Currently, whenever there is a need to transmit an Action frame, the brcmfmac driver always uses the P2P vif to send the "actframe" IOVAR to firmware. The P2P interfaces were available when wpa_supplicant is managing the wlan interface. However, the P2P interfaces are not created/initialized when only hostapd is managing the wlan interface. And if hostapd receives an ANQP Query REQ Action frame even from an un-associated STA, the brcmfmac driver tries to use an uninitialized P2P vif pointer for sending the IOVAR to firmware. This NULL pointer dereferencing triggers a driver crash. [ 1417.074538] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000 [...] [ 1417.075188] Hardware name: Raspberry Pi 4 Model B Rev 1.5 (DT) [...] [ 1417.075653] Call trace: [ 1417.075662] brcmf_p2p_send_action_frame+0x23c/0xc58 [brcmfmac] [ 1417.075738] brcmf_cfg80211_mgmt_tx+0x304/0x5c0 [brcmfmac] [ 1417.075810] cfg80211_mlme_mgmt_tx+0x1b0/0x428 [cfg80211] [ 1417.076067] nl80211_tx_mgmt+0x238/0x388 [cfg80211] [ 1417.076281] genl_family_rcv_msg_doit+0xe0/0x158 [ 1417.076302] genl_rcv_msg+0x220/0x2a0 [ 1417.076317] netlink_rcv_skb+0x68/0x140 [ 1417.076330] genl_rcv+0x40/0x60 [ 1417.076343] netlink_unicast+0x330/0x3b8 [ 1417.076357] netlink_sendmsg+0x19c/0x3f8 [ 1417.076370] __sock_sendmsg+0x64/0xc0 [ 1417.076391] ____sys_sendmsg+0x268/0x2a0 [ 1417.076408] ___sys_sendmsg+0xb8/0x118 [ 1417.076427] __sys_sendmsg+0x90/0xf8 [ 1417.076445] __arm64_sys_sendmsg+0x2c/0x40 [ 1417.076465] invoke_syscall+0x50/0x120 [ 1417.076486] el0_svc_common.constprop.0+0x48/0xf0 [ 1417.076506] do_el0_svc+0x24/0x38 [ 1417.076525] el0_svc+0x30/0x100 [ 1417.076548] el0t_64_sync_handler+0x100/0x130 [ 1417.076569] el0t_64_sync+0x190/0x198 [ 1417.076589] Code: f9401e80 aa1603e2 f9403be1 5280e483 (f9400000) Fix this, by always using the vif corresponding to the wdev on which the Action frame Transmission request was initiated by the userspace. This way, even if P2P vif is not available, the IOVAR is sent to firmware on AP vif and the ANQP Query RESP Action frame is transmitted without crashing the driver. Move init_completion() for "send_af_done" from brcmf_p2p_create_p2pdev() to brcmf_p2p_attach(). Because the former function would not get executed when only hostapd is managing wlan interface, and it is not safe to do reinit_completion() later in brcmf_p2p_tx_action_frame(), without any prior init_completion(). And in the brcmf_p2p_tx_action_frame() function, the condition check for P2P Presence response frame is not needed, since the wpa_supplicant is properly sending the P2P Presense Response frame on the P2P-GO vif instead of the P2P-Device vif. Cc: stable@vger.kernel.org Fixes: 18e2f61db3b7 ("brcmfmac: P2P action frame tx") Signed-off-by: Gokul Sivakumar Acked-by: Arend van Spriel Link: https://patch.msgid.link/20251013102819.9727-1-gokulkumar.sivakumar@infineon.com [Cc stable] Signed-off-by: Johannes Berg --- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 3 +- .../broadcom/brcm80211/brcmfmac/p2p.c | 28 +++++++------------ .../broadcom/brcm80211/brcmfmac/p2p.h | 3 +- 3 files changed, 12 insertions(+), 22 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 1ed8d43725ff41..d4ae8cc0516685 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -5619,8 +5619,7 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, brcmf_dbg(TRACE, "Action frame, cookie=%lld, len=%d, freq=%d\n", *cookie, le16_to_cpu(action_frame->len), freq); - ack = brcmf_p2p_send_action_frame(cfg, cfg_to_ndev(cfg), - af_params); + ack = brcmf_p2p_send_action_frame(vif->ifp, af_params); cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack, GFP_KERNEL); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c index 7949f78c61e13b..9752a87649866c 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c @@ -1533,6 +1533,7 @@ int brcmf_p2p_notify_action_tx_complete(struct brcmf_if *ifp, /** * brcmf_p2p_tx_action_frame() - send action frame over fil. * + * @ifp: interface to transmit on. * @p2p: p2p info struct for vif. * @af_params: action frame data/info. * @@ -1542,12 +1543,11 @@ int brcmf_p2p_notify_action_tx_complete(struct brcmf_if *ifp, * The WLC_E_ACTION_FRAME_COMPLETE event will be received when the action * frame is transmitted. */ -static s32 brcmf_p2p_tx_action_frame(struct brcmf_p2p_info *p2p, +static s32 brcmf_p2p_tx_action_frame(struct brcmf_if *ifp, + struct brcmf_p2p_info *p2p, struct brcmf_fil_af_params_le *af_params) { struct brcmf_pub *drvr = p2p->cfg->pub; - struct brcmf_cfg80211_vif *vif; - struct brcmf_p2p_action_frame *p2p_af; s32 err = 0; brcmf_dbg(TRACE, "Enter\n"); @@ -1556,14 +1556,7 @@ static s32 brcmf_p2p_tx_action_frame(struct brcmf_p2p_info *p2p, clear_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, &p2p->status); clear_bit(BRCMF_P2P_STATUS_ACTION_TX_NOACK, &p2p->status); - /* check if it is a p2p_presence response */ - p2p_af = (struct brcmf_p2p_action_frame *)af_params->action_frame.data; - if (p2p_af->subtype == P2P_AF_PRESENCE_RSP) - vif = p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif; - else - vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif; - - err = brcmf_fil_bsscfg_data_set(vif->ifp, "actframe", af_params, + err = brcmf_fil_bsscfg_data_set(ifp, "actframe", af_params, sizeof(*af_params)); if (err) { bphy_err(drvr, " sending action frame has failed\n"); @@ -1715,16 +1708,14 @@ static bool brcmf_p2p_check_dwell_overflow(u32 requested_dwell, /** * brcmf_p2p_send_action_frame() - send action frame . * - * @cfg: driver private data for cfg80211 interface. - * @ndev: net device to transmit on. + * @ifp: interface to transmit on. * @af_params: configuration data for action frame. */ -bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg, - struct net_device *ndev, +bool brcmf_p2p_send_action_frame(struct brcmf_if *ifp, struct brcmf_fil_af_params_le *af_params) { + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; struct brcmf_p2p_info *p2p = &cfg->p2p; - struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_fil_action_frame_le *action_frame; struct brcmf_config_af_params config_af_params; struct afx_hdl *afx_hdl = &p2p->afx_hdl; @@ -1862,7 +1853,7 @@ bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg, if (af_params->channel) msleep(P2P_AF_RETRY_DELAY_TIME); - ack = !brcmf_p2p_tx_action_frame(p2p, af_params); + ack = !brcmf_p2p_tx_action_frame(ifp, p2p, af_params); tx_retry++; dwell_overflow = brcmf_p2p_check_dwell_overflow(requested_dwell, dwell_jiffies); @@ -2222,7 +2213,6 @@ static struct wireless_dev *brcmf_p2p_create_p2pdev(struct brcmf_p2p_info *p2p, WARN_ON(p2p_ifp->bsscfgidx != bsscfgidx); - init_completion(&p2p->send_af_done); INIT_WORK(&p2p->afx_hdl.afx_work, brcmf_p2p_afx_handler); init_completion(&p2p->afx_hdl.act_frm_scan); init_completion(&p2p->wait_next_af); @@ -2518,6 +2508,8 @@ s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg, bool p2pdev_forced) pri_ifp = brcmf_get_ifp(cfg->pub, 0); p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif = pri_ifp->vif; + init_completion(&p2p->send_af_done); + if (p2pdev_forced) { err_ptr = brcmf_p2p_create_p2pdev(p2p, NULL, NULL); if (IS_ERR(err_ptr)) { diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h index d2ecee565bf2e2..d3137ebd715825 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h @@ -168,8 +168,7 @@ int brcmf_p2p_notify_action_frame_rx(struct brcmf_if *ifp, int brcmf_p2p_notify_action_tx_complete(struct brcmf_if *ifp, const struct brcmf_event_msg *e, void *data); -bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg, - struct net_device *ndev, +bool brcmf_p2p_send_action_frame(struct brcmf_if *ifp, struct brcmf_fil_af_params_le *af_params); bool brcmf_p2p_scan_finding_common_channel(struct brcmf_cfg80211_info *cfg, struct brcmf_bss_info_le *bi); From 4da351fb55ec934f547e9238a54835fe0980f682 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Mon, 13 Oct 2025 16:05:21 +0100 Subject: [PATCH 002/143] fbcon: Add defensive coding to logo loader There were various points where the loader was using uninitialised data, had the potential to run off the end of an array, or was handling core functions incorrectly. Fix these up. Also handle 24bpp and 32bpp framebuffers. Signed-off-by: Dave Stevenson --- drivers/video/fbdev/core/fb_logo.c | 191 ++++++++++++++++------------- 1 file changed, 105 insertions(+), 86 deletions(-) diff --git a/drivers/video/fbdev/core/fb_logo.c b/drivers/video/fbdev/core/fb_logo.c index b68e912c369c13..5666928844953e 100644 --- a/drivers/video/fbdev/core/fb_logo.c +++ b/drivers/video/fbdev/core/fb_logo.c @@ -92,19 +92,29 @@ static void fb_set_logo_truepalette(struct fb_info *info, } } -static void fb_set_logo_RGB_palette(struct image_palette *palette, - u32 *palette_to_write, int current_rows) +static void fb_set_logo_RGB_palette(struct fb_info *info, + struct image_palette *pal_in, + u32 *pal_out, int current_rows) { // Set the kernel palette from an array of RGB values - uint32_t color_code; + static const unsigned char mask[] = { + 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff + }; + unsigned char redmask, greenmask, bluemask; + int redshift, greenshift, blueshift; int i; - // Color format is RGB565, remove LSB 3 bits, and move to correct position + redmask = mask[info->var.red.length < 8 ? info->var.red.length : 8]; + greenmask = mask[info->var.green.length < 8 ? info->var.green.length : 8]; + bluemask = mask[info->var.blue.length < 8 ? info->var.blue.length : 8]; + redshift = info->var.red.offset - (8 - info->var.red.length); + greenshift = info->var.green.offset - (8 - info->var.green.length); + blueshift = info->var.blue.offset - (8 - info->var.blue.length); + for (i = 0; i < current_rows; i++) { - color_code = ((((uint16_t)palette->colors[i][0]) >> 3) << 11) | - ((((uint16_t)palette->colors[i][1]) >> 2) << 5) | - (((uint16_t)palette->colors[i][2]) >> 3); - palette_to_write[i+32] = color_code; + pal_out[i + 32] = (safe_shift((pal_in->colors[i][0] & redmask), redshift) | + safe_shift((pal_in->colors[i][1] & greenmask), greenshift) | + safe_shift((pal_in->colors[i][2] & bluemask), blueshift)); } } @@ -339,8 +349,8 @@ static void fb_set_logo_from_file(struct fb_info *info, const char *filepath, { int current_rows = 0, palette_index = 0, actual_row, skip_x = 0, skip_y = 0, ret; unsigned char *read_logo = NULL, *header; - const char *file_content = NULL; - const struct firmware *fw; + const char *file_content; + const struct firmware *fw = NULL; struct image_palette image_palette; const char *current_ptr, *end_ptr; long width = 0, height = 0; @@ -349,7 +359,7 @@ static void fb_set_logo_from_file(struct fb_info *info, const char *filepath, u8 entry[3]; ssize_t len; - ret = request_firmware(&fw, filepath, info->device); + ret = firmware_request_nowarn(&fw, filepath, info->device); if (ret) { pr_info("Failed to load logo file '%s': %d\n", filepath, ret); goto cleanup; @@ -357,90 +367,100 @@ static void fb_set_logo_from_file(struct fb_info *info, const char *filepath, len = fw->size; file_content = fw->data; - if (len > 0) { - current_ptr = file_content; - end_ptr = file_content + len; - if (len < 18) { - pr_err("Invalid logo file: TGA file too small for header\n"); - goto cleanup; - } - header = (unsigned char *)file_content; - - // Skip color map info (bytes 3-7) - // Skip image origin (bytes 8-11) - width = header[12] | (header[13] << 8); - height = header[14] | (header[15] << 8); - - // Only supports uncompressed true-color images (type 2) with 24-bit depth - if (header[2] != 2 || header[16] != 24) { - pr_err("Unsupported TGA logo format: Type=%d, Depth=%d (only support Type=2, Depth=24)\n", - header[2], header[16]); - goto cleanup; - } - // Skip header + ID field - current_ptr = file_content + 18 + header[0]; + if (!len) { + pr_err("Error: logo TGA file is empty. Not drawing fullscreen logo.\n"); + goto cleanup; + } + + current_ptr = file_content; + end_ptr = file_content + len; + if (len < 18) { + pr_err("Invalid logo file: TGA file too small for header\n"); + goto cleanup; + } + header = (unsigned char *)file_content; - read_logo = kmalloc_array(width, height, GFP_KERNEL); - if (!read_logo) - goto cleanup; + // Skip color map info (bytes 3-7) + // Skip image origin (bytes 8-11) + width = header[12] | (header[13] << 8); + height = header[14] | (header[15] << 8); - image->data = read_logo; + // Only supports uncompressed true-color images (type 2) with 24-bit depth + if (header[2] != 2 || header[16] != 24) { + pr_err("Unsupported TGA logo format: Type=%d, Depth=%d (only support Type=2, Depth=24)\n", + header[2], header[16]); + goto cleanup; + } + // Skip header + ID field + current_ptr = file_content + 18 + header[0]; - // TGA pixels are stored bottom-to-top by default, unless bit 5 of - // image_descriptor is set - top_to_bottom = (header[17] & 0x20) != 0; - skip_x = 0; - skip_y = 0; + read_logo = kmalloc_array(width, height, GFP_KERNEL); + if (!read_logo) + goto cleanup; - if (image->width > info->var.xres) { - pr_info("Logo is larger than screen, clipping horizontally"); - skip_x = (image->width - info->var.xres) / 2; - } - if (image->height > info->var.yres) { - pr_info("Logo is larger than screen, clipping vertically"); - skip_y = (image->height - info->var.yres) / 2; - } - current_ptr += skip_y * width * 3 + skip_x * 3; - // Parse pixel data (BGR format in TGA) - for (int i = 0; i < height - 2 * skip_y; i++) { - for (int j = 0; j < width - 2 * skip_x; j++) { - if (current_ptr + 3 > end_ptr) { - pr_info("TGA: Unexpected end of file\n"); - goto cleanup; - } - B = (unsigned char)*current_ptr++; - G = (unsigned char)*current_ptr++; - R = (unsigned char)*current_ptr++; - entry[0] = R; - entry[1] = G; - entry[2] = B; - palette_index = 0; - - if (!fb_palette_contains_entry(&image_palette, current_rows, - entry, 3, &palette_index)) { + image->data = read_logo; + + // TGA pixels are stored bottom-to-top by default, unless bit 5 of + // image_descriptor is set + top_to_bottom = (header[17] & 0x20) != 0; + skip_x = 0; + skip_y = 0; + + if (width > info->var.xres) { + pr_info("Logo is larger than screen (%lu vs %u), clipping horizontally\n", + width, info->var.xres); + skip_x = (width - info->var.xres) / 2; + } + if (height > info->var.yres) { + pr_info("Logo is larger than screen (%lu vs %u), clipping vertically\n", + height, info->var.yres); + skip_y = (height - info->var.yres) / 2; + } + current_ptr += skip_y * width * 3 + skip_x * 3; + // Parse pixel data (BGR format in TGA) + for (int i = 0; i < height - 2 * skip_y; i++) { + for (int j = 0; j < width - 2 * skip_x; j++) { + if (current_ptr + 3 > end_ptr) { + pr_info("TGA: Unexpected end of file\n"); + goto cleanup; + } + B = (unsigned char)*current_ptr++; + G = (unsigned char)*current_ptr++; + R = (unsigned char)*current_ptr++; + entry[0] = R; + entry[1] = G; + entry[2] = B; + palette_index = 0; + + if (!fb_palette_contains_entry(&image_palette, current_rows, + entry, 3, &palette_index)) { + if (current_rows < 224) { for (int k = 0; k < 3; k++) - image_palette.colors[current_rows][k] = entry[k]; + image_palette.colors[current_rows][k] = + entry[k]; palette_index = current_rows; - current_rows++; } - actual_row = top_to_bottom ? i : (height - 1 - i); - - read_logo[actual_row * (width - 2 * skip_x) + j] = - palette_index + 32; + current_rows++; } - current_ptr += skip_x * 3 * 2; + actual_row = top_to_bottom ? i : (height - 1 - i); + + read_logo[actual_row * (width - 2 * skip_x) + j] = + palette_index + 32; } + current_ptr += skip_x * 3 * 2; + } - // Set logo palette - palette = kmalloc(256 * 4, GFP_KERNEL); - if (palette == NULL) - goto cleanup; - fb_set_logo_RGB_palette(&image_palette, palette, current_rows); - info->pseudo_palette = palette; + if (current_rows >= 224) + pr_err("Palette overflow. Entries clipped\n"); - } else { - pr_err("Error: logo TGA file is empty. Not drawing fullscreen logo.\n"); - } + // Set logo palette + palette = kzalloc(256 * 4, GFP_KERNEL); + if (!palette) + goto cleanup; + fb_set_logo_RGB_palette(info, &image_palette, palette, current_rows); + if (info->pseudo_palette) + memcpy(palette, info->pseudo_palette, 32 * sizeof(uint32_t)); + info->pseudo_palette = palette; image->width = min_t(unsigned int, width, info->var.xres); image->height = min_t(unsigned int, height, info->var.yres); @@ -455,8 +475,7 @@ static void fb_set_logo_from_file(struct fb_info *info, const char *filepath, cleanup: kfree(read_logo); - if (file_content) - kvfree(file_content); + release_firmware(fw); } From be50b701a39b2998ec12d924345527d6aab041c2 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Mon, 20 Oct 2025 19:21:48 +0100 Subject: [PATCH 003/143] video: fbdev: simplefb: Add b8g8r8 and b5g6r5 formats The mappings are the reverse of r8g8b8 and r5g6b5 respectively Signed-off-by: Dave Stevenson --- include/linux/platform_data/simplefb.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/platform_data/simplefb.h b/include/linux/platform_data/simplefb.h index 4f94d52ac99fc5..63ba837138f958 100644 --- a/include/linux/platform_data/simplefb.h +++ b/include/linux/platform_data/simplefb.h @@ -16,10 +16,12 @@ #define SIMPLEFB_FORMATS \ { \ { "r5g6b5", 16, {11, 5}, {5, 6}, {0, 5}, {0, 0}, DRM_FORMAT_RGB565 }, \ + { "b5g6r5", 16, {0, 5}, {5, 6}, {11, 5}, {0, 0}, DRM_FORMAT_BGR565 }, \ { "r5g5b5a1", 16, {11, 5}, {6, 5}, {1, 5}, {0, 1}, DRM_FORMAT_RGBA5551 }, \ { "x1r5g5b5", 16, {10, 5}, {5, 5}, {0, 5}, {0, 0}, DRM_FORMAT_XRGB1555 }, \ { "a1r5g5b5", 16, {10, 5}, {5, 5}, {0, 5}, {15, 1}, DRM_FORMAT_ARGB1555 }, \ { "r8g8b8", 24, {16, 8}, {8, 8}, {0, 8}, {0, 0}, DRM_FORMAT_RGB888 }, \ + { "b8g8r8", 24, {0, 8}, {8, 8}, {16, 8}, {0, 0}, DRM_FORMAT_BGR888 }, \ { "x8r8g8b8", 32, {16, 8}, {8, 8}, {0, 8}, {0, 0}, DRM_FORMAT_XRGB8888 }, \ { "a8r8g8b8", 32, {16, 8}, {8, 8}, {0, 8}, {24, 8}, DRM_FORMAT_ARGB8888 }, \ { "x8b8g8r8", 32, {0, 8}, {8, 8}, {16, 8}, {0, 0}, DRM_FORMAT_XBGR8888 }, \ From 831ef266a39b47444f7ee455ff84635a22399f84 Mon Sep 17 00:00:00 2001 From: Lee Jackson Date: Fri, 17 Oct 2025 10:32:17 +0800 Subject: [PATCH 004/143] media: i2c: Modify the datatype of PDAF data in the arducam_64mp driver Modify the PDAF Datatype of the Arducam 64MP camera from 0x30 to 0x12 so that the Raspberry Pi 5 cfe driver can receive PDAF data. Signed-off-by: Lee Jackson --- drivers/media/i2c/arducam_64mp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/arducam_64mp.c b/drivers/media/i2c/arducam_64mp.c index 10a4b257539798..0562a8b1eb0fd6 100644 --- a/drivers/media/i2c/arducam_64mp.c +++ b/drivers/media/i2c/arducam_64mp.c @@ -95,7 +95,7 @@ #define ARDUCAM_64MP_TEST_PATTERN_GB_DEFAULT 0 /* Embedded metadata stream structure */ -#define ARDUCAM_64MP_EMBEDDED_LINE_WIDTH (11560 * 3) +#define ARDUCAM_64MP_EMBEDDED_LINE_WIDTH (((11560 * 3) + 15) & ~15) #define ARDUCAM_64MP_NUM_EMBEDDED_LINES 1 enum pad_types { @@ -155,7 +155,7 @@ static const struct arducam_64mp_reg mode_common_regs[] = { {0x33F1, 0x03}, {0x0111, 0x02}, {0x3062, 0x00}, - {0x3063, 0x30}, + {0x3063, 0x12}, {0x3076, 0x00}, {0x3077, 0x30}, {0x1f06, 0x06}, From 391c2e3f0599e948194a8bdc89a8ea87cd8f7d6c Mon Sep 17 00:00:00 2001 From: Naushir Patuck Date: Tue, 30 Sep 2025 09:58:09 +0100 Subject: [PATCH 005/143] drivers: pcie: Hailo: Remove Hailo PCIe driver This driver will be installed through DKMS going forward. Signed-off-by: Naushir Patuck --- arch/arm/configs/bcm2711_defconfig | 1 - drivers/media/pci/Kconfig | 1 - drivers/media/pci/Makefile | 3 +- drivers/media/pci/hailo/Kconfig | 6 - drivers/media/pci/hailo/Makefile | 34 - drivers/media/pci/hailo/common/fw_operation.c | 147 -- drivers/media/pci/hailo/common/fw_operation.h | 29 - .../media/pci/hailo/common/fw_validation.c | 114 -- .../media/pci/hailo/common/fw_validation.h | 58 - .../pci/hailo/common/hailo_ioctl_common.h | 685 -------- .../pci/hailo/common/hailo_pcie_version.h | 13 - .../media/pci/hailo/common/hailo_resource.c | 141 -- .../media/pci/hailo/common/hailo_resource.h | 39 - drivers/media/pci/hailo/common/pcie_common.c | 913 ---------- drivers/media/pci/hailo/common/pcie_common.h | 193 -- drivers/media/pci/hailo/common/soc_structs.h | 79 - drivers/media/pci/hailo/common/utils.h | 82 - drivers/media/pci/hailo/common/vdma_common.c | 876 --------- drivers/media/pci/hailo/common/vdma_common.h | 284 --- .../pci/hailo/include/hailo_pcie_version.h | 14 - drivers/media/pci/hailo/src/fops.c | 570 ------ drivers/media/pci/hailo/src/fops.h | 23 - drivers/media/pci/hailo/src/nnc.c | 299 ---- drivers/media/pci/hailo/src/nnc.h | 22 - drivers/media/pci/hailo/src/pcie.c | 1563 ----------------- drivers/media/pci/hailo/src/pcie.h | 131 -- drivers/media/pci/hailo/src/soc.c | 244 --- drivers/media/pci/hailo/src/soc.h | 26 - drivers/media/pci/hailo/src/sysfs.c | 45 - drivers/media/pci/hailo/src/sysfs.h | 13 - drivers/media/pci/hailo/src/utils.h | 21 - drivers/media/pci/hailo/utils/compact.h | 161 -- drivers/media/pci/hailo/utils/fw_common.h | 19 - .../pci/hailo/utils/integrated_nnc_utils.c | 109 -- .../pci/hailo/utils/integrated_nnc_utils.h | 30 - drivers/media/pci/hailo/utils/logs.c | 8 - drivers/media/pci/hailo/utils/logs.h | 45 - drivers/media/pci/hailo/vdma/ioctl.c | 721 -------- drivers/media/pci/hailo/vdma/ioctl.h | 37 - drivers/media/pci/hailo/vdma/memory.c | 767 -------- drivers/media/pci/hailo/vdma/memory.h | 56 - drivers/media/pci/hailo/vdma/vdma.c | 313 ---- drivers/media/pci/hailo/vdma/vdma.h | 164 -- 43 files changed, 1 insertion(+), 9098 deletions(-) delete mode 100644 drivers/media/pci/hailo/Kconfig delete mode 100644 drivers/media/pci/hailo/Makefile delete mode 100644 drivers/media/pci/hailo/common/fw_operation.c delete mode 100644 drivers/media/pci/hailo/common/fw_operation.h delete mode 100644 drivers/media/pci/hailo/common/fw_validation.c delete mode 100644 drivers/media/pci/hailo/common/fw_validation.h delete mode 100644 drivers/media/pci/hailo/common/hailo_ioctl_common.h delete mode 100644 drivers/media/pci/hailo/common/hailo_pcie_version.h delete mode 100644 drivers/media/pci/hailo/common/hailo_resource.c delete mode 100644 drivers/media/pci/hailo/common/hailo_resource.h delete mode 100644 drivers/media/pci/hailo/common/pcie_common.c delete mode 100644 drivers/media/pci/hailo/common/pcie_common.h delete mode 100644 drivers/media/pci/hailo/common/soc_structs.h delete mode 100644 drivers/media/pci/hailo/common/utils.h delete mode 100644 drivers/media/pci/hailo/common/vdma_common.c delete mode 100644 drivers/media/pci/hailo/common/vdma_common.h delete mode 100755 drivers/media/pci/hailo/include/hailo_pcie_version.h delete mode 100644 drivers/media/pci/hailo/src/fops.c delete mode 100644 drivers/media/pci/hailo/src/fops.h delete mode 100644 drivers/media/pci/hailo/src/nnc.c delete mode 100644 drivers/media/pci/hailo/src/nnc.h delete mode 100644 drivers/media/pci/hailo/src/pcie.c delete mode 100644 drivers/media/pci/hailo/src/pcie.h delete mode 100644 drivers/media/pci/hailo/src/soc.c delete mode 100644 drivers/media/pci/hailo/src/soc.h delete mode 100644 drivers/media/pci/hailo/src/sysfs.c delete mode 100644 drivers/media/pci/hailo/src/sysfs.h delete mode 100644 drivers/media/pci/hailo/src/utils.h delete mode 100644 drivers/media/pci/hailo/utils/compact.h delete mode 100644 drivers/media/pci/hailo/utils/fw_common.h delete mode 100755 drivers/media/pci/hailo/utils/integrated_nnc_utils.c delete mode 100755 drivers/media/pci/hailo/utils/integrated_nnc_utils.h delete mode 100644 drivers/media/pci/hailo/utils/logs.c delete mode 100644 drivers/media/pci/hailo/utils/logs.h delete mode 100644 drivers/media/pci/hailo/vdma/ioctl.c delete mode 100644 drivers/media/pci/hailo/vdma/ioctl.h delete mode 100644 drivers/media/pci/hailo/vdma/memory.c delete mode 100644 drivers/media/pci/hailo/vdma/memory.h delete mode 100644 drivers/media/pci/hailo/vdma/vdma.c delete mode 100644 drivers/media/pci/hailo/vdma/vdma.h diff --git a/arch/arm/configs/bcm2711_defconfig b/arch/arm/configs/bcm2711_defconfig index e5a6f28ce6a1c5..003e89e0bd0e1f 100644 --- a/arch/arm/configs/bcm2711_defconfig +++ b/arch/arm/configs/bcm2711_defconfig @@ -960,7 +960,6 @@ CONFIG_VIDEO_EM28XX_V4L2=m CONFIG_VIDEO_EM28XX_ALSA=m CONFIG_VIDEO_EM28XX_DVB=m CONFIG_MEDIA_PCI_SUPPORT=y -CONFIG_MEDIA_PCI_HAILO=m CONFIG_RADIO_SAA7706H=m CONFIG_RADIO_SHARK=m CONFIG_RADIO_SHARK2=m diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig index 944ca87d0be791..7f65aa60938834 100644 --- a/drivers/media/pci/Kconfig +++ b/drivers/media/pci/Kconfig @@ -75,7 +75,6 @@ config VIDEO_PCI_SKELETON when developing new drivers. source "drivers/media/pci/intel/Kconfig" -source "drivers/media/pci/hailo/Kconfig" endif #MEDIA_PCI_SUPPORT endif #PCI diff --git a/drivers/media/pci/Makefile b/drivers/media/pci/Makefile index 78cc75f1c8f838..f18c7e15abe3e9 100644 --- a/drivers/media/pci/Makefile +++ b/drivers/media/pci/Makefile @@ -17,8 +17,7 @@ obj-y += ttpci/ \ saa7146/ \ smipcie/ \ netup_unidvb/ \ - intel/ \ - hailo/ + intel/ # Please keep it alphabetically sorted by Kconfig name # (e. g. LC_ALL=C sort Makefile) diff --git a/drivers/media/pci/hailo/Kconfig b/drivers/media/pci/hailo/Kconfig deleted file mode 100644 index bd011b6b8f0e41..00000000000000 --- a/drivers/media/pci/hailo/Kconfig +++ /dev/null @@ -1,6 +0,0 @@ - -config MEDIA_PCI_HAILO - tristate "Hailo AI accelerator PCIe driver" - depends on PCI - help - Enable build of Hailo AI accelerator PCIe driver. diff --git a/drivers/media/pci/hailo/Makefile b/drivers/media/pci/hailo/Makefile deleted file mode 100644 index 5cf92acc7ceb1f..00000000000000 --- a/drivers/media/pci/hailo/Makefile +++ /dev/null @@ -1,34 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 - -COMMON_SRC_DIRECTORY=common -VDMA_SRC_DIRECTORY=vdma -UTILS_SRC_DIRECTORY=utils - -obj-$(CONFIG_MEDIA_PCI_HAILO) := hailo_pci.o - -hailo_pci-objs += src/pcie.o -hailo_pci-objs += src/fops.o -hailo_pci-objs += src/sysfs.o -hailo_pci-objs += src/nnc.o -hailo_pci-objs += src/soc.o - -hailo_pci-objs += $(COMMON_SRC_DIRECTORY)/fw_validation.o -hailo_pci-objs += $(COMMON_SRC_DIRECTORY)/fw_operation.o -hailo_pci-objs += $(COMMON_SRC_DIRECTORY)/pcie_common.o -hailo_pci-objs += $(COMMON_SRC_DIRECTORY)/vdma_common.o -hailo_pci-objs += $(COMMON_SRC_DIRECTORY)/hailo_resource.o - -hailo_pci-objs += $(UTILS_SRC_DIRECTORY)/logs.o -hailo_pci-objs += $(UTILS_SRC_DIRECTORY)/integrated_nnc_utils.o - -hailo_pci-objs += $(VDMA_SRC_DIRECTORY)/vdma.o -hailo_pci-objs += $(VDMA_SRC_DIRECTORY)/memory.o -hailo_pci-objs += $(VDMA_SRC_DIRECTORY)/ioctl.o - -ccflags-y += -Werror -ccflags-y += -DHAILO_RASBERRY_PIE -ccflags-y += -I $(src) -ccflags-y += -I $(src)/include -ccflags-y += -I $(src)/common - -clean-files := $(hailo_pci-objs) diff --git a/drivers/media/pci/hailo/common/fw_operation.c b/drivers/media/pci/hailo/common/fw_operation.c deleted file mode 100644 index fb3b7c16734033..00000000000000 --- a/drivers/media/pci/hailo/common/fw_operation.c +++ /dev/null @@ -1,147 +0,0 @@ -// SPDX-License-Identifier: MIT -/** - * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. - **/ - -#include "fw_operation.h" - -#include -#include -#include -#include - -typedef struct { - u32 host_offset; - u32 chip_offset; -} FW_DEBUG_BUFFER_HEADER_t; - -#define DEBUG_BUFFER_DATA_SIZE (DEBUG_BUFFER_TOTAL_SIZE - sizeof(FW_DEBUG_BUFFER_HEADER_t)) -#define PCIE_D2H_NOTIFICATION_SRAM_OFFSET (0x640 + 0x640) -#define PCIE_APP_CPU_DEBUG_OFFSET (8*1024) -#define PCIE_CORE_CPU_DEBUG_OFFSET (PCIE_APP_CPU_DEBUG_OFFSET + DEBUG_BUFFER_TOTAL_SIZE) - -int hailo_read_firmware_notification(struct hailo_resource *resource, struct hailo_d2h_notification *notification) -{ - hailo_d2h_buffer_details_t d2h_buffer_details = {0, 0}; - hailo_resource_read_buffer(resource, 0, sizeof(d2h_buffer_details), - &d2h_buffer_details); - - if ((sizeof(notification->buffer) < d2h_buffer_details.buffer_len) || (0 == d2h_buffer_details.is_buffer_in_use)) { - return -EINVAL; - } - - notification->buffer_len = d2h_buffer_details.buffer_len; - hailo_resource_read_buffer(resource, sizeof(d2h_buffer_details), notification->buffer_len, notification->buffer); - - // Write is_buffer_in_use = false - hailo_resource_write16(resource, 0, 0); - return 0; -} - -int hailo_pcie_read_firmware_notification(struct hailo_resource *resource, - struct hailo_d2h_notification *notification) -{ - struct hailo_resource notification_resource; - - if (PCIE_D2H_NOTIFICATION_SRAM_OFFSET > resource->size) { - return -EINVAL; - } - - notification_resource.address = resource->address + PCIE_D2H_NOTIFICATION_SRAM_OFFSET, - notification_resource.size = sizeof(struct hailo_d2h_notification); - - return hailo_read_firmware_notification(¬ification_resource, notification); -} - -static inline size_t calculate_log_ready_to_read(FW_DEBUG_BUFFER_HEADER_t *header) -{ - size_t ready_to_read = 0; - size_t host_offset = header->host_offset; - size_t chip_offset = header->chip_offset; - - if (chip_offset >= host_offset) { - ready_to_read = chip_offset - host_offset; - } else { - ready_to_read = DEBUG_BUFFER_DATA_SIZE - (host_offset - chip_offset); - } - - return ready_to_read; -} - -long hailo_read_firmware_log(struct hailo_resource *fw_logger_resource, struct hailo_read_log_params *params) -{ - FW_DEBUG_BUFFER_HEADER_t debug_buffer_header = {0}; - size_t read_offset = 0; - size_t ready_to_read = 0; - size_t size_to_read = 0; - uintptr_t user_buffer = (uintptr_t)params->buffer; - - if (params->buffer_size > ARRAY_SIZE(params->buffer)) { - return -EINVAL; - } - - hailo_resource_read_buffer(fw_logger_resource, 0, sizeof(debug_buffer_header), - &debug_buffer_header); - - /* Point to the start of the data buffer. */ - ready_to_read = calculate_log_ready_to_read(&debug_buffer_header); - if (0 == ready_to_read) { - params->read_bytes = 0; - return 0; - } - /* If ready to read is bigger than the buffer size, read only buffer size bytes. */ - ready_to_read = min(ready_to_read, params->buffer_size); - - /* Point to the data that is read to be read by the host. */ - read_offset = sizeof(debug_buffer_header) + debug_buffer_header.host_offset; - /* Check if the offset should cycle back to beginning. */ - if (DEBUG_BUFFER_DATA_SIZE <= debug_buffer_header.host_offset + ready_to_read) { - size_to_read = DEBUG_BUFFER_DATA_SIZE - debug_buffer_header.host_offset; - hailo_resource_read_buffer(fw_logger_resource, read_offset, size_to_read, (void*)user_buffer); - - user_buffer += size_to_read; - size_to_read = ready_to_read - size_to_read; - /* Point back to the beginning of the data buffer. */ - read_offset -= debug_buffer_header.host_offset; - } - else { - size_to_read = ready_to_read; - } - - /* size_to_read may become 0 if the read reached DEBUG_BUFFER_DATA_SIZE exactly */ - hailo_resource_read_buffer(fw_logger_resource, read_offset, size_to_read, (void*)user_buffer); - - /* Change current_offset to represent the new host offset. */ - read_offset += size_to_read; - hailo_resource_write32(fw_logger_resource, offsetof(FW_DEBUG_BUFFER_HEADER_t, host_offset), - (u32)(read_offset - sizeof(debug_buffer_header))); - - params->read_bytes = ready_to_read; - return 0; -} - -long hailo_pcie_read_firmware_log(struct hailo_resource *resource, struct hailo_read_log_params *params) -{ - long err = 0; - struct hailo_resource log_resource = {resource->address, DEBUG_BUFFER_TOTAL_SIZE}; - - if (HAILO_CPU_ID_CPU0 == params->cpu_id) { - log_resource.address += PCIE_APP_CPU_DEBUG_OFFSET; - } else if (HAILO_CPU_ID_CPU1 == params->cpu_id) { - log_resource.address += PCIE_CORE_CPU_DEBUG_OFFSET; - } else { - return -EINVAL; - } - - if (0 == params->buffer_size) { - params->read_bytes = 0; - return 0; - } - - err = hailo_read_firmware_log(&log_resource, params); - if (0 != err) { - return err; - } - - return 0; -} \ No newline at end of file diff --git a/drivers/media/pci/hailo/common/fw_operation.h b/drivers/media/pci/hailo/common/fw_operation.h deleted file mode 100644 index 8c8185ce6ba855..00000000000000 --- a/drivers/media/pci/hailo/common/fw_operation.h +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: MIT -/** - * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. - **/ - -#ifndef _HAILO_COMMON_FIRMWARE_OPERATION_H_ -#define _HAILO_COMMON_FIRMWARE_OPERATION_H_ - -#include "hailo_resource.h" - -#define DEBUG_BUFFER_TOTAL_SIZE (4*1024) - -#ifdef __cplusplus -extern "C" { -#endif - -int hailo_read_firmware_notification(struct hailo_resource *resource, struct hailo_d2h_notification *notification); - -int hailo_pcie_read_firmware_notification(struct hailo_resource *resource, struct hailo_d2h_notification *notification); - -long hailo_read_firmware_log(struct hailo_resource *fw_logger_resource, struct hailo_read_log_params *params); - -long hailo_pcie_read_firmware_log(struct hailo_resource *resource, struct hailo_read_log_params *params); - -#ifdef __cplusplus -} -#endif - -#endif /* _HAILO_COMMON_FIRMWARE_OPERATION_H_ */ diff --git a/drivers/media/pci/hailo/common/fw_validation.c b/drivers/media/pci/hailo/common/fw_validation.c deleted file mode 100644 index 2eb59dfd746644..00000000000000 --- a/drivers/media/pci/hailo/common/fw_validation.c +++ /dev/null @@ -1,114 +0,0 @@ -// SPDX-License-Identifier: MIT -/** - * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. - **/ - -#include "fw_validation.h" -#include -#include - - - -/* when reading the firmware we don't want to read past the firmware_size, - so we have a consumed_firmware_offset that is updated _before_ accessing data at that offset - of firmware_base_address */ -#define CONSUME_FIRMWARE(__size, __err) do { \ - consumed_firmware_offset += (u32) (__size); \ - if ((firmware_size < (__size)) || (firmware_size < consumed_firmware_offset)) { \ - err = __err; \ - goto exit; \ - } \ - } while(0) - -int FW_VALIDATION__validate_fw_header(uintptr_t firmware_base_address, - size_t firmware_size, u32 max_code_size, u32 *outer_consumed_firmware_offset, - firmware_header_t **out_firmware_header, enum hailo_board_type board_type) -{ - int err = -EINVAL; - firmware_header_t *firmware_header = NULL; - u32 consumed_firmware_offset = *outer_consumed_firmware_offset; - u32 expected_firmware_magic = 0; - - firmware_header = (firmware_header_t *) (firmware_base_address + consumed_firmware_offset); - CONSUME_FIRMWARE(sizeof(firmware_header_t), -EINVAL); - - switch (board_type) { - case HAILO_BOARD_TYPE_HAILO8: - expected_firmware_magic = FIRMWARE_HEADER_MAGIC_HAILO8; - break; - case HAILO_BOARD_TYPE_HAILO10H_LEGACY: - case HAILO_BOARD_TYPE_HAILO15: - case HAILO_BOARD_TYPE_HAILO10H: - expected_firmware_magic = FIRMWARE_HEADER_MAGIC_HAILO15; - break; - case HAILO_BOARD_TYPE_HAILO15L: - expected_firmware_magic = FIRMWARE_HEADER_MAGIC_HAILO15L; - break; - default: - err = -EINVAL; - goto exit; - } - - if (expected_firmware_magic != firmware_header->magic) { - err = -EINVAL; - goto exit; - } - - /* Validate that the firmware header version is supported */ - switch(firmware_header->header_version) { - case FIRMWARE_HEADER_VERSION_INITIAL: - break; - default: - err = -EINVAL; - goto exit; - break; - } - - if (MINIMUM_FIRMWARE_CODE_SIZE > firmware_header->code_size) { - err = -EINVAL; - goto exit; - } - - if (max_code_size < firmware_header->code_size) { - err = -EINVAL; - goto exit; - } - - CONSUME_FIRMWARE(firmware_header->code_size, -EINVAL); - - *outer_consumed_firmware_offset = consumed_firmware_offset; - *out_firmware_header = firmware_header; - err = 0; - -exit: - return err; -} - -int FW_VALIDATION__validate_cert_header(uintptr_t firmware_base_address, - size_t firmware_size, u32 *outer_consumed_firmware_offset, secure_boot_certificate_header_t **out_firmware_cert) -{ - - secure_boot_certificate_header_t *firmware_cert = NULL; - int err = -EINVAL; - u32 consumed_firmware_offset = *outer_consumed_firmware_offset; - - firmware_cert = (secure_boot_certificate_header_t *) (firmware_base_address + consumed_firmware_offset); - CONSUME_FIRMWARE(sizeof(secure_boot_certificate_header_t), -EINVAL); - - if ((MAXIMUM_FIRMWARE_CERT_KEY_SIZE < firmware_cert->key_size) || - (MAXIMUM_FIRMWARE_CERT_CONTENT_SIZE < firmware_cert->content_size)) { - err = -EINVAL; - goto exit; - } - - CONSUME_FIRMWARE(firmware_cert->key_size, -EINVAL); - CONSUME_FIRMWARE(firmware_cert->content_size, -EINVAL); - - *outer_consumed_firmware_offset = consumed_firmware_offset; - *out_firmware_cert = firmware_cert; - err = 0; - -exit: - return err; -} - diff --git a/drivers/media/pci/hailo/common/fw_validation.h b/drivers/media/pci/hailo/common/fw_validation.h deleted file mode 100644 index b1f7f32bc7086b..00000000000000 --- a/drivers/media/pci/hailo/common/fw_validation.h +++ /dev/null @@ -1,58 +0,0 @@ -// SPDX-License-Identifier: MIT -/** - * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. - **/ - -#ifndef PCIE_COMMON_FIRMWARE_HEADER_UTILS_H_ -#define PCIE_COMMON_FIRMWARE_HEADER_UTILS_H_ - -#include "hailo_ioctl_common.h" -#include - -#define FIRMWARE_HEADER_MAGIC_HAILO8 (0x1DD89DE0) -#define FIRMWARE_HEADER_MAGIC_HAILO15 (0xE905DAAB) -#define FIRMWARE_HEADER_MAGIC_HAILO15L (0xF94739AB) - -typedef enum { - FIRMWARE_HEADER_VERSION_INITIAL = 0, - - /* MUST BE LAST */ - FIRMWARE_HEADER_VERSION_COUNT -} firmware_header_version_t; - -typedef struct { - u32 magic; - u32 header_version; - u32 firmware_major; - u32 firmware_minor; - u32 firmware_revision; - u32 code_size; -} firmware_header_t; - - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable:4200) -#endif /* _MSC_VER */ - -typedef struct { - u32 key_size; - u32 content_size; -} secure_boot_certificate_header_t; - -#ifdef _MSC_VER -#pragma warning(pop) -#endif /* _MSC_VER */ - -#define MINIMUM_FIRMWARE_CODE_SIZE (20*4) -#define MAXIMUM_FIRMWARE_CERT_KEY_SIZE (0x1000) -#define MAXIMUM_FIRMWARE_CERT_CONTENT_SIZE (0x1000) - -int FW_VALIDATION__validate_fw_header(uintptr_t firmware_base_address, - size_t firmware_size, u32 max_code_size, u32 *outer_consumed_firmware_offset, - firmware_header_t **out_firmware_header, enum hailo_board_type board_type); - -int FW_VALIDATION__validate_cert_header(uintptr_t firmware_base_address, - size_t firmware_size, u32 *outer_consumed_firmware_offset, secure_boot_certificate_header_t **out_firmware_cert); - -#endif diff --git a/drivers/media/pci/hailo/common/hailo_ioctl_common.h b/drivers/media/pci/hailo/common/hailo_ioctl_common.h deleted file mode 100644 index 3333513ce246d8..00000000000000 --- a/drivers/media/pci/hailo/common/hailo_ioctl_common.h +++ /dev/null @@ -1,685 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0 WITH Linux-syscall-note) AND MIT -/** - * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. - **/ - -#ifndef _HAILO_IOCTL_COMMON_H_ -#define _HAILO_IOCTL_COMMON_H_ - -#define HAILO_DRV_VER_MAJOR 4 -#define HAILO_DRV_VER_MINOR 20 -#define HAILO_DRV_VER_REVISION 0 - -#define _STRINGIFY_EXPANDED( x ) #x -#define _STRINGIFY_NUMBER( x ) _STRINGIFY_EXPANDED(x) -#define HAILO_DRV_VER _STRINGIFY_NUMBER(HAILO_DRV_VER_MAJOR) "." _STRINGIFY_NUMBER(HAILO_DRV_VER_MINOR) "." _STRINGIFY_NUMBER(HAILO_DRV_VER_REVISION) - - -// This value is not easily changeable. -// For example: the channel interrupts ioctls assume we have up to 32 channels -#define MAX_VDMA_CHANNELS_PER_ENGINE (32) -#define VDMA_CHANNELS_PER_ENGINE_PER_DIRECTION (16) -#define MAX_VDMA_ENGINES (3) -#define SIZE_OF_VDMA_DESCRIPTOR (16) -#define VDMA_DEST_CHANNELS_START (16) -#define MAX_SG_DESCS_COUNT (64 * 1024u) - -#define HAILO_VDMA_MAX_ONGOING_TRANSFERS (128) -#define HAILO_VDMA_MAX_ONGOING_TRANSFERS_MASK (HAILO_VDMA_MAX_ONGOING_TRANSFERS - 1) - -#define CHANNEL_IRQ_TIMESTAMPS_SIZE (HAILO_VDMA_MAX_ONGOING_TRANSFERS * 2) -#define CHANNEL_IRQ_TIMESTAMPS_SIZE_MASK (CHANNEL_IRQ_TIMESTAMPS_SIZE - 1) - -#define INVALID_DRIVER_HANDLE_VALUE ((uintptr_t)-1) - -// Used by windows and unix driver to raise the right CPU control handle to the FW. The same as in pcie_service FW -#define FW_ACCESS_CORE_CPU_CONTROL_SHIFT (1) -#define FW_ACCESS_CORE_CPU_CONTROL_MASK (1 << FW_ACCESS_CORE_CPU_CONTROL_SHIFT) -#define FW_ACCESS_CONTROL_INTERRUPT_SHIFT (0) -#define FW_ACCESS_APP_CPU_CONTROL_MASK (1 << FW_ACCESS_CONTROL_INTERRUPT_SHIFT) -#define FW_ACCESS_DRIVER_SHUTDOWN_SHIFT (2) -#define FW_ACCESS_DRIVER_SHUTDOWN_MASK (1 << FW_ACCESS_DRIVER_SHUTDOWN_SHIFT) -// HRT-15790 TODO: separate nnc interrupts and soc interrupts -#define FW_ACCESS_SOFT_RESET_SHIFT (3) -#define FW_ACCESS_SOFT_RESET_MASK (1 << FW_ACCESS_SOFT_RESET_SHIFT) - -#define FW_ACCESS_SOC_CONTROL_SHIFT (3) -#define FW_ACCESS_SOC_CONTROL_MASK (1 << FW_ACCESS_SOC_CONTROL_SHIFT) - -#define INVALID_VDMA_CHANNEL (0xff) - - -#if !defined(__cplusplus) && defined(NTDDI_VERSION) -#include -typedef ULONG uint32_t; -typedef UCHAR uint8_t; -typedef USHORT uint16_t; -typedef ULONGLONG uint64_t; -#endif /* !defined(__cplusplus) && defined(NTDDI_VERSION) */ - - -#ifdef _MSC_VER - -#include - -#if !defined(bool) && !defined(__cplusplus) -typedef uint8_t bool; -#endif // !defined(bool) && !defined(__cplusplus) - -#if !defined(INT_MAX) -#define INT_MAX 0x7FFFFFFF -#endif // !defined(INT_MAX) - -#if !defined(ECONNRESET) -#define ECONNRESET 104 /* Connection reset by peer */ -#endif // !defined(ECONNRESET) - -// {d88d31f1-fede-4e71-ac2a-6ce0018c1501} -DEFINE_GUID (GUID_DEVINTERFACE_HailoKM_NNC, - 0xd88d31f1,0xfede,0x4e71,0xac,0x2a,0x6c,0xe0,0x01,0x8c,0x15,0x01); - -// {7f16047d-64b8-207a-0092-e970893970a2} -DEFINE_GUID (GUID_DEVINTERFACE_HailoKM_SOC, - 0x7f16047d,0x64b8,0x207a,0x00,0x92,0xe9,0x70,0x89,0x39,0x70,0xa2); - -#define HAILO_GENERAL_IOCTL_MAGIC 0 -#define HAILO_VDMA_IOCTL_MAGIC 1 -#define HAILO_SOC_IOCTL_MAGIC 2 -#define HAILO_PCI_EP_IOCTL_MAGIC 3 -#define HAILO_NNC_IOCTL_MAGIC 4 - -#define HAILO_IOCTL_COMPATIBLE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS) - - -typedef struct tCompatibleHailoIoctlParam -{ - union { - struct { - ULONG Size : 16; - ULONG Code : 8; - ULONG Type : 6; - ULONG Read : 1; - ULONG Write : 1; - } bits; - ULONG value; - } u; -} tCompatibleHailoIoctlParam; - -static ULONG FORCEINLINE _IOC_(ULONG nr, ULONG type, ULONG size, bool read, bool write) -{ - struct tCompatibleHailoIoctlParam param; - param.u.bits.Code = nr; - param.u.bits.Size = size; - param.u.bits.Type = type; - param.u.bits.Read = read ? 1 : 0; - param.u.bits.Write = write ? 1 : 0; - return param.u.value; -} - -#define _IOW_(type,nr,size) _IOC_(nr, type, sizeof(size), true, false) -#define _IOR_(type,nr,size) _IOC_(nr, type, sizeof(size), false, true) -#define _IOWR_(type,nr,size) _IOC_(nr, type, sizeof(size), true, true) -#define _IO_(type,nr) _IOC_(nr, type, 0, false, false) - -#elif defined(__linux__) // #ifdef _MSC_VER -#ifndef __KERNEL__ -// include the userspace headers only if this file is included by user space program -// It is discourged to include them when compiling the driver (https://lwn.net/Articles/113349/) -#include -#include -#else -#include -#include -#include -#endif // ifndef __KERNEL__ - -#include - -#define _IOW_ _IOW -#define _IOR_ _IOR -#define _IOWR_ _IOWR -#define _IO_ _IO - -#define HAILO_GENERAL_IOCTL_MAGIC 'g' -#define HAILO_VDMA_IOCTL_MAGIC 'v' -#define HAILO_SOC_IOCTL_MAGIC 's' -#define HAILO_NNC_IOCTL_MAGIC 'n' -#define HAILO_PCI_EP_IOCTL_MAGIC 'p' - -#elif defined(__QNX__) // #ifdef _MSC_VER -#include -#include -#include -#include -#include - -// defines for devctl -#define _IOW_ __DIOF -#define _IOR_ __DIOT -#define _IOWR_ __DIOTF -#define _IO_ __DION -#define HAILO_GENERAL_IOCTL_MAGIC _DCMD_ALL -#define HAILO_VDMA_IOCTL_MAGIC _DCMD_MISC - -#else // #ifdef _MSC_VER -#error "unsupported platform!" -#endif - -#pragma pack(push, 1) - -struct hailo_channel_interrupt_timestamp { - uint64_t timestamp_ns; - uint16_t desc_num_processed; -}; - -typedef struct { - uint16_t is_buffer_in_use; - uint16_t buffer_len; -} hailo_d2h_buffer_details_t; - -// This struct is the same as `enum dma_data_direction` (defined in linux/dma-direction) -enum hailo_dma_data_direction { - HAILO_DMA_BIDIRECTIONAL = 0, - HAILO_DMA_TO_DEVICE = 1, - HAILO_DMA_FROM_DEVICE = 2, - HAILO_DMA_NONE = 3, - - /** Max enum value to maintain ABI Integrity */ - HAILO_DMA_MAX_ENUM = INT_MAX, -}; - -// Enum that states what type of buffer we are working with in the driver -enum hailo_dma_buffer_type { - HAILO_DMA_USER_PTR_BUFFER = 0, - HAILO_DMA_DMABUF_BUFFER = 1, - - /** Max enum value to maintain ABI Integrity */ - HAILO_DMA_BUFFER_MAX_ENUM = INT_MAX, -}; - -// Enum that determines if buffer should be allocated from user space or from driver -enum hailo_allocation_mode { - HAILO_ALLOCATION_MODE_USERSPACE = 0, - HAILO_ALLOCATION_MODE_DRIVER = 1, - - /** Max enum value to maintain ABI Integrity */ - HAILO_ALLOCATION_MODE_MAX_ENUM = INT_MAX, -}; - -enum hailo_vdma_interrupts_domain { - HAILO_VDMA_INTERRUPTS_DOMAIN_NONE = 0, - HAILO_VDMA_INTERRUPTS_DOMAIN_DEVICE = (1 << 0), - HAILO_VDMA_INTERRUPTS_DOMAIN_HOST = (1 << 1), - - /** Max enum value to maintain ABI Integrity */ - HAILO_VDMA_INTERRUPTS_DOMAIN_MAX_ENUM = INT_MAX, -}; - -/* structure used in ioctl HAILO_VDMA_BUFFER_MAP */ -struct hailo_vdma_buffer_map_params { -#if defined(__linux__) || defined(_MSC_VER) - uintptr_t user_address; // in -#elif defined(__QNX__) - shm_handle_t shared_memory_handle; // in -#else -#error "unsupported platform!" -#endif // __linux__ - size_t size; // in - enum hailo_dma_data_direction data_direction; // in - enum hailo_dma_buffer_type buffer_type; // in - uintptr_t allocated_buffer_handle; // in - size_t mapped_handle; // out -}; - -/* structure used in ioctl HAILO_VDMA_BUFFER_UNMAP */ -struct hailo_vdma_buffer_unmap_params { - size_t mapped_handle; -}; - -/* structure used in ioctl HAILO_DESC_LIST_CREATE */ -struct hailo_desc_list_create_params { - size_t desc_count; // in - uint16_t desc_page_size; // in - bool is_circular; // in - uintptr_t desc_handle; // out - uint64_t dma_address; // out -}; - -/* structure used in ioctl HAILO_DESC_LIST_RELEASE */ -struct hailo_desc_list_release_params { - uintptr_t desc_handle; // in -}; - -struct hailo_write_action_list_params { - uint8_t *data; // in - size_t size; // in - uint64_t dma_address; // out -}; - -/* structure used in ioctl HAILO_DESC_LIST_BIND_VDMA_BUFFER */ -struct hailo_desc_list_program_params { - size_t buffer_handle; // in - size_t buffer_size; // in - size_t buffer_offset; // in - uintptr_t desc_handle; // in - uint8_t channel_index; // in - uint32_t starting_desc; // in - bool should_bind; // in - enum hailo_vdma_interrupts_domain last_interrupts_domain; // in - bool is_debug; // in -}; - -/* structure used in ioctl HAILO_VDMA_ENABLE_CHANNELS */ -struct hailo_vdma_enable_channels_params { - uint32_t channels_bitmap_per_engine[MAX_VDMA_ENGINES]; // in - bool enable_timestamps_measure; // in -}; - -/* structure used in ioctl HAILO_VDMA_DISABLE_CHANNELS */ -struct hailo_vdma_disable_channels_params { - uint32_t channels_bitmap_per_engine[MAX_VDMA_ENGINES]; // in -}; - -/* structure used in ioctl HAILO_VDMA_INTERRUPTS_WAIT */ -struct hailo_vdma_interrupts_channel_data { - uint8_t engine_index; - uint8_t channel_index; - bool is_active; // If not activate, num_processed is ignored. - uint8_t transfers_completed; // Number of transfers completed. - uint8_t host_error; // Channel errors bits on source side - uint8_t device_error; // Channel errors bits on dest side - bool validation_success; // If the validation of the channel was successful -}; - -struct hailo_vdma_interrupts_wait_params { - uint32_t channels_bitmap_per_engine[MAX_VDMA_ENGINES]; // in - uint8_t channels_count; // out - struct hailo_vdma_interrupts_channel_data - irq_data[MAX_VDMA_CHANNELS_PER_ENGINE * MAX_VDMA_ENGINES]; // out -}; - -/* structure used in ioctl HAILO_VDMA_INTERRUPTS_READ_TIMESTAMPS */ -struct hailo_vdma_interrupts_read_timestamp_params { - uint8_t engine_index; // in - uint8_t channel_index; // in - uint32_t timestamps_count; // out - struct hailo_channel_interrupt_timestamp timestamps[CHANNEL_IRQ_TIMESTAMPS_SIZE]; // out -}; - -/* structure used in ioctl HAILO_FW_CONTROL */ -#define MAX_CONTROL_LENGTH (1500) -#define PCIE_EXPECTED_MD5_LENGTH (16) - - -/* structure used in ioctl HAILO_FW_CONTROL and HAILO_READ_LOG */ -enum hailo_cpu_id { - HAILO_CPU_ID_CPU0 = 0, - HAILO_CPU_ID_CPU1, - HAILO_CPU_ID_NONE, - - /** Max enum value to maintain ABI Integrity */ - HAILO_CPU_MAX_ENUM = INT_MAX, -}; - -struct hailo_fw_control { - // expected_md5+buffer_len+buffer must be in this order at the start of the struct - uint8_t expected_md5[PCIE_EXPECTED_MD5_LENGTH]; - uint32_t buffer_len; - uint8_t buffer[MAX_CONTROL_LENGTH]; - uint32_t timeout_ms; - enum hailo_cpu_id cpu_id; -}; - -/* structure used in ioctl HAILO_MEMORY_TRANSFER */ -// Max bar transfer size gotten from ATR0_TABLE_SIZE -#define MAX_MEMORY_TRANSFER_LENGTH (4096) - -enum hailo_transfer_direction { - TRANSFER_READ = 0, - TRANSFER_WRITE, - - /** Max enum value to maintain ABI Integrity */ - TRANSFER_MAX_ENUM = INT_MAX, -}; - -enum hailo_transfer_memory_type { - HAILO_TRANSFER_DEVICE_DIRECT_MEMORY, - - // vDMA memories - HAILO_TRANSFER_MEMORY_VDMA0 = 0x100, - HAILO_TRANSFER_MEMORY_VDMA1, - HAILO_TRANSFER_MEMORY_VDMA2, - - // PCIe driver memories - HAILO_TRANSFER_MEMORY_PCIE_BAR0 = 0x200, - HAILO_TRANSFER_MEMORY_PCIE_BAR2 = 0x202, - HAILO_TRANSFER_MEMORY_PCIE_BAR4 = 0x204, - - // DRAM DMA driver memories - HAILO_TRANSFER_MEMORY_DMA_ENGINE0 = 0x300, - HAILO_TRANSFER_MEMORY_DMA_ENGINE1, - HAILO_TRANSFER_MEMORY_DMA_ENGINE2, - - // PCIe EP driver memories - HAILO_TRANSFER_MEMORY_PCIE_EP_CONFIG = 0x400, - HAILO_TRANSFER_MEMORY_PCIE_EP_BRIDGE, - - /** Max enum value to maintain ABI Integrity */ - HAILO_TRANSFER_MEMORY_MAX_ENUM = INT_MAX, -}; - -struct hailo_memory_transfer_params { - enum hailo_transfer_direction transfer_direction; // in - enum hailo_transfer_memory_type memory_type; // in - uint64_t address; // in - size_t count; // in - uint8_t buffer[MAX_MEMORY_TRANSFER_LENGTH]; // in/out -}; - -/* structure used in ioctl HAILO_VDMA_BUFFER_SYNC */ -enum hailo_vdma_buffer_sync_type { - HAILO_SYNC_FOR_CPU, - HAILO_SYNC_FOR_DEVICE, - - /** Max enum value to maintain ABI Integrity */ - HAILO_SYNC_MAX_ENUM = INT_MAX, -}; - -struct hailo_vdma_buffer_sync_params { - size_t handle; // in - enum hailo_vdma_buffer_sync_type sync_type; // in - size_t offset; // in - size_t count; // in -}; - -/* structure used in ioctl HAILO_READ_NOTIFICATION */ -#define MAX_NOTIFICATION_LENGTH (1500) - -struct hailo_d2h_notification { - size_t buffer_len; // out - uint8_t buffer[MAX_NOTIFICATION_LENGTH]; // out -}; - -enum hailo_board_type { - HAILO_BOARD_TYPE_HAILO8 = 0, - HAILO_BOARD_TYPE_HAILO15, - HAILO_BOARD_TYPE_HAILO15L, - HAILO_BOARD_TYPE_HAILO10H, - HAILO_BOARD_TYPE_HAILO10H_LEGACY, - HAILO_BOARD_TYPE_COUNT, - - /** Max enum value to maintain ABI Integrity */ - HAILO_BOARD_TYPE_MAX_ENUM = INT_MAX -}; - -enum hailo_accelerator_type { - HAILO_ACCELERATOR_TYPE_NNC, - HAILO_ACCELERATOR_TYPE_SOC, - - /** Max enum value to maintain ABI Integrity */ - HAILO_ACCELERATOR_TYPE_MAX_ENUM = INT_MAX -}; - -enum hailo_dma_type { - HAILO_DMA_TYPE_PCIE, - HAILO_DMA_TYPE_DRAM, - HAILO_DMA_TYPE_PCI_EP, - - /** Max enum value to maintain ABI Integrity */ - HAILO_DMA_TYPE_MAX_ENUM = INT_MAX, -}; - -struct hailo_device_properties { - uint16_t desc_max_page_size; - enum hailo_board_type board_type; - enum hailo_allocation_mode allocation_mode; - enum hailo_dma_type dma_type; - size_t dma_engines_count; - bool is_fw_loaded; -#ifdef __QNX__ - pid_t resource_manager_pid; -#endif // __QNX__ -}; - -struct hailo_driver_info { - uint32_t major_version; - uint32_t minor_version; - uint32_t revision_version; -}; - -/* structure used in ioctl HAILO_READ_LOG */ -#define MAX_FW_LOG_BUFFER_LENGTH (512) - -struct hailo_read_log_params { - enum hailo_cpu_id cpu_id; // in - uint8_t buffer[MAX_FW_LOG_BUFFER_LENGTH]; // out - size_t buffer_size; // in - size_t read_bytes; // out -}; - -/* structure used in ioctl HAILO_VDMA_LOW_MEMORY_BUFFER_ALLOC */ -struct hailo_allocate_low_memory_buffer_params { - size_t buffer_size; // in - uintptr_t buffer_handle; // out -}; - -/* structure used in ioctl HAILO_VDMA_LOW_MEMORY_BUFFER_FREE */ -struct hailo_free_low_memory_buffer_params { - uintptr_t buffer_handle; // in -}; - -struct hailo_mark_as_in_use_params { - bool in_use; // out -}; - -/* structure used in ioctl HAILO_VDMA_CONTINUOUS_BUFFER_ALLOC */ -struct hailo_allocate_continuous_buffer_params { - size_t buffer_size; // in - uintptr_t buffer_handle; // out - uint64_t dma_address; // out -}; - -/* structure used in ioctl HAILO_VDMA_CONTINUOUS_BUFFER_FREE */ -struct hailo_free_continuous_buffer_params { - uintptr_t buffer_handle; // in -}; - -/* structures used in ioctl HAILO_VDMA_LAUNCH_TRANSFER */ -struct hailo_vdma_transfer_buffer { - size_t mapped_buffer_handle; // in - uint32_t offset; // in - uint32_t size; // in -}; - -// We allow maximum 2 buffers per transfer since we may have an extra buffer -// to make sure each buffer is aligned to page size. -#define HAILO_MAX_BUFFERS_PER_SINGLE_TRANSFER (2) - -struct hailo_vdma_launch_transfer_params { - uint8_t engine_index; // in - uint8_t channel_index; // in - - uintptr_t desc_handle; // in - uint32_t starting_desc; // in - - bool should_bind; // in, if false, assumes buffer already bound. - uint8_t buffers_count; // in - struct hailo_vdma_transfer_buffer - buffers[HAILO_MAX_BUFFERS_PER_SINGLE_TRANSFER]; // in - - enum hailo_vdma_interrupts_domain first_interrupts_domain; // in - enum hailo_vdma_interrupts_domain last_interrupts_domain; // in - - bool is_debug; // in, if set program hw to send - // more info (e.g desc complete status) - - uint32_t descs_programed; // out, amount of descriptors programed. - int launch_transfer_status; // out, status of the launch transfer call. (only used in case of error) -}; - -/* structure used in ioctl HAILO_SOC_CONNECT */ -struct hailo_soc_connect_params { - uint16_t port_number; // in - uint8_t input_channel_index; // out - uint8_t output_channel_index; // out - uintptr_t input_desc_handle; // in - uintptr_t output_desc_handle; // in -}; - -/* structure used in ioctl HAILO_SOC_CLOSE */ -struct hailo_soc_close_params { - uint8_t input_channel_index; // in - uint8_t output_channel_index; // in -}; - -/* structure used in ioctl HAILO_PCI_EP_ACCEPT */ -struct hailo_pci_ep_accept_params { - uint16_t port_number; // in - uint8_t input_channel_index; // out - uint8_t output_channel_index; // out - uintptr_t input_desc_handle; // in - uintptr_t output_desc_handle; // in -}; - -/* structure used in ioctl HAILO_PCI_EP_CLOSE */ -struct hailo_pci_ep_close_params { - uint8_t input_channel_index; // in - uint8_t output_channel_index; // in -}; - -#ifdef _MSC_VER -struct tCompatibleHailoIoctlData -{ - tCompatibleHailoIoctlParam Parameters; - ULONG_PTR Value; - union { - struct hailo_memory_transfer_params MemoryTransfer; - struct hailo_vdma_enable_channels_params VdmaEnableChannels; - struct hailo_vdma_disable_channels_params VdmaDisableChannels; - struct hailo_vdma_interrupts_read_timestamp_params VdmaInterruptsReadTimestamps; - struct hailo_vdma_interrupts_wait_params VdmaInterruptsWait; - struct hailo_vdma_buffer_sync_params VdmaBufferSync; - struct hailo_fw_control FirmwareControl; - struct hailo_vdma_buffer_map_params VdmaBufferMap; - struct hailo_vdma_buffer_unmap_params VdmaBufferUnmap; - struct hailo_desc_list_create_params DescListCreate; - struct hailo_desc_list_release_params DescListReleaseParam; - struct hailo_desc_list_program_params DescListProgram; - struct hailo_d2h_notification D2HNotification; - struct hailo_device_properties DeviceProperties; - struct hailo_driver_info DriverInfo; - struct hailo_read_log_params ReadLog; - struct hailo_mark_as_in_use_params MarkAsInUse; - struct hailo_vdma_launch_transfer_params LaunchTransfer; - struct hailo_soc_connect_params ConnectParams; - struct hailo_soc_close_params SocCloseParams; - struct hailo_pci_ep_accept_params AcceptParams; - struct hailo_pci_ep_close_params PciEpCloseParams; - struct hailo_write_action_list_params WriteActionListParams; - } Buffer; -}; -#endif // _MSC_VER - -#pragma pack(pop) - -enum hailo_general_ioctl_code { - HAILO_MEMORY_TRANSFER_CODE, - HAILO_QUERY_DEVICE_PROPERTIES_CODE, - HAILO_QUERY_DRIVER_INFO_CODE, - - // Must be last - HAILO_GENERAL_IOCTL_MAX_NR, -}; - -#define HAILO_MEMORY_TRANSFER _IOWR_(HAILO_GENERAL_IOCTL_MAGIC, HAILO_MEMORY_TRANSFER_CODE, struct hailo_memory_transfer_params) -#define HAILO_QUERY_DEVICE_PROPERTIES _IOW_(HAILO_GENERAL_IOCTL_MAGIC, HAILO_QUERY_DEVICE_PROPERTIES_CODE, struct hailo_device_properties) -#define HAILO_QUERY_DRIVER_INFO _IOW_(HAILO_GENERAL_IOCTL_MAGIC, HAILO_QUERY_DRIVER_INFO_CODE, struct hailo_driver_info) - -enum hailo_vdma_ioctl_code { - HAILO_VDMA_ENABLE_CHANNELS_CODE, - HAILO_VDMA_DISABLE_CHANNELS_CODE, - HAILO_VDMA_INTERRUPTS_WAIT_CODE, - HAILO_VDMA_INTERRUPTS_READ_TIMESTAMPS_CODE, - HAILO_VDMA_BUFFER_MAP_CODE, - HAILO_VDMA_BUFFER_UNMAP_CODE, - HAILO_VDMA_BUFFER_SYNC_CODE, - HAILO_DESC_LIST_CREATE_CODE, - HAILO_DESC_LIST_RELEASE_CODE, - HAILO_DESC_LIST_PROGRAM_CODE, - HAILO_VDMA_LOW_MEMORY_BUFFER_ALLOC_CODE, - HAILO_VDMA_LOW_MEMORY_BUFFER_FREE_CODE, - HAILO_MARK_AS_IN_USE_CODE, - HAILO_VDMA_CONTINUOUS_BUFFER_ALLOC_CODE, - HAILO_VDMA_CONTINUOUS_BUFFER_FREE_CODE, - HAILO_VDMA_LAUNCH_TRANSFER_CODE, - - // Must be last - HAILO_VDMA_IOCTL_MAX_NR, -}; - -#define HAILO_VDMA_ENABLE_CHANNELS _IOR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_ENABLE_CHANNELS_CODE, struct hailo_vdma_enable_channels_params) -#define HAILO_VDMA_DISABLE_CHANNELS _IOR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_DISABLE_CHANNELS_CODE, struct hailo_vdma_disable_channels_params) -#define HAILO_VDMA_INTERRUPTS_WAIT _IOWR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_INTERRUPTS_WAIT_CODE, struct hailo_vdma_interrupts_wait_params) -#define HAILO_VDMA_INTERRUPTS_READ_TIMESTAMPS _IOWR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_INTERRUPTS_READ_TIMESTAMPS_CODE, struct hailo_vdma_interrupts_read_timestamp_params) - -#define HAILO_VDMA_BUFFER_MAP _IOWR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_BUFFER_MAP_CODE, struct hailo_vdma_buffer_map_params) -#define HAILO_VDMA_BUFFER_UNMAP _IOR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_BUFFER_UNMAP_CODE, struct hailo_vdma_buffer_unmap_params) -#define HAILO_VDMA_BUFFER_SYNC _IOR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_BUFFER_SYNC_CODE, struct hailo_vdma_buffer_sync_params) - -#define HAILO_DESC_LIST_CREATE _IOWR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_DESC_LIST_CREATE_CODE, struct hailo_desc_list_create_params) -#define HAILO_DESC_LIST_RELEASE _IOR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_DESC_LIST_RELEASE_CODE, struct hailo_desc_list_release_params) -#define HAILO_DESC_LIST_PROGRAM _IOR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_DESC_LIST_PROGRAM_CODE, struct hailo_desc_list_program_params) - -#define HAILO_VDMA_LOW_MEMORY_BUFFER_ALLOC _IOWR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_LOW_MEMORY_BUFFER_ALLOC_CODE, struct hailo_allocate_low_memory_buffer_params) -#define HAILO_VDMA_LOW_MEMORY_BUFFER_FREE _IOR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_LOW_MEMORY_BUFFER_FREE_CODE, struct hailo_free_low_memory_buffer_params) - -#define HAILO_MARK_AS_IN_USE _IOW_(HAILO_VDMA_IOCTL_MAGIC, HAILO_MARK_AS_IN_USE_CODE, struct hailo_mark_as_in_use_params) - -#define HAILO_VDMA_CONTINUOUS_BUFFER_ALLOC _IOWR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_CONTINUOUS_BUFFER_ALLOC_CODE, struct hailo_allocate_continuous_buffer_params) -#define HAILO_VDMA_CONTINUOUS_BUFFER_FREE _IOR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_CONTINUOUS_BUFFER_FREE_CODE, struct hailo_free_continuous_buffer_params) - -#define HAILO_VDMA_LAUNCH_TRANSFER _IOWR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_LAUNCH_TRANSFER_CODE, struct hailo_vdma_launch_transfer_params) - -enum hailo_nnc_ioctl_code { - HAILO_FW_CONTROL_CODE, - HAILO_READ_NOTIFICATION_CODE, - HAILO_DISABLE_NOTIFICATION_CODE, - HAILO_READ_LOG_CODE, - HAILO_RESET_NN_CORE_CODE, - HAILO_WRITE_ACTION_LIST_CODE, - - // Must be last - HAILO_NNC_IOCTL_MAX_NR -}; - -#define HAILO_FW_CONTROL _IOWR_(HAILO_NNC_IOCTL_MAGIC, HAILO_FW_CONTROL_CODE, struct hailo_fw_control) -#define HAILO_READ_NOTIFICATION _IOW_(HAILO_NNC_IOCTL_MAGIC, HAILO_READ_NOTIFICATION_CODE, struct hailo_d2h_notification) -#define HAILO_DISABLE_NOTIFICATION _IO_(HAILO_NNC_IOCTL_MAGIC, HAILO_DISABLE_NOTIFICATION_CODE) -#define HAILO_READ_LOG _IOWR_(HAILO_NNC_IOCTL_MAGIC, HAILO_READ_LOG_CODE, struct hailo_read_log_params) -#define HAILO_RESET_NN_CORE _IO_(HAILO_NNC_IOCTL_MAGIC, HAILO_RESET_NN_CORE_CODE) -#define HAILO_WRITE_ACTION_LIST _IOW_(HAILO_NNC_IOCTL_MAGIC, HAILO_WRITE_ACTION_LIST_CODE, struct hailo_write_action_list_params) - -enum hailo_soc_ioctl_code { - HAILO_SOC_IOCTL_CONNECT_CODE, - HAILO_SOC_IOCTL_CLOSE_CODE, - - // Must be last - HAILO_SOC_IOCTL_MAX_NR, -}; - -#define HAILO_SOC_CONNECT _IOWR_(HAILO_SOC_IOCTL_MAGIC, HAILO_SOC_IOCTL_CONNECT_CODE, struct hailo_soc_connect_params) -#define HAILO_SOC_CLOSE _IOR_(HAILO_SOC_IOCTL_MAGIC, HAILO_SOC_IOCTL_CLOSE_CODE, struct hailo_soc_close_params) - - -enum hailo_pci_ep_ioctl_code { - HAILO_PCI_EP_ACCEPT_CODE, - HAILO_PCI_EP_CLOSE_CODE, - - // Must be last - HAILO_PCI_EP_IOCTL_MAX_NR, -}; - -#define HAILO_PCI_EP_ACCEPT _IOWR_(HAILO_PCI_EP_IOCTL_MAGIC, HAILO_PCI_EP_ACCEPT_CODE, struct hailo_pci_ep_accept_params) -#define HAILO_PCI_EP_CLOSE _IOR_(HAILO_PCI_EP_IOCTL_MAGIC, HAILO_PCI_EP_CLOSE_CODE, struct hailo_pci_ep_close_params) - -#endif /* _HAILO_IOCTL_COMMON_H_ */ diff --git a/drivers/media/pci/hailo/common/hailo_pcie_version.h b/drivers/media/pci/hailo/common/hailo_pcie_version.h deleted file mode 100644 index 059e5d8a5c8757..00000000000000 --- a/drivers/media/pci/hailo/common/hailo_pcie_version.h +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/** - * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. - **/ - -#ifndef _HAILO_COMMON_PCIE_VERSION_H_ -#define _HAILO_COMMON_PCIE_VERSION_H_ - -#define HAILO_DRV_VER_MAJOR 4 -#define HAILO_DRV_VER_MINOR 17 -#define HAILO_DRV_VER_REVISION 0 - -#endif /* _HAILO_COMMON_PCIE_VERSION_H_ */ \ No newline at end of file diff --git a/drivers/media/pci/hailo/common/hailo_resource.c b/drivers/media/pci/hailo/common/hailo_resource.c deleted file mode 100644 index 548deb2da262a0..00000000000000 --- a/drivers/media/pci/hailo/common/hailo_resource.c +++ /dev/null @@ -1,141 +0,0 @@ -// SPDX-License-Identifier: MIT -/** - * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. - **/ - -#include "hailo_resource.h" - -#include "utils.h" - -#include -#include -#include -#include - -#define ALIGN_TO_32_BIT(addr) ((addr) & (~((uintptr_t)0x3))) - -u8 hailo_resource_read8(struct hailo_resource *resource, size_t offset) -{ - u32 val = ioread32((u8*)ALIGN_TO_32_BIT(resource->address + offset)); - u64 offset_in_bits = BITS_IN_BYTE * ((resource->address + offset) - ALIGN_TO_32_BIT(resource->address + offset)); - return (u8)READ_BITS_AT_OFFSET(BYTE_SIZE * BITS_IN_BYTE, offset_in_bits, val); -} - -u16 hailo_resource_read16(struct hailo_resource *resource, size_t offset) -{ - u32 val = ioread32((u8*)ALIGN_TO_32_BIT(resource->address + offset)); - u64 offset_in_bits = BITS_IN_BYTE * ((resource->address + offset) - ALIGN_TO_32_BIT(resource->address + offset)); - return (u16)READ_BITS_AT_OFFSET(WORD_SIZE * BITS_IN_BYTE, offset_in_bits, val); -} - -u32 hailo_resource_read32(struct hailo_resource *resource, size_t offset) -{ - return ioread32((u8*)resource->address + offset); -} - -void hailo_resource_write8(struct hailo_resource *resource, size_t offset, u8 value) -{ - u32 initial_val = ioread32((u8*)ALIGN_TO_32_BIT(resource->address + offset)); - u64 offset_in_bits = BITS_IN_BYTE * ((resource->address + offset) - ALIGN_TO_32_BIT(resource->address + offset)); - iowrite32(WRITE_BITS_AT_OFFSET(BYTE_SIZE * BITS_IN_BYTE, offset_in_bits, initial_val, value), - (u8*)ALIGN_TO_32_BIT(resource->address + offset)); -} - -void hailo_resource_write16(struct hailo_resource *resource, size_t offset, u16 value) -{ - u32 initial_val = ioread32((u8*)ALIGN_TO_32_BIT(resource->address + offset)); - u64 offset_in_bits = BITS_IN_BYTE * ((resource->address + offset) - ALIGN_TO_32_BIT(resource->address + offset)); - iowrite32(WRITE_BITS_AT_OFFSET(WORD_SIZE * BITS_IN_BYTE, offset_in_bits, initial_val, value), - (u8*)ALIGN_TO_32_BIT(resource->address + offset)); -} - -void hailo_resource_write32(struct hailo_resource *resource, size_t offset, u32 value) -{ - iowrite32(value, (u8*)resource->address + offset); -} - -void hailo_resource_read_buffer(struct hailo_resource *resource, size_t offset, size_t count, void *to) -{ - // Copied and modified from linux aarch64 (using ioread32 instead of readq that does not work all the time) - uintptr_t to_ptr = (uintptr_t)to; - while ((count > 0) && (!IS_ALIGNED(to_ptr, 4) || !IS_ALIGNED((uintptr_t)resource->address + offset, 4))) { - *(u8*)to_ptr = hailo_resource_read8(resource, offset); - to_ptr++; - offset++; - count--; - } - - while (count >= 4) { - *(u32*)to_ptr = hailo_resource_read32(resource, offset); - to_ptr += 4; - offset += 4; - count -= 4; - } - - while (count > 0) { - *(u8*)to_ptr = hailo_resource_read8(resource, offset); - to_ptr++; - offset++; - count--; - } -} - -int hailo_resource_write_buffer(struct hailo_resource *resource, size_t offset, size_t count, const void *from) -{ - // read the bytes after writing them for flushing the data. This function also checks if the pcie link - // is broken. - uintptr_t from_ptr = (uintptr_t)from; - while (count && (!IS_ALIGNED(resource->address + offset, 4) || !IS_ALIGNED(from_ptr, 4))) { - hailo_resource_write8(resource, offset, *(u8*)from_ptr); - if (hailo_resource_read8(resource, offset) != *(u8*)from_ptr) { - return -EIO; - } - from_ptr++; - offset++; - count--; - } - - while (count >= 4) { - hailo_resource_write32(resource, offset, *(u32*)from_ptr); - if (hailo_resource_read32(resource, offset) != *(u32*)from_ptr) { - return -EIO; - } - from_ptr += 4; - offset += 4; - count -= 4; - } - - while (count) { - hailo_resource_write8(resource, offset, *(u8*)from_ptr); - if (hailo_resource_read8(resource, offset) != *(u8*)from_ptr) { - return -EIO; - } - from_ptr++; - offset++; - count--; - } - - return 0; -} - -int hailo_resource_transfer(struct hailo_resource *resource, struct hailo_memory_transfer_params *transfer) -{ - // Check for transfer size (address is in resources address-space) - if ((transfer->address + transfer->count) > (u64)resource->size) { - return -EINVAL; - } - - if (transfer->count > ARRAY_SIZE(transfer->buffer)) { - return -EINVAL; - } - - switch (transfer->transfer_direction) { - case TRANSFER_READ: - hailo_resource_read_buffer(resource, (u32)transfer->address, transfer->count, transfer->buffer); - return 0; - case TRANSFER_WRITE: - return hailo_resource_write_buffer(resource, (u32)transfer->address, transfer->count, transfer->buffer); - default: - return -EINVAL; - } -} diff --git a/drivers/media/pci/hailo/common/hailo_resource.h b/drivers/media/pci/hailo/common/hailo_resource.h deleted file mode 100644 index c27a097568760e..00000000000000 --- a/drivers/media/pci/hailo/common/hailo_resource.h +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-License-Identifier: MIT -/** - * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. - **/ - -#ifndef _HAILO_COMMON_HAILO_RESOURCE_H_ -#define _HAILO_COMMON_HAILO_RESOURCE_H_ - -#include "hailo_ioctl_common.h" -#include - -struct hailo_resource { - uintptr_t address; - size_t size; -}; - -#ifdef __cplusplus -extern "C" { -#endif - -// Implemented by the specific platform -u32 hailo_resource_read32(struct hailo_resource *resource, size_t offset); -u16 hailo_resource_read16(struct hailo_resource *resource, size_t offset); -u8 hailo_resource_read8(struct hailo_resource *resource, size_t offset); -void hailo_resource_write32(struct hailo_resource *resource, size_t offset, u32 value); -void hailo_resource_write16(struct hailo_resource *resource, size_t offset, u16 value); -void hailo_resource_write8(struct hailo_resource *resource, size_t offset, u8 value); - -void hailo_resource_read_buffer(struct hailo_resource *resource, size_t offset, size_t count, void *to); -int hailo_resource_write_buffer(struct hailo_resource *resource, size_t offset, size_t count, const void *from); - -// Transfer (read/write) the given resource into/from transfer params. -int hailo_resource_transfer(struct hailo_resource *resource, struct hailo_memory_transfer_params *transfer); - -#ifdef __cplusplus -} -#endif - -#endif /* _HAILO_COMMON_HAILO_RESOURCE_H_ */ \ No newline at end of file diff --git a/drivers/media/pci/hailo/common/pcie_common.c b/drivers/media/pci/hailo/common/pcie_common.c deleted file mode 100644 index a119d637cf4d75..00000000000000 --- a/drivers/media/pci/hailo/common/pcie_common.c +++ /dev/null @@ -1,913 +0,0 @@ -// SPDX-License-Identifier: MIT -/** - * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. - **/ - -#include "pcie_common.h" -#include "fw_operation.h" -#include "soc_structs.h" - -#include -#include -#include -#include -#include -#include - - -#define BSC_IMASK_HOST (0x0188) -#define BCS_ISTATUS_HOST (0x018C) -#define BCS_SOURCE_INTERRUPT_PER_CHANNEL (0x400) -#define BCS_DESTINATION_INTERRUPT_PER_CHANNEL (0x500) - -#define PO2_ROUND_UP(size, alignment) ((size + alignment-1) & ~(alignment-1)) - -#define ATR_PARAM (0x17) -#define ATR_SRC_ADDR (0x0) -#define ATR_TRSL_PARAM (6) -#define ATR_TABLE_SIZE (0x1000u) -#define ATR_TABLE_SIZE_MASK (0x1000u - 1) - -#define ATR0_PCIE_BRIDGE_OFFSET (0x700) - -#define ATR_PCIE_BRIDGE_OFFSET(atr_index) (ATR0_PCIE_BRIDGE_OFFSET + (atr_index * 0x20)) - -#define MAXIMUM_APP_FIRMWARE_CODE_SIZE (0x40000) -#define MAXIMUM_CORE_FIRMWARE_CODE_SIZE (0x20000) - -#define FIRMWARE_LOAD_WAIT_MAX_RETRIES (100) -#define FIRMWARE_LOAD_SLEEP_MS (50) - -#define PCIE_REQUEST_SIZE_OFFSET (0x640) - -#define PCIE_CONFIG_VENDOR_OFFSET (0x0098) - -#define HAILO_PCIE_DMA_DEVICE_INTERRUPTS_BITMASK (1 << 4) -#define HAILO_PCIE_DMA_HOST_INTERRUPTS_BITMASK (1 << 5) -#define HAILO_PCIE_DMA_SRC_CHANNELS_BITMASK (0x0000FFFF) - -#define HAILO_PCIE_MAX_ATR_TABLE_INDEX (3) - -#define BOOT_STATUS_UNINITIALIZED (0x1) - -#define PCIE_CONTROL_SECTION_ADDRESS_H8 (0x60000000) -#define PCIE_BLOCK_ADDRESS_ATR1 (0x200000) - -#define PCIE_CONFIG_PCIE_CFG_QM_ROUTING_MODE_SET(reg_offset) \ - (reg_offset) = (((reg_offset) & ~0x00000004L) | ((uint32_t)(1) << 2)) - - -struct hailo_fw_addresses { - u32 boot_fw_header; - u32 app_fw_code_ram_base; - u32 boot_key_cert; - u32 boot_cont_cert; - u32 core_code_ram_base; - u32 core_fw_header; - u32 raise_ready_offset; - u32 boot_status; - u32 pcie_cfg_regs; -}; - -struct hailo_board_compatibility { - struct hailo_fw_addresses fw_addresses; - const struct hailo_pcie_loading_stage stages[MAX_LOADING_STAGES]; -}; - -static const struct hailo_file_batch hailo10h_files_stg1[] = { - { - .filename = "hailo/hailo10h/customer_certificate.bin", - .address = 0xA0000, - .max_size = 0x8004, - .is_mandatory = true, - .has_header = false, - .has_core = false - }, - { - .filename = "hailo/hailo10h/u-boot.dtb.signed", - .address = 0xA8004, - .max_size = 0x20000, - .is_mandatory = true, - .has_header = false, - .has_core = false - }, - { - .filename = "hailo/hailo10h/scu_fw.bin", - .address = 0x20000, - .max_size = 0x40000, - .is_mandatory = true, - .has_header = true, - .has_core = false - }, - { - .filename = NULL, - .address = 0x00, - .max_size = 0x00, - .is_mandatory = false, - .has_header = false, - .has_core = false - } -}; - -static const struct hailo_file_batch hailo10h_files_stg2[] = { - { - .filename = "hailo/hailo10h/u-boot-spl.bin", - .address = 0x85000000, - .max_size = 0x1000000, - .is_mandatory = true, - .has_header = false, - .has_core = false - }, - { - .filename = "hailo/hailo10h/u-boot-tfa.itb", - .address = 0x86000000, - .max_size = 0x1000000, - .is_mandatory = true, - .has_header = false, - .has_core = false - }, - { - .filename = "hailo/hailo10h/fitImage", - .address = 0x87000000, - .max_size = 0x1000000, - .is_mandatory = true, - .has_header = false, - .has_core = false - }, - { - .filename = "hailo/hailo10h/image-fs", -#ifndef HAILO_EMULATOR - .address = 0x88000000, -#else - // TODO : HRT-15692 - merge two cases - .address = 0x89000000, -#endif /* ifndef HAILO_EMULATOR */ - .max_size = 0x20000000, // Max size 512MB - .is_mandatory = true, - .has_header = false, - .has_core = false - } -}; - -// If loading linux from EMMC - only need few files from second batch (u-boot-spl.bin and u-boot-tfa.itb) -static const struct hailo_file_batch hailo10h_files_stg2_linux_in_emmc[] = { - { - .filename = "hailo/hailo10h/u-boot-spl.bin", - .address = 0x85000000, - .max_size = 0x1000000, - .is_mandatory = true, - .has_header = false, - .has_core = false - }, - { - .filename = "hailo/hailo10h/u-boot-tfa.itb", - .address = 0x86000000, - .max_size = 0x1000000, - .is_mandatory = true, - .has_header = false, - .has_core = false - }, - { - .filename = NULL, - .address = 0x00, - .max_size = 0x00, - .is_mandatory = false, - .has_header = false, - .has_core = false - }, -}; - -static const struct hailo_file_batch hailo8_files_stg1[] = { - { - .filename = "hailo/hailo8_fw.bin", - .address = 0x20000, - .max_size = 0x50000, - .is_mandatory = true, - .has_header = true, - .has_core = true - }, - { - .filename = "hailo/hailo8_board_cfg.bin", - .address = 0x60001000, - .max_size = PCIE_HAILO8_BOARD_CFG_MAX_SIZE, - .is_mandatory = false, - .has_header = false, - .has_core = false - }, - { - .filename = "hailo/hailo8_fw_cfg.bin", - .address = 0x60001500, - .max_size = PCIE_HAILO8_FW_CFG_MAX_SIZE, - .is_mandatory = false, - .has_header = false, - .has_core = false - }, - { - .filename = NULL, - .address = 0x00, - .max_size = 0x00, - .is_mandatory = false, - .has_header = false, - .has_core = false - } -}; - -static const struct hailo_file_batch hailo10h_legacy_files_stg1[] = { - { - .filename = "hailo/hailo15_fw.bin", - .address = 0x20000, - .max_size = 0x100000, - .is_mandatory = true, - .has_header = true, - .has_core = true - }, - { - .filename = NULL, - .address = 0x00, - .max_size = 0x00, - .is_mandatory = false, - .has_header = false, - .has_core = false - } -}; - -// TODO HRT-15014 - Fix names for hailo15l legacy accelerator -static const struct hailo_file_batch hailo15l_files_stg1[] = { - { - .filename = "hailo/hailo15l_fw.bin", - .address = 0x20000, - .max_size = 0x100000, - .is_mandatory = true, - .has_header = true, - .has_core = true - }, - { - .filename = NULL, - .address = 0x00, - .max_size = 0x00, - .is_mandatory = false, - .has_header = false, - .has_core = false - } -}; - -static const struct hailo_board_compatibility compat[HAILO_BOARD_TYPE_COUNT] = { - [HAILO_BOARD_TYPE_HAILO8] = { - .fw_addresses = { - .boot_fw_header = 0xE0030, - .boot_key_cert = 0xE0048, - .boot_cont_cert = 0xE0390, - .app_fw_code_ram_base = 0x60000, - .core_code_ram_base = 0xC0000, - .core_fw_header = 0xA0000, - .raise_ready_offset = 0x1684, - .boot_status = 0xe0000, - }, - .stages = { - { - .batch = hailo8_files_stg1, - .trigger_address = 0xE0980, - .timeout = FIRMWARE_WAIT_TIMEOUT_MS, - .amount_of_files_in_stage = 3 - }, - }, - }, - [HAILO_BOARD_TYPE_HAILO10H_LEGACY] = { - .fw_addresses = { - .boot_fw_header = 0x88000, - .boot_key_cert = 0x88018, - .boot_cont_cert = 0x886a8, - .app_fw_code_ram_base = 0x20000, - .core_code_ram_base = 0x60000, - .core_fw_header = 0xC0000, - .raise_ready_offset = 0x1754, - .boot_status = 0x80000, - }, - .stages = { - { - .batch = hailo10h_legacy_files_stg1, - .trigger_address = 0x88c98, - .timeout = FIRMWARE_WAIT_TIMEOUT_MS, - .amount_of_files_in_stage = 1 - }, - }, - }, - [HAILO_BOARD_TYPE_HAILO10H] = { - .fw_addresses = { - .boot_fw_header = 0x88000, - .boot_key_cert = 0x88018, - .boot_cont_cert = 0x886a8, - .app_fw_code_ram_base = 0x20000, - .core_code_ram_base = 0, - .core_fw_header = 0, - .raise_ready_offset = 0x1754, - .boot_status = 0x80000, - .pcie_cfg_regs = 0x002009dc, - }, - .stages = { - { - .batch = hailo10h_files_stg1, - .trigger_address = 0x88c98, - .timeout = FIRMWARE_WAIT_TIMEOUT_MS, - .amount_of_files_in_stage = 3 - }, - { - .batch = hailo10h_files_stg2, - .trigger_address = 0x84000000, - .timeout = PCI_EP_WAIT_TIMEOUT_MS, - .amount_of_files_in_stage = 4 - }, - { - .batch = hailo10h_files_stg2_linux_in_emmc, - .trigger_address = 0x84000000, - .timeout = FIRMWARE_WAIT_TIMEOUT_MS, - .amount_of_files_in_stage = 2 - }, - }, - }, - // HRT-11344 : none of these matter except raise_ready_offset seeing as we load fw seperately - not through driver - // After implementing bootloader put correct values here - [HAILO_BOARD_TYPE_HAILO15L] = { - .fw_addresses = { - .boot_fw_header = 0x88000, - .boot_key_cert = 0x88018, - .boot_cont_cert = 0x886a8, - .app_fw_code_ram_base = 0x20000, - .core_code_ram_base = 0x60000, - .core_fw_header = 0xC0000, - // NOTE: After they update hw consts - check register fw_access_interrupt_w1s of pcie_config - .raise_ready_offset = 0x174c, - .boot_status = 0x80000, - }, - .stages = { - { - .batch = hailo15l_files_stg1, - .trigger_address = 0x88c98, - .timeout = FIRMWARE_WAIT_TIMEOUT_MS, - .amount_of_files_in_stage = 1 - }, - }, - } -}; - -const struct hailo_pcie_loading_stage *hailo_pcie_get_loading_stage_info(enum hailo_board_type board_type, - enum loading_stages stage) -{ - return &compat[board_type].stages[stage]; -} - -static u32 read_and_clear_reg(struct hailo_resource *resource, u32 offset) -{ - u32 value = hailo_resource_read32(resource, offset); - if (value != 0) { - hailo_resource_write32(resource, offset, value); - } - return value; -} - -bool hailo_pcie_read_interrupt(struct hailo_pcie_resources *resources, struct hailo_pcie_interrupt_source *source) -{ - u32 istatus_host = 0; - memset(source, 0, sizeof(*source)); - - istatus_host = read_and_clear_reg(&resources->config, BCS_ISTATUS_HOST); - if (0 == istatus_host) { - return false; - } - - source->sw_interrupts = (istatus_host >> BCS_ISTATUS_HOST_SW_IRQ_SHIFT); - - if (istatus_host & BCS_ISTATUS_HOST_VDMA_SRC_IRQ_MASK) { - source->vdma_channels_bitmap |= read_and_clear_reg(&resources->config, BCS_SOURCE_INTERRUPT_PER_CHANNEL); - } - if (istatus_host & BCS_ISTATUS_HOST_VDMA_DEST_IRQ_MASK) { - source->vdma_channels_bitmap |= read_and_clear_reg(&resources->config, BCS_DESTINATION_INTERRUPT_PER_CHANNEL); - } - - return true; -} - -int hailo_pcie_write_firmware_control(struct hailo_pcie_resources *resources, const struct hailo_fw_control *command) -{ - int err = 0; - u32 request_size = 0; - u8 fw_access_value = FW_ACCESS_APP_CPU_CONTROL_MASK; - const struct hailo_fw_addresses *fw_addresses = &(compat[resources->board_type].fw_addresses); - - if (!hailo_pcie_is_firmware_loaded(resources)) { - return -ENODEV; - } - - // Copy md5 + buffer_len + buffer - request_size = sizeof(command->expected_md5) + sizeof(command->buffer_len) + command->buffer_len; - err = hailo_resource_write_buffer(&resources->fw_access, 0, PO2_ROUND_UP(request_size, FW_CODE_SECTION_ALIGNMENT), - command); - if (err < 0) { - return err; - } - - // Raise the bit for the CPU that will handle the control - fw_access_value = (command->cpu_id == HAILO_CPU_ID_CPU1) ? FW_ACCESS_CORE_CPU_CONTROL_MASK : - FW_ACCESS_APP_CPU_CONTROL_MASK; - - // Raise ready flag to FW - hailo_resource_write32(&resources->fw_access, fw_addresses->raise_ready_offset, (u32)fw_access_value); - return 0; -} - -int hailo_pcie_read_firmware_control(struct hailo_pcie_resources *resources, struct hailo_fw_control *command) -{ - u32 response_header_size = 0; - - // Copy response md5 + buffer_len - response_header_size = sizeof(command->expected_md5) + sizeof(command->buffer_len); - - hailo_resource_read_buffer(&resources->fw_access, PCIE_REQUEST_SIZE_OFFSET, response_header_size, command); - - if (sizeof(command->buffer) < command->buffer_len) { - return -EINVAL; - } - - // Copy response buffer - hailo_resource_read_buffer(&resources->fw_access, PCIE_REQUEST_SIZE_OFFSET + (size_t)response_header_size, - command->buffer_len, &command->buffer); - - return 0; -} - -void hailo_pcie_write_firmware_driver_shutdown(struct hailo_pcie_resources *resources) -{ - const struct hailo_fw_addresses *fw_addresses = &(compat[resources->board_type].fw_addresses); - const u32 fw_access_value = FW_ACCESS_DRIVER_SHUTDOWN_MASK; - - // Write shutdown flag to FW - hailo_resource_write32(&resources->fw_access, fw_addresses->raise_ready_offset, fw_access_value); -} - -void hailo_pcie_write_firmware_soft_reset(struct hailo_pcie_resources *resources) -{ - const struct hailo_fw_addresses *fw_addresses = &(compat[resources->board_type].fw_addresses); - const u32 fw_access_value = FW_ACCESS_SOFT_RESET_MASK; - - // Write shutdown flag to FW - hailo_resource_write32(&resources->fw_access, fw_addresses->raise_ready_offset, fw_access_value); -} - -int hailo_pcie_configure_atr_table(struct hailo_resource *bridge_config, u64 trsl_addr, u32 atr_index) -{ - size_t offset = 0; - struct hailo_atr_config atr = { - .atr_param = (ATR_PARAM | (atr_index << 12)), - .atr_src = ATR_SRC_ADDR, - .atr_trsl_addr_1 = (u32)(trsl_addr & 0xFFFFFFFF), - .atr_trsl_addr_2 = (u32)(trsl_addr >> 32), - .atr_trsl_param = ATR_TRSL_PARAM - }; - - BUG_ON(HAILO_PCIE_MAX_ATR_TABLE_INDEX < atr_index); - offset = ATR_PCIE_BRIDGE_OFFSET(atr_index); - - return hailo_resource_write_buffer(bridge_config, offset, sizeof(atr), (void*)&atr); -} - -void hailo_pcie_read_atr_table(struct hailo_resource *bridge_config, struct hailo_atr_config *atr, u32 atr_index) -{ - size_t offset = 0; - - BUG_ON(HAILO_PCIE_MAX_ATR_TABLE_INDEX < atr_index); - offset = ATR_PCIE_BRIDGE_OFFSET(atr_index); - - hailo_resource_read_buffer(bridge_config, offset, sizeof(*atr), (void*)atr); -} - -static void write_memory_chunk(struct hailo_pcie_resources *resources, - hailo_ptr_t dest, u32 dest_offset, const void *src, u32 len) -{ - u32 ATR_INDEX = 0; - BUG_ON(dest_offset + len > (u32)resources->fw_access.size); - - (void)hailo_pcie_configure_atr_table(&resources->config, dest, ATR_INDEX); - (void)hailo_resource_write_buffer(&resources->fw_access, dest_offset, len, src); -} - -static void read_memory_chunk( - struct hailo_pcie_resources *resources, hailo_ptr_t src, u32 src_offset, void *dest, u32 len) -{ - u32 ATR_INDEX = 0; - BUG_ON(src_offset + len > (u32)resources->fw_access.size); - - (void)hailo_pcie_configure_atr_table(&resources->config, src, ATR_INDEX); - (void)hailo_resource_read_buffer(&resources->fw_access, src_offset, len, dest); -} - -// Note: this function modify the device ATR table (that is also used by the firmware for control and vdma). -// Use with caution, and restore the original atr if needed. -static void write_memory(struct hailo_pcie_resources *resources, hailo_ptr_t dest, const void *src, u32 len) -{ - struct hailo_atr_config previous_atr = {0}; - hailo_ptr_t base_address = (dest & ~ATR_TABLE_SIZE_MASK); - u32 chunk_len = 0; - u32 offset = 0; - u32 ATR_INDEX = 0; - - // Store previous ATR (Read/write modify the ATR). - hailo_pcie_read_atr_table(&resources->config, &previous_atr, ATR_INDEX); - - if (base_address != dest) { - // Data is not aligned, write the first chunk - chunk_len = min((u32)(base_address + ATR_TABLE_SIZE - dest), len); - write_memory_chunk(resources, base_address, (u32)(dest - base_address), src, chunk_len); - offset += chunk_len; - } - - while (offset < len) { - chunk_len = min(len - offset, ATR_TABLE_SIZE); - write_memory_chunk(resources, dest + offset, 0, (const u8*)src + offset, chunk_len); - offset += chunk_len; - } - - (void)hailo_pcie_configure_atr_table(&resources->config, - (((u64)(previous_atr.atr_trsl_addr_2) << 32) | previous_atr.atr_trsl_addr_1), ATR_INDEX); -} - -// Note: this function modify the device ATR table (that is also used by the firmware for control and vdma). -// Use with caution, and restore the original atr if needed. -static void read_memory(struct hailo_pcie_resources *resources, hailo_ptr_t src, void *dest, u32 len) -{ - struct hailo_atr_config previous_atr = {0}; - hailo_ptr_t base_address = (src & ~ATR_TABLE_SIZE_MASK); - u32 chunk_len = 0; - u32 offset = 0; - u32 ATR_INDEX = 0; - - // Store previous ATR (Read/write modify the ATR). - hailo_pcie_read_atr_table(&resources->config, &previous_atr, ATR_INDEX); - - if (base_address != src) { - // Data is not aligned, read the first chunk - chunk_len = min((u32)(base_address + ATR_TABLE_SIZE - src), len); - read_memory_chunk(resources, base_address, (u32)(src - base_address), dest, chunk_len); - offset += chunk_len; - } - - while (offset < len) { - chunk_len = min(len - offset, ATR_TABLE_SIZE); - read_memory_chunk(resources, src + offset, 0, (u8*)dest + offset, chunk_len); - offset += chunk_len; - } - - (void)hailo_pcie_configure_atr_table(&resources->config, - (((u64)(previous_atr.atr_trsl_addr_2) << 32) | previous_atr.atr_trsl_addr_1), ATR_INDEX); -} - -// Note: This function use for enabling the vDMA transaction host<->device by read modify write of the EP registers in the SOC - for fast boot over vDMA. -void hailo_pcie_configure_ep_registers_for_dma_transaction(struct hailo_pcie_resources *resources) -{ - u32 reg_routing_mercury = 0; - - BUG_ON(compat[resources->board_type].fw_addresses.pcie_cfg_regs == 0); - - read_memory(resources, compat[resources->board_type].fw_addresses.pcie_cfg_regs, ®_routing_mercury, sizeof(reg_routing_mercury)); - PCIE_CONFIG_PCIE_CFG_QM_ROUTING_MODE_SET(reg_routing_mercury); - write_memory(resources, compat[resources->board_type].fw_addresses.pcie_cfg_regs, ®_routing_mercury, sizeof(reg_routing_mercury)); -} - -static void hailo_write_app_firmware(struct hailo_pcie_resources *resources, firmware_header_t *fw_header, - secure_boot_certificate_header_t *fw_cert) -{ - const struct hailo_fw_addresses *fw_addresses = &(compat[resources->board_type].fw_addresses); - u8 *fw_code = ((u8*)fw_header + sizeof(firmware_header_t)); - u8 *key_data = ((u8*)fw_cert + sizeof(secure_boot_certificate_header_t)); - u8 *content_data = key_data + fw_cert->key_size; - - write_memory(resources, fw_addresses->boot_fw_header, fw_header, sizeof(firmware_header_t)); - - write_memory(resources, fw_addresses->app_fw_code_ram_base, fw_code, fw_header->code_size); - - write_memory(resources, fw_addresses->boot_key_cert, key_data, fw_cert->key_size); - write_memory(resources, fw_addresses->boot_cont_cert, content_data, fw_cert->content_size); -} - -static void hailo_write_core_firmware(struct hailo_pcie_resources *resources, firmware_header_t *fw_header) -{ - const struct hailo_fw_addresses *fw_addresses = &(compat[resources->board_type].fw_addresses); - void *fw_code = (void*)((u8*)fw_header + sizeof(firmware_header_t)); - - write_memory(resources, fw_addresses->core_code_ram_base, fw_code, fw_header->code_size); - write_memory(resources, fw_addresses->core_fw_header, fw_header, sizeof(firmware_header_t)); -} - -void hailo_trigger_firmware_boot(struct hailo_pcie_resources *resources, u32 stage) -{ - u32 pcie_finished = 1; - - write_memory(resources, compat[resources->board_type].stages[stage].trigger_address, (void*)&pcie_finished, sizeof(pcie_finished)); -} - -u32 hailo_get_boot_status(struct hailo_pcie_resources *resources) -{ - u32 boot_status = 0; - const struct hailo_fw_addresses *fw_addresses = &(compat[resources->board_type].fw_addresses); - - read_memory(resources, fw_addresses->boot_status, &boot_status, sizeof(boot_status)); - - return boot_status; -} - -/** -* Validates the FW headers. -* @param[in] address Address of the firmware. -* @param[in] firmware_size Size of the firmware. -* @param[out] out_app_firmware_header (optional) App firmware header -* @param[out] out_core_firmware_header (optional) Core firmware header -* @param[out] out_firmware_cert (optional) Firmware certificate header -*/ -static int FW_VALIDATION__validate_fw_headers(uintptr_t firmware_base_address, size_t firmware_size, - firmware_header_t **out_app_firmware_header, firmware_header_t **out_core_firmware_header, - secure_boot_certificate_header_t **out_firmware_cert, enum hailo_board_type board_type) -{ - firmware_header_t *app_firmware_header = NULL; - firmware_header_t *core_firmware_header = NULL; - secure_boot_certificate_header_t *firmware_cert = NULL; - int err = -EINVAL; - u32 consumed_firmware_offset = 0; - - err = FW_VALIDATION__validate_fw_header(firmware_base_address, firmware_size, MAXIMUM_APP_FIRMWARE_CODE_SIZE, - &consumed_firmware_offset, &app_firmware_header, board_type); - if (0 != err) { - err = -EINVAL; - goto exit; - } - - err = FW_VALIDATION__validate_cert_header(firmware_base_address, firmware_size, - &consumed_firmware_offset, &firmware_cert); - if (0 != err) { - err = -EINVAL; - goto exit; - } - - // Not validating with HAILO10H since core firmware doesn't loaded over pcie - if (HAILO_BOARD_TYPE_HAILO10H != board_type) { - err = FW_VALIDATION__validate_fw_header(firmware_base_address, firmware_size, MAXIMUM_CORE_FIRMWARE_CODE_SIZE, - &consumed_firmware_offset, &core_firmware_header, board_type); - if (0 != err) { - err = -EINVAL; - goto exit; - } - } - - if (consumed_firmware_offset != firmware_size) { - /* it is an error if there is leftover data after the last firmware header */ - err = -EINVAL; - goto exit; - } - - /* the out params are all optional */ - if (NULL != out_app_firmware_header) { - *out_app_firmware_header = app_firmware_header; - } - if (NULL != out_firmware_cert) { - *out_firmware_cert = firmware_cert; - } - if (NULL != out_core_firmware_header) { - *out_core_firmware_header = core_firmware_header; - } - err = 0; - -exit: - return err; -} - -static int write_single_file(struct hailo_pcie_resources *resources, const struct hailo_file_batch *file_info, struct device *dev) -{ - const struct firmware *firmware = NULL; - firmware_header_t *app_firmware_header = NULL; - secure_boot_certificate_header_t *firmware_cert = NULL; - firmware_header_t *core_firmware_header = NULL; - int err = 0; - - err = request_firmware_direct(&firmware, file_info->filename, dev); - if (err < 0) { - return err; - } - - if (firmware->size > file_info->max_size) { - release_firmware(firmware); - return -EFBIG; - } - - if (file_info->has_header) { - err = FW_VALIDATION__validate_fw_headers((uintptr_t)firmware->data, firmware->size, - &app_firmware_header, &core_firmware_header, &firmware_cert, resources->board_type); - if (err < 0) { - release_firmware(firmware); - return err; - } - - hailo_write_app_firmware(resources, app_firmware_header, firmware_cert); - if (file_info->has_core) { - hailo_write_core_firmware(resources, core_firmware_header); - } - } else { - write_memory(resources, file_info->address, (void*)firmware->data, firmware->size); - } - - release_firmware(firmware); - - return 0; -} - -int hailo_pcie_write_firmware_batch(struct device *dev, struct hailo_pcie_resources *resources, u32 stage) -{ - const struct hailo_pcie_loading_stage *stage_info = hailo_pcie_get_loading_stage_info(resources->board_type, stage); - const struct hailo_file_batch *files_batch = stage_info->batch; - const u8 amount_of_files = stage_info->amount_of_files_in_stage; - int file_index = 0; - int err = 0; - - for (file_index = 0; file_index < amount_of_files; file_index++) - { - dev_notice(dev, "Writing file %s\n", files_batch[file_index].filename); - - err = write_single_file(resources, &files_batch[file_index], dev); - if (err < 0) { - pr_warn("Failed to write file %s\n", files_batch[file_index].filename); - if (files_batch[file_index].is_mandatory) { - return err; - } - } - - dev_notice(dev, "File %s written successfully\n", files_batch[file_index].filename); - } - - hailo_trigger_firmware_boot(resources, stage); - - return 0; -} - -bool hailo_pcie_is_firmware_loaded(struct hailo_pcie_resources *resources) -{ - u32 offset; - u32 atr_value; - - if (HAILO_BOARD_TYPE_HAILO8 == resources->board_type) { - offset = ATR_PCIE_BRIDGE_OFFSET(0) + offsetof(struct hailo_atr_config, atr_trsl_addr_1); - atr_value = hailo_resource_read32(&resources->config, offset); - - return (PCIE_CONTROL_SECTION_ADDRESS_H8 == atr_value); - } - else { - offset = ATR_PCIE_BRIDGE_OFFSET(1) + offsetof(struct hailo_atr_config, atr_trsl_addr_1); - atr_value = hailo_resource_read32(&resources->config, offset); - - return (PCIE_BLOCK_ADDRESS_ATR1 == atr_value); - } - -} - -bool hailo_pcie_wait_for_firmware(struct hailo_pcie_resources *resources) -{ - size_t retries; - for (retries = 0; retries < FIRMWARE_LOAD_WAIT_MAX_RETRIES; retries++) { - if (hailo_pcie_is_firmware_loaded(resources)) { - return true; - } - - msleep(FIRMWARE_LOAD_SLEEP_MS); - } - - return false; -} - -void hailo_pcie_update_channel_interrupts_mask(struct hailo_pcie_resources* resources, u32 channels_bitmap) -{ - size_t i = 0; - u32 mask = hailo_resource_read32(&resources->config, BSC_IMASK_HOST); - - // Clear old channel interrupts - mask &= ~BCS_ISTATUS_HOST_VDMA_SRC_IRQ_MASK; - mask &= ~BCS_ISTATUS_HOST_VDMA_DEST_IRQ_MASK; - // Set interrupt by the bitmap - for (i = 0; i < MAX_VDMA_CHANNELS_PER_ENGINE; ++i) { - if (hailo_test_bit(i, &channels_bitmap)) { - // based on 18.5.2 "vDMA Interrupt Registers" in PLDA documentation - u32 offset = (i & 16) ? 8 : 0; - hailo_set_bit((((int)i*8) / MAX_VDMA_CHANNELS_PER_ENGINE) + offset, &mask); - } - } - hailo_resource_write32(&resources->config, BSC_IMASK_HOST, mask); -} - -void hailo_pcie_enable_interrupts(struct hailo_pcie_resources *resources) -{ - u32 mask = hailo_resource_read32(&resources->config, BSC_IMASK_HOST); - - hailo_resource_write32(&resources->config, BCS_ISTATUS_HOST, 0xFFFFFFFF); - hailo_resource_write32(&resources->config, BCS_DESTINATION_INTERRUPT_PER_CHANNEL, 0xFFFFFFFF); - hailo_resource_write32(&resources->config, BCS_SOURCE_INTERRUPT_PER_CHANNEL, 0xFFFFFFFF); - - mask |= BCS_ISTATUS_HOST_SW_IRQ_MASK; - hailo_resource_write32(&resources->config, BSC_IMASK_HOST, mask); -} - -void hailo_pcie_disable_interrupts(struct hailo_pcie_resources* resources) -{ - hailo_resource_write32(&resources->config, BSC_IMASK_HOST, 0); -} - -static int direct_memory_transfer(struct hailo_pcie_resources *resources, - struct hailo_memory_transfer_params *params) -{ - switch (params->transfer_direction) { - case TRANSFER_READ: - read_memory(resources, params->address, params->buffer, (u32)params->count); - break; - case TRANSFER_WRITE: - write_memory(resources, params->address, params->buffer, (u32)params->count); - break; - default: - return -EINVAL; - } - - return 0; -} - -int hailo_pcie_memory_transfer(struct hailo_pcie_resources *resources, struct hailo_memory_transfer_params *params) -{ - if (params->count > ARRAY_SIZE(params->buffer)) { - return -EINVAL; - } - - switch (params->memory_type) { - case HAILO_TRANSFER_DEVICE_DIRECT_MEMORY: - return direct_memory_transfer(resources, params); - case HAILO_TRANSFER_MEMORY_PCIE_BAR0: - return hailo_resource_transfer(&resources->config, params); - case HAILO_TRANSFER_MEMORY_PCIE_BAR2: - case HAILO_TRANSFER_MEMORY_VDMA0: - return hailo_resource_transfer(&resources->vdma_registers, params); - case HAILO_TRANSFER_MEMORY_PCIE_BAR4: - return hailo_resource_transfer(&resources->fw_access, params); - default: - return -EINVAL; - } -} - -bool hailo_pcie_is_device_connected(struct hailo_pcie_resources *resources) -{ - return PCI_VENDOR_ID_HAILO == hailo_resource_read16(&resources->config, PCIE_CONFIG_VENDOR_OFFSET); -} - -int hailo_set_device_type(struct hailo_pcie_resources *resources) -{ - switch(resources->board_type) { - case HAILO_BOARD_TYPE_HAILO8: - case HAILO_BOARD_TYPE_HAILO10H_LEGACY: - case HAILO_BOARD_TYPE_HAILO15L: - resources->accelerator_type = HAILO_ACCELERATOR_TYPE_NNC; - break; - case HAILO_BOARD_TYPE_HAILO10H: - resources->accelerator_type = HAILO_ACCELERATOR_TYPE_SOC; - break; - default: - return -EINVAL; - } - - return 0; -} - -// On PCIe, just return the start address -u64 hailo_pcie_encode_desc_dma_address_range(dma_addr_t dma_address_start, dma_addr_t dma_address_end, u32 step, u8 channel_id) -{ - (void)channel_id; - (void)dma_address_end; - (void)step; - return (u64)dma_address_start; -} - -struct hailo_vdma_hw hailo_pcie_vdma_hw = { - .hw_ops = { - .encode_desc_dma_address_range = hailo_pcie_encode_desc_dma_address_range, - }, - .ddr_data_id = HAILO_PCIE_HOST_DMA_DATA_ID, - .device_interrupts_bitmask = HAILO_PCIE_DMA_DEVICE_INTERRUPTS_BITMASK, - .host_interrupts_bitmask = HAILO_PCIE_DMA_HOST_INTERRUPTS_BITMASK, - .src_channels_bitmask = HAILO_PCIE_DMA_SRC_CHANNELS_BITMASK, -}; - -void hailo_pcie_soc_write_request(struct hailo_pcie_resources *resources, - const struct hailo_pcie_soc_request *request) -{ - const struct hailo_fw_addresses *fw_addresses = &(compat[resources->board_type].fw_addresses); - BUILD_BUG_ON_MSG((sizeof(*request) % sizeof(u32)) != 0, "Request must be a multiple of 4 bytes"); - - hailo_resource_write_buffer(&resources->fw_access, 0, sizeof(*request), (void*)request); - hailo_resource_write32(&resources->fw_access, fw_addresses->raise_ready_offset, FW_ACCESS_SOC_CONTROL_MASK); -} - -void hailo_pcie_soc_read_response(struct hailo_pcie_resources *resources, - struct hailo_pcie_soc_response *response) -{ - BUILD_BUG_ON_MSG((sizeof(*response) % sizeof(u32)) != 0, "Request must be a multiple of 4 bytes"); - hailo_resource_read_buffer(&resources->fw_access, 0, sizeof(*response), response); -} diff --git a/drivers/media/pci/hailo/common/pcie_common.h b/drivers/media/pci/hailo/common/pcie_common.h deleted file mode 100644 index 9248a3bbdd3a31..00000000000000 --- a/drivers/media/pci/hailo/common/pcie_common.h +++ /dev/null @@ -1,193 +0,0 @@ -// SPDX-License-Identifier: MIT -/** - * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. - **/ - -#ifndef _HAILO_COMMON_PCIE_COMMON_H_ -#define _HAILO_COMMON_PCIE_COMMON_H_ - -#include "hailo_resource.h" -#include "hailo_ioctl_common.h" -#include "fw_validation.h" -#include "fw_operation.h" -#include "utils.h" -#include "vdma_common.h" -#include "soc_structs.h" - -#include -#include - - -#define BCS_ISTATUS_HOST_SW_IRQ_MASK (0xFF000000) -#define BCS_ISTATUS_HOST_SW_IRQ_SHIFT (24) -#define BCS_ISTATUS_HOST_VDMA_SRC_IRQ_MASK (0x000000FF) -#define BCS_ISTATUS_HOST_VDMA_DEST_IRQ_MASK (0x0000FF00) - -#define PCIE_HAILO8_BOARD_CFG_MAX_SIZE (0x500) -#define PCIE_HAILO8_FW_CFG_MAX_SIZE (0x500) - -#define FW_CODE_SECTION_ALIGNMENT (4) - -#define HAILO_PCIE_CONFIG_BAR (0) -#define HAILO_PCIE_VDMA_REGS_BAR (2) -#define HAILO_PCIE_FW_ACCESS_BAR (4) - -#define HAILO_PCIE_DMA_ENGINES_COUNT (1) -#define PCI_VDMA_ENGINE_INDEX (0) - -#define MAX_FILES_PER_STAGE (4) - -#define HAILO_PCIE_HOST_DMA_DATA_ID (0) -#define HAILO_PCI_EP_HOST_DMA_DATA_ID (6) - -#define DRIVER_NAME "hailo" - -#define PCI_VENDOR_ID_HAILO 0x1e60 -#define PCI_DEVICE_ID_HAILO_HAILO8 0x2864 -#define PCI_DEVICE_ID_HAILO_HAILO10H 0x45C4 -#define PCI_DEVICE_ID_HAILO_HAILO15L 0x43a2 - -typedef u64 hailo_ptr_t; - -struct hailo_pcie_resources { - struct hailo_resource config; // BAR0 - struct hailo_resource vdma_registers; // BAR2 - struct hailo_resource fw_access; // BAR4 - enum hailo_board_type board_type; - enum hailo_accelerator_type accelerator_type; -}; - -struct hailo_atr_config { - u32 atr_param; - u32 atr_src; - u32 atr_trsl_addr_1; - u32 atr_trsl_addr_2; - u32 atr_trsl_param; -}; - -enum loading_stages { - FIRST_STAGE = 0, - SECOND_STAGE = 1, - SECOND_STAGE_LINUX_IN_EMMC = 2, - MAX_LOADING_STAGES = 3 -}; - -enum hailo_pcie_nnc_sw_interrupt_masks { - HAILO_PCIE_NNC_FW_NOTIFICATION_IRQ = 0x2, - HAILO_PCIE_NNC_FW_CONTROL_IRQ = 0x4, - HAILO_PCIE_NNC_DRIVER_DOWN_IRQ = 0x8, -}; - -enum hailo_pcie_soc_sw_interrupt_masks { - HAILO_PCIE_SOC_CONTROL_IRQ = 0x10, - HAILO_PCIE_SOC_CLOSE_IRQ = 0x20, -}; - -enum hailo_pcie_boot_interrupt_masks { - HAILO_PCIE_BOOT_SOFT_RESET_IRQ = 0x1, - HAILO_PCIE_BOOT_IRQ = 0x2, -}; - -struct hailo_pcie_interrupt_source { - u32 sw_interrupts; - u32 vdma_channels_bitmap; -}; - -struct hailo_file_batch { - const char *filename; - u32 address; - size_t max_size; - bool is_mandatory; - bool has_header; - bool has_core; -}; - -struct hailo_pcie_loading_stage { - const struct hailo_file_batch *batch; - u32 trigger_address; - u32 timeout; - u8 amount_of_files_in_stage; -}; - -// TODO: HRT-6144 - Align Windows/Linux to QNX -#ifdef __QNX__ -enum hailo_bar_index { - BAR0 = 0, - BAR2, - BAR4, - MAX_BAR -}; -#else -enum hailo_bar_index { - BAR0 = 0, - BAR1, - BAR2, - BAR3, - BAR4, - BAR5, - MAX_BAR -}; -#endif // ifdef (__QNX__) - -#ifdef __cplusplus -extern "C" { -#endif - - -#ifndef HAILO_EMULATOR -#define TIME_UNTIL_REACH_BOOTLOADER (10) -#define PCI_EP_WAIT_TIMEOUT_MS (40000) -#define FIRMWARE_WAIT_TIMEOUT_MS (5000) -#else /* ifndef HAILO_EMULATOR */ -// PCI EP timeout is defined to 50000000 because on Emulator the boot time + linux init time can be very long (4+ hours) -#define TIME_UNTIL_REACH_BOOTLOADER (10000) -#define PCI_EP_WAIT_TIMEOUT_MS (50000000) -#define FIRMWARE_WAIT_TIMEOUT_MS (5000000) -#endif /* ifndef HAILO_EMULATOR */ - -extern struct hailo_vdma_hw hailo_pcie_vdma_hw; - -const struct hailo_pcie_loading_stage* hailo_pcie_get_loading_stage_info(enum hailo_board_type board_type, - enum loading_stages stage); - -// Reads the interrupt source from BARs, return false if there is no interrupt. -// note - this function clears the interrupt signals. -bool hailo_pcie_read_interrupt(struct hailo_pcie_resources *resources, struct hailo_pcie_interrupt_source *source); -void hailo_pcie_update_channel_interrupts_mask(struct hailo_pcie_resources *resources, u32 channels_bitmap); -void hailo_pcie_enable_interrupts(struct hailo_pcie_resources *resources); -void hailo_pcie_disable_interrupts(struct hailo_pcie_resources *resources); - -int hailo_pcie_write_firmware_control(struct hailo_pcie_resources *resources, const struct hailo_fw_control *command); -int hailo_pcie_read_firmware_control(struct hailo_pcie_resources *resources, struct hailo_fw_control *command); - -int hailo_pcie_write_firmware_batch(struct device *dev, struct hailo_pcie_resources *resources, u32 stage); -bool hailo_pcie_is_firmware_loaded(struct hailo_pcie_resources *resources); -bool hailo_pcie_wait_for_firmware(struct hailo_pcie_resources *resources); - -int hailo_pcie_memory_transfer(struct hailo_pcie_resources *resources, struct hailo_memory_transfer_params *params); - -bool hailo_pcie_is_device_connected(struct hailo_pcie_resources *resources); -void hailo_pcie_write_firmware_driver_shutdown(struct hailo_pcie_resources *resources); -void hailo_pcie_write_firmware_soft_reset(struct hailo_pcie_resources *resources); -void hailo_pcie_configure_ep_registers_for_dma_transaction(struct hailo_pcie_resources *resources); -void hailo_trigger_firmware_boot(struct hailo_pcie_resources *resources, u32 stage); - -int hailo_set_device_type(struct hailo_pcie_resources *resources); - -u32 hailo_get_boot_status(struct hailo_pcie_resources *resources); - -int hailo_pcie_configure_atr_table(struct hailo_resource *bridge_config, u64 trsl_addr, u32 atr_index); -void hailo_pcie_read_atr_table(struct hailo_resource *bridge_config, struct hailo_atr_config *atr, u32 atr_index); - -u64 hailo_pcie_encode_desc_dma_address_range(dma_addr_t dma_address_start, dma_addr_t dma_address_end, u32 step, u8 channel_id); - -void hailo_pcie_soc_write_request(struct hailo_pcie_resources *resources, - const struct hailo_pcie_soc_request *request); -void hailo_pcie_soc_read_response(struct hailo_pcie_resources *resources, - struct hailo_pcie_soc_response *response); - -#ifdef __cplusplus -} -#endif - -#endif /* _HAILO_COMMON_PCIE_COMMON_H_ */ diff --git a/drivers/media/pci/hailo/common/soc_structs.h b/drivers/media/pci/hailo/common/soc_structs.h deleted file mode 100644 index 5a00c028c87f4e..00000000000000 --- a/drivers/media/pci/hailo/common/soc_structs.h +++ /dev/null @@ -1,79 +0,0 @@ -// SPDX-License-Identifier: MIT -/** - * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. - **/ -/** - * Contains definitions for pcie soc to pcie ep communication - */ - -#ifndef __HAILO_COMMON_SOC_STRUCTS__ -#define __HAILO_COMMON_SOC_STRUCTS__ - -#include - -#pragma pack(push, 1) - -struct hailo_pcie_soc_connect_request { - u16 port; -}; - -struct hailo_pcie_soc_connect_response { - u8 input_channel_index; - u8 output_channel_index; -}; - - -struct hailo_pcie_soc_close_request { - u32 channels_bitmap; -}; - -struct hailo_pcie_soc_close_response { - u8 reserved; -}; - -enum hailo_pcie_soc_control_code { - // Start from big initial value to ensure the right code was used (using 0 - // as initiale may cause confusion if the code was not set correctly). - HAILO_PCIE_SOC_CONTROL_CODE_CONNECT = 0x100, - HAILO_PCIE_SOC_CONTROL_CODE_CLOSE, - HAILO_PCIE_SOC_CONTROL_CODE_INVALID, -}; - -#define HAILO_PCIE_SOC_MAX_REQUEST_SIZE_BYTES (16) -#define HAILO_PCIE_SOC_MAX_RESPONSE_SIZE_BYTES (16) - -// IRQ to signal the PCIe that the EP was closed/released -#define PCI_EP_SOC_CLOSED_IRQ (0x00000020) -#define PCI_EP_SOC_CONNECT_RESPONSE (0x00000010) - -struct hailo_pcie_soc_request { - u32 control_code; - union { - struct hailo_pcie_soc_connect_request connect; - struct hailo_pcie_soc_close_request close; - u8 pad[HAILO_PCIE_SOC_MAX_REQUEST_SIZE_BYTES]; - }; -}; - -struct hailo_pcie_soc_response { - u32 control_code; - s32 status; - union { - struct hailo_pcie_soc_connect_response connect; - struct hailo_pcie_soc_close_response close; - u8 pad[HAILO_PCIE_SOC_MAX_RESPONSE_SIZE_BYTES]; - }; -}; - -#pragma pack(pop) - -// Compile time validate function. Don't need to call it. -static inline void __validate_soc_struct_sizes(void) -{ - BUILD_BUG_ON_MSG(sizeof(struct hailo_pcie_soc_request) != - sizeof(u32) + HAILO_PCIE_SOC_MAX_REQUEST_SIZE_BYTES, "Invalid request size"); - BUILD_BUG_ON_MSG(sizeof(struct hailo_pcie_soc_response) != - sizeof(u32) + sizeof(s32) + HAILO_PCIE_SOC_MAX_RESPONSE_SIZE_BYTES, "Invalid response size"); -} - -#endif /* __HAILO_COMMON_SOC_STRUCTS__ */ \ No newline at end of file diff --git a/drivers/media/pci/hailo/common/utils.h b/drivers/media/pci/hailo/common/utils.h deleted file mode 100644 index 0d53b65799b838..00000000000000 --- a/drivers/media/pci/hailo/common/utils.h +++ /dev/null @@ -1,82 +0,0 @@ -// SPDX-License-Identifier: MIT -/** - * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. - **/ - -#ifndef _HAILO_DRIVER_UTILS_H_ -#define _HAILO_DRIVER_UTILS_H_ - -#include - -#define DWORD_SIZE (4) -#define WORD_SIZE (2) -#define BYTE_SIZE (1) -#define BITS_IN_BYTE (8) - -#define hailo_clear_bit(bit, pval) { *(pval) &= ~(1 << bit); } -#define hailo_test_bit(pos,var_addr) ((*var_addr) & (1<<(pos))) - -#define READ_BITS_AT_OFFSET(amount_bits, offset, initial_value) \ - (((initial_value) >> (offset)) & ((1 << (amount_bits)) - 1)) -#define WRITE_BITS_AT_OFFSET(amount_bits, offset, initial_value, value) \ - (((initial_value) & ~(((1 << (amount_bits)) - 1) << (offset))) | \ - (((value) & ((1 << (amount_bits)) - 1)) << (offset))) - -#ifdef __cplusplus -extern "C" -{ -#endif - -static inline bool is_powerof2(size_t v) { - // bit trick - return (v & (v - 1)) == 0; -} - -static inline void hailo_set_bit(int nr, u32* addr) { - u32 mask = BIT_MASK(nr); - u32 *p = addr + BIT_WORD(nr); - - *p |= mask; -} - -static inline uint8_t ceil_log2(uint32_t n) -{ - uint8_t result = 0; - - if (n <= 1) { - return 0; - } - - while (n > 1) { - result++; - n = (n + 1) >> 1; - } - - return result; -} - -// Gets the nearest power of 2 >= value, for any value <= MAX_POWER_OF_2_VALUE. Otherwise POWER_OF_2_ERROR is returned. -#define MAX_POWER_OF_2_VALUE (0x80000000) -#define POWER_OF_2_ERROR ((uint32_t)-1) -static inline uint32_t get_nearest_powerof_2(uint32_t value) -{ - uint32_t power_of_2 = 1; - if (value > MAX_POWER_OF_2_VALUE) { - return POWER_OF_2_ERROR; - } - - while (value > power_of_2) { - power_of_2 <<= 1; - } - return power_of_2; -} - -#ifndef DIV_ROUND_UP -#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) -#endif - -#ifdef __cplusplus -} -#endif - -#endif // _HAILO_DRIVER_UTILS_H_ \ No newline at end of file diff --git a/drivers/media/pci/hailo/common/vdma_common.c b/drivers/media/pci/hailo/common/vdma_common.c deleted file mode 100644 index 56d879eed86472..00000000000000 --- a/drivers/media/pci/hailo/common/vdma_common.c +++ /dev/null @@ -1,876 +0,0 @@ -// SPDX-License-Identifier: MIT -/** - * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. - **/ - -#include "vdma_common.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define VDMA_CHANNEL_CONTROL_START (0x1) -#define VDMA_CHANNEL_CONTROL_ABORT (0b00) -#define VDMA_CHANNEL_CONTROL_ABORT_PAUSE (0b10) -#define VDMA_CHANNEL_CONTROL_START_ABORT_PAUSE_RESUME_BITMASK (0x3) -#define VDMA_CHANNEL_CONTROL_START_ABORT_BITMASK (0x1) -#define VDMA_CHANNEL_CONTROL_MASK (0xFC) -#define VDMA_CHANNEL_CONTROL_START_RESUME (0b01) -#define VDMA_CHANNEL_CONTROL_START_PAUSE (0b11) -#define VDMA_CHANNEL_CONTROL_ABORT (0b00) -#define VDMA_CHANNEL_CONTROL_ABORT_PAUSE (0b10) -#define VDMA_CHANNEL_CONTROL_START_ABORT_PAUSE_RESUME_BITMASK (0x3) -#define VDMA_CHANNEL_DESC_DEPTH_WIDTH (4) -#define VDMA_CHANNEL_DESC_DEPTH_SHIFT (11) -#define VDMA_CHANNEL_DATA_ID_SHIFT (8) -#define VDMA_CHANNEL__MAX_CHECKS_CHANNEL_IS_IDLE (10000) -#define VDMA_CHANNEL__ADDRESS_L_OFFSET (0x0A) -#define VDMA_CHANNEL__ALIGNED_ADDRESS_L_OFFSET (0x8) -#define VDMA_CHANNEL__ADDRESS_H_OFFSET (0x0C) - -#define DESCRIPTOR_PAGE_SIZE_SHIFT (8) -#define DESCRIPTOR_DESC_CONTROL (0x2) -#define DESCRIPTOR_ADDR_L_MASK (0xFFFFFFC0) -#define DESCRIPTOR_LIST_MAX_DEPTH (16) - -#define DESCRIPTOR_DESC_STATUS_DONE_BIT (0x0) -#define DESCRIPTOR_DESC_STATUS_ERROR_BIT (0x1) -#define DESCRIPTOR_DESC_STATUS_MASK (0xFF) - -#define DESC_STATUS_REQ (1 << 0) -#define DESC_STATUS_REQ_ERR (1 << 1) -#define DESC_REQUEST_IRQ_PROCESSED (1 << 2) -#define DESC_REQUEST_IRQ_ERR (1 << 3) - -#define VDMA_CHANNEL_NUM_PROCESSED_WIDTH (16) -#define VDMA_CHANNEL_NUM_PROCESSED_MASK ((1 << VDMA_CHANNEL_NUM_PROCESSED_WIDTH) - 1) -#define VDMA_CHANNEL_NUM_ONGOING_MASK VDMA_CHANNEL_NUM_PROCESSED_MASK - -#define TIMESTAMPS_CIRC_SPACE(timestamp_list) \ - CIRC_SPACE((timestamp_list).head, (timestamp_list).tail, CHANNEL_IRQ_TIMESTAMPS_SIZE) -#define TIMESTAMPS_CIRC_CNT(timestamp_list) \ - CIRC_CNT((timestamp_list).head, (timestamp_list).tail, CHANNEL_IRQ_TIMESTAMPS_SIZE) - -#define ONGOING_TRANSFERS_CIRC_SPACE(transfers_list) \ - CIRC_SPACE((transfers_list).head, (transfers_list).tail, HAILO_VDMA_MAX_ONGOING_TRANSFERS) -#define ONGOING_TRANSFERS_CIRC_CNT(transfers_list) \ - CIRC_CNT((transfers_list).head, (transfers_list).tail, HAILO_VDMA_MAX_ONGOING_TRANSFERS) - -#ifndef for_each_sgtable_dma_sg -#define for_each_sgtable_dma_sg(sgt, sg, i) \ - for_each_sg((sgt)->sgl, sg, (sgt)->nents, i) -#endif /* for_each_sgtable_dma_sg */ - - -static int ongoing_transfer_push(struct hailo_vdma_channel *channel, - struct hailo_ongoing_transfer *ongoing_transfer) -{ - struct hailo_ongoing_transfers_list *transfers = &channel->ongoing_transfers; - if (!ONGOING_TRANSFERS_CIRC_SPACE(*transfers)) { - return -EFAULT; - } - - if (ongoing_transfer->dirty_descs_count > ARRAY_SIZE(ongoing_transfer->dirty_descs)) { - return -EFAULT; - } - - transfers->transfers[transfers->head] = *ongoing_transfer; - transfers->head = (transfers->head + 1) & HAILO_VDMA_MAX_ONGOING_TRANSFERS_MASK; - return 0; -} - -static int ongoing_transfer_pop(struct hailo_vdma_channel *channel, - struct hailo_ongoing_transfer *ongoing_transfer) -{ - struct hailo_ongoing_transfers_list *transfers = &channel->ongoing_transfers; - if (!ONGOING_TRANSFERS_CIRC_CNT(*transfers)) { - return -EFAULT; - } - - if (ongoing_transfer) { - *ongoing_transfer = transfers->transfers[transfers->tail]; - } - transfers->tail = (transfers->tail + 1) & HAILO_VDMA_MAX_ONGOING_TRANSFERS_MASK; - return 0; -} - -static void clear_dirty_desc(struct hailo_vdma_descriptors_list *desc_list, u16 desc) -{ - desc_list->desc_list[desc].PageSize_DescControl = - (u32)((desc_list->desc_page_size << DESCRIPTOR_PAGE_SIZE_SHIFT) + DESCRIPTOR_DESC_CONTROL); -} - -static void clear_dirty_descs(struct hailo_vdma_channel *channel, - struct hailo_ongoing_transfer *ongoing_transfer) -{ - u8 i = 0; - struct hailo_vdma_descriptors_list *desc_list = channel->last_desc_list; - BUG_ON(ongoing_transfer->dirty_descs_count > ARRAY_SIZE(ongoing_transfer->dirty_descs)); - for (i = 0; i < ongoing_transfer->dirty_descs_count; i++) { - clear_dirty_desc(desc_list, ongoing_transfer->dirty_descs[i]); - } -} - -static bool validate_last_desc_status(struct hailo_vdma_channel *channel, - struct hailo_ongoing_transfer *ongoing_transfer) -{ - u16 last_desc = ongoing_transfer->last_desc; - u32 last_desc_control = channel->last_desc_list->desc_list[last_desc].RemainingPageSize_Status & - DESCRIPTOR_DESC_STATUS_MASK; - if (!hailo_test_bit(DESCRIPTOR_DESC_STATUS_DONE_BIT, &last_desc_control)) { - pr_err("Expecting desc %d to be done\n", last_desc); - return false; - } - if (hailo_test_bit(DESCRIPTOR_DESC_STATUS_ERROR_BIT, &last_desc_control)) { - pr_err("Got unexpected error on desc %d\n", last_desc); - return false; - } - - return true; -} - -static void hailo_vdma_program_descriptor(struct hailo_vdma_descriptor *descriptor, u64 dma_address, size_t page_size, - u8 data_id) -{ - descriptor->PageSize_DescControl = (u32)((page_size << DESCRIPTOR_PAGE_SIZE_SHIFT) + - DESCRIPTOR_DESC_CONTROL); - descriptor->AddrL_rsvd_DataID = (u32)(((dma_address & DESCRIPTOR_ADDR_L_MASK)) | data_id); - descriptor->AddrH = (u32)(dma_address >> 32); - descriptor->RemainingPageSize_Status = 0 ; -} - -static u8 get_channel_id(u8 channel_index) -{ - return (channel_index < MAX_VDMA_CHANNELS_PER_ENGINE) ? (channel_index & 0xF) : INVALID_VDMA_CHANNEL; -} - -int hailo_vdma_program_descriptors_in_chunk( - struct hailo_vdma_hw *vdma_hw, - dma_addr_t chunk_addr, - unsigned int chunk_size, - struct hailo_vdma_descriptors_list *desc_list, - u32 desc_index, - u32 max_desc_index, - u8 channel_index, - u8 data_id) -{ - const u16 page_size = desc_list->desc_page_size; - const u32 descs_to_program = DIV_ROUND_UP(chunk_size, page_size); - const u32 starting_desc_index = desc_index; - const u32 residue_size = chunk_size % page_size; - struct hailo_vdma_descriptor *dma_desc = NULL; - u64 encoded_addr = 0; - - if (descs_to_program == 0) { - // Nothing to program - return 0; - } - - // We iterate through descriptors [desc_index, desc_index + descs_to_program) - if (desc_index + descs_to_program > max_desc_index + 1) { - return -ERANGE; - } - - encoded_addr = vdma_hw->hw_ops.encode_desc_dma_address_range(chunk_addr, chunk_addr + chunk_size, page_size, get_channel_id(channel_index)); - if (INVALID_VDMA_ADDRESS == encoded_addr) { - return -EFAULT; - } - - // Program all descriptors except the last one - for (desc_index = starting_desc_index; desc_index < starting_desc_index + descs_to_program - 1; desc_index++) { - // 'desc_index & desc_list_len_mask' is used instead of modulo; see hailo_vdma_descriptors_list documentation. - hailo_vdma_program_descriptor( - &desc_list->desc_list[desc_index & desc_list->desc_count_mask], - encoded_addr, page_size, data_id); - encoded_addr += page_size; - } - - // Handle the last descriptor outside of the loop - // 'desc_index & desc_list_len_mask' is used instead of modulo; see hailo_vdma_descriptors_list documentation. - dma_desc = &desc_list->desc_list[desc_index & desc_list->desc_count_mask]; - hailo_vdma_program_descriptor(dma_desc, encoded_addr, - (residue_size == 0) ? page_size : (u16)residue_size, data_id); - - return (int)descs_to_program; -} - -static unsigned long get_interrupts_bitmask(struct hailo_vdma_hw *vdma_hw, - enum hailo_vdma_interrupts_domain interrupts_domain, bool is_debug) -{ - unsigned long bitmask = 0; - - if (0 != (HAILO_VDMA_INTERRUPTS_DOMAIN_DEVICE & interrupts_domain)) { - bitmask |= vdma_hw->device_interrupts_bitmask; - } - if (0 != (HAILO_VDMA_INTERRUPTS_DOMAIN_HOST & interrupts_domain)) { - bitmask |= vdma_hw->host_interrupts_bitmask; - } - - if (bitmask != 0) { - bitmask |= DESC_REQUEST_IRQ_PROCESSED | DESC_REQUEST_IRQ_ERR; - if (is_debug) { - bitmask |= DESC_STATUS_REQ | DESC_STATUS_REQ_ERR; - } - } - - return bitmask; -} - -static int bind_and_program_descriptors_list( - struct hailo_vdma_hw *vdma_hw, - struct hailo_vdma_descriptors_list *desc_list, - u32 starting_desc, - struct hailo_vdma_mapped_transfer_buffer *buffer, - u8 channel_index, - enum hailo_vdma_interrupts_domain last_desc_interrupts, - bool is_debug) -{ - int desc_programmed = 0; - int descs_programmed_in_chunk = 0; - u32 max_desc_index = 0; - u32 chunk_size = 0; - struct scatterlist *sg_entry = NULL; - unsigned int i = 0; - size_t buffer_current_offset = 0; - dma_addr_t chunk_start_addr = 0; - u32 program_size = buffer->size; - - if (starting_desc >= desc_list->desc_count) { - return -EFAULT; - } - - if (buffer->offset % desc_list->desc_page_size != 0) { - return -EFAULT; - } - - // On circular buffer, allow programming desc_count descriptors (starting - // from starting_desc). On non circular, don't allow is to pass desc_count - max_desc_index = desc_list->is_circular ? - starting_desc + desc_list->desc_count - 1 : - desc_list->desc_count - 1; - for_each_sgtable_dma_sg(buffer->sg_table, sg_entry, i) { - // Skip sg entries until we reach the right buffer offset. offset can be in the middle of an sg entry. - if (buffer_current_offset + sg_dma_len(sg_entry) < buffer->offset) { - buffer_current_offset += sg_dma_len(sg_entry); - continue; - } - chunk_start_addr = (buffer_current_offset < buffer->offset) ? - sg_dma_address(sg_entry) + (buffer->offset - buffer_current_offset) : - sg_dma_address(sg_entry); - chunk_size = (buffer_current_offset < buffer->offset) ? - (u32)(sg_dma_len(sg_entry) - (buffer->offset - buffer_current_offset)) : - (u32)(sg_dma_len(sg_entry)); - chunk_size = min((u32)program_size, chunk_size); - - descs_programmed_in_chunk = hailo_vdma_program_descriptors_in_chunk(vdma_hw, chunk_start_addr, chunk_size, desc_list, - starting_desc, max_desc_index, channel_index, vdma_hw->ddr_data_id); - if (descs_programmed_in_chunk < 0) { - return descs_programmed_in_chunk; - } - - desc_programmed += descs_programmed_in_chunk; - starting_desc = starting_desc + descs_programmed_in_chunk; - program_size -= chunk_size; - buffer_current_offset += sg_dma_len(sg_entry); - } - - if (program_size != 0) { - // We didn't program all the buffer. - return -EFAULT; - } - - desc_list->desc_list[(starting_desc - 1) % desc_list->desc_count].PageSize_DescControl |= - get_interrupts_bitmask(vdma_hw, last_desc_interrupts, is_debug); - - return desc_programmed; -} - -static int program_last_desc( - struct hailo_vdma_hw *vdma_hw, - struct hailo_vdma_descriptors_list *desc_list, - u32 starting_desc, - struct hailo_vdma_mapped_transfer_buffer *transfer_buffer, - enum hailo_vdma_interrupts_domain last_desc_interrupts, - bool is_debug) -{ - u8 control = (u8)(DESCRIPTOR_DESC_CONTROL | get_interrupts_bitmask(vdma_hw, last_desc_interrupts, is_debug)); - u32 total_descs = DIV_ROUND_UP(transfer_buffer->size, desc_list->desc_page_size); - u32 last_desc = (starting_desc + total_descs - 1) % desc_list->desc_count; - u32 last_desc_size = transfer_buffer->size - (total_descs - 1) * desc_list->desc_page_size; - - // Configure only last descriptor with residue size - desc_list->desc_list[last_desc].PageSize_DescControl = (u32) - ((last_desc_size << DESCRIPTOR_PAGE_SIZE_SHIFT) + control); - return (int)total_descs; -} - -int hailo_vdma_program_descriptors_list( - struct hailo_vdma_hw *vdma_hw, - struct hailo_vdma_descriptors_list *desc_list, - u32 starting_desc, - struct hailo_vdma_mapped_transfer_buffer *buffer, - bool should_bind, - u8 channel_index, - enum hailo_vdma_interrupts_domain last_desc_interrupts, - bool is_debug) -{ - return should_bind ? - bind_and_program_descriptors_list(vdma_hw, desc_list, starting_desc, - buffer, channel_index, last_desc_interrupts, is_debug) : - program_last_desc(vdma_hw, desc_list, starting_desc, buffer, - last_desc_interrupts, is_debug); -} - - -static bool channel_control_reg_is_active(u8 control) -{ - return (control & VDMA_CHANNEL_CONTROL_START_ABORT_BITMASK) == VDMA_CHANNEL_CONTROL_START; -} - -static int validate_channel_state(struct hailo_vdma_channel *channel) -{ - u32 host_regs_value = ioread32(channel->host_regs); - const u8 control = READ_BITS_AT_OFFSET(BYTE_SIZE * BITS_IN_BYTE, CHANNEL_CONTROL_OFFSET * BITS_IN_BYTE, host_regs_value); - const u16 hw_num_avail = READ_BITS_AT_OFFSET(WORD_SIZE * BITS_IN_BYTE, CHANNEL_NUM_AVAIL_OFFSET * BITS_IN_BYTE, host_regs_value); - - if (!channel_control_reg_is_active(control)) { - return -ECONNRESET; - } - - if (hw_num_avail != channel->state.num_avail) { - pr_err("Channel %d hw state out of sync. num available is %d, expected %d\n", - channel->index, hw_num_avail, channel->state.num_avail); - return -EFAULT; - } - - return 0; -} - -void hailo_vdma_set_num_avail(u8 __iomem *regs, u16 num_avail) -{ - u32 regs_val = ioread32(regs); - iowrite32(WRITE_BITS_AT_OFFSET(WORD_SIZE * BITS_IN_BYTE, CHANNEL_NUM_AVAIL_OFFSET * BITS_IN_BYTE, regs_val, num_avail), - regs); -} - -u16 hailo_vdma_get_num_proc(u8 __iomem *regs) -{ - return READ_BITS_AT_OFFSET(WORD_SIZE * BITS_IN_BYTE, 0, ioread32(regs + CHANNEL_NUM_PROC_OFFSET)); -} - -int hailo_vdma_launch_transfer( - struct hailo_vdma_hw *vdma_hw, - struct hailo_vdma_channel *channel, - struct hailo_vdma_descriptors_list *desc_list, - u32 starting_desc, - u8 buffers_count, - struct hailo_vdma_mapped_transfer_buffer *buffers, - bool should_bind, - enum hailo_vdma_interrupts_domain first_interrupts_domain, - enum hailo_vdma_interrupts_domain last_desc_interrupts, - bool is_debug) -{ - int ret = -EFAULT; - u32 total_descs = 0; - u32 first_desc = starting_desc; - u32 last_desc = U32_MAX; - u16 new_num_avail = 0; - struct hailo_ongoing_transfer ongoing_transfer = {0}; - u8 i = 0; - - channel->state.desc_count_mask = (desc_list->desc_count - 1); - - if (NULL == channel->last_desc_list) { - // First transfer on this active channel, store desc list. - channel->last_desc_list = desc_list; - } else if (desc_list != channel->last_desc_list) { - // Shouldn't happen, desc list may change only after channel deactivation. - pr_err("Inconsistent desc list given to channel %d\n", channel->index); - return -EINVAL; - } - - ret = validate_channel_state(channel); - if (ret < 0) { - return ret; - } - - if (channel->state.num_avail != (u16)starting_desc) { - pr_err("Channel %d state out of sync. num available is %d, expected %d\n", - channel->index, channel->state.num_avail, (u16)starting_desc); - return -EFAULT; - } - - if (buffers_count > HAILO_MAX_BUFFERS_PER_SINGLE_TRANSFER) { - pr_err("Too many buffers %u for single transfer\n", buffers_count); - return -EINVAL; - } - - BUILD_BUG_ON_MSG((HAILO_MAX_BUFFERS_PER_SINGLE_TRANSFER + 1) != ARRAY_SIZE(ongoing_transfer.dirty_descs), - "Unexpected amount of dirty descriptors"); - ongoing_transfer.dirty_descs_count = buffers_count + 1; - ongoing_transfer.dirty_descs[0] = (u16)starting_desc; - - for (i = 0; i < buffers_count; i++) { - ret = hailo_vdma_program_descriptors_list(vdma_hw, desc_list, - starting_desc, &buffers[i], should_bind, channel->index, - (i == (buffers_count - 1) ? last_desc_interrupts : HAILO_VDMA_INTERRUPTS_DOMAIN_NONE), - is_debug); - - total_descs += ret; - last_desc = (starting_desc + ret - 1) % desc_list->desc_count; - starting_desc = (starting_desc + ret) % desc_list->desc_count; - - ongoing_transfer.dirty_descs[i+1] = (u16)last_desc; - ongoing_transfer.buffers[i] = buffers[i]; - } - ongoing_transfer.buffers_count = buffers_count; - - desc_list->desc_list[first_desc].PageSize_DescControl |= - get_interrupts_bitmask(vdma_hw, first_interrupts_domain, is_debug); - - ongoing_transfer.last_desc = (u16)last_desc; - ongoing_transfer.is_debug = is_debug; - ret = ongoing_transfer_push(channel, &ongoing_transfer); - if (ret < 0) { - pr_err("Failed push ongoing transfer to channel %d\n", channel->index); - return ret; - } - - new_num_avail = (u16)((last_desc + 1) % desc_list->desc_count); - channel->state.num_avail = new_num_avail; - hailo_vdma_set_num_avail(channel->host_regs, new_num_avail); - - return (int)total_descs; -} - -static void hailo_vdma_push_timestamp(struct hailo_vdma_channel *channel) -{ - struct hailo_channel_interrupt_timestamp_list *timestamp_list = &channel->timestamp_list; - const u16 num_proc = hailo_vdma_get_num_proc(channel->host_regs); - if (TIMESTAMPS_CIRC_SPACE(*timestamp_list) != 0) { - timestamp_list->timestamps[timestamp_list->head].timestamp_ns = ktime_get_ns(); - timestamp_list->timestamps[timestamp_list->head].desc_num_processed = num_proc; - timestamp_list->head = (timestamp_list->head + 1) & CHANNEL_IRQ_TIMESTAMPS_SIZE_MASK; - } -} - -// Returns false if there are no items -static bool hailo_vdma_pop_timestamp(struct hailo_channel_interrupt_timestamp_list *timestamp_list, - struct hailo_channel_interrupt_timestamp *out_timestamp) -{ - if (0 == TIMESTAMPS_CIRC_CNT(*timestamp_list)) { - return false; - } - - *out_timestamp = timestamp_list->timestamps[timestamp_list->tail]; - timestamp_list->tail = (timestamp_list->tail+1) & CHANNEL_IRQ_TIMESTAMPS_SIZE_MASK; - return true; -} - -static void hailo_vdma_pop_timestamps_to_response(struct hailo_vdma_channel *channel, - struct hailo_vdma_interrupts_read_timestamp_params *result) -{ - const u32 max_timestamps = ARRAY_SIZE(result->timestamps); - u32 i = 0; - - while (hailo_vdma_pop_timestamp(&channel->timestamp_list, &result->timestamps[i]) && - (i < max_timestamps)) { - // Although the hw_num_processed should be a number between 0 and - // desc_count-1, if desc_count < 0x10000 (the maximum desc size), - // the actual hw_num_processed is a number between 1 and desc_count. - // Therefore the value can be desc_count, in this case we change it to - // zero. - result->timestamps[i].desc_num_processed = result->timestamps[i].desc_num_processed & - channel->state.desc_count_mask; - i++; - } - - result->timestamps_count = i; -} - -static void channel_state_init(struct hailo_vdma_channel_state *state) -{ - state->num_avail = state->num_proc = 0; - - // Special value used when the channel is not activate. - state->desc_count_mask = U32_MAX; -} - -static u8 __iomem *get_channel_regs(u8 __iomem *regs_base, u8 channel_index, bool is_host_side, u32 src_channels_bitmask) -{ - // Check if getting host side regs or device side - u8 __iomem *channel_regs_base = regs_base + CHANNEL_BASE_OFFSET(channel_index); - if (is_host_side) { - return hailo_test_bit(channel_index, &src_channels_bitmask) ? channel_regs_base : - (channel_regs_base + CHANNEL_DEST_REGS_OFFSET); - } else { - return hailo_test_bit(channel_index, &src_channels_bitmask) ? (channel_regs_base + CHANNEL_DEST_REGS_OFFSET) : - channel_regs_base; - } -} - -void hailo_vdma_engine_init(struct hailo_vdma_engine *engine, u8 engine_index, - const struct hailo_resource *channel_registers, u32 src_channels_bitmask) -{ - u8 channel_index = 0; - struct hailo_vdma_channel *channel; - - engine->index = engine_index; - engine->enabled_channels = 0x0; - engine->interrupted_channels = 0x0; - - for_each_vdma_channel(engine, channel, channel_index) { - u8 __iomem *regs_base = (u8 __iomem *)channel_registers->address; - channel->host_regs = get_channel_regs(regs_base, channel_index, true, src_channels_bitmask); - channel->device_regs = get_channel_regs(regs_base, channel_index, false, src_channels_bitmask); - channel->index = channel_index; - channel->timestamp_measure_enabled = false; - - channel_state_init(&channel->state); - channel->last_desc_list = NULL; - - channel->ongoing_transfers.head = 0; - channel->ongoing_transfers.tail = 0; - } -} - -/** - * Enables the given channels bitmap in the given engine. Allows launching transfer - * and reading interrupts from the channels. - * - * @param engine - dma engine. - * @param bitmap - channels bitmap to enable. - * @param measure_timestamp - if set, allow interrupts timestamp measure. - */ -void hailo_vdma_engine_enable_channels(struct hailo_vdma_engine *engine, u32 bitmap, - bool measure_timestamp) -{ - struct hailo_vdma_channel *channel = NULL; - u8 channel_index = 0; - - for_each_vdma_channel(engine, channel, channel_index) { - if (hailo_test_bit(channel_index, &bitmap)) { - channel->timestamp_measure_enabled = measure_timestamp; - channel->timestamp_list.head = channel->timestamp_list.tail = 0; - } - } - - engine->enabled_channels |= bitmap; -} - -/** - * Disables the given channels bitmap in the given engine. - * - * @param engine - dma engine. - * @param bitmap - channels bitmap to enable. - * @param measure_timestamp - if set, allow interrupts timestamp measure. - */ -void hailo_vdma_engine_disable_channels(struct hailo_vdma_engine *engine, u32 bitmap) -{ - struct hailo_vdma_channel *channel = NULL; - u8 channel_index = 0; - - engine->enabled_channels &= ~bitmap; - - for_each_vdma_channel(engine, channel, channel_index) { - if (hailo_test_bit(channel_index, &bitmap)) { - channel_state_init(&channel->state); - - while (ONGOING_TRANSFERS_CIRC_CNT(channel->ongoing_transfers) > 0) { - struct hailo_ongoing_transfer transfer; - ongoing_transfer_pop(channel, &transfer); - - if (channel->last_desc_list == NULL) { - pr_err("Channel %d has ongoing transfers but no desc list\n", channel->index); - continue; - } - - clear_dirty_descs(channel, &transfer); - } - - channel->last_desc_list = NULL; - } - } -} - -void hailo_vdma_engine_push_timestamps(struct hailo_vdma_engine *engine, u32 bitmap) -{ - struct hailo_vdma_channel *channel = NULL; - u8 channel_index = 0; - - for_each_vdma_channel(engine, channel, channel_index) { - if (unlikely(hailo_test_bit(channel_index, &bitmap) && - channel->timestamp_measure_enabled)) { - hailo_vdma_push_timestamp(channel); - } - } -} - -int hailo_vdma_engine_read_timestamps(struct hailo_vdma_engine *engine, - struct hailo_vdma_interrupts_read_timestamp_params *params) -{ - struct hailo_vdma_channel *channel = NULL; - - if (params->channel_index >= MAX_VDMA_CHANNELS_PER_ENGINE) { - return -EINVAL; - } - - channel = &engine->channels[params->channel_index]; - hailo_vdma_pop_timestamps_to_response(channel, params); - return 0; -} - -void hailo_vdma_engine_clear_channel_interrupts(struct hailo_vdma_engine *engine, u32 bitmap) -{ - engine->interrupted_channels &= ~bitmap; -} - -void hailo_vdma_engine_set_channel_interrupts(struct hailo_vdma_engine *engine, u32 bitmap) -{ - engine->interrupted_channels |= bitmap; -} - -static void fill_channel_irq_data(struct hailo_vdma_interrupts_channel_data *irq_data, - struct hailo_vdma_engine *engine, struct hailo_vdma_channel *channel, u8 transfers_completed, - bool validation_success) -{ - u8 host_control = READ_BITS_AT_OFFSET(BYTE_SIZE * BITS_IN_BYTE, CHANNEL_CONTROL_OFFSET * BITS_IN_BYTE, ioread32(channel->host_regs)); - u8 device_control = READ_BITS_AT_OFFSET(BYTE_SIZE * BITS_IN_BYTE, CHANNEL_CONTROL_OFFSET * BITS_IN_BYTE, ioread32(channel->device_regs)); - - irq_data->engine_index = engine->index; - irq_data->channel_index = channel->index; - - irq_data->is_active = channel_control_reg_is_active(host_control) && - channel_control_reg_is_active(device_control); - - irq_data->transfers_completed = transfers_completed; - irq_data->host_error = READ_BITS_AT_OFFSET(BYTE_SIZE * BITS_IN_BYTE, 0, ioread32(channel->host_regs + CHANNEL_ERROR_OFFSET)); - irq_data->device_error = READ_BITS_AT_OFFSET(BYTE_SIZE * BITS_IN_BYTE, 0, ioread32(channel->device_regs + CHANNEL_ERROR_OFFSET)); - irq_data->validation_success = validation_success; -} - -static bool is_desc_between(u16 begin, u16 end, u16 desc) -{ - if (begin == end) { - // There is nothing between - return false; - } - if (begin < end) { - // desc needs to be in [begin, end) - return (begin <= desc) && (desc < end); - } - else { - // desc needs to be in [0, end) or [begin, m_descs.size()-1] - return (desc < end) || (begin <= desc); - } -} - -static bool is_transfer_complete(struct hailo_vdma_channel *channel, - struct hailo_ongoing_transfer *transfer, u16 hw_num_proc) -{ - if (channel->state.num_avail == hw_num_proc) { - return true; - } - - return is_desc_between(channel->state.num_proc, hw_num_proc, transfer->last_desc); -} - -int hailo_vdma_engine_fill_irq_data(struct hailo_vdma_interrupts_wait_params *irq_data, - struct hailo_vdma_engine *engine, u32 irq_channels_bitmap, - transfer_done_cb_t transfer_done, void *transfer_done_opaque) -{ - struct hailo_vdma_channel *channel = NULL; - u8 channel_index = 0; - bool validation_success = true; - - for_each_vdma_channel(engine, channel, channel_index) { - u8 transfers_completed = 0; - u16 hw_num_proc = U16_MAX; - - BUILD_BUG_ON_MSG(HAILO_VDMA_MAX_ONGOING_TRANSFERS >= U8_MAX, - "HAILO_VDMA_MAX_ONGOING_TRANSFERS must be less than U8_MAX to use transfers_completed as u8"); - - if (!hailo_test_bit(channel->index, &irq_channels_bitmap)) { - continue; - } - - if (channel->last_desc_list == NULL) { - // Channel not active or no transfer, skipping. - continue; - } - - if (irq_data->channels_count >= ARRAY_SIZE(irq_data->irq_data)) { - return -EINVAL; - } - - // Although the hw_num_processed should be a number between 0 and - // desc_count-1, if desc_count < 0x10000 (the maximum desc size), - // the actual hw_num_processed is a number between 1 and desc_count. - // Therefore the value can be desc_count, in this case we change it to - // zero. - hw_num_proc = hailo_vdma_get_num_proc(channel->host_regs) & channel->state.desc_count_mask; - - while (ONGOING_TRANSFERS_CIRC_CNT(channel->ongoing_transfers) > 0) { - struct hailo_ongoing_transfer *cur_transfer = - &channel->ongoing_transfers.transfers[channel->ongoing_transfers.tail]; - if (!is_transfer_complete(channel, cur_transfer, hw_num_proc)) { - break; - } - - if (cur_transfer->is_debug && - !validate_last_desc_status(channel, cur_transfer)) { - validation_success = false; - } - - clear_dirty_descs(channel, cur_transfer); - transfer_done(cur_transfer, transfer_done_opaque); - channel->state.num_proc = (u16)((cur_transfer->last_desc + 1) & channel->state.desc_count_mask); - - ongoing_transfer_pop(channel, NULL); - transfers_completed++; - } - - fill_channel_irq_data(&irq_data->irq_data[irq_data->channels_count], - engine, channel, transfers_completed, validation_success); - irq_data->channels_count++; - } - - return 0; -} - -// For all these functions - best way to optimize might be to not call the function when need to pause and then abort, -// Rather read value once and maybe save -// This function reads and writes the register - should try to make more optimized in future -static void start_vdma_control_register(u8 __iomem *host_regs) -{ - u32 host_regs_value = ioread32(host_regs); - iowrite32(WRITE_BITS_AT_OFFSET(BYTE_SIZE * BITS_IN_BYTE, CHANNEL_CONTROL_OFFSET * BITS_IN_BYTE, host_regs_value, - VDMA_CHANNEL_CONTROL_START_RESUME), host_regs); -} - -static void hailo_vdma_channel_pause(u8 __iomem *host_regs) -{ - u32 host_regs_value = ioread32(host_regs); - iowrite32(WRITE_BITS_AT_OFFSET(BYTE_SIZE * BITS_IN_BYTE, CHANNEL_CONTROL_OFFSET * BITS_IN_BYTE, host_regs_value, - VDMA_CHANNEL_CONTROL_START_PAUSE), host_regs); -} - -// This function reads and writes the register - should try to make more optimized in future -static void hailo_vdma_channel_abort(u8 __iomem *host_regs) -{ - u32 host_regs_value = ioread32(host_regs); - iowrite32(WRITE_BITS_AT_OFFSET(BYTE_SIZE * BITS_IN_BYTE, CHANNEL_CONTROL_OFFSET * BITS_IN_BYTE, host_regs_value, - VDMA_CHANNEL_CONTROL_ABORT), host_regs); -} - -int hailo_vdma_start_channel(u8 __iomem *regs, uint64_t desc_dma_address, uint32_t desc_count, - uint8_t data_id) -{ - u16 dma_address_l = 0; - u32 dma_address_h = 0; - u32 desc_depth_data_id = 0; - u8 desc_depth = ceil_log2(desc_count); - - if (((desc_dma_address & 0xFFFF) != 0) || - (desc_depth > DESCRIPTOR_LIST_MAX_DEPTH)) { - return -EINVAL; - } - - // According to spec, depth 16 is equivalent to depth 0. - if (DESCRIPTOR_LIST_MAX_DEPTH == desc_depth) { - desc_depth = 0; - } - - // Stop old channel state - hailo_vdma_stop_channel(regs); - - // Configure address, depth and id - dma_address_l = (uint16_t)((desc_dma_address >> 16) & 0xFFFF); - iowrite32(WRITE_BITS_AT_OFFSET(WORD_SIZE * BITS_IN_BYTE, (VDMA_CHANNEL__ADDRESS_L_OFFSET - - VDMA_CHANNEL__ALIGNED_ADDRESS_L_OFFSET) * BITS_IN_BYTE, ioread32(regs + - VDMA_CHANNEL__ALIGNED_ADDRESS_L_OFFSET), dma_address_l), regs + VDMA_CHANNEL__ALIGNED_ADDRESS_L_OFFSET); - - dma_address_h = (uint32_t)(desc_dma_address >> 32); - iowrite32(dma_address_h, regs + VDMA_CHANNEL__ADDRESS_H_OFFSET); - - desc_depth_data_id = (uint32_t)(desc_depth << VDMA_CHANNEL_DESC_DEPTH_SHIFT) | - (data_id << VDMA_CHANNEL_DATA_ID_SHIFT); - iowrite32(desc_depth_data_id, regs); - - start_vdma_control_register(regs); - - return 0; -} - -static bool hailo_vdma_channel_is_idle(u8 __iomem *host_regs, size_t host_side_max_desc_count) -{ - // Num processed and ongoing are next to each other in the memory. - // Reading them both in order to save BAR reads. - u32 host_side_num_processed_ongoing = ioread32(host_regs + CHANNEL_NUM_PROC_OFFSET); - u16 host_side_num_processed = (host_side_num_processed_ongoing & VDMA_CHANNEL_NUM_PROCESSED_MASK); - u16 host_side_num_ongoing = (host_side_num_processed_ongoing >> VDMA_CHANNEL_NUM_PROCESSED_WIDTH) & - VDMA_CHANNEL_NUM_ONGOING_MASK; - - if ((host_side_num_processed % host_side_max_desc_count) == (host_side_num_ongoing % host_side_max_desc_count)) { - return true; - } - - return false; -} - -static int hailo_vdma_wait_until_channel_idle(u8 __iomem *host_regs) -{ - bool is_idle = false; - uint32_t check_counter = 0; - - u8 depth = (uint8_t)(READ_BITS_AT_OFFSET(VDMA_CHANNEL_DESC_DEPTH_WIDTH, VDMA_CHANNEL_DESC_DEPTH_SHIFT, - ioread32(host_regs))); - size_t host_side_max_desc_count = (size_t)(1 << depth); - - for (check_counter = 0; check_counter < VDMA_CHANNEL__MAX_CHECKS_CHANNEL_IS_IDLE; check_counter++) { - is_idle = hailo_vdma_channel_is_idle(host_regs, host_side_max_desc_count); - if (is_idle) { - return 0; - } - } - - return -ETIMEDOUT; -} - -void hailo_vdma_stop_channel(u8 __iomem *regs) -{ - int err = 0; - u8 host_side_channel_regs = READ_BITS_AT_OFFSET(BYTE_SIZE * BITS_IN_BYTE, CHANNEL_CONTROL_OFFSET * BITS_IN_BYTE, ioread32(regs)); - - if ((host_side_channel_regs & VDMA_CHANNEL_CONTROL_START_ABORT_PAUSE_RESUME_BITMASK) == VDMA_CHANNEL_CONTROL_ABORT_PAUSE) { - // The channel is aborted (we set the channel to VDMA_CHANNEL_CONTROL_ABORT_PAUSE at the end of this function) - return; - } - - // Pause the channel - // The channel is paused to allow for "all transfers from fetched descriptors..." to be "...completed" - // (from PLDA PCIe refernce manual, "9.2.5 Starting a Channel and Transferring Data") - hailo_vdma_channel_pause(regs); - - // Even if channel is stuck and not idle, force abort and return error in the end - err = hailo_vdma_wait_until_channel_idle(regs); - // Success oriented - if error occured print error but still abort channel - if (err < 0) { - pr_err("Timeout occured while waiting for channel to become idle\n"); - } - - // Abort the channel (even of hailo_vdma_wait_until_channel_idle function fails) - hailo_vdma_channel_abort(regs); -} - -bool hailo_check_channel_index(u8 channel_index, u32 src_channels_bitmask, bool is_input_channel) -{ - return is_input_channel ? hailo_test_bit(channel_index, &src_channels_bitmask) : - (!hailo_test_bit(channel_index, &src_channels_bitmask)); -} \ No newline at end of file diff --git a/drivers/media/pci/hailo/common/vdma_common.h b/drivers/media/pci/hailo/common/vdma_common.h deleted file mode 100644 index 9176543b085c36..00000000000000 --- a/drivers/media/pci/hailo/common/vdma_common.h +++ /dev/null @@ -1,284 +0,0 @@ -// SPDX-License-Identifier: MIT -/** - * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. - **/ - -#ifndef _HAILO_COMMON_VDMA_COMMON_H_ -#define _HAILO_COMMON_VDMA_COMMON_H_ - -#include "hailo_resource.h" -#include "utils.h" - -#include -#include -#include - -#define VDMA_DESCRIPTOR_LIST_ALIGN (1 << 16) -#define INVALID_VDMA_ADDRESS (0) - -#define CHANNEL_BASE_OFFSET(channel_index) ((channel_index) << 5) - -#define CHANNEL_CONTROL_OFFSET (0x0) -#define CHANNEL_DEPTH_ID_OFFSET (0x1) -#define CHANNEL_NUM_AVAIL_OFFSET (0x2) -#define CHANNEL_NUM_PROC_OFFSET (0x4) -#define CHANNEL_ERROR_OFFSET (0x8) -#define CHANNEL_DEST_REGS_OFFSET (0x10) - -#ifdef __cplusplus -extern "C" -{ -#endif - -struct hailo_vdma_descriptor { - u32 PageSize_DescControl; - u32 AddrL_rsvd_DataID; - u32 AddrH; - u32 RemainingPageSize_Status; -}; - -struct hailo_vdma_descriptors_list { - struct hailo_vdma_descriptor *desc_list; - // Must be power of 2 if is_circular is set. - u32 desc_count; - // The nearest power of 2 to desc_count (including desc_count), minus 1. - // * If the list is circular, then 'index & desc_count_mask' can be used instead of modulo. - // * Otherwise, we can't wrap around the list anyway. However, for any index < desc_count, 'index & desc_count_mask' - // will return the same value. - u32 desc_count_mask; - u16 desc_page_size; - bool is_circular; -}; - -struct hailo_channel_interrupt_timestamp_list { - int head; - int tail; - struct hailo_channel_interrupt_timestamp timestamps[CHANNEL_IRQ_TIMESTAMPS_SIZE]; -}; - - -// For each buffers in transfer, the last descriptor will be programmed with -// the residue size. In addition, if configured, the first descriptor (in -// all transfer) may be programmed with interrupts. -#define MAX_DIRTY_DESCRIPTORS_PER_TRANSFER \ - (HAILO_MAX_BUFFERS_PER_SINGLE_TRANSFER + 1) - -struct hailo_vdma_mapped_transfer_buffer { - struct sg_table *sg_table; - u32 size; - u32 offset; - void *opaque; // Drivers can set any opaque data here. -}; - -struct hailo_ongoing_transfer { - uint16_t last_desc; - - u8 buffers_count; - struct hailo_vdma_mapped_transfer_buffer buffers[HAILO_MAX_BUFFERS_PER_SINGLE_TRANSFER]; - - // Contains all descriptors that were programmed with non-default values - // for the transfer (by non-default we mean - different size or different - // interrupts domain). - uint8_t dirty_descs_count; - uint16_t dirty_descs[MAX_DIRTY_DESCRIPTORS_PER_TRANSFER]; - - // If set, validate descriptors status on transfer completion. - bool is_debug; -}; - -struct hailo_ongoing_transfers_list { - unsigned long head; - unsigned long tail; - struct hailo_ongoing_transfer transfers[HAILO_VDMA_MAX_ONGOING_TRANSFERS]; -}; - -struct hailo_vdma_channel_state { - // vdma channel counters. num_avail should be synchronized with the hw - // num_avail value. num_proc is the last num proc updated when the user - // reads interrupts. - u16 num_avail; - u16 num_proc; - - // Mask of the num-avail/num-proc counters. - u32 desc_count_mask; -}; - -struct hailo_vdma_channel { - u8 index; - - u8 __iomem *host_regs; - u8 __iomem *device_regs; - - // Last descriptors list attached to the channel. When it changes, - // assumes that the channel got reset. - struct hailo_vdma_descriptors_list *last_desc_list; - - struct hailo_vdma_channel_state state; - struct hailo_ongoing_transfers_list ongoing_transfers; - - bool timestamp_measure_enabled; - struct hailo_channel_interrupt_timestamp_list timestamp_list; -}; - -struct hailo_vdma_engine { - u8 index; - u32 enabled_channels; - u32 interrupted_channels; - struct hailo_vdma_channel channels[MAX_VDMA_CHANNELS_PER_ENGINE]; -}; - -struct hailo_vdma_hw_ops { - // Accepts start, end and step of an address range (of type dma_addr_t). - // Returns the encoded base address or INVALID_VDMA_ADDRESS if the range/step is invalid. - // All addresses in the range of [returned_addr, returned_addr + step, returned_addr + 2*step, ..., dma_address_end) are valid. - u64 (*encode_desc_dma_address_range)(dma_addr_t dma_address_start, dma_addr_t dma_address_end, u32 step, u8 channel_id); -}; - -struct hailo_vdma_hw { - struct hailo_vdma_hw_ops hw_ops; - - // The data_id code of ddr addresses. - u8 ddr_data_id; - - // Bitmask needed to set on each descriptor to enable interrupts (either host/device). - unsigned long host_interrupts_bitmask; - unsigned long device_interrupts_bitmask; - - // Bitmask for each vdma hw, which channels are src side by index (on pcie/dram - 0x0000FFFF, pci ep - 0xFFFF0000) - u32 src_channels_bitmask; -}; - -#define _for_each_element_array(array, size, element, index) \ - for (index = 0, element = &array[index]; index < size; index++, element = &array[index]) - -#define for_each_vdma_channel(engine, channel, channel_index) \ - _for_each_element_array((engine)->channels, MAX_VDMA_CHANNELS_PER_ENGINE, \ - channel, channel_index) - -/** - * Program the given descriptors list to map the given buffer. - * - * @param vdma_hw vdma hw object - * @param desc_list descriptors list object to program - * @param starting_desc index of the first descriptor to program. If the list - * is circular, this function may wrap around the list. - * @param buffer buffer to program to the descriptors list. - * @param should_bind If false, assumes the buffer was already bound to the - * desc list. Used for optimization. - * @param channel_index channel index of the channel attached. - * @param last_desc_interrupts - interrupts settings on last descriptor. - * @param is_debug program descriptors for debug run. - * - * @return On success - the amount of descriptors programmed, negative value on error. - */ -int hailo_vdma_program_descriptors_list( - struct hailo_vdma_hw *vdma_hw, - struct hailo_vdma_descriptors_list *desc_list, - u32 starting_desc, - struct hailo_vdma_mapped_transfer_buffer *buffer, - bool should_bind, - u8 channel_index, - enum hailo_vdma_interrupts_domain last_desc_interrupts, - bool is_debug); - -int hailo_vdma_program_descriptors_in_chunk( - struct hailo_vdma_hw *vdma_hw, - dma_addr_t chunk_addr, - unsigned int chunk_size, - struct hailo_vdma_descriptors_list *desc_list, - u32 desc_index, - u32 max_desc_index, - u8 channel_index, - u8 data_id); - -void hailo_vdma_set_num_avail(u8 __iomem *regs, u16 num_avail); - -u16 hailo_vdma_get_num_proc(u8 __iomem *regs); - -/** - * Launch a transfer on some vdma channel. Includes: - * 1. Binding the transfer buffers to the descriptors list. - * 2. Program the descriptors list. - * 3. Increase num available - * - * @param vdma_hw vdma hw object - * @param channel vdma channel object. - * @param desc_list descriptors list object to program. - * @param starting_desc index of the first descriptor to program. - * @param buffers_count amount of transfer mapped buffers to program. - * @param buffers array of buffers to program to the descriptors list. - * @param should_bind whether to bind the buffer to the descriptors list. - * @param first_interrupts_domain - interrupts settings on first descriptor. - * @param last_desc_interrupts - interrupts settings on last descriptor. - * @param is_debug program descriptors for debug run, adds some overhead (for - * example, hw will write desc complete status). - * - * @return On success - the amount of descriptors programmed, negative value on error. - */ -int hailo_vdma_launch_transfer( - struct hailo_vdma_hw *vdma_hw, - struct hailo_vdma_channel *channel, - struct hailo_vdma_descriptors_list *desc_list, - u32 starting_desc, - u8 buffers_count, - struct hailo_vdma_mapped_transfer_buffer *buffers, - bool should_bind, - enum hailo_vdma_interrupts_domain first_interrupts_domain, - enum hailo_vdma_interrupts_domain last_desc_interrupts, - bool is_debug); - -void hailo_vdma_engine_init(struct hailo_vdma_engine *engine, u8 engine_index, - const struct hailo_resource *channel_registers, u32 src_channels_bitmask); - -void hailo_vdma_engine_enable_channels(struct hailo_vdma_engine *engine, u32 bitmap, - bool measure_timestamp); - -void hailo_vdma_engine_disable_channels(struct hailo_vdma_engine *engine, u32 bitmap); - -void hailo_vdma_engine_push_timestamps(struct hailo_vdma_engine *engine, u32 bitmap); -int hailo_vdma_engine_read_timestamps(struct hailo_vdma_engine *engine, - struct hailo_vdma_interrupts_read_timestamp_params *params); - -static inline bool hailo_vdma_engine_got_interrupt(struct hailo_vdma_engine *engine, - u32 channels_bitmap) -{ - // Reading interrupts without lock is ok (needed only for writes) - const bool any_interrupt = (0 != (channels_bitmap & engine->interrupted_channels)); - const bool any_disabled = (channels_bitmap != (channels_bitmap & engine->enabled_channels)); - return (any_disabled || any_interrupt); -} - -// Set/Clear/Read channels interrupts, must called under some lock (driver specific) -void hailo_vdma_engine_clear_channel_interrupts(struct hailo_vdma_engine *engine, u32 bitmap); -void hailo_vdma_engine_set_channel_interrupts(struct hailo_vdma_engine *engine, u32 bitmap); - -static inline u32 hailo_vdma_engine_read_interrupts(struct hailo_vdma_engine *engine, - u32 requested_bitmap) -{ - // Interrupts only for channels that are requested and enabled. - u32 irq_channels_bitmap = requested_bitmap & - engine->enabled_channels & - engine->interrupted_channels; - engine->interrupted_channels &= ~irq_channels_bitmap; - - return irq_channels_bitmap; -} - -typedef void(*transfer_done_cb_t)(struct hailo_ongoing_transfer *transfer, void *opaque); - -// Assuming irq_data->channels_count contains the amount of channels already -// written (used for multiple engines). -int hailo_vdma_engine_fill_irq_data(struct hailo_vdma_interrupts_wait_params *irq_data, - struct hailo_vdma_engine *engine, u32 irq_channels_bitmap, - transfer_done_cb_t transfer_done, void *transfer_done_opaque); - -int hailo_vdma_start_channel(u8 __iomem *regs, uint64_t desc_dma_address, uint32_t desc_count, uint8_t data_id); - -void hailo_vdma_stop_channel(u8 __iomem *regs); - -bool hailo_check_channel_index(u8 channel_index, u32 src_channels_bitmask, bool is_input_channel); - -#ifdef __cplusplus -} -#endif -#endif /* _HAILO_COMMON_VDMA_COMMON_H_ */ diff --git a/drivers/media/pci/hailo/include/hailo_pcie_version.h b/drivers/media/pci/hailo/include/hailo_pcie_version.h deleted file mode 100755 index 936bd7d4a477ff..00000000000000 --- a/drivers/media/pci/hailo/include/hailo_pcie_version.h +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/** - * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. - **/ - -#ifndef _HAILO_PCIE_VERSION_H_ -#define _HAILO_PCIE_VERSION_H_ - -#include -#include "../common/hailo_pcie_version.h" - -#define HAILO_DRV_VER __stringify(HAILO_DRV_VER_MAJOR) "." __stringify(HAILO_DRV_VER_MINOR) "." __stringify(HAILO_DRV_VER_REVISION) - -#endif /* _HAILO_PCIE_VERSION_H_ */ diff --git a/drivers/media/pci/hailo/src/fops.c b/drivers/media/pci/hailo/src/fops.c deleted file mode 100644 index 3b1cba647be729..00000000000000 --- a/drivers/media/pci/hailo/src/fops.c +++ /dev/null @@ -1,570 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/** - * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. - **/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) -#include -#endif - -#include "fops.h" -#include "vdma_common.h" -#include "utils/logs.h" -#include "vdma/memory.h" -#include "vdma/ioctl.h" -#include "utils/compact.h" -#include "nnc.h" -#include "soc.h" - - -#if LINUX_VERSION_CODE >= KERNEL_VERSION( 4, 13, 0 ) -#define wait_queue_t wait_queue_entry_t -#endif - -#if LINUX_VERSION_CODE >= KERNEL_VERSION( 4, 15, 0 ) -#define ACCESS_ONCE READ_ONCE -#endif - -#ifndef VM_RESERVED - #define VMEM_FLAGS (VM_IO | VM_DONTEXPAND | VM_DONTDUMP) -#else - #define VMEM_FLAGS (VM_IO | VM_RESERVED) -#endif - -#define IS_PO2_ALIGNED(size, alignment) (!(size & (alignment-1))) - -// On pcie driver there is only one dma engine -#define DEFAULT_VDMA_ENGINE_INDEX (0) - - -static struct hailo_file_context *create_file_context(struct hailo_pcie_board *board, struct file *filp) -{ - struct hailo_file_context *context = kzalloc(sizeof(*context), GFP_KERNEL); - if (!context) { - hailo_err(board, "Failed to alloc file context (required size %zu)\n", sizeof(*context)); - return ERR_PTR(-ENOMEM); - } - - context->filp = filp; - hailo_vdma_file_context_init(&context->vdma_context); - list_add(&context->open_files_list, &board->open_files_list); - context->is_valid = true; - return context; -} - -static void release_file_context(struct hailo_file_context *context) -{ - context->is_valid = false; - list_del(&context->open_files_list); - kfree(context); -} - -static struct hailo_file_context *find_file_context(struct hailo_pcie_board *board, struct file *filp) -{ - struct hailo_file_context *cur = NULL; - list_for_each_entry(cur, &board->open_files_list, open_files_list) { - if (cur->filp == filp) { - return cur; - } - } - return NULL; -} - -int hailo_pcie_fops_open(struct inode *inode, struct file *filp) -{ - u32 major = MAJOR(inode->i_rdev); - u32 minor = MINOR(inode->i_rdev); - struct hailo_pcie_board *pBoard; - int err = 0; - pci_power_t previous_power_state = PCI_UNKNOWN; - bool interrupts_enabled_by_filp = false; - struct hailo_file_context *context = NULL; - - pr_debug(DRIVER_NAME ": (%d: %d-%d): fops_open\n", current->tgid, major, minor); - - // allow multiple processes to open a device, count references in hailo_pcie_get_board_index. - if (!(pBoard = hailo_pcie_get_board_index(minor))) { - pr_err(DRIVER_NAME ": fops_open: PCIe board not found for /dev/hailo%d node.\n", minor); - err = -ENODEV; - goto l_exit; - } - - filp->private_data = pBoard; - - if (down_interruptible(&pBoard->mutex)) { - hailo_err(pBoard, "fops_open down_interruptible fail tgid:%d\n", current->tgid); - err = -ERESTARTSYS; - goto l_decrease_ref_count; - } - - context = create_file_context(pBoard, filp); - if (IS_ERR(context)) { - err = PTR_ERR(context); - goto l_release_mutex; - } - - previous_power_state = pBoard->pDev->current_state; - if (PCI_D0 != previous_power_state) { - hailo_info(pBoard, "Waking up board change state from %d to PCI_D0\n", previous_power_state); - err = pci_set_power_state(pBoard->pDev, PCI_D0); - if (err < 0) { - hailo_err(pBoard, "Failed waking up board %d", err); - goto l_free_context; - } - } - - if (!hailo_pcie_is_device_connected(&pBoard->pcie_resources)) { - hailo_err(pBoard, "Device disconnected while opening device\n"); - err = -ENXIO; - goto l_revert_power_state; - } - - // enable interrupts - if (!pBoard->interrupts_enabled) { - err = hailo_enable_interrupts(pBoard); - if (err < 0) { - hailo_err(pBoard, "Failed Enabling interrupts %d\n", err); - goto l_revert_power_state; - } - interrupts_enabled_by_filp = true; - } - - if (pBoard->pcie_resources.accelerator_type == HAILO_ACCELERATOR_TYPE_NNC) { - err = hailo_nnc_file_context_init(pBoard, context); - } else { - err = hailo_soc_file_context_init(pBoard, context); - } - if (err < 0) { - goto l_release_irq; - } - - hailo_dbg(pBoard, "(%d: %d-%d): fops_open: SUCCESS on /dev/hailo%d\n", current->tgid, - major, minor, minor); - - up(&pBoard->mutex); - return 0; - -l_release_irq: - if (interrupts_enabled_by_filp) { - hailo_disable_interrupts(pBoard); - } - -l_revert_power_state: - if (pBoard->pDev->current_state != previous_power_state) { - hailo_info(pBoard, "Power changing state from %d to %d\n", previous_power_state, pBoard->pDev->current_state); - if (pci_set_power_state(pBoard->pDev, previous_power_state) < 0) { - hailo_err(pBoard, "Failed setting power state back to %d\n", (int)previous_power_state); - } - } -l_free_context: - release_file_context(context); -l_release_mutex: - up(&pBoard->mutex); -l_decrease_ref_count: - atomic_dec(&pBoard->ref_count); -l_exit: - return err; -} - -int hailo_pcie_fops_release(struct inode *inode, struct file *filp) -{ - struct hailo_pcie_board *board = (struct hailo_pcie_board *)filp->private_data; - struct hailo_file_context *context = NULL; - - u32 major = MAJOR(inode->i_rdev); - u32 minor = MINOR(inode->i_rdev); - - if (board) { - hailo_info(board, "(%d: %d-%d): fops_release\n", current->tgid, major, minor); - - - down(&board->mutex); - - context = find_file_context(board, filp); - if (NULL == context) { - hailo_err(board, "Invalid driver state, file context does not exist\n"); - up(&board->mutex); - return -EINVAL; - } - - if (false == context->is_valid) { - // File context is invalid, but open. It's OK to continue finalize and release it. - hailo_err(board, "Invalid file context\n"); - } - - if (board->pcie_resources.accelerator_type == HAILO_ACCELERATOR_TYPE_NNC) { - hailo_nnc_file_context_finalize(board, context); - } else { - hailo_soc_file_context_finalize(board, context); - } - - hailo_vdma_file_context_finalize(&context->vdma_context, &board->vdma, filp); - release_file_context(context); - - if (atomic_dec_and_test(&board->ref_count)) { - // Disable interrupts - hailo_disable_interrupts(board); - - if (power_mode_enabled()) { - hailo_info(board, "Power change state to PCI_D3hot\n"); - if (board->pDev && pci_set_power_state(board->pDev, PCI_D3hot) < 0) { - hailo_err(board, "Failed setting power state to D3hot"); - } - } - - // deallocate board if already removed - if (!board->pDev) { - hailo_dbg(board, "fops_release, freed board\n"); - up(&board->mutex); - kfree(board); - board = NULL; - } else { - hailo_dbg(board, "fops_release, released resources for board\n"); - up(&board->mutex); - } - } else { - up(&board->mutex); - } - - hailo_dbg(board, "(%d: %d-%d): fops_release: SUCCESS on /dev/hailo%d\n", current->tgid, - major, minor, minor); - } - - return 0; -} - -static long hailo_memory_transfer_ioctl(struct hailo_pcie_board *board, unsigned long arg) -{ - long err = 0; - struct hailo_memory_transfer_params* transfer = &board->memory_transfer_params; - - hailo_dbg(board, "Start memory transfer ioctl\n"); - - if (copy_from_user(transfer, (void __user*)arg, sizeof(*transfer))) { - hailo_err(board, "copy_from_user fail\n"); - return -ENOMEM; - } - - err = hailo_pcie_memory_transfer(&board->pcie_resources, transfer); - if (err < 0) { - hailo_err(board, "memory transfer failed %ld", err); - } - - if (copy_to_user((void __user*)arg, transfer, sizeof(*transfer))) { - hailo_err(board, "copy_to_user fail\n"); - return -ENOMEM; - } - - return err; -} - -static void firmware_notification_irq_handler(struct hailo_pcie_board *board) -{ - struct hailo_notification_wait *notif_wait_cursor = NULL; - int err = 0; - unsigned long irq_saved_flags = 0; - - spin_lock_irqsave(&board->nnc.notification_read_spinlock, irq_saved_flags); - err = hailo_pcie_read_firmware_notification(&board->pcie_resources.fw_access, &board->nnc.notification_cache); - spin_unlock_irqrestore(&board->nnc.notification_read_spinlock, irq_saved_flags); - - if (err < 0) { - hailo_err(board, "Failed reading firmware notification"); - } - else { - // TODO: HRT-14502 move interrupt handling to nnc - rcu_read_lock(); - list_for_each_entry_rcu(notif_wait_cursor, &board->nnc.notification_wait_list, notification_wait_list) - { - complete(¬if_wait_cursor->notification_completion); - } - rcu_read_unlock(); - } -} - -static void boot_irq_handler(struct hailo_pcie_board *board, struct hailo_pcie_interrupt_source *irq_source) -{ - if (irq_source->sw_interrupts & HAILO_PCIE_BOOT_SOFT_RESET_IRQ) { - hailo_dbg(board, "soft reset trigger IRQ\n"); - complete(&board->soft_reset.reset_completed); - } - if (irq_source->sw_interrupts & HAILO_PCIE_BOOT_IRQ) { - hailo_dbg(board, "boot trigger IRQ\n"); - complete_all(&board->fw_boot.fw_loaded_completion); - } else { - board->fw_boot.boot_used_channel_bitmap &= ~irq_source->vdma_channels_bitmap; - hailo_dbg(board, "boot vDMA data IRQ - channel_bitmap = 0x%x\n", irq_source->vdma_channels_bitmap); - if (0 == board->fw_boot.boot_used_channel_bitmap) { - complete_all(&board->fw_boot.vdma_boot_completion); - hailo_dbg(board, "boot vDMA data trigger IRQ\n"); - } - } -} - -static void nnc_irq_handler(struct hailo_pcie_board *board, struct hailo_pcie_interrupt_source *irq_source) -{ - if (irq_source->sw_interrupts & HAILO_PCIE_NNC_FW_CONTROL_IRQ) { - complete(&board->nnc.fw_control.completion); - } - - if (irq_source->sw_interrupts & HAILO_PCIE_NNC_DRIVER_DOWN_IRQ) { - complete(&board->driver_down.reset_completed); - } - - if (irq_source->sw_interrupts & HAILO_PCIE_NNC_FW_NOTIFICATION_IRQ) { - firmware_notification_irq_handler(board); - } -} - -static void soc_irq_handler(struct hailo_pcie_board *board, struct hailo_pcie_interrupt_source *irq_source) -{ - if (irq_source->sw_interrupts & HAILO_PCIE_SOC_CONTROL_IRQ) { - complete_all(&board->soc.control_resp_ready); - } - - if (irq_source->sw_interrupts & HAILO_PCIE_SOC_CLOSE_IRQ) { - hailo_info(board, "soc_irq_handler - HAILO_PCIE_SOC_CLOSE_IRQ\n"); - // always use bitmap=0xFFFFFFFF - it is ok to wake all interrupts since each handler will check if the stream was aborted or not. - hailo_vdma_wakeup_interrupts(&board->vdma, &board->vdma.vdma_engines[DEFAULT_VDMA_ENGINE_INDEX], - 0xFFFFFFFF); - } -} - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) -irqreturn_t hailo_irqhandler(int irq, void *dev_id, struct pt_regs *regs) -#else -irqreturn_t hailo_irqhandler(int irq, void *dev_id) -#endif -{ - irqreturn_t return_value = IRQ_NONE; - struct hailo_pcie_board *board = (struct hailo_pcie_board *)dev_id; - bool got_interrupt = false; - struct hailo_pcie_interrupt_source irq_source = {0}; - - hailo_dbg(board, "hailo_irqhandler\n"); - - while (true) { - if (!hailo_pcie_is_device_connected(&board->pcie_resources)) { - hailo_err(board, "Device disconnected while handling irq\n"); - break; - } - - got_interrupt = hailo_pcie_read_interrupt(&board->pcie_resources, &irq_source); - if (!got_interrupt) { - break; - } - - return_value = IRQ_HANDLED; - - if (board->fw_boot.is_in_boot) { - boot_irq_handler(board, &irq_source); - } else { - if (HAILO_ACCELERATOR_TYPE_NNC == board->pcie_resources.accelerator_type) { - nnc_irq_handler(board, &irq_source); - } else if (HAILO_ACCELERATOR_TYPE_SOC == board->pcie_resources.accelerator_type) { - soc_irq_handler(board, &irq_source); - } else { - hailo_err(board, "Invalid accelerator type %d\n", board->pcie_resources.accelerator_type); - } - - if (0 != irq_source.vdma_channels_bitmap) { - hailo_vdma_irq_handler(&board->vdma, DEFAULT_VDMA_ENGINE_INDEX, - irq_source.vdma_channels_bitmap); - } - } - } - - return return_value; -} - -static long hailo_query_device_properties(struct hailo_pcie_board *board, unsigned long arg) -{ - struct hailo_device_properties props = { - .desc_max_page_size = board->desc_max_page_size, - .board_type = board->pcie_resources.board_type, - .allocation_mode = board->allocation_mode, - .dma_type = HAILO_DMA_TYPE_PCIE, - .dma_engines_count = board->vdma.vdma_engines_count, - .is_fw_loaded = hailo_pcie_is_firmware_loaded(&board->pcie_resources), - }; - - hailo_info(board, "HAILO_QUERY_DEVICE_PROPERTIES: desc_max_page_size=%u\n", props.desc_max_page_size); - - if (copy_to_user((void __user*)arg, &props, sizeof(props))) { - hailo_err(board, "HAILO_QUERY_DEVICE_PROPERTIES, copy_to_user failed\n"); - return -ENOMEM; - } - - return 0; -} - -static long hailo_query_driver_info(struct hailo_pcie_board *board, unsigned long arg) -{ - struct hailo_driver_info info = { - .major_version = HAILO_DRV_VER_MAJOR, - .minor_version = HAILO_DRV_VER_MINOR, - .revision_version = HAILO_DRV_VER_REVISION - }; - - hailo_info(board, "HAILO_QUERY_DRIVER_INFO: major=%u, minor=%u, revision=%u\n", - info.major_version, info.minor_version, info.revision_version); - - if (copy_to_user((void __user*)arg, &info, sizeof(info))) { - hailo_err(board, "HAILO_QUERY_DRIVER_INFO, copy_to_user failed\n"); - return -ENOMEM; - } - - return 0; -} - -static long hailo_general_ioctl(struct hailo_pcie_board *board, unsigned int cmd, unsigned long arg) -{ - switch (cmd) { - case HAILO_MEMORY_TRANSFER: - return hailo_memory_transfer_ioctl(board, arg); - case HAILO_QUERY_DEVICE_PROPERTIES: - return hailo_query_device_properties(board, arg); - case HAILO_QUERY_DRIVER_INFO: - return hailo_query_driver_info(board, arg); - default: - hailo_err(board, "Invalid general ioctl code 0x%x (nr: %d)\n", cmd, _IOC_NR(cmd)); - return -ENOTTY; - } -} - -long hailo_pcie_fops_unlockedioctl(struct file* filp, unsigned int cmd, unsigned long arg) -{ - long err = 0; - struct hailo_pcie_board* board = (struct hailo_pcie_board*) filp->private_data; - struct hailo_file_context *context = NULL; - bool should_up_board_mutex = true; - - - if (!board || !board->pDev) return -ENODEV; - - hailo_dbg(board, "(%d): fops_unlockedioctl. cmd:%d\n", current->tgid, _IOC_NR(cmd)); - - if (_IOC_DIR(cmd) & _IOC_READ) - { - err = !compatible_access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd)); - } - else if (_IOC_DIR(cmd) & _IOC_WRITE) - { - err = !compatible_access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd)); - } - - if (err) { - hailo_err(board, "Invalid ioctl parameter access 0x%x", cmd); - return -EFAULT; - } - - if (down_interruptible(&board->mutex)) { - hailo_err(board, "unlockedioctl down_interruptible failed"); - return -ERESTARTSYS; - } - BUG_ON(board->mutex.count != 0); - - context = find_file_context(board, filp); - if (NULL == context) { - hailo_err(board, "Invalid driver state, file context does not exist\n"); - up(&board->mutex); - return -EINVAL; - } - - if (false == context->is_valid) { - hailo_err(board, "Invalid file context\n"); - up(&board->mutex); - return -EINVAL; - } - - switch (_IOC_TYPE(cmd)) { - case HAILO_GENERAL_IOCTL_MAGIC: - err = hailo_general_ioctl(board, cmd, arg); - break; - case HAILO_VDMA_IOCTL_MAGIC: - err = hailo_vdma_ioctl(&context->vdma_context, &board->vdma, cmd, arg, filp, &board->mutex, - &should_up_board_mutex); - break; - case HAILO_SOC_IOCTL_MAGIC: - if (HAILO_ACCELERATOR_TYPE_SOC != board->pcie_resources.accelerator_type) { - hailo_err(board, "Ioctl %d is not supported on this accelerator type\n", _IOC_TYPE(cmd)); - err = -EINVAL; - } else { - err = hailo_soc_ioctl(board, context, &board->vdma, cmd, arg); - } - break; - case HAILO_NNC_IOCTL_MAGIC: - if (HAILO_ACCELERATOR_TYPE_NNC != board->pcie_resources.accelerator_type) { - hailo_err(board, "Ioctl %d is not supported on this accelerator type\n", _IOC_TYPE(cmd)); - err = -EINVAL; - } else { - err = hailo_nnc_ioctl(board, cmd, arg, filp, &should_up_board_mutex); - } - break; - default: - hailo_err(board, "Invalid ioctl type %d\n", _IOC_TYPE(cmd)); - err = -ENOTTY; - } - - if (should_up_board_mutex) { - up(&board->mutex); - } - - hailo_dbg(board, "(%d): fops_unlockedioct: SUCCESS\n", current->tgid); - return err; - -} - -int hailo_pcie_fops_mmap(struct file* filp, struct vm_area_struct *vma) -{ - int err = 0; - - uintptr_t vdma_handle = vma->vm_pgoff << PAGE_SHIFT; - - struct hailo_pcie_board* board = (struct hailo_pcie_board*)filp->private_data; - struct hailo_file_context *context = NULL; - - BUILD_BUG_ON_MSG(sizeof(vma->vm_pgoff) < sizeof(vdma_handle), - "If this expression fails to compile it means the target HW is not compatible with our approach to use " - "the page offset paramter of 'mmap' to pass the driver the 'handle' of the desired descriptor"); - - vma->vm_pgoff = 0; // vm_pgoff contains vdma_handle page offset, the actual offset from the phys addr is 0 - - hailo_info(board, "%d fops_mmap\n", current->tgid); - - if (!board || !board->pDev) return -ENODEV; - - if (down_interruptible(&board->mutex)) { - hailo_err(board, "hailo_pcie_fops_mmap down_interruptible fail tgid:%d\n", current->tgid); - return -ERESTARTSYS; - } - - context = find_file_context(board, filp); - if (NULL == context) { - up(&board->mutex); - hailo_err(board, "Invalid driver state, file context does not exist\n"); - return -EINVAL; - } - - if (false == context->is_valid) { - up(&board->mutex); - hailo_err(board, "Invalid file context\n"); - return -EINVAL; - } - - err = hailo_vdma_mmap(&context->vdma_context, &board->vdma, vma, vdma_handle); - up(&board->mutex); - return err; -} diff --git a/drivers/media/pci/hailo/src/fops.h b/drivers/media/pci/hailo/src/fops.h deleted file mode 100644 index 9b940249b1a7a6..00000000000000 --- a/drivers/media/pci/hailo/src/fops.h +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/** - * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. - **/ - -#ifndef _HAILO_PCI_FOPS_H_ -#define _HAILO_PCI_FOPS_H_ - -#include "pcie.h" - -int hailo_pcie_fops_open(struct inode* inode, struct file* filp); -int hailo_pcie_fops_release(struct inode* inode, struct file* filp); -long hailo_pcie_fops_unlockedioctl(struct file* filp, unsigned int cmd, unsigned long arg); -int hailo_pcie_fops_mmap(struct file* filp, struct vm_area_struct *vma); -void hailo_pcie_ep_init(struct hailo_pcie_board *board); - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) -irqreturn_t hailo_irqhandler(int irq, void* dev_id, struct pt_regs *regs); -#else -irqreturn_t hailo_irqhandler(int irq, void* dev_id); -#endif - -#endif /* _HAILO_PCI_FOPS_H_ */ diff --git a/drivers/media/pci/hailo/src/nnc.c b/drivers/media/pci/hailo/src/nnc.c deleted file mode 100644 index 10faafcb415f60..00000000000000 --- a/drivers/media/pci/hailo/src/nnc.c +++ /dev/null @@ -1,299 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/** - * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. - **/ -/** - * A Hailo PCIe NNC device is a device contains a NNC (neural network core) and some basic FW. - * The device supports sending controls, receiving notification and reading the FW log. - */ - -#include "nnc.h" -#include "hailo_ioctl_common.h" - -#include "utils/logs.h" -#include "utils/compact.h" - -#include - -#if !defined(HAILO_EMULATOR) -#define DEFAULT_SHUTDOWN_TIMEOUT_MS (5) -#else /* !defined(HAILO_EMULATOR) */ -#define DEFAULT_SHUTDOWN_TIMEOUT_MS (1000) -#endif /* !defined(HAILO_EMULATOR) */ - -void hailo_nnc_init(struct hailo_pcie_nnc *nnc) -{ - sema_init(&nnc->fw_control.mutex, 1); - spin_lock_init(&nnc->notification_read_spinlock); - init_completion(&nnc->fw_control.completion); - INIT_LIST_HEAD(&nnc->notification_wait_list); - memset(&nnc->notification_cache, 0, sizeof(nnc->notification_cache)); -} - -void hailo_nnc_finalize(struct hailo_pcie_nnc *nnc) -{ - struct hailo_notification_wait *cursor = NULL; - - // Lock rcu_read_lock and send notification_completion to wake anyone waiting on the notification_wait_list when removed - rcu_read_lock(); - list_for_each_entry_rcu(cursor, &nnc->notification_wait_list, notification_wait_list) { - cursor->is_disabled = true; - complete(&cursor->notification_completion); - } - rcu_read_unlock(); -} - -static int hailo_fw_control(struct hailo_pcie_board *board, unsigned long arg, bool* should_up_board_mutex) -{ - struct hailo_fw_control *command = &board->nnc.fw_control.command; - long completion_result = 0; - int err = 0; - - up(&board->mutex); - *should_up_board_mutex = false; - - if (down_interruptible(&board->nnc.fw_control.mutex)) { - hailo_info(board, "hailo_fw_control down_interruptible fail tgid:%d (process was interrupted or killed)\n", current->tgid); - return -ERESTARTSYS; - } - - if (copy_from_user(command, (void __user*)arg, sizeof(*command))) { - hailo_err(board, "hailo_fw_control, copy_from_user fail\n"); - err = -ENOMEM; - goto l_exit; - } - - reinit_completion(&board->nnc.fw_control.completion); - - err = hailo_pcie_write_firmware_control(&board->pcie_resources, command); - if (err < 0) { - hailo_err(board, "Failed writing fw control to pcie\n"); - goto l_exit; - } - - // Wait for response - completion_result = wait_for_completion_interruptible_timeout(&board->nnc.fw_control.completion, msecs_to_jiffies(command->timeout_ms)); - if (completion_result <= 0) { - if (0 == completion_result) { - hailo_err(board, "hailo_fw_control, timeout waiting for control (timeout_ms=%d)\n", command->timeout_ms); - err = -ETIMEDOUT; - } else { - hailo_info(board, "hailo_fw_control, wait for completion failed with err=%ld (process was interrupted or killed)\n", completion_result); - err = -EINTR; - } - goto l_exit; - } - - err = hailo_pcie_read_firmware_control(&board->pcie_resources, command); - if (err < 0) { - hailo_err(board, "Failed reading fw control from pcie\n"); - goto l_exit; - } - - if (copy_to_user((void __user*)arg, command, sizeof(*command))) { - hailo_err(board, "hailo_fw_control, copy_to_user fail\n"); - err = -ENOMEM; - goto l_exit; - } - -l_exit: - up(&board->nnc.fw_control.mutex); - return err; -} - -static long hailo_get_notification_wait_thread(struct hailo_pcie_board *board, struct file *filp, - struct hailo_notification_wait **current_waiting_thread) -{ - struct hailo_notification_wait *cursor = NULL; - // note: safe to access without rcu because the notification_wait_list is closed only on file release - list_for_each_entry(cursor, &board->nnc.notification_wait_list, notification_wait_list) - { - if ((current->tgid == cursor->tgid) && (filp == cursor->filp)) { - *current_waiting_thread = cursor; - return 0; - } - } - - return -EFAULT; -} - -static long hailo_read_notification_ioctl(struct hailo_pcie_board *board, unsigned long arg, struct file *filp, - bool* should_up_board_mutex) -{ - long err = 0; - struct hailo_notification_wait *current_waiting_thread = NULL; - struct hailo_d2h_notification *notification = &board->nnc.notification_to_user; - unsigned long irq_saved_flags; - - err = hailo_get_notification_wait_thread(board, filp, ¤t_waiting_thread); - if (0 != err) { - goto l_exit; - } - up(&board->mutex); - - if (0 > (err = wait_for_completion_interruptible(¤t_waiting_thread->notification_completion))) { - hailo_info(board, - "HAILO_READ_NOTIFICATION - wait_for_completion_interruptible error. err=%ld. tgid=%d (process was interrupted or killed)\n", - err, current_waiting_thread->tgid); - *should_up_board_mutex = false; - goto l_exit; - } - - if (down_interruptible(&board->mutex)) { - hailo_info(board, "HAILO_READ_NOTIFICATION - down_interruptible error (process was interrupted or killed)\n"); - *should_up_board_mutex = false; - err = -ERESTARTSYS; - goto l_exit; - } - - // Check if was disabled - if (current_waiting_thread->is_disabled) { - hailo_info(board, "HAILO_READ_NOTIFICATION - notification disabled for tgid=%d\n", current->tgid); - err = -ECANCELED; - goto l_exit; - } - - reinit_completion(¤t_waiting_thread->notification_completion); - - spin_lock_irqsave(&board->nnc.notification_read_spinlock, irq_saved_flags); - notification->buffer_len = board->nnc.notification_cache.buffer_len; - memcpy(notification->buffer, board->nnc.notification_cache.buffer, notification->buffer_len); - spin_unlock_irqrestore(&board->nnc.notification_read_spinlock, irq_saved_flags); - - if (copy_to_user((void __user*)arg, notification, sizeof(*notification))) { - hailo_err(board, "HAILO_READ_NOTIFICATION copy_to_user fail\n"); - err = -ENOMEM; - goto l_exit; - } - -l_exit: - return err; -} - -static long hailo_disable_notification(struct hailo_pcie_board *board, struct file *filp) -{ - struct hailo_notification_wait *cursor = NULL; - - hailo_info(board, "HAILO_DISABLE_NOTIFICATION: disable notification"); - rcu_read_lock(); - list_for_each_entry_rcu(cursor, &board->nnc.notification_wait_list, notification_wait_list) { - if ((current->tgid == cursor->tgid) && (filp == cursor->filp)) { - cursor->is_disabled = true; - complete(&cursor->notification_completion); - break; - } - } - rcu_read_unlock(); - - return 0; -} - -static long hailo_read_log_ioctl(struct hailo_pcie_board *board, unsigned long arg) -{ - long err = 0; - struct hailo_read_log_params params; - - if (copy_from_user(¶ms, (void __user*)arg, sizeof(params))) { - hailo_err(board, "HAILO_READ_LOG, copy_from_user fail\n"); - return -ENOMEM; - } - - if (0 > (err = hailo_pcie_read_firmware_log(&board->pcie_resources.fw_access, ¶ms))) { - hailo_err(board, "HAILO_READ_LOG, reading from log failed with error: %ld \n", err); - return err; - } - - if (copy_to_user((void*)arg, ¶ms, sizeof(params))) { - return -ENOMEM; - } - - return 0; -} - -long hailo_nnc_ioctl(struct hailo_pcie_board *board, unsigned int cmd, unsigned long arg, - struct file *filp, bool *should_up_board_mutex) -{ - switch (cmd) { - case HAILO_FW_CONTROL: - return hailo_fw_control(board, arg, should_up_board_mutex); - case HAILO_READ_NOTIFICATION: - return hailo_read_notification_ioctl(board, arg, filp, should_up_board_mutex); - case HAILO_DISABLE_NOTIFICATION: - return hailo_disable_notification(board, filp); - case HAILO_READ_LOG: - return hailo_read_log_ioctl(board, arg); - default: - hailo_err(board, "Invalid nnc ioctl code 0x%x (nr: %d)\n", cmd, _IOC_NR(cmd)); - return -ENOTTY; - } -} - - -static int add_notification_wait(struct hailo_pcie_board *board, struct file *filp) -{ - struct hailo_notification_wait *wait = kmalloc(sizeof(*wait), GFP_KERNEL); - if (!wait) { - hailo_err(board, "Failed to allocate notification wait structure.\n"); - return -ENOMEM; - } - wait->tgid = current->tgid; - wait->filp = filp; - wait->is_disabled = false; - init_completion(&wait->notification_completion); - list_add_rcu(&wait->notification_wait_list, &board->nnc.notification_wait_list); - return 0; -} - -int hailo_nnc_file_context_init(struct hailo_pcie_board *board, struct hailo_file_context *context) -{ - return add_notification_wait(board, context->filp); -} - -static void clear_notification_wait_list(struct hailo_pcie_board *board, struct file *filp) -{ - struct hailo_notification_wait *cur = NULL, *next = NULL; - list_for_each_entry_safe(cur, next, &board->nnc.notification_wait_list, notification_wait_list) { - if (cur->filp == filp) { - list_del_rcu(&cur->notification_wait_list); - synchronize_rcu(); - kfree(cur); - } - } -} - -int hailo_nnc_driver_down(struct hailo_pcie_board *board) -{ - long completion_result = 0; - int err = 0; - - reinit_completion(&board->driver_down.reset_completed); - - hailo_pcie_write_firmware_driver_shutdown(&board->pcie_resources); - - // Wait for response - completion_result = - wait_for_completion_timeout(&board->driver_down.reset_completed, msecs_to_jiffies(DEFAULT_SHUTDOWN_TIMEOUT_MS)); - if (completion_result <= 0) { - if (0 == completion_result) { - hailo_err(board, "hailo_nnc_driver_down, timeout waiting for shutdown response (timeout_ms=%d)\n", DEFAULT_SHUTDOWN_TIMEOUT_MS); - err = -ETIMEDOUT; - } else { - hailo_info(board, "hailo_nnc_driver_down, wait for completion failed with err=%ld (process was interrupted or killed)\n", - completion_result); - err = completion_result; - } - goto l_exit; - } - -l_exit: - return err; -} - -void hailo_nnc_file_context_finalize(struct hailo_pcie_board *board, struct hailo_file_context *context) -{ - clear_notification_wait_list(board, context->filp); - - if (context->filp == board->vdma.used_by_filp) { - hailo_nnc_driver_down(board); - } -} \ No newline at end of file diff --git a/drivers/media/pci/hailo/src/nnc.h b/drivers/media/pci/hailo/src/nnc.h deleted file mode 100644 index 1bfac99202f152..00000000000000 --- a/drivers/media/pci/hailo/src/nnc.h +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/** - * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. - **/ - -#ifndef _HAILO_PCI_NNC_H_ -#define _HAILO_PCI_NNC_H_ - -#include "pcie.h" - -void hailo_nnc_init(struct hailo_pcie_nnc *nnc); -void hailo_nnc_finalize(struct hailo_pcie_nnc *nnc); - -long hailo_nnc_ioctl(struct hailo_pcie_board *board, unsigned int cmd, unsigned long arg, - struct file *filp, bool *should_up_board_mutex); - -int hailo_nnc_file_context_init(struct hailo_pcie_board *board, struct hailo_file_context *context); -void hailo_nnc_file_context_finalize(struct hailo_pcie_board *board, struct hailo_file_context *context); - -int hailo_nnc_driver_down(struct hailo_pcie_board *board); - -#endif /* _HAILO_PCI_NNC_H_ */ \ No newline at end of file diff --git a/drivers/media/pci/hailo/src/pcie.c b/drivers/media/pci/hailo/src/pcie.c deleted file mode 100644 index cbac1e5787a8c5..00000000000000 --- a/drivers/media/pci/hailo/src/pcie.c +++ /dev/null @@ -1,1563 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/** - * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. - **/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) -#include -#endif - -#define KERNEL_CODE 1 - -#include "hailo_ioctl_common.h" -#include "pcie.h" -#include "nnc.h" -#include "soc.h" -#include "fops.h" -#include "sysfs.h" -#include "utils/logs.h" -#include "utils/compact.h" -#include "vdma/vdma.h" -#include "vdma/memory.h" - -#if LINUX_VERSION_CODE < KERNEL_VERSION( 5, 4, 0 ) -#include -#endif - -// enum that represents values for the driver parameter to either force buffer from driver , userspace or not force -// and let driver decide -enum hailo_allocate_driver_buffer_driver_param { - HAILO_NO_FORCE_BUFFER = 0, - HAILO_FORCE_BUFFER_FROM_USERSPACE = 1, - HAILO_FORCE_BUFFER_FROM_DRIVER = 2, -}; - -// Debug flag -static int force_desc_page_size = 0; -static bool g_is_power_mode_enabled = true; -static int force_allocation_from_driver = HAILO_NO_FORCE_BUFFER; -static bool force_hailo10h_legacy_mode = false; -static bool force_boot_linux_from_eemc = false; -static bool support_soft_reset = true; - -#define DEVICE_NODE_NAME "hailo" -static int char_major = 0; -static struct class *chardev_class; - -static LIST_HEAD(g_hailo_board_list); -static struct semaphore g_hailo_add_board_mutex = __SEMAPHORE_INITIALIZER(g_hailo_add_board_mutex, 1); - -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)) -#define HAILO_IRQ_FLAGS (SA_SHIRQ | SA_INTERRUPT) -#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) && LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0)) -#define HAILO_IRQ_FLAGS (IRQF_SHARED | IRQF_DISABLED) -#else -#define HAILO_IRQ_FLAGS (IRQF_SHARED) -#endif - - /* **************************** - ******************************* */ -bool power_mode_enabled(void) -{ -#if !defined(HAILO_EMULATOR) - return g_is_power_mode_enabled; -#else /* !defined(HAILO_EMULATOR) */ - return false; -#endif /* !defined(HAILO_EMULATOR) */ -} - - -/** - * Due to an HW bug, on system with low MaxReadReq ( < 512) we need to use different descriptors size. - * Returns the max descriptor size or 0 on failure. - */ -static int hailo_get_desc_page_size(struct pci_dev *pdev, u32 *out_page_size) -{ - u16 pcie_device_control = 0; - int err = 0; - // The default page size must be smaller/equal to 32K (due to PLDA registers limit). - const u32 max_page_size = 32u * 1024u; - const u32 defualt_page_size = min((u32)PAGE_SIZE, max_page_size); - - if (force_desc_page_size != 0) { - // The user given desc_page_size as a module parameter - if ((force_desc_page_size & (force_desc_page_size - 1)) != 0) { - pci_err(pdev, "force_desc_page_size must be a power of 2\n"); - return -EINVAL; - } - - if (force_desc_page_size > max_page_size) { - pci_err(pdev, "force_desc_page_size %d mustn't be larger than %u", force_desc_page_size, max_page_size); - return -EINVAL; - } - - pci_notice(pdev, "Probing: Force setting max_desc_page_size to %d (recommended value is %lu)\n", - force_desc_page_size, PAGE_SIZE); - *out_page_size = force_desc_page_size; - return 0; - } - - err = pcie_capability_read_word(pdev, PCI_EXP_DEVCTL, &pcie_device_control); - if (err < 0) { - pci_err(pdev, "Couldn't read DEVCTL capability\n"); - return err; - } - - switch (pcie_device_control & PCI_EXP_DEVCTL_READRQ) { - case PCI_EXP_DEVCTL_READRQ_128B: - pci_notice(pdev, "Probing: Setting max_desc_page_size to 128 (recommended value is %u)\n", defualt_page_size); - *out_page_size = 128; - return 0; - case PCI_EXP_DEVCTL_READRQ_256B: - pci_notice(pdev, "Probing: Setting max_desc_page_size to 256 (recommended value is %u)\n", defualt_page_size); - *out_page_size = 256; - return 0; - default: - pci_notice(pdev, "Probing: Setting max_desc_page_size to %u, (page_size=%lu)\n", defualt_page_size, PAGE_SIZE); - *out_page_size = defualt_page_size; - return 0; - }; -} - -// should be called only from fops_open (once) -struct hailo_pcie_board* hailo_pcie_get_board_index(u32 index) -{ - struct hailo_pcie_board *pBoard, *pRet = NULL; - - down(&g_hailo_add_board_mutex); - list_for_each_entry(pBoard, &g_hailo_board_list, board_list) - { - if ( index == pBoard->board_index ) - { - atomic_inc(&pBoard->ref_count); - pRet = pBoard; - break; - } - } - up(&g_hailo_add_board_mutex); - - return pRet; -} - -/** - * hailo_pcie_disable_aspm - Disable ASPM states - * @board: pointer to PCI board struct - * @state: bit-mask of ASPM states to disable - * @locked: indication if this context holds pci_bus_sem locked. - * - * Some devices *must* have certain ASPM states disabled per hardware errata. - **/ -static int hailo_pcie_disable_aspm(struct hailo_pcie_board *board, u16 state, bool locked) -{ - struct pci_dev *pdev = board->pDev; - struct pci_dev *parent = pdev->bus->self; - u16 aspm_dis_mask = 0; - u16 pdev_aspmc = 0; - u16 parent_aspmc = 0; - int err = 0; - - switch (state) { - case PCIE_LINK_STATE_L0S: - aspm_dis_mask |= PCI_EXP_LNKCTL_ASPM_L0S; - break; - case PCIE_LINK_STATE_L1: - aspm_dis_mask |= PCI_EXP_LNKCTL_ASPM_L1; - break; - default: - break; - } - - err = pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &pdev_aspmc); - if (err < 0) { - hailo_err(board, "Couldn't read LNKCTL capability\n"); - return err; - } - - pdev_aspmc &= PCI_EXP_LNKCTL_ASPMC; - - if (parent) { - err = pcie_capability_read_word(parent, PCI_EXP_LNKCTL, &parent_aspmc); - if (err < 0) { - hailo_err(board, "Couldn't read slot LNKCTL capability\n"); - return err; - } - parent_aspmc &= PCI_EXP_LNKCTL_ASPMC; - } - - hailo_notice(board, "Disabling ASPM %s %s\n", - (aspm_dis_mask & PCI_EXP_LNKCTL_ASPM_L0S) ? "L0s" : "", - (aspm_dis_mask & PCI_EXP_LNKCTL_ASPM_L1) ? "L1" : ""); - - // Disable L0s even if it is currently disabled as ASPM states can be enabled by the kernel when changing power modes -#ifdef CONFIG_PCIEASPM - if (locked) { - // Older kernel versions (<5.2.21) don't return value for this functions, so we try manual disabling anyway - (void)pci_disable_link_state_locked(pdev, state); - } else { - (void)pci_disable_link_state(pdev, state); - } - - /* Double-check ASPM control. If not disabled by the above, the - * BIOS is preventing that from happening (or CONFIG_PCIEASPM is - * not enabled); override by writing PCI config space directly. - */ - err = pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &pdev_aspmc); - if (err < 0) { - hailo_err(board, "Couldn't read LNKCTL capability\n"); - return err; - } - pdev_aspmc &= PCI_EXP_LNKCTL_ASPMC; - - if (!(aspm_dis_mask & pdev_aspmc)) { - hailo_notice(board, "Successfully disabled ASPM %s %s\n", - (aspm_dis_mask & PCI_EXP_LNKCTL_ASPM_L0S) ? "L0s" : "", - (aspm_dis_mask & PCI_EXP_LNKCTL_ASPM_L1) ? "L1" : ""); - return 0; - } -#endif - - /* Both device and parent should have the same ASPM setting. - * Disable ASPM in downstream component first and then upstream. - */ - err = pcie_capability_clear_word(pdev, PCI_EXP_LNKCTL, aspm_dis_mask); - if (err < 0) { - hailo_err(board, "Couldn't read LNKCTL capability\n"); - return err; - } - if (parent) { - err = pcie_capability_clear_word(parent, PCI_EXP_LNKCTL, aspm_dis_mask); - if (err < 0) { - hailo_err(board, "Couldn't read slot LNKCTL capability\n"); - return err; - } - } - hailo_notice(board, "Manually disabled ASPM %s %s\n", - (aspm_dis_mask & PCI_EXP_LNKCTL_ASPM_L0S) ? "L0s" : "", - (aspm_dis_mask & PCI_EXP_LNKCTL_ASPM_L1) ? "L1" : ""); - - return 0; -} - -static void hailo_pcie_insert_board(struct hailo_pcie_board* pBoard) -{ - u32 index = 0; - struct hailo_pcie_board *pCurrent, *pNext; - - - down(&g_hailo_add_board_mutex); - if ( list_empty(&g_hailo_board_list) || - list_first_entry(&g_hailo_board_list, struct hailo_pcie_board, board_list)->board_index > 0) - { - pBoard->board_index = 0; - list_add(&pBoard->board_list, &g_hailo_board_list); - - up(&g_hailo_add_board_mutex); - return; - } - - list_for_each_entry_safe(pCurrent, pNext, &g_hailo_board_list, board_list) - { - index = pCurrent->board_index+1; - if( list_is_last(&pCurrent->board_list, &g_hailo_board_list) || (index != pNext->board_index)) - { - break; - } - } - - pBoard->board_index = index; - list_add(&pBoard->board_list, &pCurrent->board_list); - - up(&g_hailo_add_board_mutex); - - return; -} - -static void hailo_pcie_remove_board(struct hailo_pcie_board* pBoard) -{ - down(&g_hailo_add_board_mutex); - if (pBoard) - { - list_del(&pBoard->board_list); - } - up(&g_hailo_add_board_mutex); -} - -/** - * Wait until the relevant completion is done. - * - * @param completion - pointer to the completion struct to wait for. - * @param msecs - the amount of time to wait in milliseconds. - * @return false if timed out, true if completed. - */ -static bool wait_for_firmware_completion(struct completion *completion, unsigned int msecs) -{ - return (0 != wait_for_completion_timeout(completion, msecs_to_jiffies(msecs))); -} - -/** - * Program one FW file descriptors to the vDMA engine. - * - * @param dev - pointer to the device struct we are working on. - * @param boot_dma_state - pointer to the boot dma state struct which includes all of the boot resources. - * @param file_address - the address of the file in the device memory. - * @param transfer_buffer - the buffer to program to the vDMA engine. - * @param channel_index - the index of the channel to program. - * @param filename - the name of the file to program. - * @param raise_int_on_completion - true if this is the last descriptors chunk in the specific channel in the boot flow, false otherwise. If true - will enable - * an IRQ for the relevant channel when the transfer is finished. - * @return the amount of descriptors programmed on success, negative error code on failure. - */ -static int pcie_vdma_program_one_file_descriptors(struct device *dev, struct hailo_pcie_boot_dma_channel_state *boot_channel_state, - u32 file_address, struct hailo_vdma_mapped_transfer_buffer transfer_buffer, u8 channel_index, const char *filename, bool raise_int_on_completion) -{ - int device_desc = 0, host_desc = 0; - enum hailo_vdma_interrupts_domain interrupts_domain = raise_int_on_completion ? HAILO_VDMA_INTERRUPTS_DOMAIN_HOST : - HAILO_VDMA_INTERRUPTS_DOMAIN_NONE; - - hailo_dev_dbg(dev, "channel_index = %d, file_name = %s, file_address = 0x%x, transfer_buffer.offset = 0x%x,\ - size_to_program = 0x%x, starting_desc/desc_index = 0x%x\n", channel_index, filename, file_address, - transfer_buffer.offset, transfer_buffer.size, boot_channel_state->desc_program_num); - - // program descriptors - device_desc = hailo_vdma_program_descriptors_in_chunk(&hailo_pcie_vdma_hw, file_address, transfer_buffer.size, - &boot_channel_state->device_descriptors_buffer.desc_list, boot_channel_state->desc_program_num, - (boot_channel_state->device_descriptors_buffer.desc_list.desc_count - 1), channel_index, HAILO_PCI_EP_HOST_DMA_DATA_ID); - if (device_desc < 0) { - hailo_dev_err(dev, "Failed to program device descriptors, error = %u\n", device_desc); - return device_desc; - } - - host_desc = hailo_vdma_program_descriptors_list(&hailo_pcie_vdma_hw, &boot_channel_state->host_descriptors_buffer.desc_list, - boot_channel_state->desc_program_num, &transfer_buffer, true, channel_index, interrupts_domain, false); - if (host_desc < 0) { - hailo_dev_err(dev, "Failed to program host descriptors, error = %u\n", host_desc); - return host_desc; - } - - // checks that same amount of decsriptors were programmed on device side and host side - if (host_desc != device_desc) { - hailo_dev_err(dev, "Host and device descriptors should be the same\n"); - return -EINVAL; - } - - return host_desc; -} - -/** - * Program one FW file to the vDMA engine. - * - * @param board - pointer to the board struct we are working on. - * @param boot_dma_state - pointer to the boot dma state struct which includes all of the boot resources. - * @param file_address - the address of the file in the device memory. - * @param filename - the name of the file to program. - * @param raise_int_on_completion - true if this is the last file in the boot flow, false otherwise. uses to enable an IRQ for the - * relevant channel when the transfer is finished. - * @return 0 on success, negative error code on failure. at the end of the function the firmware is released. - */ -static int pcie_vdma_program_one_file(struct hailo_pcie_board *board, struct hailo_pcie_boot_dma_state *boot_dma_state, u32 file_address, - const char *filename, bool raise_int_on_completion) -{ - const struct firmware *firmware = NULL; - struct hailo_vdma_mapped_transfer_buffer transfer_buffer = {0}; - int desc_programmed = 0; - int err = 0; - size_t bytes_copied = 0, remaining_size = 0, data_offset = 0, desc_num_left = 0, current_desc_to_program = 0; - - hailo_notice(board, "Programing file %s for dma transfer\n", filename); - - // load firmware directly without usermode helper for the relevant file - err = request_firmware_direct(&firmware, filename, board->vdma.dev); - if (err < 0) { - hailo_err(board, "Failed to allocate memory for file %s\n", filename); - return err; - } - - // set the remaining size as the whole file size to begin with - remaining_size = firmware->size; - - while (remaining_size > 0) { - struct hailo_pcie_boot_dma_channel_state *channel = &boot_dma_state->channels[boot_dma_state->curr_channel_index]; - bool is_last_desc_chunk_of_curr_channel = false; - bool rais_interrupt_on_last_chunk = false; - - hailo_dbg(board, "desc_program_num = 0x%x, desc_page_size = 0x%x, on channel = %d\n", - channel->desc_program_num, HAILO_PCI_OVER_VDMA_PAGE_SIZE, boot_dma_state->curr_channel_index); - - // increment the channel index if the current channel is full - if ((MAX_SG_DESCS_COUNT - 1) == channel->desc_program_num) { - boot_dma_state->curr_channel_index++; - channel = &boot_dma_state->channels[boot_dma_state->curr_channel_index]; - board->fw_boot.boot_used_channel_bitmap |= (1 << boot_dma_state->curr_channel_index); - } - - // calculate the number of descriptors left to program and the number of bytes left to program - desc_num_left = (MAX_SG_DESCS_COUNT - 1) - channel->desc_program_num; - - // prepare the transfer buffer to make sure all the fields are initialized - transfer_buffer.sg_table = &channel->sg_table; - transfer_buffer.size = min(remaining_size, (desc_num_left * HAILO_PCI_OVER_VDMA_PAGE_SIZE)); - // no need to check for overflow since the variables are constant and always desc_program_num <= max u16 (65536) - // & the buffer max size is 256 Mb << 4G (max u32) - transfer_buffer.offset = (channel->desc_program_num * HAILO_PCI_OVER_VDMA_PAGE_SIZE); - - // check if this is the last descriptor chunk to program in the whole boot flow - current_desc_to_program = (transfer_buffer.size / HAILO_PCI_OVER_VDMA_PAGE_SIZE); - is_last_desc_chunk_of_curr_channel = ((MAX_SG_DESCS_COUNT - 1) == - (current_desc_to_program + channel->desc_program_num)); - rais_interrupt_on_last_chunk = (is_last_desc_chunk_of_curr_channel || (raise_int_on_completion && - (remaining_size == transfer_buffer.size))); - - // try to copy the file to the buffer, if failed, release the firmware and return - bytes_copied = sg_pcopy_from_buffer(transfer_buffer.sg_table->sgl, transfer_buffer.sg_table->orig_nents, - &firmware->data[data_offset], transfer_buffer.size, transfer_buffer.offset); - if (transfer_buffer.size != bytes_copied) { - hailo_err(board, "There is not enough memory allocated to copy file %s\n", filename); - release_firmware(firmware); - return -EFBIG; - } - - // program the descriptors - desc_programmed = pcie_vdma_program_one_file_descriptors(&board->pDev->dev, channel, (file_address + data_offset), - transfer_buffer, boot_dma_state->curr_channel_index, filename, rais_interrupt_on_last_chunk); - if (desc_programmed < 0) { - hailo_err(board, "Failed to program descriptors for file %s, on cahnnel = %d\n", filename, - boot_dma_state->curr_channel_index); - release_firmware(firmware); - return desc_programmed; - } - - // Update remaining size, data_offset and desc_program_num for the next iteration - remaining_size -= transfer_buffer.size; - data_offset += transfer_buffer.size; - channel->desc_program_num += desc_programmed; - } - - hailo_notice(board, "File %s programed successfully\n", filename); - - release_firmware(firmware); - - return desc_programmed; -} - -/** - * Program the entire batch of firmware files to the vDMA engine. - * - * @param board - pointer to the board struct we are working on. - * @param boot_dma_state - pointer to the boot dma state struct which includes all of the boot resources. - * @param resources - pointer to the hailo_pcie_resources struct. - * @param stage - the stage to program. - * @return 0 on success, negative error code on failure. - */ -static long pcie_vdma_program_entire_batch(struct hailo_pcie_board *board, struct hailo_pcie_boot_dma_state *boot_dma_state, - struct hailo_pcie_resources *resources, u32 stage) -{ - long err = 0; - int file_index = 0; - const struct hailo_pcie_loading_stage *stage_info = hailo_pcie_get_loading_stage_info(resources->board_type, stage); - const struct hailo_file_batch *files_batch = stage_info->batch; - const u8 amount_of_files = stage_info->amount_of_files_in_stage; - const char *filename = NULL; - u32 file_address = 0; - - for (file_index = 0; file_index < amount_of_files; file_index++) - { - filename = files_batch[file_index].filename; - file_address = files_batch[file_index].address; - - if (NULL == filename) { - hailo_err(board, "The amount of files wasn't specified for stage %d\n", stage); - break; - } - - err = pcie_vdma_program_one_file(board, boot_dma_state, file_address, filename, - (file_index == (amount_of_files - 1))); - if (err < 0) { - hailo_err(board, "Failed to program file %s\n", filename); - return err; - } - } - - return 0; -} - -/** - * Release noncontinuous memory (virtual continuous memory). (sg table and kernel_addrs) - * - * @param dev - pointer to the device struct we are working on. - * @param sg_table - the sg table to release. - * @param kernel_addrs - the kernel address to release. - */ -static void pcie_vdma_release_noncontinuous_memory(struct device *dev, struct sg_table *sg_table, void *kernel_addrs) -{ - dma_unmap_sg(dev, sg_table->sgl, sg_table->orig_nents, DMA_TO_DEVICE); - sg_free_table(sg_table); - vfree(kernel_addrs); -} - -/** - * Allocate noncontinuous memory (virtual continuous memory). - * - * @param dev - pointer to the device struct we are working on. - * @param buffer_size - the size of the buffer to allocate. - * @param kernel_addrs - pointer to the allocated buffer. - * @param sg_table - pointer to the sg table struct. - * @return 0 on success, negative error code on failure. on failure all resurces are released. (pages array, sg table, kernel_addrs) - */ -static long pcie_vdma_allocate_noncontinuous_memory(struct device *dev, u64 buffer_size, void **kernel_addrs, struct sg_table *sg_table) -{ - struct page **pages = NULL; - size_t npages = 0; - struct scatterlist *sgl = NULL; - long err = 0; - size_t i = 0; - - // allocate noncontinuous memory for the kernel address (virtual continuous memory) - *kernel_addrs = vmalloc(buffer_size); - if (NULL == *kernel_addrs) { - hailo_dev_err(dev, "Failed to allocate memory for kernel_addrs\n"); - err = -ENOMEM; - goto exit; - } - - // map the memory to pages - npages = DIV_ROUND_UP(buffer_size, PAGE_SIZE); - - // allocate memory for a virtually contiguous array for the pages - pages = kvmalloc_array(npages, sizeof(*pages), GFP_KERNEL); - if (!pages) { - err = -ENOMEM; - hailo_dev_err(dev, "Failed to allocate memory for pages\n"); - goto release_user_addrs; - } - - // walk a vmap address to the struct page it maps - for (i = 0; i < npages; i++) { - pages[i] = vmalloc_to_page(*kernel_addrs + (i * PAGE_SIZE)); - if (!pages[i]) { - err = -ENOMEM; - hailo_dev_err(dev, "Failed to get page from vmap address\n"); - goto release_array; - } - } - - // allocate and initialize the sg table from a list of pages - sgl = sg_alloc_table_from_pages_segment_compat(sg_table, pages, npages, 0, buffer_size, SGL_MAX_SEGMENT_SIZE, NULL, - 0, GFP_KERNEL); - if (IS_ERR(sgl)) { - err = PTR_ERR(sgl); - hailo_dev_err(dev, "sg table alloc failed (err %ld)..\n", err); - goto release_array; - } - - // map the sg list - sg_table->nents = dma_map_sg(dev, sg_table->sgl, sg_table->orig_nents, DMA_TO_DEVICE); - if (0 == sg_table->nents) { - hailo_dev_err(dev, "failed to map sg list for user buffer\n"); - err = -ENXIO; - goto release_sg_table; - } - - // clean exit - just release the pages array & return err = 0 - err = 0; - kfree(pages); - goto exit; - -release_sg_table: - dma_unmap_sg(dev, sg_table->sgl, sg_table->orig_nents, DMA_TO_DEVICE); -release_array: - kfree(pages); -release_user_addrs: - vfree(*kernel_addrs); -exit: - return err; -} - -/** - * Release all boot resources. - * - * @param board - pointer to the board struct we are working on. - * @param engine - pointer to the vdma engine struct. - * @param boot_dma_state - pointer to the boot dma state struct which includes all of the boot resources. - */ -static void pcie_vdme_release_boot_resources(struct hailo_pcie_board *board, struct hailo_vdma_engine *engine, - struct hailo_pcie_boot_dma_state *boot_dma_state) -{ - u8 channel_index = 0; - - // release all the resources - for (channel_index = 0; channel_index < HAILO_PCI_OVER_VDMA_NUM_CHANNELS; channel_index++) { - struct hailo_pcie_boot_dma_channel_state *channel = &boot_dma_state->channels[channel_index]; - // release descriptor lists - if (channel->host_descriptors_buffer.kernel_address != NULL) { - hailo_desc_list_release(&board->pDev->dev, &channel->host_descriptors_buffer); - } - if (channel->device_descriptors_buffer.kernel_address != NULL) { - hailo_desc_list_release(&board->pDev->dev, &channel->device_descriptors_buffer); - } - - // stops all boot vDMA channels - hailo_vdma_stop_channel(engine->channels[channel_index].host_regs); - hailo_vdma_stop_channel(engine->channels[channel_index].device_regs); - - // release noncontinuous memory (virtual continuous memory) - if (channel->kernel_addrs != NULL) { - pcie_vdma_release_noncontinuous_memory(&board->pDev->dev, &channel->sg_table, channel->kernel_addrs); - } - } -} - -/** - * Allocate boot resources for vDMA transfer. - * - * @param desc_page_size - the size of the descriptor page. - * @param board - pointer to the board struct we are working on. - * @param boot_dma_state - pointer to the boot dma state struct which includes all of the boot resources. - * @param engine - pointer to the vDMA engine struct. - * @return 0 on success, negative error code on failure. in case of failure descriptor lists are released, - * boot vDMA channels are stopped and memory is released. - */ -static long pcie_vdme_allocate_boot_resources(u32 desc_page_size, struct hailo_pcie_board *board, - struct hailo_pcie_boot_dma_state *boot_dma_state, struct hailo_vdma_engine *engine) -{ - long err = 0; - uintptr_t device_handle = 0, host_handle = 0; - u8 channel_index = 0; - - for (channel_index = 0; channel_index < HAILO_PCI_OVER_VDMA_NUM_CHANNELS; channel_index++) { - struct hailo_pcie_boot_dma_channel_state *channel = &boot_dma_state->channels[channel_index]; - - // create 2 descriptors list - 1 for the host & 1 for the device for each channel - err = hailo_desc_list_create(&board->pDev->dev, MAX_SG_DESCS_COUNT, desc_page_size, host_handle, false, - &channel->host_descriptors_buffer); - if (err < 0) { - hailo_err(board, "failed to allocate host descriptors list buffer\n"); - goto release_all_resources; - } - - err = hailo_desc_list_create(&board->pDev->dev, MAX_SG_DESCS_COUNT, desc_page_size, device_handle, false, - &channel->device_descriptors_buffer); - if (err < 0) { - hailo_err(board, "failed to allocate device descriptors list buffer\n"); - goto release_all_resources; - } - - // start vDMA channels - both sides with DDR at the host side (AKA ID 0) - err = hailo_vdma_start_channel(engine->channels[channel_index].host_regs, - channel->host_descriptors_buffer.dma_address, - channel->host_descriptors_buffer.desc_list.desc_count, board->vdma.hw->ddr_data_id); - if (err < 0) { - hailo_err(board, "Error starting host vdma channel\n"); - goto release_all_resources; - } - - err = hailo_vdma_start_channel(engine->channels[channel_index].device_regs, - channel->device_descriptors_buffer.dma_address, - channel->device_descriptors_buffer.desc_list.desc_count, board->vdma.hw->ddr_data_id); - if (err < 0) { - hailo_err(board, "Error starting device vdma channel\n"); - goto release_all_resources; - } - - // initialize the buffer size per channel - channel->buffer_size = (MAX_SG_DESCS_COUNT * desc_page_size); - - // allocate noncontinuous memory (virtual continuous memory) - err = pcie_vdma_allocate_noncontinuous_memory(&board->pDev->dev, channel->buffer_size, &channel->kernel_addrs, - &channel->sg_table); - if (err < 0) { - hailo_err(board, "Failed to allocate noncontinuous memory\n"); - goto release_all_resources; - } - } - - return 0; - -release_all_resources: - pcie_vdme_release_boot_resources(board, engine, boot_dma_state); - return err; -} - -/** - * Write FW boot files over vDMA using multiple channels for timing optimizations. - * - * The function is divided into the following steps: - * 1) Allocate resources for the boot process. - * 2) Programs descriptors to point to the memory and start the vDMA. - * 3) Waits until the vDMA is done and triggers the device to start the boot process. - * 4) Releases all the resources. - * - * @param board - pointer to the board struct. - * @param stage - the stage of the boot process. - * @param desc_page_size - the size of the descriptor page. - * @return 0 on success, negative error code on failure. in any case all resurces are released. - */ -static long pcie_write_firmware_batch_over_dma(struct hailo_pcie_board *board, u32 stage, u32 desc_page_size) -{ - long err = 0; - struct hailo_vdma_engine *engine = &board->vdma.vdma_engines[PCI_VDMA_ENGINE_INDEX]; - u8 channel_index = 0; - - err = pcie_vdme_allocate_boot_resources(desc_page_size, board, &board->fw_boot.boot_dma_state, engine); - if (err < 0) { - hailo_err(board, "Failed to create descriptors and start channels\n"); - return err; - } - - // initialize the completion for the vDMA boot data completion - reinit_completion(&board->fw_boot.vdma_boot_completion); - - err = pcie_vdma_program_entire_batch(board, &board->fw_boot.boot_dma_state, &board->pcie_resources, stage); - if (err < 0) { - hailo_err(board, "Failed to program entire batch\n"); - goto release_all; - } - - // sync the sg tables for the device before statirng the vDMA - for (channel_index = 0; channel_index < HAILO_PCI_OVER_VDMA_NUM_CHANNELS; channel_index++) { - dma_sync_sgtable_for_device(&board->pDev->dev, &board->fw_boot.boot_dma_state.channels[channel_index].sg_table, - DMA_TO_DEVICE); - } - - // start the vDMA transfer on all channels - for (channel_index = 0; channel_index < HAILO_PCI_OVER_VDMA_NUM_CHANNELS; channel_index++) { - struct hailo_pcie_boot_dma_channel_state *channel = &board->fw_boot.boot_dma_state.channels[channel_index]; - if (channel->desc_program_num != 0) { - hailo_vdma_set_num_avail(engine->channels[channel_index].host_regs, channel->desc_program_num); - hailo_vdma_set_num_avail(engine->channels[channel_index].device_regs, channel->desc_program_num); - hailo_dbg(board, "Set num avail to %u, on channel %u\n", channel->desc_program_num, channel_index); - } - } - - if (!wait_for_firmware_completion(&board->fw_boot.vdma_boot_completion, hailo_pcie_get_loading_stage_info(board->pcie_resources.board_type, SECOND_STAGE)->timeout)) { - hailo_err(board, "Timeout waiting for vDMA boot data completion\n"); - err = -ETIMEDOUT; - goto release_all; - } - - hailo_notice(board, "vDMA transfer completed, triggering boot\n"); - reinit_completion(&board->fw_boot.fw_loaded_completion); - hailo_trigger_firmware_boot(&board->pcie_resources, stage); - -release_all: - pcie_vdme_release_boot_resources(board, engine, &board->fw_boot.boot_dma_state); - return err; -} - -static int load_soc_firmware(struct hailo_pcie_board *board, struct hailo_pcie_resources *resources, - struct device *dev, struct completion *fw_load_completion) -{ - u32 boot_status = 0; - int err = 0; - u32 second_stage = force_boot_linux_from_eemc ? SECOND_STAGE_LINUX_IN_EMMC : SECOND_STAGE; - - if (hailo_pcie_is_firmware_loaded(resources)) { - hailo_dev_warn(dev, "SOC Firmware batch was already loaded\n"); - return 0; - } - - // configure the EP registers for the DMA transaction - hailo_pcie_configure_ep_registers_for_dma_transaction(resources); - - init_completion(fw_load_completion); - init_completion(&board->fw_boot.vdma_boot_completion); - - err = hailo_pcie_write_firmware_batch(dev, resources, FIRST_STAGE); - if (err < 0) { - hailo_dev_err(dev, "Failed writing SOC FIRST_STAGE firmware files. err %d\n", err); - return err; - } - - if (!wait_for_firmware_completion(fw_load_completion, hailo_pcie_get_loading_stage_info(resources->board_type, FIRST_STAGE)->timeout)) { - boot_status = hailo_get_boot_status(resources); - hailo_dev_err(dev, "Timeout waiting for SOC FIRST_STAGE firmware file, boot status %u\n", boot_status); - return -ETIMEDOUT; - } - - reinit_completion(fw_load_completion); - - err = (int)pcie_write_firmware_batch_over_dma(board, second_stage, HAILO_PCI_OVER_VDMA_PAGE_SIZE); - if (err < 0) { - hailo_dev_err(dev, "Failed writing SOC SECOND_STAGE firmware files over vDMA. err %d\n", err); - return err; - } - - if (!wait_for_firmware_completion(fw_load_completion, hailo_pcie_get_loading_stage_info(resources->board_type, SECOND_STAGE)->timeout)) { - boot_status = hailo_get_boot_status(resources); - hailo_dev_err(dev, "Timeout waiting for SOC SECOND_STAGE firmware file, boot status %u\n", boot_status); - return -ETIMEDOUT; - } - - reinit_completion(fw_load_completion); - reinit_completion(&board->fw_boot.vdma_boot_completion); - - hailo_dev_notice(dev, "SOC Firmware Batch loaded successfully\n"); - - return 0; -} -static int load_nnc_firmware(struct hailo_pcie_board *board) -{ - u32 boot_status = 0; - int err = 0; - struct device *dev = &board->pDev->dev; - - if (hailo_pcie_is_firmware_loaded(&board->pcie_resources)) { - if (support_soft_reset) { - err = hailo_pcie_soft_reset(&board->pcie_resources, &board->soft_reset.reset_completed); // send control, wait for done - if (err < 0) { - hailo_dev_err(dev, "Failed hailo pcie soft reset. err %d\n", err); - return 0; - } - hailo_dev_notice(dev, "Soft reset done\n"); - } else { - hailo_dev_warn(dev, "NNC Firmware batch was already loaded\n"); - return 0; - } - } - - init_completion(&board->fw_boot.fw_loaded_completion); - - err = hailo_pcie_write_firmware_batch(dev, &board->pcie_resources, FIRST_STAGE); - if (err < 0) { - hailo_dev_err(dev, "Failed writing NNC firmware files. err %d\n", err); - return err; - } - - if (!wait_for_firmware_completion(&board->fw_boot.fw_loaded_completion, hailo_pcie_get_loading_stage_info(board->pcie_resources.board_type, FIRST_STAGE)->timeout)) { - boot_status = hailo_get_boot_status(&board->pcie_resources); - hailo_dev_err(dev, "Timeout waiting for NNC firmware file, boot status %u\n", boot_status); - return -ETIMEDOUT; - } - - hailo_dev_notice(dev, "NNC Firmware loaded successfully\n"); - - return 0; -} - -int hailo_pcie_soft_reset(struct hailo_pcie_resources *resources, struct completion *reset_completed) -{ - bool completion_result = false; - int err = 0; - - hailo_pcie_write_firmware_soft_reset(resources); - - reinit_completion(reset_completed); - - // Wait for response - completion_result = - wait_for_firmware_completion(reset_completed, msecs_to_jiffies(FIRMWARE_WAIT_TIMEOUT_MS)); - if (completion_result == false) { - pr_warn("hailo reset firmware, timeout waiting for shutdown response (timeout_ms=%d)\n", FIRMWARE_WAIT_TIMEOUT_MS); - err = -ETIMEDOUT; - return err; - } - - msleep(TIME_UNTIL_REACH_BOOTLOADER); - pr_notice("hailo_driver_down finished\n"); - - return err; -} - -static int load_firmware(struct hailo_pcie_board *board) -{ - switch (board->pcie_resources.accelerator_type) { - case HAILO_ACCELERATOR_TYPE_SOC: - return load_soc_firmware(board, &board->pcie_resources, &board->pDev->dev, &board->fw_boot.fw_loaded_completion); - case HAILO_ACCELERATOR_TYPE_NNC: - return load_nnc_firmware(board); - default: - hailo_err(board, "Invalid board type %d\n", board->pcie_resources.accelerator_type); - return -EINVAL; - } -} - -static int enable_boot_interrupts(struct hailo_pcie_board *board) -{ - int err = hailo_enable_interrupts(board); - if (err < 0) { - hailo_err(board, "Failed enabling interrupts %d\n", err); - return err; - } - - board->fw_boot.is_in_boot = true; - return 0; -} - -static void disable_boot_interrupts(struct hailo_pcie_board *board) -{ - board->fw_boot.is_in_boot = false; - hailo_disable_interrupts(board); -} - -static int hailo_activate_board(struct hailo_pcie_board *board) -{ - int err = 0; - ktime_t start_time = 0, end_time = 0; - - (void)hailo_pcie_disable_aspm(board, PCIE_LINK_STATE_L0S, false); - - err = enable_boot_interrupts(board); - if (err < 0) { - return err; - } - - start_time = ktime_get(); - err = load_firmware(board); - end_time = ktime_get(); - hailo_notice(board, "FW loaded, took %lld ms\n", ktime_to_ms(ktime_sub(end_time, start_time))); - disable_boot_interrupts(board); - - if (err < 0) { - hailo_err(board, "Firmware load failed\n"); - return err; - } - - if (power_mode_enabled()) { - // Setting the device to low power state, until the user opens the device - hailo_info(board, "Power change state to PCI_D3hot\n"); - err = pci_set_power_state(board->pDev, PCI_D3hot); - if (err < 0) { - hailo_err(board, "Set power state failed %d\n", err); - return err; - } - } - - return 0; -} - -int hailo_enable_interrupts(struct hailo_pcie_board *board) -{ - int err = 0; - - if (board->interrupts_enabled) { - hailo_crit(board, "Failed enabling interrupts (already enabled)\n"); - return -EINVAL; - } - - // TODO HRT-2253: use new api for enabling msi: (pci_alloc_irq_vectors) - if ((err = pci_enable_msi(board->pDev))) { - hailo_err(board, "Failed to enable MSI %d\n", err); - return err; - } - hailo_info(board, "Enabled MSI interrupt\n"); - - err = request_irq(board->pDev->irq, hailo_irqhandler, HAILO_IRQ_FLAGS, DRIVER_NAME, board); - if (err) { - hailo_err(board, "request_irq failed %d\n", err); - pci_disable_msi(board->pDev); - return err; - } - hailo_info(board, "irq enabled %u\n", board->pDev->irq); - - hailo_pcie_enable_interrupts(&board->pcie_resources); - - board->interrupts_enabled = true; - return 0; -} - -void hailo_disable_interrupts(struct hailo_pcie_board *board) -{ - // Sanity Check - if ((NULL == board) || (NULL == board->pDev)) { - pr_err("Failed to access board or device\n"); - return; - } - - if (!board->interrupts_enabled) { - return; - } - - board->interrupts_enabled = false; - hailo_pcie_disable_interrupts(&board->pcie_resources); - free_irq(board->pDev->irq, board); - pci_disable_msi(board->pDev); -} - -static int hailo_bar_iomap(struct pci_dev *pdev, int bar, struct hailo_resource *resource) -{ - resource->size = pci_resource_len(pdev, bar); - resource->address = (uintptr_t)(pci_iomap(pdev, bar, resource->size)); - - if (!resource->size || !resource->address) { - pci_err(pdev, "Probing: Invalid PCIe BAR %d", bar); - return -EINVAL; - } - - pci_notice(pdev, "Probing: mapped bar %d - %p %zu\n", bar, - (void*)resource->address, resource->size); - return 0; -} - -static void hailo_bar_iounmap(struct pci_dev *pdev, struct hailo_resource *resource) -{ - if (resource->address) { - pci_iounmap(pdev, (void*)resource->address); - resource->address = 0; - resource->size = 0; - } -} - -static int pcie_resources_init(struct pci_dev *pdev, struct hailo_pcie_resources *resources, - enum hailo_board_type board_type) -{ - int err = -EINVAL; - if (board_type >= HAILO_BOARD_TYPE_COUNT) { - pci_err(pdev, "Probing: Invalid board type %d\n", (int)board_type); - err = -EINVAL; - goto failure_exit; - } - - err = pci_request_regions(pdev, DRIVER_NAME); - if (err < 0) { - pci_err(pdev, "Probing: Error allocating bars %d\n", err); - goto failure_exit; - } - - err = hailo_bar_iomap(pdev, HAILO_PCIE_CONFIG_BAR, &resources->config); - if (err < 0) { - goto failure_release_regions; - } - - err = hailo_bar_iomap(pdev, HAILO_PCIE_VDMA_REGS_BAR, &resources->vdma_registers); - if (err < 0) { - goto failure_release_config; - } - - err = hailo_bar_iomap(pdev, HAILO_PCIE_FW_ACCESS_BAR, &resources->fw_access); - if (err < 0) { - goto failure_release_vdma_regs; - } - - - if (HAILO_BOARD_TYPE_HAILO10H == board_type){ - if (true == force_hailo10h_legacy_mode) { - board_type = HAILO_BOARD_TYPE_HAILO10H_LEGACY; - } - } - - resources->board_type = board_type; - - err = hailo_set_device_type(resources); - if (err < 0) { - goto failure_release_fw_access; - } - - if (!hailo_pcie_is_device_connected(resources)) { - pci_err(pdev, "Probing: Failed reading device BARs, device may be disconnected\n"); - err = -ENODEV; - goto failure_release_fw_access; - } - - return 0; - -failure_release_fw_access: - hailo_bar_iounmap(pdev, &resources->fw_access); -failure_release_vdma_regs: - hailo_bar_iounmap(pdev, &resources->vdma_registers); -failure_release_config: - hailo_bar_iounmap(pdev, &resources->config); -failure_release_regions: - pci_release_regions(pdev); -failure_exit: - return err; -} - -static void pcie_resources_release(struct pci_dev *pdev, struct hailo_pcie_resources *resources) -{ - hailo_bar_iounmap(pdev, &resources->config); - hailo_bar_iounmap(pdev, &resources->vdma_registers); - hailo_bar_iounmap(pdev, &resources->fw_access); - pci_release_regions(pdev); -} - -static void update_channel_interrupts(struct hailo_vdma_controller *controller, - size_t engine_index, u32 channels_bitmap) -{ - struct hailo_pcie_board *board = (struct hailo_pcie_board*) dev_get_drvdata(controller->dev); - if (engine_index >= board->vdma.vdma_engines_count) { - hailo_err(board, "Invalid engine index %zu", engine_index); - return; - } - - hailo_pcie_update_channel_interrupts_mask(&board->pcie_resources, channels_bitmap); -} - -static struct hailo_vdma_controller_ops pcie_vdma_controller_ops = { - .update_channel_interrupts = update_channel_interrupts, -}; - - -static int hailo_pcie_vdma_controller_init(struct hailo_vdma_controller *controller, - struct device *dev, struct hailo_resource *vdma_registers) -{ - const size_t engines_count = 1; - return hailo_vdma_controller_init(controller, dev, &hailo_pcie_vdma_hw, - &pcie_vdma_controller_ops, vdma_registers, engines_count); -} - -// Tries to check if address allocated with kmalloc is dma capable. -// If kmalloc address is not dma capable we assume other addresses -// won't be dma capable as well. -static bool is_kmalloc_dma_capable(struct device *dev) -{ - void *check_addr = NULL; - dma_addr_t dma_addr = 0; - phys_addr_t phys_addr = 0; - bool capable = false; - - if (!dev->dma_mask) { - return false; - } - - check_addr = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (NULL == check_addr) { - dev_err(dev, "failed allocating page!\n"); - return false; - } - - phys_addr = virt_to_phys(check_addr); - dma_addr = phys_to_dma(dev, phys_addr); - - capable = is_dma_capable(dev, dma_addr, PAGE_SIZE); - kfree(check_addr); - return capable; -} - -static int hailo_get_allocation_mode(struct pci_dev *pdev, enum hailo_allocation_mode *allocation_mode) -{ - // Check if module paramater was given to override driver choice - if (HAILO_NO_FORCE_BUFFER != force_allocation_from_driver) { - if (HAILO_FORCE_BUFFER_FROM_USERSPACE == force_allocation_from_driver) { - *allocation_mode = HAILO_ALLOCATION_MODE_USERSPACE; - pci_notice(pdev, "Probing: Using userspace allocated vdma buffers\n"); - } - else if (HAILO_FORCE_BUFFER_FROM_DRIVER == force_allocation_from_driver) { - *allocation_mode = HAILO_ALLOCATION_MODE_DRIVER; - pci_notice(pdev, "Probing: Using driver allocated vdma buffers\n"); - } - else { - pci_err(pdev, "Invalid value for force allocation driver paramater - value given: %d!\n", - force_allocation_from_driver); - return -EINVAL; - } - - return 0; - } - - if (is_kmalloc_dma_capable(&pdev->dev)) { - *allocation_mode = HAILO_ALLOCATION_MODE_USERSPACE; - pci_notice(pdev, "Probing: Using userspace allocated vdma buffers\n"); - } else { - *allocation_mode = HAILO_ALLOCATION_MODE_DRIVER; - pci_notice(pdev, "Probing: Using driver allocated vdma buffers\n"); - } - - return 0; -} - -static int hailo_pcie_probe(struct pci_dev* pDev, const struct pci_device_id* id) -{ - struct hailo_pcie_board * pBoard; - struct device *char_device = NULL; - int err = -EINVAL; - - pci_notice(pDev, "Probing on: %04x:%04x...\n", pDev->vendor, pDev->device); -#ifdef HAILO_EMULATOR - pci_notice(pDev, "PCIe driver was compiled in emulator mode\n"); -#endif /* HAILO_EMULATOR */ - if (!g_is_power_mode_enabled) { - pci_notice(pDev, "PCIe driver was compiled with power modes disabled\n"); - } - - /* Initialize device extension for the board*/ - pci_notice(pDev, "Probing: Allocate memory for device extension, %zu\n", sizeof(struct hailo_pcie_board)); - pBoard = (struct hailo_pcie_board*) kzalloc( sizeof(struct hailo_pcie_board), GFP_KERNEL); - if (pBoard == NULL) - { - pci_err(pDev, "Probing: Failed to allocate memory for device extension structure\n"); - err = -ENOMEM; - goto probe_exit; - } - - pBoard->pDev = pDev; - - if ( (err = pci_enable_device(pDev)) ) - { - pci_err(pDev, "Probing: Failed calling pci_enable_device %d\n", err); - goto probe_free_board; - } - pci_notice(pDev, "Probing: Device enabled\n"); - - pci_set_master(pDev); - - err = pcie_resources_init(pDev, &pBoard->pcie_resources, id->driver_data); - if (err < 0) { - pci_err(pDev, "Probing: Failed init pcie resources"); - goto probe_disable_device; - } - - err = hailo_get_desc_page_size(pDev, &pBoard->desc_max_page_size); - if (err < 0) { - goto probe_release_pcie_resources; - } - - pBoard->interrupts_enabled = false; - pBoard->fw_boot.is_in_boot = false; - init_completion(&pBoard->fw_boot.fw_loaded_completion); - - sema_init(&pBoard->mutex, 1); - atomic_set(&pBoard->ref_count, 0); - INIT_LIST_HEAD(&pBoard->open_files_list); - - // Init both soc and nnc, since the interrupts are shared. - hailo_nnc_init(&pBoard->nnc); - hailo_soc_init(&pBoard->soc); - - init_completion(&pBoard->driver_down.reset_completed); - init_completion(&pBoard->soft_reset.reset_completed); - - memset(&pBoard->memory_transfer_params, 0, sizeof(pBoard->memory_transfer_params)); - - err = hailo_pcie_vdma_controller_init(&pBoard->vdma, &pBoard->pDev->dev, - &pBoard->pcie_resources.vdma_registers); - if (err < 0) { - hailo_err(pBoard, "Failed init vdma controller %d\n", err); - goto probe_release_pcie_resources; - } - - // Checks the dma mask => it must be called after the device's dma_mask is set by hailo_pcie_vdma_controller_init - err = hailo_get_allocation_mode(pDev, &pBoard->allocation_mode); - if (err < 0) { - pci_err(pDev, "Failed determining allocation of buffers from driver. error type: %d\n", err); - goto probe_release_pcie_resources; - } - - // Initialize the boot channel bitmap to 1 since channel 0 is always used for boot - // (we will always use at least 1 channel which is LSB in the bitmap) - pBoard->fw_boot.boot_used_channel_bitmap = (1 << 0); - memset(&pBoard->fw_boot.boot_dma_state, 0, sizeof(pBoard->fw_boot.boot_dma_state)); - err = hailo_activate_board(pBoard); - if (err < 0) { - hailo_err(pBoard, "Failed activating board %d\n", err); - goto probe_release_pcie_resources; - } - - /* Keep track on the device, in order, to be able to remove it later */ - pci_set_drvdata(pDev, pBoard); - hailo_pcie_insert_board(pBoard); - - /* Create dynamically the device node*/ - char_device = device_create_with_groups(chardev_class, NULL, - MKDEV(char_major, pBoard->board_index), - pBoard, - g_hailo_dev_groups, - DEVICE_NODE_NAME"%d", pBoard->board_index); - if (IS_ERR(char_device)) { - hailo_err(pBoard, "Failed creating dynamic device %d\n", pBoard->board_index); - err = PTR_ERR(char_device); - goto probe_remove_board; - } - - hailo_notice(pBoard, "Probing: Added board %0x-%0x, /dev/hailo%d\n", pDev->vendor, pDev->device, pBoard->board_index); - - return 0; - -probe_remove_board: - hailo_pcie_remove_board(pBoard); - -probe_release_pcie_resources: - pcie_resources_release(pBoard->pDev, &pBoard->pcie_resources); - -probe_disable_device: - pci_disable_device(pDev); - -probe_free_board: - kfree(pBoard); - -probe_exit: - - return err; -} - -static void hailo_pcie_remove(struct pci_dev* pDev) -{ - struct hailo_pcie_board* pBoard = (struct hailo_pcie_board*) pci_get_drvdata(pDev); - - pci_notice(pDev, "Remove: Releasing board\n"); - - if (pBoard) - { - - // lock board to wait for any pending operations and for synchronization with open - down(&pBoard->mutex); - - - // remove board from active boards list - hailo_pcie_remove_board(pBoard); - - - /* Delete the device node */ - device_destroy(chardev_class, MKDEV(char_major, pBoard->board_index)); - - // disable interrupts - will only disable if they have not been disabled in release already - hailo_disable_interrupts(pBoard); - - pcie_resources_release(pBoard->pDev, &pBoard->pcie_resources); - - // deassociate device from board to be picked up by char device - pBoard->pDev = NULL; - - pBoard->vdma.dev = NULL; - - pci_disable_device(pDev); - - pci_set_drvdata(pDev, NULL); - - hailo_nnc_finalize(&pBoard->nnc); - - up(&pBoard->mutex); - - if ( 0 == atomic_read(&pBoard->ref_count) ) - { - // nobody has the board open - free - pci_notice(pDev, "Remove: Freed board, /dev/hailo%d\n", pBoard->board_index); - kfree(pBoard); - } - else - { - // board resources are freed on last close - pci_notice(pDev, "Remove: Scheduled for board removal, /dev/hailo%d\n", pBoard->board_index); - } - } - -} - -inline int driver_down(struct hailo_pcie_board *board) -{ - if (board->pcie_resources.accelerator_type == HAILO_ACCELERATOR_TYPE_NNC) { - return hailo_nnc_driver_down(board); - } else { - return hailo_soc_driver_down(board); - } -} - -#ifdef CONFIG_PM_SLEEP -static int hailo_pcie_suspend(struct device *dev) -{ - struct hailo_pcie_board *board = (struct hailo_pcie_board*) dev_get_drvdata(dev); - struct hailo_file_context *cur = NULL; - int err = 0; - - // lock board to wait for any pending operations - down(&board->mutex); - - if (board->vdma.used_by_filp != NULL) { - err = driver_down(board); - if (err < 0) { - dev_notice(dev, "Error while trying to call FW to close vdma channels\n"); - } - } - - // Disable all interrupts. All interrupts from Hailo chip would be masked. - hailo_disable_interrupts(board); - - // Un validate all activae file contexts so every new action would return error to the user. - list_for_each_entry(cur, &board->open_files_list, open_files_list) { - cur->is_valid = false; - } - - // Release board - up(&board->mutex); - - dev_notice(dev, "PM's suspend\n"); - // Success Oriented - Continue system suspend even in case of error (otherwise system will not suspend correctly) - return 0; -} - -static int hailo_pcie_resume(struct device *dev) -{ - struct hailo_pcie_board *board = (struct hailo_pcie_board*) dev_get_drvdata(dev); - int err = 0; - - if ((err = hailo_activate_board(board)) < 0) { - dev_err(dev, "Failed activating board %d\n", err); - } - - dev_notice(dev, "PM's resume\n"); - // Success Oriented - Continue system resume even in case of error (otherwise system will not suspend correctly) - return 0; -} -#endif /* CONFIG_PM_SLEEP */ - -static SIMPLE_DEV_PM_OPS(hailo_pcie_pm_ops, hailo_pcie_suspend, hailo_pcie_resume); - -#if LINUX_VERSION_CODE >= KERNEL_VERSION( 3, 16, 0 ) -static void hailo_pci_reset_prepare(struct pci_dev *pdev) -{ - struct hailo_pcie_board* board = (struct hailo_pcie_board*) pci_get_drvdata(pdev); - int err = 0; - /* Reset preparation logic goes here */ - pci_err(pdev, "Reset preparation for PCI device \n"); - - if (board) - { - // lock board to wait for any pending operations and for synchronization with open - down(&board->mutex); - if (board->vdma.used_by_filp != NULL) { - // Try to close all vDMA channels before reset - err = driver_down(board); - if (err < 0) { - pci_err(pdev, "Error while trying to call FW to close vdma channels (errno %d)\n", err); - } - } - up(&board->mutex); - } -} -#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION( 3, 16, 0 ) */ - -#if LINUX_VERSION_CODE < KERNEL_VERSION( 4, 13, 0 ) && LINUX_VERSION_CODE >= KERNEL_VERSION( 3, 16, 0 ) -static void hailo_pci_reset_notify(struct pci_dev *pdev, bool prepare) -{ - if (prepare) { - hailo_pci_reset_prepare(pdev); - } -} -#endif - -static const struct pci_error_handlers hailo_pcie_err_handlers = { -#if LINUX_VERSION_CODE < KERNEL_VERSION( 3, 16, 0 ) -/* No FLR callback */ -#elif LINUX_VERSION_CODE < KERNEL_VERSION( 4, 13, 0 ) -/* FLR Callback is reset_notify */ - .reset_notify = hailo_pci_reset_notify, -#else -/* FLR Callback is reset_prepare */ - .reset_prepare = hailo_pci_reset_prepare, -#endif -}; - -static struct pci_device_id hailo_pcie_id_table[] = -{ - {PCI_DEVICE_DATA(HAILO, HAILO8, HAILO_BOARD_TYPE_HAILO8)}, - {PCI_DEVICE_DATA(HAILO, HAILO10H, HAILO_BOARD_TYPE_HAILO10H)}, - {PCI_DEVICE_DATA(HAILO, HAILO15L, HAILO_BOARD_TYPE_HAILO15L)}, - {0,0,0,0,0,0,0 }, -}; - -static struct file_operations hailo_pcie_fops = -{ - owner: THIS_MODULE, - unlocked_ioctl: hailo_pcie_fops_unlockedioctl, - mmap: hailo_pcie_fops_mmap, - open: hailo_pcie_fops_open, - release: hailo_pcie_fops_release -}; - - -static struct pci_driver hailo_pci_driver = -{ - name: DRIVER_NAME, - id_table: hailo_pcie_id_table, - probe: hailo_pcie_probe, - remove: hailo_pcie_remove, - driver: { - pm: &hailo_pcie_pm_ops, - }, - err_handler: &hailo_pcie_err_handlers, -}; - -MODULE_DEVICE_TABLE (pci, hailo_pcie_id_table); - -static int hailo_pcie_register_chrdev(unsigned int major, const char *name) -{ - int char_major; - - char_major = register_chrdev(major, name, &hailo_pcie_fops); - - chardev_class = class_create_compat("hailo_chardev"); - - return char_major; -} - -static void hailo_pcie_unregister_chrdev(unsigned int major, const char *name) -{ - class_destroy(chardev_class); - unregister_chrdev(major, name); -} - -static int __init hailo_pcie_module_init(void) -{ - int err; - - pr_notice(DRIVER_NAME ": Init module. driver version %s\n", HAILO_DRV_VER); - - if ( 0 > (char_major = hailo_pcie_register_chrdev(0, DRIVER_NAME)) ) - { - pr_err(DRIVER_NAME ": Init Error, failed to call register_chrdev.\n"); - - return char_major; - } - - if ( 0 != (err = pci_register_driver(&hailo_pci_driver))) - { - pr_err(DRIVER_NAME ": Init Error, failed to call pci_register_driver.\n"); - class_destroy(chardev_class); - hailo_pcie_unregister_chrdev(char_major, DRIVER_NAME); - return err; - } - - return 0; -} - -static void __exit hailo_pcie_module_exit(void) -{ - - pr_notice(DRIVER_NAME ": Exit module.\n"); - - // Unregister the driver from pci bus - pci_unregister_driver(&hailo_pci_driver); - hailo_pcie_unregister_chrdev(char_major, DRIVER_NAME); - - pr_notice(DRIVER_NAME ": Hailo PCIe driver unloaded.\n"); -} - - -module_init(hailo_pcie_module_init); -module_exit(hailo_pcie_module_exit); - -module_param(o_dbg, int, S_IRUGO | S_IWUSR); - -module_param_named(no_power_mode, g_is_power_mode_enabled, invbool, S_IRUGO); -MODULE_PARM_DESC(no_power_mode, "Disables automatic D0->D3 PCIe transactions"); - -module_param(force_allocation_from_driver, int, S_IRUGO); -MODULE_PARM_DESC(force_allocation_from_driver, "Determines whether to force buffer allocation from driver or userspace"); - -module_param(force_desc_page_size, int, S_IRUGO); -MODULE_PARM_DESC(force_desc_page_size, "Determines the maximum DMA descriptor page size (must be a power of 2)"); - -module_param(force_hailo10h_legacy_mode, bool, S_IRUGO); -MODULE_PARM_DESC(force_hailo10h_legacy_mode, "Forces work with Hailo10h in legacy mode(relevant for emulators)"); - -module_param(force_boot_linux_from_eemc, bool, S_IRUGO); -MODULE_PARM_DESC(force_boot_linux_from_eemc, "Boot the linux image from eemc (Requires special Image)"); - -module_param(support_soft_reset, bool, S_IRUGO); -MODULE_PARM_DESC(support_soft_reset, "enables driver reload to reload a new firmware as well"); - -MODULE_AUTHOR("Hailo Technologies Ltd."); -MODULE_DESCRIPTION("Hailo PCIe driver"); -MODULE_LICENSE("GPL v2"); -MODULE_VERSION(HAILO_DRV_VER); - diff --git a/drivers/media/pci/hailo/src/pcie.h b/drivers/media/pci/hailo/src/pcie.h deleted file mode 100644 index b90cbb6b2268e2..00000000000000 --- a/drivers/media/pci/hailo/src/pcie.h +++ /dev/null @@ -1,131 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/** - * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. - **/ - -#ifndef _HAILO_PCI_PCIE_H_ -#define _HAILO_PCI_PCIE_H_ - -#include "vdma/vdma.h" -#include "hailo_ioctl_common.h" -#include "pcie_common.h" -#include "utils/fw_common.h" - -#include -#include -#include -#include -#include - -#include - -#define HAILO_PCI_OVER_VDMA_NUM_CHANNELS (8) -#define HAILO_PCI_OVER_VDMA_PAGE_SIZE (512) - -struct hailo_fw_control_info { - // protects that only one fw control will be send at a time - struct semaphore mutex; - // called from the interrupt handler to notify that a response is ready - struct completion completion; - // the command we are currently handling - struct hailo_fw_control command; -}; - -struct hailo_pcie_driver_down_info { - // called from the interrupt handler to notify that FW completed reset - struct completion reset_completed; -}; - -struct hailo_pcie_soft_reset { - // called from the interrupt handler to notify that FW completed reset - struct completion reset_completed; -}; - -struct hailo_fw_boot { - // the filp that enabled interrupts for fw boot. the interrupt is enabled if this is not null - struct file *filp; - // called from the interrupt handler to notify that an interrupt was raised - struct completion completion; -}; - - -struct hailo_pcie_nnc { - struct hailo_fw_control_info fw_control; - - spinlock_t notification_read_spinlock; - struct list_head notification_wait_list; - struct hailo_d2h_notification notification_cache; - struct hailo_d2h_notification notification_to_user; -}; - -struct hailo_pcie_soc { - struct completion control_resp_ready; -}; - -// Context for each open file handle -// TODO: store board and use as actual context -struct hailo_file_context { - struct list_head open_files_list; - struct file *filp; - struct hailo_vdma_file_context vdma_context; - bool is_valid; - u32 soc_used_channels_bitmap; -}; - -struct hailo_pcie_boot_dma_channel_state { - struct hailo_descriptors_list_buffer host_descriptors_buffer; - struct hailo_descriptors_list_buffer device_descriptors_buffer; - struct sg_table sg_table; - u64 buffer_size; - void *kernel_addrs; - u32 desc_program_num; -}; - -struct hailo_pcie_boot_dma_state { - struct hailo_pcie_boot_dma_channel_state channels[HAILO_PCI_OVER_VDMA_NUM_CHANNELS]; - u8 curr_channel_index; -}; - -struct hailo_pcie_fw_boot { - struct hailo_pcie_boot_dma_state boot_dma_state; - // is_in_boot is set to true when the board is in boot mode - bool is_in_boot; - // boot_used_channel_bitmap is a bitmap of the channels that are used for boot - u16 boot_used_channel_bitmap; - // fw_loaded_completion is used to notify that the FW was loaded - SOC & NNC - struct completion fw_loaded_completion; - // vdma_boot_completion is used to notify that the vDMA boot data was transferred completely on all used channels for boot - struct completion vdma_boot_completion; -}; - -struct hailo_pcie_board { - struct list_head board_list; - struct pci_dev *pDev; - u32 board_index; - atomic_t ref_count; - struct list_head open_files_list; - struct hailo_pcie_resources pcie_resources; - struct hailo_pcie_nnc nnc; - struct hailo_pcie_soc soc; - struct hailo_pcie_driver_down_info driver_down; - struct hailo_pcie_soft_reset soft_reset; - struct semaphore mutex; - struct hailo_vdma_controller vdma; - - struct hailo_pcie_fw_boot fw_boot; - - struct hailo_memory_transfer_params memory_transfer_params; - u32 desc_max_page_size; - enum hailo_allocation_mode allocation_mode; - bool interrupts_enabled; -}; - -bool power_mode_enabled(void); - -struct hailo_pcie_board* hailo_pcie_get_board_index(u32 index); -void hailo_disable_interrupts(struct hailo_pcie_board *board); -int hailo_enable_interrupts(struct hailo_pcie_board *board); -int hailo_pcie_soft_reset(struct hailo_pcie_resources *resources, struct completion *reset_completed); - -#endif /* _HAILO_PCI_PCIE_H_ */ - diff --git a/drivers/media/pci/hailo/src/soc.c b/drivers/media/pci/hailo/src/soc.c deleted file mode 100644 index 064567ce406aef..00000000000000 --- a/drivers/media/pci/hailo/src/soc.c +++ /dev/null @@ -1,244 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/** - * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. - **/ -/** - * A Hailo PCIe NNC device is a device contains a full SoC over PCIe. The SoC contains NNC (neural network core) and - * some application processor (pci_ep). - */ - -#include "soc.h" - -#include "vdma_common.h" -#include "utils/logs.h" -#include "vdma/memory.h" -#include "pcie_common.h" - -#include - -#ifndef HAILO_EMULATOR -#define PCI_SOC_CONTROL_CONNECT_TIMEOUT_MS (1000) -#else -#define PCI_SOC_CONTROL_CONNECT_TIMEOUT_MS (1000000) -#endif /* ifndef HAILO_EMULATOR */ - -void hailo_soc_init(struct hailo_pcie_soc *soc) -{ - init_completion(&soc->control_resp_ready); -} - -long hailo_soc_ioctl(struct hailo_pcie_board *board, struct hailo_file_context *context, - struct hailo_vdma_controller *controller, unsigned int cmd, unsigned long arg) -{ - switch (cmd) { - case HAILO_SOC_CONNECT: - return hailo_soc_connect_ioctl(board, context, controller, arg); - case HAILO_SOC_CLOSE: - return hailo_soc_close_ioctl(board, controller, context, arg); - default: - hailo_err(board, "Invalid pcie EP ioctl code 0x%x (nr: %d)\n", cmd, _IOC_NR(cmd)); - return -ENOTTY; - } -} - -static int soc_control(struct hailo_pcie_board *board, - const struct hailo_pcie_soc_request *request, - struct hailo_pcie_soc_response *response) -{ - int ret = 0; - reinit_completion(&board->soc.control_resp_ready); - - hailo_pcie_soc_write_request(&board->pcie_resources, request); - - ret = wait_for_completion_interruptible_timeout(&board->soc.control_resp_ready, - msecs_to_jiffies(PCI_SOC_CONTROL_CONNECT_TIMEOUT_MS)); - if (ret <= 0) { - if (0 == ret) { - hailo_err(board, "Timeout waiting for soc control (timeout_ms=%d)\n", PCI_SOC_CONTROL_CONNECT_TIMEOUT_MS); - return -ETIMEDOUT; - } else { - hailo_info(board, "soc control failed with err=%d (process was interrupted or killed)\n", - ret); - return ret; - } - } - - hailo_pcie_soc_read_response(&board->pcie_resources, response); - - if (response->status < 0) { - hailo_err(board, "soc control failed with status=%d\n", response->status); - return response->status; - } - - if (response->control_code != request->control_code) { - hailo_err(board, "Invalid response control code %d (expected %d)\n", - response->control_code, request->control_code); - return -EINVAL; - } - - return 0; -} - -long hailo_soc_connect_ioctl(struct hailo_pcie_board *board, struct hailo_file_context *context, - struct hailo_vdma_controller *controller, unsigned long arg) -{ - struct hailo_pcie_soc_request request = {0}; - struct hailo_pcie_soc_response response = {0}; - struct hailo_soc_connect_params params; - struct hailo_vdma_channel *input_channel = NULL; - struct hailo_vdma_channel *output_channel = NULL; - struct hailo_vdma_engine *vdma_engine = &controller->vdma_engines[PCI_VDMA_ENGINE_INDEX]; - struct hailo_descriptors_list_buffer *input_descriptors_buffer = NULL; - struct hailo_descriptors_list_buffer *output_descriptors_buffer = NULL; - int err = 0; - - if (copy_from_user(¶ms, (void *)arg, sizeof(params))) { - hailo_err(board, "copy_from_user fail\n"); - return -ENOMEM; - } - - request = (struct hailo_pcie_soc_request) { - .control_code = HAILO_PCIE_SOC_CONTROL_CODE_CONNECT, - .connect = { - .port = params.port_number - } - }; - err = soc_control(board, &request, &response); - if (err < 0) { - return err; - } - - params.input_channel_index = response.connect.input_channel_index; - params.output_channel_index = response.connect.output_channel_index; - - if (!hailo_check_channel_index(params.input_channel_index, controller->hw->src_channels_bitmask, true)) { - hailo_dev_err(&board->pDev->dev, "Invalid input channel index %u\n", params.input_channel_index); - return -EINVAL; - } - - if (!hailo_check_channel_index(params.output_channel_index, controller->hw->src_channels_bitmask, false)) { - hailo_dev_err(&board->pDev->dev, "Invalid output channel index %u\n", params.output_channel_index); - return -EINVAL; - } - - input_channel = &vdma_engine->channels[params.input_channel_index]; - output_channel = &vdma_engine->channels[params.output_channel_index]; - - input_descriptors_buffer = hailo_vdma_find_descriptors_buffer(&context->vdma_context, params.input_desc_handle); - output_descriptors_buffer = hailo_vdma_find_descriptors_buffer(&context->vdma_context, params.output_desc_handle); - if (NULL == input_descriptors_buffer || NULL == output_descriptors_buffer) { - hailo_dev_err(&board->pDev->dev, "input / output descriptors buffer not found \n"); - return -EINVAL; - } - - if (!is_powerof2((size_t)input_descriptors_buffer->desc_list.desc_count) || - !is_powerof2((size_t)output_descriptors_buffer->desc_list.desc_count)) { - hailo_dev_err(&board->pDev->dev, "Invalid desc list size\n"); - return -EINVAL; - } - - // configure and start input channel - // DMA Direction is only to get channel index - so - err = hailo_vdma_start_channel(input_channel->host_regs, input_descriptors_buffer->dma_address, input_descriptors_buffer->desc_list.desc_count, - board->vdma.hw->ddr_data_id); - if (err < 0) { - hailo_dev_err(&board->pDev->dev, "Error starting vdma input channel index %u\n", params.input_channel_index); - return -EINVAL; - } - - // Store the input channels state in bitmap (open) - hailo_set_bit(params.input_channel_index, &context->soc_used_channels_bitmap); - - // configure and start output channel - // DMA Direction is only to get channel index - so - err = hailo_vdma_start_channel(output_channel->host_regs, output_descriptors_buffer->dma_address, output_descriptors_buffer->desc_list.desc_count, - board->vdma.hw->ddr_data_id); - if (err < 0) { - hailo_dev_err(&board->pDev->dev, "Error starting vdma output channel index %u\n", params.output_channel_index); - // Close input channel - hailo_vdma_stop_channel(input_channel->host_regs); - return -EINVAL; - } - - // Store the output channels state in bitmap (open) - hailo_set_bit(params.output_channel_index, &context->soc_used_channels_bitmap); - - if (copy_to_user((void *)arg, ¶ms, sizeof(params))) { - hailo_dev_err(&board->pDev->dev, "copy_to_user fail\n"); - return -ENOMEM; - } - - return 0; -} - -static int close_channels(struct hailo_pcie_board *board, u32 channels_bitmap) -{ - struct hailo_pcie_soc_request request = {0}; - struct hailo_pcie_soc_response response = {0}; - struct hailo_vdma_engine *engine = &board->vdma.vdma_engines[PCI_VDMA_ENGINE_INDEX]; - struct hailo_vdma_channel *channel = NULL; - u8 channel_index = 0; - - hailo_info(board, "Closing channels bitmap 0x%x\n", channels_bitmap); - for_each_vdma_channel(engine, channel, channel_index) { - if (hailo_test_bit(channel_index, &channels_bitmap)) { - hailo_vdma_stop_channel(channel->host_regs); - } - } - - request = (struct hailo_pcie_soc_request) { - .control_code = HAILO_PCIE_SOC_CONTROL_CODE_CLOSE, - .close = { - .channels_bitmap = channels_bitmap - } - }; - return soc_control(board, &request, &response); -} - -long hailo_soc_close_ioctl(struct hailo_pcie_board *board, struct hailo_vdma_controller *controller, - struct hailo_file_context *context, unsigned long arg) -{ - struct hailo_soc_close_params params; - u32 channels_bitmap = 0; - int err = 0; - - if (copy_from_user(¶ms, (void *)arg, sizeof(params))) { - hailo_dev_err(&board->pDev->dev, "copy_from_user fail\n"); - return -ENOMEM; - } - - // TOOD: check channels are connected - - channels_bitmap = (1 << params.input_channel_index) | (1 << params.output_channel_index); - - err = close_channels(board, channels_bitmap); - if (0 != err) { - hailo_dev_err(&board->pDev->dev, "Error closing channels\n"); - return err; - } - - // Store the channel state in bitmap (closed) - hailo_clear_bit(params.input_channel_index, &context->soc_used_channels_bitmap); - hailo_clear_bit(params.output_channel_index, &context->soc_used_channels_bitmap); - - return err; -} - -int hailo_soc_file_context_init(struct hailo_pcie_board *board, struct hailo_file_context *context) -{ - // Nothing to init yet - return 0; -} - -void hailo_soc_file_context_finalize(struct hailo_pcie_board *board, struct hailo_file_context *context) -{ - // close only channels connected by this (by bitmap) - if (context->soc_used_channels_bitmap != 0) { - close_channels(board, context->soc_used_channels_bitmap); - } -} - -int hailo_soc_driver_down(struct hailo_pcie_board *board) -{ - return close_channels(board, 0xFFFFFFFF); -} \ No newline at end of file diff --git a/drivers/media/pci/hailo/src/soc.h b/drivers/media/pci/hailo/src/soc.h deleted file mode 100644 index 6b2e64deb30011..00000000000000 --- a/drivers/media/pci/hailo/src/soc.h +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/** - * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. - **/ - -#ifndef _HAILO_PCI_SOC_IOCTL_H_ -#define _HAILO_PCI_SOC_IOCTL_H_ - -#include "vdma/ioctl.h" -#include "pcie.h" - - -void hailo_soc_init(struct hailo_pcie_soc *soc); - -long hailo_soc_ioctl(struct hailo_pcie_board *board, struct hailo_file_context *context, - struct hailo_vdma_controller *controller, unsigned int cmd, unsigned long arg); -long hailo_soc_connect_ioctl(struct hailo_pcie_board *board, struct hailo_file_context *context, - struct hailo_vdma_controller *controller, unsigned long arg); -long hailo_soc_close_ioctl(struct hailo_pcie_board *board, struct hailo_vdma_controller *controller, struct hailo_file_context *context, unsigned long arg); - -int hailo_soc_file_context_init(struct hailo_pcie_board *board, struct hailo_file_context *context); -void hailo_soc_file_context_finalize(struct hailo_pcie_board *board, struct hailo_file_context *context); - -int hailo_soc_driver_down(struct hailo_pcie_board *board); - -#endif // _HAILO_PCI_SOC_IOCTL_H_ \ No newline at end of file diff --git a/drivers/media/pci/hailo/src/sysfs.c b/drivers/media/pci/hailo/src/sysfs.c deleted file mode 100644 index 67bccac5e097c1..00000000000000 --- a/drivers/media/pci/hailo/src/sysfs.c +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/** - * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. - **/ - -#include "sysfs.h" -#include "pcie.h" - -#include -#include - -static ssize_t board_location_show(struct device *dev, struct device_attribute *_attr, - char *buf) -{ - struct hailo_pcie_board *board = (struct hailo_pcie_board *)dev_get_drvdata(dev); - const char *dev_info = pci_name(board->pDev); - return sprintf(buf, "%s", dev_info); -} -static DEVICE_ATTR_RO(board_location); - -static ssize_t device_id_show(struct device *dev, struct device_attribute *_attr, - char *buf) -{ - struct hailo_pcie_board *board = (struct hailo_pcie_board *)dev_get_drvdata(dev); - return sprintf(buf, "%x:%x", board->pDev->vendor, board->pDev->device); -} -static DEVICE_ATTR_RO(device_id); - -static ssize_t accelerator_type_show(struct device *dev, struct device_attribute *_attr, - char *buf) -{ - struct hailo_pcie_board *board = (struct hailo_pcie_board *)dev_get_drvdata(dev); - return sprintf(buf, "%d", board->pcie_resources.accelerator_type); -} -static DEVICE_ATTR_RO(accelerator_type); - -static struct attribute *hailo_dev_attrs[] = { - &dev_attr_board_location.attr, - &dev_attr_device_id.attr, - &dev_attr_accelerator_type.attr, - NULL -}; - -ATTRIBUTE_GROUPS(hailo_dev); -const struct attribute_group **g_hailo_dev_groups = hailo_dev_groups; diff --git a/drivers/media/pci/hailo/src/sysfs.h b/drivers/media/pci/hailo/src/sysfs.h deleted file mode 100644 index 135fb37f794286..00000000000000 --- a/drivers/media/pci/hailo/src/sysfs.h +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/** - * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. - **/ - -#ifndef _HAILO_PCI_SYSFS_H_ -#define _HAILO_PCI_SYSFS_H_ - -#include - -extern const struct attribute_group **g_hailo_dev_groups; - -#endif /* _HAILO_PCI_SYSFS_H_ */ diff --git a/drivers/media/pci/hailo/src/utils.h b/drivers/media/pci/hailo/src/utils.h deleted file mode 100644 index b357150086c70c..00000000000000 --- a/drivers/media/pci/hailo/src/utils.h +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/** - * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. - **/ - -#ifndef _HAILO_PCI_UTILS_H_ -#define _HAILO_PCI_UTILS_H_ - -#include -#include -#include -#include -#include -#include -#include - -#include "pcie.h" - -void hailo_pcie_clear_notification_wait_list(struct hailo_pcie_board *pBoard, struct file *filp); - -#endif /* _HAILO_PCI_UTILS_H_ */ diff --git a/drivers/media/pci/hailo/utils/compact.h b/drivers/media/pci/hailo/utils/compact.h deleted file mode 100644 index 81d0dd5c053174..00000000000000 --- a/drivers/media/pci/hailo/utils/compact.h +++ /dev/null @@ -1,161 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/** - * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. - **/ - -#ifndef _HAILO_PCI_COMPACT_H_ -#define _HAILO_PCI_COMPACT_H_ - -#include -#include -#include - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0) -#define class_create_compat class_create -#else -#define class_create_compat(name) class_create(THIS_MODULE, name) -#endif - -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0) -#define pci_printk(level, pdev, fmt, arg...) \ - dev_printk(level, &(pdev)->dev, fmt, ##arg) -#define pci_emerg(pdev, fmt, arg...) dev_emerg(&(pdev)->dev, fmt, ##arg) -#define pci_alert(pdev, fmt, arg...) dev_alert(&(pdev)->dev, fmt, ##arg) -#define pci_crit(pdev, fmt, arg...) dev_crit(&(pdev)->dev, fmt, ##arg) -#define pci_err(pdev, fmt, arg...) dev_err(&(pdev)->dev, fmt, ##arg) -#define pci_warn(pdev, fmt, arg...) dev_warn(&(pdev)->dev, fmt, ##arg) -#define pci_notice(pdev, fmt, arg...) dev_notice(&(pdev)->dev, fmt, ##arg) -#define pci_info(pdev, fmt, arg...) dev_info(&(pdev)->dev, fmt, ##arg) -#define pci_dbg(pdev, fmt, arg...) dev_dbg(&(pdev)->dev, fmt, ##arg) -#endif - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 5, 0) -#define get_user_pages_compact get_user_pages -#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0) -#define get_user_pages_compact(start, nr_pages, gup_flags, pages) \ - get_user_pages(start, nr_pages, gup_flags, pages, NULL) -#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 168)) && (LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0)) -#define get_user_pages_compact(start, nr_pages, gup_flags, pages) \ - get_user_pages(current, current->mm, start, nr_pages, gup_flags, pages, NULL) -#else -static inline long get_user_pages_compact(unsigned long start, unsigned long nr_pages, - unsigned int gup_flags, struct page **pages) -{ - int write = !!((gup_flags & FOLL_WRITE) == FOLL_WRITE); - int force = !!((gup_flags & FOLL_FORCE) == FOLL_FORCE); - return get_user_pages(current, current->mm, start, nr_pages, write, force, - pages, NULL); -} -#endif - -#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 8, 0) -static inline void dma_sync_sgtable_for_device(struct device *dev, - struct sg_table *sgt, enum dma_data_direction dir) -{ - dma_sync_sg_for_device(dev, sgt->sgl, sgt->orig_nents, dir); -} -#endif - -#ifndef _LINUX_MMAP_LOCK_H -static inline void mmap_read_lock(struct mm_struct *mm) -{ - down_read(&mm->mmap_sem); -} - -static inline void mmap_read_unlock(struct mm_struct *mm) -{ - up_read(&mm->mmap_sem); -} -#endif /* _LINUX_MMAP_LOCK_H */ - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) && LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0) -#define sg_alloc_table_from_pages_segment_compat __sg_alloc_table_from_pages -#else -static inline struct scatterlist *sg_alloc_table_from_pages_segment_compat(struct sg_table *sgt, - struct page **pages, unsigned int n_pages, unsigned int offset, - unsigned long size, unsigned int max_segment, - struct scatterlist *prv, unsigned int left_pages, - gfp_t gfp_mask) -{ - int res = 0; - - if (NULL != prv) { - // prv not suported - return ERR_PTR(-EINVAL); - } - - if (0 != left_pages) { - // Left pages not supported - return ERR_PTR(-EINVAL); - } - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) - res = sg_alloc_table_from_pages_segment(sgt, pages, n_pages, offset, size, max_segment, gfp_mask); -#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) - res = __sg_alloc_table_from_pages(sgt, pages, n_pages, offset, size, max_segment, gfp_mask); -#else - res = sg_alloc_table_from_pages(sgt, pages, n_pages, offset, size, gfp_mask); -#endif - if (res < 0) { - return ERR_PTR(res); - } - - return sgt->sgl; -} -#endif - -#if LINUX_VERSION_CODE >= KERNEL_VERSION( 5, 0, 0 ) -#define compatible_access_ok(a,b,c) access_ok(b, c) -#else -#define compatible_access_ok(a,b,c) access_ok(a, b, c) -#endif - -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0) -#define PCI_DEVICE_DATA(vend, dev, data) \ - .vendor = PCI_VENDOR_ID_##vend, .device = PCI_DEVICE_ID_##vend##_##dev, \ - .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, 0, 0, \ - .driver_data = (kernel_ulong_t)(data) -#endif - -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0) -// On kernels < 4.1.12, kvmalloc, kvfree is not implemented. For simplicity, instead of implement our own -// kvmalloc/kvfree, just using vmalloc and vfree (It may reduce allocate/access performance, but it worth it). -static inline void *kvmalloc_array(size_t n, size_t size, gfp_t flags) -{ - (void)flags; //ignore - return vmalloc(n * size); -} - -#define kvfree vfree -#endif - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) -static inline bool is_dma_capable(struct device *dev, dma_addr_t dma_addr, size_t size) -{ -// Case for Rasberry Pie kernel versions 5.4.83 <=> 5.5.0 - already changed bus_dma_mask -> bus_dma_limit -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0)) || (defined(HAILO_RASBERRY_PIE) && LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 83)) - const u64 bus_dma_limit = dev->bus_dma_limit; -#else - const u64 bus_dma_limit = dev->bus_dma_mask; -#endif - - return (dma_addr <= min_not_zero(*dev->dma_mask, bus_dma_limit)); -} -#else -static inline bool is_dma_capable(struct device *dev, dma_addr_t dma_addr, size_t size) -{ - // Implementation of dma_capable from linux kernel - const u64 bus_dma_limit = (*dev->dma_mask + 1) & ~(*dev->dma_mask); - if (bus_dma_limit && size > bus_dma_limit) { - return false; - } - - if ((dma_addr | (dma_addr + size - 1)) & ~(*dev->dma_mask)) { - return false; - } - - return true; -} -#endif // LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) - -#endif /* _HAILO_PCI_COMPACT_H_ */ \ No newline at end of file diff --git a/drivers/media/pci/hailo/utils/fw_common.h b/drivers/media/pci/hailo/utils/fw_common.h deleted file mode 100644 index 5f61cf3f07399f..00000000000000 --- a/drivers/media/pci/hailo/utils/fw_common.h +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/** - * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. - **/ - -#ifndef _HAILO_LINUX_COMMON_H_ -#define _HAILO_LINUX_COMMON_H_ - -#include "hailo_ioctl_common.h" - -struct hailo_notification_wait { - struct list_head notification_wait_list; - int tgid; - struct file* filp; - struct completion notification_completion; - bool is_disabled; -}; - -#endif /* _HAILO_LINUX_COMMON_H_ */ \ No newline at end of file diff --git a/drivers/media/pci/hailo/utils/integrated_nnc_utils.c b/drivers/media/pci/hailo/utils/integrated_nnc_utils.c deleted file mode 100755 index 4b717e42b4e982..00000000000000 --- a/drivers/media/pci/hailo/utils/integrated_nnc_utils.c +++ /dev/null @@ -1,109 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/** - * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. - **/ - -#include "integrated_nnc_utils.h" -#include "utils/logs.h" - -#include -#include -#include -#include - -int hailo_ioremap_resource(struct platform_device *pdev, struct hailo_resource *resource, - const char *name) -{ - void __iomem *address; - struct resource *platform_resource = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); - if (NULL == platform_resource) { - return -ENOENT; - } - - address = devm_ioremap_resource(&pdev->dev, platform_resource); - if (IS_ERR(address)) { - return PTR_ERR(address); - } - - resource->address = (uintptr_t)address; - resource->size = resource_size(platform_resource); - - hailo_dev_dbg(&pdev->dev, "resource[%s]: remap %pr of %zx bytes to virtual start address %lx\n", - platform_resource->name, platform_resource, resource->size, (uintptr_t)address); - - return 0; -} - -// TODO: HRT-8475 - change to name instead of index -int hailo_ioremap_shmem(struct platform_device *pdev, int index, struct hailo_resource *resource) -{ - int ret; - struct resource res; - struct device_node *shmem; - void __iomem * remap_ptr; - - shmem = of_parse_phandle(pdev->dev.of_node, "shmem", index); - if (!shmem) { - hailo_dev_err(&pdev->dev, "Failed to find shmem node index: %d in device tree\n", index); - return -ENODEV; - } - - ret = of_address_to_resource(shmem, 0, &res); - if (ret) { - hailo_dev_err(&pdev->dev, "hailo_ioremap_shmem, failed to get memory (index: %d)\n", index); - of_node_put(shmem); - return ret; - } - - // Decrement the refcount of the node - of_node_put(shmem); - - remap_ptr = devm_ioremap(&pdev->dev, res.start, resource_size(&res)); - if (!remap_ptr) { - hailo_dev_err(&pdev->dev, "hailo_ioremap_shmem, failed to ioremap shmem (index: %d)\n", index); - return -EADDRNOTAVAIL; - } - - resource->address = (uintptr_t)remap_ptr; - resource->size = resource_size(&res); - - return 0; -} - -int direct_memory_transfer(struct platform_device *pdev, struct hailo_memory_transfer_params *params) -{ - int err = -EINVAL; - void __iomem *mem = ioremap(params->address, params->count); - if (NULL == mem) { - hailo_dev_err(&pdev->dev, "Failed ioremap %llu %zu\n", params->address, params->count); - return -ENOMEM; - } - - switch (params->transfer_direction) { - case TRANSFER_READ: - memcpy_fromio(params->buffer, mem, params->count); - err = 0; - break; - case TRANSFER_WRITE: - memcpy_toio(mem, params->buffer, params->count); - err = 0; - break; - default: - hailo_dev_err(&pdev->dev, "Invalid transfer direction %d\n", (int)params->transfer_direction); - err = -EINVAL; - } - - iounmap(mem); - return err; -} - -int hailo_get_resource_physical_addr(struct platform_device *pdev, const char *name, u64 *address) -{ - struct resource *platform_resource = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); - if (NULL == platform_resource) { - return -ENOENT; - } - - *address = (u64)(platform_resource->start); - return 0; -} \ No newline at end of file diff --git a/drivers/media/pci/hailo/utils/integrated_nnc_utils.h b/drivers/media/pci/hailo/utils/integrated_nnc_utils.h deleted file mode 100755 index 4ec23d8ce3f980..00000000000000 --- a/drivers/media/pci/hailo/utils/integrated_nnc_utils.h +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/** - * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. - **/ - -#ifndef _INTEGRATED_NNC_UTILS_H_ -#define _INTEGRATED_NNC_UTILS_H_ - -#include -#include "hailo_resource.h" - -#define HAILO15_CORE_CONTROL_MAILBOX_INDEX (0) -#define HAILO15_CORE_NOTIFICATION_MAILBOX_INDEX (1) -#define HAILO15_CORE_DRIVER_DOWN_MAILBOX_INDEX (2) - -#define HAILO15_CORE_CONTROL_MAILBOX_TX_SHMEM_INDEX (0) -#define HAILO15_CORE_CONTROL_MAILBOX_RX_SHMEM_INDEX (1) -#define HAILO15_CORE_NOTIFICATION_MAILBOX_RX_SHMEM_INDEX (2) - -int hailo_ioremap_resource(struct platform_device *pdev, struct hailo_resource *resource, - const char *name); - -// TODO: HRT-8475 - change to name instead of index -int hailo_ioremap_shmem(struct platform_device *pdev, int index, struct hailo_resource *resource); - -int direct_memory_transfer(struct platform_device *pDev, struct hailo_memory_transfer_params *params); - -int hailo_get_resource_physical_addr(struct platform_device *pdev, const char *name, u64 *address); - -#endif /* _INTEGRATED_NNC_UTILS_H_ */ diff --git a/drivers/media/pci/hailo/utils/logs.c b/drivers/media/pci/hailo/utils/logs.c deleted file mode 100644 index 3787fe1ffd0666..00000000000000 --- a/drivers/media/pci/hailo/utils/logs.c +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/** - * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. - **/ - -#include "logs.h" - -int o_dbg = LOGLEVEL_NOTICE; diff --git a/drivers/media/pci/hailo/utils/logs.h b/drivers/media/pci/hailo/utils/logs.h deleted file mode 100644 index 95b80a40524912..00000000000000 --- a/drivers/media/pci/hailo/utils/logs.h +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/** - * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. - **/ - -#ifndef _COMMON_LOGS_H_ -#define _COMMON_LOGS_H_ - -#include - -// Should be used only by "module_param". -// Specify the current debug level for the logs -extern int o_dbg; - - -// Logging, same interface as dev_*, uses o_dbg to filter -// log messages -#define hailo_printk(level, dev, fmt, ...) \ - do { \ - int __level = (level[1] - '0'); \ - if (__level <= o_dbg) { \ - dev_printk((level), dev, fmt, ##__VA_ARGS__); \ - } \ - } while (0) - -#define hailo_emerg(board, fmt, ...) hailo_printk(KERN_EMERG, &(board)->pDev->dev, fmt, ##__VA_ARGS__) -#define hailo_alert(board, fmt, ...) hailo_printk(KERN_ALERT, &(board)->pDev->dev, fmt, ##__VA_ARGS__) -#define hailo_crit(board, fmt, ...) hailo_printk(KERN_CRIT, &(board)->pDev->dev, fmt, ##__VA_ARGS__) -#define hailo_err(board, fmt, ...) hailo_printk(KERN_ERR, &(board)->pDev->dev, fmt, ##__VA_ARGS__) -#define hailo_warn(board, fmt, ...) hailo_printk(KERN_WARNING, &(board)->pDev->dev, fmt, ##__VA_ARGS__) -#define hailo_notice(board, fmt, ...) hailo_printk(KERN_NOTICE, &(board)->pDev->dev, fmt, ##__VA_ARGS__) -#define hailo_info(board, fmt, ...) hailo_printk(KERN_INFO, &(board)->pDev->dev, fmt, ##__VA_ARGS__) -#define hailo_dbg(board, fmt, ...) hailo_printk(KERN_DEBUG, &(board)->pDev->dev, fmt, ##__VA_ARGS__) - -#define hailo_dev_emerg(dev, fmt, ...) hailo_printk(KERN_EMERG, dev, fmt, ##__VA_ARGS__) -#define hailo_dev_alert(dev, fmt, ...) hailo_printk(KERN_ALERT, dev, fmt, ##__VA_ARGS__) -#define hailo_dev_crit(dev, fmt, ...) hailo_printk(KERN_CRIT, dev, fmt, ##__VA_ARGS__) -#define hailo_dev_err(dev, fmt, ...) hailo_printk(KERN_ERR, dev, fmt, ##__VA_ARGS__) -#define hailo_dev_warn(dev, fmt, ...) hailo_printk(KERN_WARNING, dev, fmt, ##__VA_ARGS__) -#define hailo_dev_notice(dev, fmt, ...) hailo_printk(KERN_NOTICE, dev, fmt, ##__VA_ARGS__) -#define hailo_dev_info(dev, fmt, ...) hailo_printk(KERN_INFO, dev, fmt, ##__VA_ARGS__) -#define hailo_dev_dbg(dev, fmt, ...) hailo_printk(KERN_DEBUG, dev, fmt, ##__VA_ARGS__) - - -#endif //_COMMON_LOGS_H_ \ No newline at end of file diff --git a/drivers/media/pci/hailo/vdma/ioctl.c b/drivers/media/pci/hailo/vdma/ioctl.c deleted file mode 100644 index d5be26a6d1883d..00000000000000 --- a/drivers/media/pci/hailo/vdma/ioctl.c +++ /dev/null @@ -1,721 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/** - * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. - **/ - -#include "ioctl.h" -#include "memory.h" -#include "utils/logs.h" -#include "utils.h" - -#include -#include - - -long hailo_vdma_enable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg, struct hailo_vdma_file_context *context) -{ - struct hailo_vdma_enable_channels_params input; - struct hailo_vdma_engine *engine = NULL; - u8 engine_index = 0; - u32 channels_bitmap = 0; - - if (copy_from_user(&input, (void *)arg, sizeof(input))) { - hailo_dev_err(controller->dev, "copy_from_user fail\n"); - return -ENOMEM; - } - - // Validate params (ignoring engine_index >= controller->vdma_engines_count). - for_each_vdma_engine(controller, engine, engine_index) { - channels_bitmap = input.channels_bitmap_per_engine[engine_index]; - if (0 != (channels_bitmap & engine->enabled_channels)) { - hailo_dev_err(controller->dev, "Trying to enable channels that are already enabled\n"); - return -EINVAL; - } - } - - for_each_vdma_engine(controller, engine, engine_index) { - channels_bitmap = input.channels_bitmap_per_engine[engine_index]; - hailo_vdma_engine_enable_channels(engine, channels_bitmap, - input.enable_timestamps_measure); - hailo_vdma_update_interrupts_mask(controller, engine_index); - hailo_dev_info(controller->dev, "Enabled interrupts for engine %u, channels bitmap 0x%x\n", - engine_index, channels_bitmap); - - // Update the context with the enabled channels bitmap - context->enabled_channels_bitmap[engine_index] |= channels_bitmap; - } - - return 0; -} - -long hailo_vdma_disable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg, struct hailo_vdma_file_context *context) -{ - struct hailo_vdma_disable_channels_params input; - struct hailo_vdma_engine *engine = NULL; - u8 engine_index = 0; - u32 channels_bitmap = 0; - unsigned long irq_saved_flags = 0; - - if (copy_from_user(&input, (void*)arg, sizeof(input))) { - hailo_dev_err(controller->dev, "copy_from_user fail\n"); - return -ENOMEM; - } - - // Validate params (ignoring engine_index >= controller->vdma_engines_count). - for_each_vdma_engine(controller, engine, engine_index) { - channels_bitmap = input.channels_bitmap_per_engine[engine_index]; - if (channels_bitmap != (channels_bitmap & engine->enabled_channels)) { - hailo_dev_warn(controller->dev, "Trying to disable channels that were not enabled\n"); - } - } - - for_each_vdma_engine(controller, engine, engine_index) { - channels_bitmap = input.channels_bitmap_per_engine[engine_index]; - hailo_vdma_engine_disable_channels(engine, channels_bitmap); - hailo_vdma_update_interrupts_mask(controller, engine_index); - - spin_lock_irqsave(&controller->interrupts_lock, irq_saved_flags); - hailo_vdma_engine_clear_channel_interrupts(engine, channels_bitmap); - spin_unlock_irqrestore(&controller->interrupts_lock, irq_saved_flags); - - hailo_dev_info(controller->dev, "Disabled channels for engine %u, bitmap 0x%x\n", - engine_index, channels_bitmap); - - // Update the context with the disabled channels bitmap - context->enabled_channels_bitmap[engine_index] &= ~channels_bitmap; - } - - // Wake up threads waiting - wake_up_interruptible_all(&controller->interrupts_wq); - - return 0; -} - -static bool got_interrupt(struct hailo_vdma_controller *controller, - u32 channels_bitmap_per_engine[MAX_VDMA_ENGINES]) -{ - struct hailo_vdma_engine *engine = NULL; - u8 engine_index = 0; - for_each_vdma_engine(controller, engine, engine_index) { - if (hailo_vdma_engine_got_interrupt(engine, - channels_bitmap_per_engine[engine_index])) { - return true; - } - } - return false; -} - -static void transfer_done(struct hailo_ongoing_transfer *transfer, void *opaque) -{ - u8 i = 0; - struct hailo_vdma_controller *controller = (struct hailo_vdma_controller *)opaque; - for (i = 0; i < transfer->buffers_count; i++) { - struct hailo_vdma_buffer *mapped_buffer = (struct hailo_vdma_buffer *)transfer->buffers[i].opaque; - hailo_vdma_buffer_sync_cyclic(controller, mapped_buffer, HAILO_SYNC_FOR_CPU, - transfer->buffers[i].offset, transfer->buffers[i].size); - } -} - -long hailo_vdma_interrupts_wait_ioctl(struct hailo_vdma_controller *controller, unsigned long arg, - struct semaphore *mutex, bool *should_up_board_mutex) -{ - long err = 0; - struct hailo_vdma_interrupts_wait_params params = {0}; - struct hailo_vdma_engine *engine = NULL; - bool bitmap_not_empty = false; - u8 engine_index = 0; - u32 irq_bitmap = 0; - unsigned long irq_saved_flags = 0; - - if (copy_from_user(¶ms, (void*)arg, sizeof(params))) { - hailo_dev_err(controller->dev, "HAILO_VDMA_INTERRUPTS_WAIT, copy_from_user fail\n"); - return -ENOMEM; - } - - // We don't need to validate that channels_bitmap_per_engine are enabled - - // If the channel is not enabled we just return an empty interrupts list. - - // Validate params (ignoring engine_index >= controller->vdma_engines_count). - // It us ok to wait on a disabled channel - the wait will just exit. - for_each_vdma_engine(controller, engine, engine_index) { - if (0 != params.channels_bitmap_per_engine[engine_index]) { - bitmap_not_empty = true; - } - } - if (!bitmap_not_empty) { - hailo_dev_err(controller->dev, "Got an empty bitmap for wait interrupts\n"); - return -EINVAL; - } - - up(mutex); - err = wait_event_interruptible(controller->interrupts_wq, - got_interrupt(controller, params.channels_bitmap_per_engine)); - if (err < 0) { - hailo_dev_info(controller->dev, - "wait channel interrupts failed with err=%ld (process was interrupted or killed)\n", err); - *should_up_board_mutex = false; - return err; - } - - if (down_interruptible(mutex)) { - hailo_dev_info(controller->dev, "down_interruptible error (process was interrupted or killed)\n"); - *should_up_board_mutex = false; - return -ERESTARTSYS; - } - - params.channels_count = 0; - for_each_vdma_engine(controller, engine, engine_index) { - - spin_lock_irqsave(&controller->interrupts_lock, irq_saved_flags); - irq_bitmap = hailo_vdma_engine_read_interrupts(engine, - params.channels_bitmap_per_engine[engine->index]); - spin_unlock_irqrestore(&controller->interrupts_lock, irq_saved_flags); - - err = hailo_vdma_engine_fill_irq_data(¶ms, engine, irq_bitmap, - transfer_done, controller); - if (err < 0) { - hailo_dev_err(controller->dev, "Failed fill irq data %ld", err); - return err; - } - } - - if (copy_to_user((void __user*)arg, ¶ms, sizeof(params))) { - hailo_dev_err(controller->dev, "copy_to_user fail\n"); - return -ENOMEM; - } - - return 0; -} - -static uintptr_t hailo_get_next_vdma_handle(struct hailo_vdma_file_context *context) -{ - // Note: The kernel code left-shifts the 'offset' param from the user-space call to mmap by PAGE_SHIFT bits and - // stores the result in 'vm_area_struct.vm_pgoff'. We pass the desc_handle to mmap in the offset param. To - // counter this, we right-shift the desc_handle. See also 'mmap function'. - uintptr_t next_handle = 0; - next_handle = atomic_inc_return(&context->last_vdma_handle); - return (next_handle << PAGE_SHIFT); -} - -long hailo_vdma_buffer_map_ioctl(struct hailo_vdma_file_context *context, struct hailo_vdma_controller *controller, - unsigned long arg) -{ - struct hailo_vdma_buffer_map_params buf_info; - struct hailo_vdma_buffer *mapped_buffer = NULL; - enum dma_data_direction direction = DMA_NONE; - struct hailo_vdma_low_memory_buffer *low_memory_buffer = NULL; - - if (copy_from_user(&buf_info, (void __user*)arg, sizeof(buf_info))) { - hailo_dev_err(controller->dev, "copy from user fail\n"); - return -EFAULT; - } - - hailo_dev_dbg(controller->dev, "address %lx tgid %d size: %zu\n", - buf_info.user_address, current->tgid, buf_info.size); - - direction = get_dma_direction(buf_info.data_direction); - if (DMA_NONE == direction) { - hailo_dev_err(controller->dev, "invalid data direction %d\n", buf_info.data_direction); - return -EINVAL; - } - - low_memory_buffer = hailo_vdma_find_low_memory_buffer(context, buf_info.allocated_buffer_handle); - - mapped_buffer = hailo_vdma_buffer_map(controller->dev, - buf_info.user_address, buf_info.size, direction, buf_info.buffer_type, low_memory_buffer); - if (IS_ERR(mapped_buffer)) { - hailo_dev_err(controller->dev, "failed map buffer %lx\n", buf_info.user_address); - return PTR_ERR(mapped_buffer); - } - - mapped_buffer->handle = atomic_inc_return(&context->last_vdma_user_buffer_handle); - buf_info.mapped_handle = mapped_buffer->handle; - if (copy_to_user((void __user*)arg, &buf_info, sizeof(buf_info))) { - hailo_dev_err(controller->dev, "copy_to_user fail\n"); - hailo_vdma_buffer_put(mapped_buffer); - return -EFAULT; - } - - list_add(&mapped_buffer->mapped_user_buffer_list, &context->mapped_user_buffer_list); - hailo_dev_dbg(controller->dev, "buffer %lx (handle %zu) is mapped\n", - buf_info.user_address, buf_info.mapped_handle); - return 0; -} - -long hailo_vdma_buffer_unmap_ioctl(struct hailo_vdma_file_context *context, struct hailo_vdma_controller *controller, - unsigned long arg) -{ - struct hailo_vdma_buffer *mapped_buffer = NULL; - struct hailo_vdma_buffer_unmap_params buffer_unmap_params; - - if (copy_from_user(&buffer_unmap_params, (void __user*)arg, sizeof(buffer_unmap_params))) { - hailo_dev_err(controller->dev, "copy from user fail\n"); - return -EFAULT; - } - - hailo_dev_dbg(controller->dev, "unmap user buffer handle %zu\n", buffer_unmap_params.mapped_handle); - - mapped_buffer = hailo_vdma_find_mapped_user_buffer(context, buffer_unmap_params.mapped_handle); - if (mapped_buffer == NULL) { - hailo_dev_warn(controller->dev, "buffer handle %zu not found\n", buffer_unmap_params.mapped_handle); - return -EINVAL; - } - - list_del(&mapped_buffer->mapped_user_buffer_list); - hailo_vdma_buffer_put(mapped_buffer); - return 0; -} - -long hailo_vdma_buffer_sync_ioctl(struct hailo_vdma_file_context *context, struct hailo_vdma_controller *controller, unsigned long arg) -{ - struct hailo_vdma_buffer_sync_params sync_info = {}; - struct hailo_vdma_buffer *mapped_buffer = NULL; - - if (copy_from_user(&sync_info, (void __user*)arg, sizeof(sync_info))) { - hailo_dev_err(controller->dev, "copy_from_user fail\n"); - return -EFAULT; - } - - if (!(mapped_buffer = hailo_vdma_find_mapped_user_buffer(context, sync_info.handle))) { - hailo_dev_err(controller->dev, "buffer handle %zu doesn't exist\n", sync_info.handle); - return -EINVAL; - } - - if ((sync_info.sync_type != HAILO_SYNC_FOR_CPU) && (sync_info.sync_type != HAILO_SYNC_FOR_DEVICE)) { - hailo_dev_err(controller->dev, "Invalid sync_type given for vdma buffer sync.\n"); - return -EINVAL; - } - - if (sync_info.offset + sync_info.count > mapped_buffer->size) { - hailo_dev_err(controller->dev, "Invalid offset/count given for vdma buffer sync. offset %zu count %zu buffer size %u\n", - sync_info.offset, sync_info.count, mapped_buffer->size); - return -EINVAL; - } - - hailo_vdma_buffer_sync(controller, mapped_buffer, sync_info.sync_type, - sync_info.offset, sync_info.count); - return 0; -} - -long hailo_desc_list_create_ioctl(struct hailo_vdma_file_context *context, struct hailo_vdma_controller *controller, - unsigned long arg) -{ - struct hailo_desc_list_create_params params; - struct hailo_descriptors_list_buffer *descriptors_buffer = NULL; - uintptr_t next_handle = 0; - long err = -EINVAL; - - if (copy_from_user(¶ms, (void __user*)arg, sizeof(params))) { - hailo_dev_err(controller->dev, "copy_from_user fail\n"); - return -EFAULT; - } - - if (params.is_circular && !is_powerof2(params.desc_count)) { - hailo_dev_err(controller->dev, "Invalid desc count given : %zu , circular descriptors count must be power of 2\n", - params.desc_count); - return -EINVAL; - } - - if (!is_powerof2(params.desc_page_size)) { - hailo_dev_err(controller->dev, "Invalid desc page size given : %u\n", - params.desc_page_size); - return -EINVAL; - } - - hailo_dev_info(controller->dev, - "Create desc list desc_count: %zu desc_page_size: %u\n", - params.desc_count, params.desc_page_size); - - descriptors_buffer = kzalloc(sizeof(*descriptors_buffer), GFP_KERNEL); - if (NULL == descriptors_buffer) { - hailo_dev_err(controller->dev, "Failed to allocate buffer for descriptors list struct\n"); - return -ENOMEM; - } - - next_handle = hailo_get_next_vdma_handle(context); - - err = hailo_desc_list_create(controller->dev, params.desc_count, - params.desc_page_size, next_handle, params.is_circular, - descriptors_buffer); - if (err < 0) { - hailo_dev_err(controller->dev, "failed to allocate descriptors buffer\n"); - kfree(descriptors_buffer); - return err; - } - - list_add(&descriptors_buffer->descriptors_buffer_list, &context->descriptors_buffer_list); - - // Note: The physical address is required for CONTEXT_SWITCH firmware controls - BUILD_BUG_ON(sizeof(params.dma_address) < sizeof(descriptors_buffer->dma_address)); - params.dma_address = descriptors_buffer->dma_address; - params.desc_handle = descriptors_buffer->handle; - - if(copy_to_user((void __user*)arg, ¶ms, sizeof(params))){ - hailo_dev_err(controller->dev, "copy_to_user fail\n"); - list_del(&descriptors_buffer->descriptors_buffer_list); - hailo_desc_list_release(controller->dev, descriptors_buffer); - kfree(descriptors_buffer); - return -EFAULT; - } - - hailo_dev_info(controller->dev, "Created desc list, handle 0x%llu\n", - (u64)params.desc_handle); - return 0; -} - -long hailo_desc_list_release_ioctl(struct hailo_vdma_file_context *context, struct hailo_vdma_controller *controller, - unsigned long arg) -{ - struct hailo_desc_list_release_params params; - struct hailo_descriptors_list_buffer *descriptors_buffer = NULL; - - if (copy_from_user(¶ms, (void __user*)arg, sizeof(params))) { - hailo_dev_err(controller->dev, "copy_from_user fail\n"); - return -EFAULT; - } - - descriptors_buffer = hailo_vdma_find_descriptors_buffer(context, params.desc_handle); - if (descriptors_buffer == NULL) { - hailo_dev_warn(controller->dev, "not found desc handle %llu\n", (unsigned long long)params.desc_handle); - return -EINVAL; - } - - list_del(&descriptors_buffer->descriptors_buffer_list); - hailo_desc_list_release(controller->dev, descriptors_buffer); - kfree(descriptors_buffer); - return 0; -} - -long hailo_desc_list_program_ioctl(struct hailo_vdma_file_context *context, struct hailo_vdma_controller *controller, - unsigned long arg) -{ - struct hailo_desc_list_program_params configure_info; - struct hailo_vdma_buffer *mapped_buffer = NULL; - struct hailo_descriptors_list_buffer *descriptors_buffer = NULL; - struct hailo_vdma_mapped_transfer_buffer transfer_buffer = {0}; - - if (copy_from_user(&configure_info, (void __user*)arg, sizeof(configure_info))) { - hailo_dev_err(controller->dev, "copy from user fail\n"); - return -EFAULT; - } - hailo_dev_info(controller->dev, "config buffer_handle=%zu desc_handle=%llu starting_desc=%u\n", - configure_info.buffer_handle, (u64)configure_info.desc_handle, configure_info.starting_desc); - - mapped_buffer = hailo_vdma_find_mapped_user_buffer(context, configure_info.buffer_handle); - descriptors_buffer = hailo_vdma_find_descriptors_buffer(context, configure_info.desc_handle); - if (mapped_buffer == NULL || descriptors_buffer == NULL) { - hailo_dev_err(controller->dev, "invalid user/descriptors buffer\n"); - return -EFAULT; - } - - if (configure_info.buffer_size > mapped_buffer->size) { - hailo_dev_err(controller->dev, "invalid buffer size. \n"); - return -EFAULT; - } - - transfer_buffer.sg_table = &mapped_buffer->sg_table; - transfer_buffer.size = configure_info.buffer_size; - transfer_buffer.offset = configure_info.buffer_offset; - - return hailo_vdma_program_descriptors_list( - controller->hw, - &descriptors_buffer->desc_list, - configure_info.starting_desc, - &transfer_buffer, - configure_info.should_bind, - configure_info.channel_index, - configure_info.last_interrupts_domain, - configure_info.is_debug - ); -} - -long hailo_vdma_low_memory_buffer_alloc_ioctl(struct hailo_vdma_file_context *context, struct hailo_vdma_controller *controller, - unsigned long arg) -{ - struct hailo_allocate_low_memory_buffer_params buf_info = {0}; - struct hailo_vdma_low_memory_buffer *low_memory_buffer = NULL; - long err = -EINVAL; - - if (copy_from_user(&buf_info, (void __user*)arg, sizeof(buf_info))) { - hailo_dev_err(controller->dev, "copy from user fail\n"); - return -EFAULT; - } - - low_memory_buffer = kzalloc(sizeof(*low_memory_buffer), GFP_KERNEL); - if (NULL == low_memory_buffer) { - hailo_dev_err(controller->dev, "memory alloc failed\n"); - return -ENOMEM; - } - - err = hailo_vdma_low_memory_buffer_alloc(buf_info.buffer_size, low_memory_buffer); - if (err < 0) { - kfree(low_memory_buffer); - hailo_dev_err(controller->dev, "failed allocating buffer from driver\n"); - return err; - } - - // Get handle for allocated buffer - low_memory_buffer->handle = hailo_get_next_vdma_handle(context); - - list_add(&low_memory_buffer->vdma_low_memory_buffer_list, &context->vdma_low_memory_buffer_list); - - buf_info.buffer_handle = low_memory_buffer->handle; - if (copy_to_user((void __user*)arg, &buf_info, sizeof(buf_info))) { - hailo_dev_err(controller->dev, "copy_to_user fail\n"); - list_del(&low_memory_buffer->vdma_low_memory_buffer_list); - hailo_vdma_low_memory_buffer_free(low_memory_buffer); - kfree(low_memory_buffer); - return -EFAULT; - } - - return 0; -} - -long hailo_vdma_low_memory_buffer_free_ioctl(struct hailo_vdma_file_context *context, struct hailo_vdma_controller *controller, - unsigned long arg) -{ - struct hailo_vdma_low_memory_buffer *low_memory_buffer = NULL; - struct hailo_free_low_memory_buffer_params params = {0}; - - if (copy_from_user(¶ms, (void __user*)arg, sizeof(params))) { - hailo_dev_err(controller->dev, "copy from user fail\n"); - return -EFAULT; - } - - low_memory_buffer = hailo_vdma_find_low_memory_buffer(context, params.buffer_handle); - if (NULL == low_memory_buffer) { - hailo_dev_warn(controller->dev, "vdma buffer handle %lx not found\n", params.buffer_handle); - return -EINVAL; - } - - list_del(&low_memory_buffer->vdma_low_memory_buffer_list); - hailo_vdma_low_memory_buffer_free(low_memory_buffer); - kfree(low_memory_buffer); - return 0; -} - -long hailo_mark_as_in_use(struct hailo_vdma_controller *controller, unsigned long arg, struct file *filp) -{ - struct hailo_mark_as_in_use_params params = {0}; - - // If device is used by this FD, return false to indicate its free for usage - if (filp == controller->used_by_filp) { - params.in_use = false; - } else if (NULL != controller->used_by_filp) { - params.in_use = true; - } else { - controller->used_by_filp = filp; - params.in_use = false; - } - - if (copy_to_user((void __user*)arg, ¶ms, sizeof(params))) { - hailo_dev_err(controller->dev, "copy_to_user fail\n"); - return -EFAULT; - } - - return 0; -} - -long hailo_vdma_continuous_buffer_alloc_ioctl(struct hailo_vdma_file_context *context, struct hailo_vdma_controller *controller, unsigned long arg) -{ - struct hailo_allocate_continuous_buffer_params buf_info = {0}; - struct hailo_vdma_continuous_buffer *continuous_buffer = NULL; - long err = -EINVAL; - size_t aligned_buffer_size = 0; - - if (copy_from_user(&buf_info, (void __user*)arg, sizeof(buf_info))) { - hailo_dev_err(controller->dev, "copy from user fail\n"); - return -EFAULT; - } - - continuous_buffer = kzalloc(sizeof(*continuous_buffer), GFP_KERNEL); - if (NULL == continuous_buffer) { - hailo_dev_err(controller->dev, "memory alloc failed\n"); - return -ENOMEM; - } - - // We use PAGE_ALIGN to support mmap - aligned_buffer_size = PAGE_ALIGN(buf_info.buffer_size); - err = hailo_vdma_continuous_buffer_alloc(controller->dev, aligned_buffer_size, continuous_buffer); - if (err < 0) { - kfree(continuous_buffer); - return err; - } - - continuous_buffer->handle = hailo_get_next_vdma_handle(context); - list_add(&continuous_buffer->continuous_buffer_list, &context->continuous_buffer_list); - - buf_info.buffer_handle = continuous_buffer->handle; - buf_info.dma_address = continuous_buffer->dma_address; - if (copy_to_user((void __user*)arg, &buf_info, sizeof(buf_info))) { - hailo_dev_err(controller->dev, "copy_to_user fail\n"); - list_del(&continuous_buffer->continuous_buffer_list); - hailo_vdma_continuous_buffer_free(controller->dev, continuous_buffer); - kfree(continuous_buffer); - return -EFAULT; - } - - return 0; -} - -long hailo_vdma_continuous_buffer_free_ioctl(struct hailo_vdma_file_context *context, struct hailo_vdma_controller *controller, unsigned long arg) -{ - struct hailo_free_continuous_buffer_params params; - struct hailo_vdma_continuous_buffer *continuous_buffer = NULL; - - if (copy_from_user(¶ms, (void __user*)arg, sizeof(params))) { - hailo_dev_err(controller->dev, "copy from user fail\n"); - return -EFAULT; - } - - continuous_buffer = hailo_vdma_find_continuous_buffer(context, params.buffer_handle); - if (NULL == continuous_buffer) { - hailo_dev_warn(controller->dev, "vdma buffer handle %lx not found\n", params.buffer_handle); - return -EINVAL; - } - - list_del(&continuous_buffer->continuous_buffer_list); - hailo_vdma_continuous_buffer_free(controller->dev, continuous_buffer); - kfree(continuous_buffer); - return 0; -} - -long hailo_vdma_interrupts_read_timestamps_ioctl(struct hailo_vdma_controller *controller, unsigned long arg) -{ - struct hailo_vdma_interrupts_read_timestamp_params *params = &controller->read_interrupt_timestamps_params; - struct hailo_vdma_engine *engine = NULL; - int err = -EINVAL; - - hailo_dev_dbg(controller->dev, "Start read interrupt timestamps ioctl\n"); - - if (copy_from_user(params, (void __user*)arg, sizeof(*params))) { - hailo_dev_err(controller->dev, "copy_from_user fail\n"); - return -ENOMEM; - } - - if (params->engine_index >= controller->vdma_engines_count) { - hailo_dev_err(controller->dev, "Invalid engine %u", params->engine_index); - return -EINVAL; - } - engine = &controller->vdma_engines[params->engine_index]; - - err = hailo_vdma_engine_read_timestamps(engine, params); - if (err < 0) { - hailo_dev_err(controller->dev, "Failed read engine interrupts for %u:%u", - params->engine_index, params->channel_index); - return err; - } - - if (copy_to_user((void __user*)arg, params, sizeof(*params))) { - hailo_dev_err(controller->dev, "copy_to_user fail\n"); - return -ENOMEM; - } - - return 0; -} - -long hailo_vdma_launch_transfer_ioctl(struct hailo_vdma_file_context *context, struct hailo_vdma_controller *controller, - unsigned long arg) -{ - struct hailo_vdma_launch_transfer_params params; - struct hailo_vdma_engine *engine = NULL; - struct hailo_vdma_channel *channel = NULL; - struct hailo_descriptors_list_buffer *descriptors_buffer = NULL; - struct hailo_vdma_mapped_transfer_buffer mapped_transfer_buffers[ARRAY_SIZE(params.buffers)] = {0}; - int ret = -EINVAL; - u8 i = 0; - - if (copy_from_user(¶ms, (void __user*)arg, sizeof(params))) { - hailo_dev_err(controller->dev, "copy from user fail\n"); - return -EFAULT; - } - - if (params.engine_index >= controller->vdma_engines_count) { - hailo_dev_err(controller->dev, "Invalid engine %u", params.engine_index); - return -EINVAL; - } - engine = &controller->vdma_engines[params.engine_index]; - - if (params.channel_index >= ARRAY_SIZE(engine->channels)) { - hailo_dev_err(controller->dev, "Invalid channel %u", params.channel_index); - return -EINVAL; - } - channel = &engine->channels[params.channel_index]; - - if (params.buffers_count > ARRAY_SIZE(params.buffers)) { - hailo_dev_err(controller->dev, "too many buffers %u\n", params.buffers_count); - return -EINVAL; - } - - descriptors_buffer = hailo_vdma_find_descriptors_buffer(context, params.desc_handle); - if (descriptors_buffer == NULL) { - hailo_dev_err(controller->dev, "invalid descriptors list handle\n"); - return -EFAULT; - } - - for (i = 0; i < params.buffers_count; i++) { - struct hailo_vdma_buffer *mapped_buffer = - hailo_vdma_find_mapped_user_buffer(context, params.buffers[i].mapped_buffer_handle); - if (mapped_buffer == NULL) { - hailo_dev_err(controller->dev, "invalid user buffer\n"); - return -EFAULT; - } - - if (params.buffers[i].size > mapped_buffer->size) { - hailo_dev_err(controller->dev, "Syncing size %u while buffer size is %u\n", - params.buffers[i].size, mapped_buffer->size); - return -EINVAL; - } - - if (params.buffers[i].offset > mapped_buffer->size) { - hailo_dev_err(controller->dev, "Syncing offset %u while buffer size is %u\n", - params.buffers[i].offset, mapped_buffer->size); - return -EINVAL; - } - - // Syncing the buffer to device change its ownership from host to the device. - // We sync on D2H as well if the user owns the buffer since the buffer might have been changed by - // the host between the time it was mapped and the current async transfer. - hailo_vdma_buffer_sync_cyclic(controller, mapped_buffer, HAILO_SYNC_FOR_DEVICE, - params.buffers[i].offset, params.buffers[i].size); - - mapped_transfer_buffers[i].sg_table = &mapped_buffer->sg_table; - mapped_transfer_buffers[i].size = params.buffers[i].size; - mapped_transfer_buffers[i].offset = params.buffers[i].offset; - mapped_transfer_buffers[i].opaque = mapped_buffer; - } - - ret = hailo_vdma_launch_transfer( - controller->hw, - channel, - &descriptors_buffer->desc_list, - params.starting_desc, - params.buffers_count, - mapped_transfer_buffers, - params.should_bind, - params.first_interrupts_domain, - params.last_interrupts_domain, - params.is_debug - ); - if (ret < 0) { - params.launch_transfer_status = ret; - if (-ECONNRESET != ret) { - hailo_dev_err(controller->dev, "Failed launch transfer %d\n", ret); - } - // Still need to copy fail status back to userspace - success oriented - if (copy_to_user((void __user*)arg, ¶ms, sizeof(params))) { - hailo_dev_err(controller->dev, "copy_to_user fail\n"); - } - return ret; - } - - params.descs_programed = ret; - params.launch_transfer_status = 0; - - if (copy_to_user((void __user*)arg, ¶ms, sizeof(params))) { - hailo_dev_err(controller->dev, "copy_to_user fail\n"); - return -EFAULT; - } - - return 0; -} \ No newline at end of file diff --git a/drivers/media/pci/hailo/vdma/ioctl.h b/drivers/media/pci/hailo/vdma/ioctl.h deleted file mode 100644 index a9016c3162a3c4..00000000000000 --- a/drivers/media/pci/hailo/vdma/ioctl.h +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/** - * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. - **/ - -#ifndef _HAILO_VDMA_IOCTL_H_ -#define _HAILO_VDMA_IOCTL_H_ - -#include "vdma/vdma.h" - -long hailo_vdma_enable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg, struct hailo_vdma_file_context *context); -long hailo_vdma_disable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg, struct hailo_vdma_file_context *context); -long hailo_vdma_interrupts_wait_ioctl(struct hailo_vdma_controller *controller, unsigned long arg, - struct semaphore *mutex, bool *should_up_board_mutex); - -long hailo_vdma_buffer_map_ioctl(struct hailo_vdma_file_context *context, struct hailo_vdma_controller *controller, unsigned long arg); -long hailo_vdma_buffer_unmap_ioctl(struct hailo_vdma_file_context *context, struct hailo_vdma_controller *controller, unsigned long handle); -long hailo_vdma_buffer_sync_ioctl(struct hailo_vdma_file_context *context, struct hailo_vdma_controller *controller, unsigned long arg); - -long hailo_desc_list_create_ioctl(struct hailo_vdma_file_context *context, struct hailo_vdma_controller *controller, unsigned long arg); -long hailo_desc_list_release_ioctl(struct hailo_vdma_file_context *context, struct hailo_vdma_controller *controller, unsigned long arg); -long hailo_desc_list_program_ioctl(struct hailo_vdma_file_context *context, struct hailo_vdma_controller *controller, unsigned long arg); - -long hailo_vdma_low_memory_buffer_alloc_ioctl(struct hailo_vdma_file_context *context, struct hailo_vdma_controller *controller, unsigned long arg); -long hailo_vdma_low_memory_buffer_free_ioctl(struct hailo_vdma_file_context *context, struct hailo_vdma_controller *controller, unsigned long arg); - -long hailo_mark_as_in_use(struct hailo_vdma_controller *controller, unsigned long arg, struct file *filp); - -long hailo_vdma_continuous_buffer_alloc_ioctl(struct hailo_vdma_file_context *context, struct hailo_vdma_controller *controller, unsigned long arg); -long hailo_vdma_continuous_buffer_free_ioctl(struct hailo_vdma_file_context *context, struct hailo_vdma_controller *controller, unsigned long arg); - -long hailo_vdma_interrupts_read_timestamps_ioctl(struct hailo_vdma_controller *controller, unsigned long arg); - -long hailo_vdma_launch_transfer_ioctl(struct hailo_vdma_file_context *context, struct hailo_vdma_controller *controller, - unsigned long arg); - -#endif /* _HAILO_VDMA_IOCTL_H_ */ \ No newline at end of file diff --git a/drivers/media/pci/hailo/vdma/memory.c b/drivers/media/pci/hailo/vdma/memory.c deleted file mode 100644 index 7a8b8e011810aa..00000000000000 --- a/drivers/media/pci/hailo/vdma/memory.c +++ /dev/null @@ -1,767 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/** - * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. - **/ - -#define pr_fmt(fmt) "hailo: " fmt - -#include "memory.h" -#include "utils.h" -#include "utils/compact.h" - -#include -#include -#include -#include -#include - -#define SGL_MAX_SEGMENT_SIZE (0x10000) -// See linux/mm.h -#define MMIO_AND_NO_PAGES_VMA_MASK (VM_IO | VM_PFNMAP) -// The linux kernel names the dmabuf's vma vm_file field "dmabuf" -#define VMA_VM_FILE_DMABUF_NAME ("dmabuf") - -static int map_mmio_address(uintptr_t user_address, u32 size, struct vm_area_struct *vma, - struct sg_table *sgt); -static int prepare_sg_table(struct sg_table *sg_table, uintptr_t user_address, u32 size, - struct hailo_vdma_low_memory_buffer *low_mem_driver_allocated_buffer); -static void clear_sg_table(struct sg_table *sgt); - -#if LINUX_VERSION_CODE >= KERNEL_VERSION( 3, 3, 0 ) - -#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 13, 0) -#define DMA_NS_NAME DMA_BUF -#else -#define DMA_NS_NAME "DMA_BUF" -#endif // LINUX_VERSION_CODE < KERNEL_VERSION(6, 13, 0) - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) -// Import DMA_BUF namespace for needed kernels -MODULE_IMPORT_NS(DMA_NS_NAME); -#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) */ - -static int hailo_map_dmabuf(struct device *dev, int dmabuf_fd, enum dma_data_direction direction, struct sg_table *sgt, - struct hailo_dmabuf_info *dmabuf_info) -{ - int ret = -EINVAL; - struct dma_buf *dmabuf = NULL; - struct dma_buf_attachment *dmabuf_attachment = NULL; - struct sg_table *res_sgt = NULL; - - dmabuf = dma_buf_get(dmabuf_fd); - if (IS_ERR(dmabuf)) { - dev_err(dev, "dma_buf_get failed, err=%ld\n", PTR_ERR(dmabuf)); - ret = -EINVAL; - goto cleanup; - } - - dmabuf_attachment = dma_buf_attach(dmabuf, dev); - if (IS_ERR(dmabuf_attachment)) { - dev_err(dev, "dma_buf_attach failed, err=%ld\n", PTR_ERR(dmabuf_attachment)); - ret = -EINVAL; - goto l_buf_get; - } - - res_sgt = dma_buf_map_attachment(dmabuf_attachment, direction); - if (IS_ERR(res_sgt)) { - dev_err(dev, "dma_buf_map_attachment failed, err=%ld\n", PTR_ERR(res_sgt)); - goto l_buf_attach; - } - - *sgt = *res_sgt; - - dmabuf_info->dmabuf = dmabuf; - dmabuf_info->dmabuf_attachment = dmabuf_attachment; - dmabuf_info->dmabuf_sg_table = res_sgt; - return 0; - -l_buf_attach: - dma_buf_detach(dmabuf, dmabuf_attachment); -l_buf_get: - dma_buf_put(dmabuf); -cleanup: - return ret; -} - -static void hailo_unmap_dmabuf(struct hailo_vdma_buffer *vdma_buffer) -{ - dma_buf_unmap_attachment(vdma_buffer->dmabuf_info.dmabuf_attachment, vdma_buffer->dmabuf_info.dmabuf_sg_table, vdma_buffer->data_direction); - dma_buf_detach(vdma_buffer->dmabuf_info.dmabuf, vdma_buffer->dmabuf_info.dmabuf_attachment); - dma_buf_put(vdma_buffer->dmabuf_info.dmabuf); -} - -#else /* LINUX_VERSION_CODE >= KERNEL_VERSION( 3, 3, 0 ) */ - -static int hailo_map_dmabuf(struct device *dev, int dmabuf_fd, enum dma_data_direction direction, struct sg_table *sgt, - struct hailo_dmabuf_info *dmabuf_info) -{ - (void) dmabuf_fd; - (void) direction; - (void) sgt; - (void) mapped_buffer; - dev_err(dev, "dmabuf not supported in kernel versions lower than 3.3.0\n"); - return -EINVAL; -} - -static void hailo_unmap_dmabuf(struct hailo_vdma_buffer *vdma_buffer) -{ - dev_err(vdma_buffer->device, "dmabuf not supported in kernel versions lower than 3.3.0\n"); - return -EINVAL; -} - -#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION( 3, 3, 0 ) */ - -// Function that checks if the vma is backed by a mapped dmabuf -static bool is_dmabuf_vma(struct vm_area_struct *vma) -{ - return (vma && vma->vm_file && (0 == strcmp(vma->vm_file->f_path.dentry->d_name.name, VMA_VM_FILE_DMABUF_NAME))); -} - -static int create_fd_from_vma(struct device *dev, struct vm_area_struct *vma) { - struct file *file = NULL; - int fd = 0; - - if (!vma || !vma->vm_file) { - dev_err(dev, "Invalid VMA or no associated file.\n"); - return -EINVAL; - } - - file = vma->vm_file; - - // This functions increments the ref count of the file - get_file(file); - - // 0 for default flags - fd = get_unused_fd_flags(0); - if (fd < 0) { - dev_err(dev, "Failed to get unused file descriptor.\n"); - fput(file); - return fd; - } - - // Install the file into the file descriptor table - fd_install(fd, file); - return fd; -} - -struct hailo_vdma_buffer *hailo_vdma_buffer_map(struct device *dev, - uintptr_t user_address, size_t size, enum dma_data_direction direction, - enum hailo_dma_buffer_type buffer_type, struct hailo_vdma_low_memory_buffer *low_mem_driver_allocated_buffer) -{ - int ret = -EINVAL; - struct hailo_vdma_buffer *mapped_buffer = NULL; - struct sg_table sgt = {0}; - struct vm_area_struct *vma = NULL; - bool is_mmio = false; - struct hailo_dmabuf_info dmabuf_info = {0}; - bool created_dmabuf_fd_from_vma = false; - - mapped_buffer = kzalloc(sizeof(*mapped_buffer), GFP_KERNEL); - if (NULL == mapped_buffer) { - dev_err(dev, "memory alloc failed\n"); - ret = -ENOMEM; - goto cleanup; - - } - - if (HAILO_DMA_DMABUF_BUFFER != buffer_type) { - mmap_read_lock(current->mm); - vma = find_vma(current->mm, user_address); - mmap_read_unlock(current->mm); - if (IS_ENABLED(HAILO_SUPPORT_MMIO_DMA_MAPPING)) { - if (NULL == vma) { - dev_err(dev, "no vma for virt_addr/size = 0x%08lx/0x%08zx\n", user_address, size); - ret = -EFAULT; - goto cleanup; - } - } - - if (is_dmabuf_vma(vma)) { - dev_dbg(dev, "Given vma is backed by dmabuf - creating fd and mapping as dmabuf\n"); - buffer_type = HAILO_DMA_DMABUF_BUFFER; - ret = create_fd_from_vma(dev, vma); - if (ret < 0) { - dev_err(dev, "Failed creating fd from vma in given dmabuf\n"); - goto cleanup; - } - // Override user address with fd to the dmabuf - like normal dmabuf flow - user_address = ret; - created_dmabuf_fd_from_vma = true; - } - } - - // TODO: is MMIO DMA MAPPINGS STILL needed after dmabuf - if (IS_ENABLED(HAILO_SUPPORT_MMIO_DMA_MAPPING) && - (MMIO_AND_NO_PAGES_VMA_MASK == (vma->vm_flags & MMIO_AND_NO_PAGES_VMA_MASK)) && - (HAILO_DMA_DMABUF_BUFFER != buffer_type)) { - // user_address represents memory mapped I/O and isn't backed by 'struct page' (only by pure pfn) - if (NULL != low_mem_driver_allocated_buffer) { - // low_mem_driver_allocated_buffer are backed by regular 'struct page' addresses, just in low memory - dev_err(dev, "low_mem_driver_allocated_buffer shouldn't be provided with an mmio address\n"); - ret = -EINVAL; - goto free_buffer_struct; - } - - ret = map_mmio_address(user_address, size, vma, &sgt); - if (ret < 0) { - dev_err(dev, "failed to map mmio address %d\n", ret); - goto free_buffer_struct; - } - - is_mmio = true; - - } else if (HAILO_DMA_DMABUF_BUFFER == buffer_type) { - // Content user_address in case of dmabuf is fd - for now - ret = hailo_map_dmabuf(dev, user_address, direction, &sgt, &dmabuf_info); - if (ret < 0) { - dev_err(dev, "Failed mapping dmabuf\n"); - goto cleanup; - } - // If created dmabuf fd from vma need to decrement refcount and release fd - if (created_dmabuf_fd_from_vma) { - fput(vma->vm_file); - put_unused_fd(user_address); - } - } else { - // user_address is a standard 'struct page' backed memory address - ret = prepare_sg_table(&sgt, user_address, size, low_mem_driver_allocated_buffer); - if (ret < 0) { - dev_err(dev, "failed to set sg list for user buffer %d\n", ret); - goto free_buffer_struct; - } - sgt.nents = dma_map_sg(dev, sgt.sgl, sgt.orig_nents, direction); - if (0 == sgt.nents) { - dev_err(dev, "failed to map sg list for user buffer\n"); - ret = -ENXIO; - goto clear_sg_table; - } - } - - kref_init(&mapped_buffer->kref); - mapped_buffer->device = dev; - mapped_buffer->user_address = user_address; - mapped_buffer->size = size; - mapped_buffer->data_direction = direction; - mapped_buffer->sg_table = sgt; - mapped_buffer->is_mmio = is_mmio; - mapped_buffer->dmabuf_info = dmabuf_info; - - return mapped_buffer; - -clear_sg_table: - clear_sg_table(&sgt); -free_buffer_struct: - kfree(mapped_buffer); -cleanup: - return ERR_PTR(ret); -} - -static void unmap_buffer(struct kref *kref) -{ - struct hailo_vdma_buffer *buf = container_of(kref, struct hailo_vdma_buffer, kref); - - // If dmabuf - unmap and detatch dmabuf - if (NULL != buf->dmabuf_info.dmabuf) { - hailo_unmap_dmabuf(buf); - } else { - if (!buf->is_mmio) { - dma_unmap_sg(buf->device, buf->sg_table.sgl, buf->sg_table.orig_nents, buf->data_direction); - } - - clear_sg_table(&buf->sg_table); - } - kfree(buf); -} - -void hailo_vdma_buffer_get(struct hailo_vdma_buffer *buf) -{ - kref_get(&buf->kref); -} - -void hailo_vdma_buffer_put(struct hailo_vdma_buffer *buf) -{ - kref_put(&buf->kref, unmap_buffer); -} - -static void vdma_sync_entire_buffer(struct hailo_vdma_controller *controller, - struct hailo_vdma_buffer *mapped_buffer, enum hailo_vdma_buffer_sync_type sync_type) -{ - if (sync_type == HAILO_SYNC_FOR_CPU) { - dma_sync_sg_for_cpu(controller->dev, mapped_buffer->sg_table.sgl, mapped_buffer->sg_table.nents, - mapped_buffer->data_direction); - } else { - dma_sync_sg_for_device(controller->dev, mapped_buffer->sg_table.sgl, mapped_buffer->sg_table.nents, - mapped_buffer->data_direction); - } -} - -typedef void (*dma_sync_single_callback)(struct device *, dma_addr_t, size_t, enum dma_data_direction); -// Map sync_info->count bytes starting at sync_info->offset -static void vdma_sync_buffer_interval(struct hailo_vdma_controller *controller, - struct hailo_vdma_buffer *mapped_buffer, - size_t offset, size_t size, enum hailo_vdma_buffer_sync_type sync_type) -{ - size_t sync_start_offset = offset; - size_t sync_end_offset = offset + size; - dma_sync_single_callback dma_sync_single = (sync_type == HAILO_SYNC_FOR_CPU) ? - dma_sync_single_for_cpu : - dma_sync_single_for_device; - struct scatterlist* sg_entry = NULL; - size_t current_iter_offset = 0; - int i = 0; - - for_each_sg(mapped_buffer->sg_table.sgl, sg_entry, mapped_buffer->sg_table.nents, i) { - // Check if the intervals: [current_iter_offset, sg_dma_len(sg_entry)] and [sync_start_offset, sync_end_offset] - // have any intersection. If offset isn't at the start of a sg_entry, we still want to sync it. - if (max(sync_start_offset, current_iter_offset) <= min(sync_end_offset, current_iter_offset + sg_dma_len(sg_entry))) { - dma_sync_single(controller->dev, sg_dma_address(sg_entry), sg_dma_len(sg_entry), - mapped_buffer->data_direction); - } - - current_iter_offset += sg_dma_len(sg_entry); - } -} - -void hailo_vdma_buffer_sync(struct hailo_vdma_controller *controller, - struct hailo_vdma_buffer *mapped_buffer, enum hailo_vdma_buffer_sync_type sync_type, - size_t offset, size_t size) -{ - if ((IS_ENABLED(HAILO_SUPPORT_MMIO_DMA_MAPPING) && mapped_buffer->is_mmio) || - (NULL != mapped_buffer->dmabuf_info.dmabuf)) { - // MMIO buffers and dmabufs don't need to be sync'd - return; - } - - if ((offset == 0) && (size == mapped_buffer->size)) { - vdma_sync_entire_buffer(controller, mapped_buffer, sync_type); - } else { - vdma_sync_buffer_interval(controller, mapped_buffer, offset, size, sync_type); - } -} - -// Similar to vdma_buffer_sync, allow circular sync of the buffer. -void hailo_vdma_buffer_sync_cyclic(struct hailo_vdma_controller *controller, - struct hailo_vdma_buffer *mapped_buffer, enum hailo_vdma_buffer_sync_type sync_type, - size_t offset, size_t size) -{ - size_t size_to_end = min(size, mapped_buffer->size - offset); - - hailo_vdma_buffer_sync(controller, mapped_buffer, sync_type, offset, size_to_end); - - if (size_to_end < size) { - hailo_vdma_buffer_sync(controller, mapped_buffer, sync_type, 0, size - size_to_end); - } -} - -struct hailo_vdma_buffer* hailo_vdma_find_mapped_user_buffer(struct hailo_vdma_file_context *context, - size_t buffer_handle) -{ - struct hailo_vdma_buffer *cur = NULL; - list_for_each_entry(cur, &context->mapped_user_buffer_list, mapped_user_buffer_list) { - if (cur->handle == buffer_handle) { - return cur; - } - } - return NULL; -} - -void hailo_vdma_clear_mapped_user_buffer_list(struct hailo_vdma_file_context *context, - struct hailo_vdma_controller *controller) -{ - struct hailo_vdma_buffer *cur = NULL, *next = NULL; - list_for_each_entry_safe(cur, next, &context->mapped_user_buffer_list, mapped_user_buffer_list) { - list_del(&cur->mapped_user_buffer_list); - hailo_vdma_buffer_put(cur); - } -} - - -int hailo_desc_list_create(struct device *dev, u32 descriptors_count, u16 desc_page_size, - uintptr_t desc_handle, bool is_circular, struct hailo_descriptors_list_buffer *descriptors) -{ - size_t buffer_size = 0; - const u64 align = VDMA_DESCRIPTOR_LIST_ALIGN; //First addr must be aligned on 64 KB (from the VDMA registers documentation) - - if (MAX_POWER_OF_2_VALUE < descriptors_count) { - dev_err(dev, "Invalid descriptors count %u\n", descriptors_count); - return -EINVAL; - } - - buffer_size = descriptors_count * sizeof(struct hailo_vdma_descriptor); - buffer_size = ALIGN(buffer_size, align); - - descriptors->kernel_address = dma_alloc_coherent(dev, buffer_size, - &descriptors->dma_address, GFP_KERNEL | __GFP_ZERO); - if (descriptors->kernel_address == NULL) { - dev_err(dev, "Failed to allocate descriptors list, desc_count 0x%x, buffer_size 0x%zx, This failure means there is not a sufficient amount of CMA memory " - "(contiguous physical memory), This usually is caused by lack of general system memory. Please check you have sufficient memory.\n", - descriptors_count, buffer_size); - return -ENOBUFS; - } - - descriptors->buffer_size = buffer_size; - descriptors->handle = desc_handle; - - descriptors->desc_list.desc_list = descriptors->kernel_address; - descriptors->desc_list.desc_count = descriptors_count; - // No need to check the return value of get_nearest_powerof_2 because we already checked the input - descriptors->desc_list.desc_count_mask = is_circular ? (descriptors_count - 1) : (get_nearest_powerof_2(descriptors_count) - 1); - descriptors->desc_list.desc_page_size = desc_page_size; - descriptors->desc_list.is_circular = is_circular; - - return 0; -} - -void hailo_desc_list_release(struct device *dev, struct hailo_descriptors_list_buffer *descriptors) -{ - dma_free_coherent(dev, descriptors->buffer_size, descriptors->kernel_address, descriptors->dma_address); -} - -struct hailo_descriptors_list_buffer* hailo_vdma_find_descriptors_buffer(struct hailo_vdma_file_context *context, - uintptr_t desc_handle) -{ - struct hailo_descriptors_list_buffer *cur = NULL; - list_for_each_entry(cur, &context->descriptors_buffer_list, descriptors_buffer_list) { - if (cur->handle == desc_handle) { - return cur; - } - } - return NULL; -} - -void hailo_vdma_clear_descriptors_buffer_list(struct hailo_vdma_file_context *context, - struct hailo_vdma_controller *controller) -{ - struct hailo_descriptors_list_buffer *cur = NULL, *next = NULL; - list_for_each_entry_safe(cur, next, &context->descriptors_buffer_list, descriptors_buffer_list) { - list_del(&cur->descriptors_buffer_list); - hailo_desc_list_release(controller->dev, cur); - kfree(cur); - } -} - -int hailo_vdma_low_memory_buffer_alloc(size_t size, struct hailo_vdma_low_memory_buffer *low_memory_buffer) -{ - int ret = -EINVAL; - void *kernel_address = NULL; - size_t pages_count = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; - size_t num_allocated = 0, i = 0; - void **pages = NULL; - - pages = kcalloc(pages_count, sizeof(*pages), GFP_KERNEL); - if (NULL == pages) { - pr_err("Failed to allocate pages for buffer (size %zu)\n", size); - ret = -ENOMEM; - goto cleanup; - } - - for (num_allocated = 0; num_allocated < pages_count; num_allocated++) { - // __GFP_DMA32 flag is used to limit system memory allocations to the lowest 4 GB of physical memory in order to guarantee DMA - // Operations will not have to use bounce buffers on certain architectures (e.g 32-bit DMA enabled architectures) - kernel_address = (void*)__get_free_page(__GFP_DMA32); - if (NULL == kernel_address) { - pr_err("Failed to allocate %zu coherent bytes\n", (size_t)PAGE_SIZE); - ret = -ENOMEM; - goto cleanup; - } - - pages[num_allocated] = kernel_address; - } - - low_memory_buffer->pages_count = pages_count; - low_memory_buffer->pages_address = pages; - - return 0; - -cleanup: - if (NULL != pages) { - for (i = 0; i < num_allocated; i++) { - free_page((long unsigned)pages[i]); - } - - kfree(pages); - } - - return ret; -} - -void hailo_vdma_low_memory_buffer_free(struct hailo_vdma_low_memory_buffer *low_memory_buffer) -{ - size_t i = 0; - if (NULL == low_memory_buffer) { - return; - } - - for (i = 0; i < low_memory_buffer->pages_count; i++) { - free_page((long unsigned)low_memory_buffer->pages_address[i]); - } - - kfree(low_memory_buffer->pages_address); -} - -struct hailo_vdma_low_memory_buffer* hailo_vdma_find_low_memory_buffer(struct hailo_vdma_file_context *context, - uintptr_t buf_handle) -{ - struct hailo_vdma_low_memory_buffer *cur = NULL; - list_for_each_entry(cur, &context->vdma_low_memory_buffer_list, vdma_low_memory_buffer_list) { - if (cur->handle == buf_handle) { - return cur; - } - } - - return NULL; -} - -void hailo_vdma_clear_low_memory_buffer_list(struct hailo_vdma_file_context *context) -{ - struct hailo_vdma_low_memory_buffer *cur = NULL, *next = NULL; - list_for_each_entry_safe(cur, next, &context->vdma_low_memory_buffer_list, vdma_low_memory_buffer_list) { - list_del(&cur->vdma_low_memory_buffer_list); - hailo_vdma_low_memory_buffer_free(cur); - kfree(cur); - } -} - -int hailo_vdma_continuous_buffer_alloc(struct device *dev, size_t size, - struct hailo_vdma_continuous_buffer *continuous_buffer) -{ - dma_addr_t dma_address = 0; - void *kernel_address = NULL; - - kernel_address = dma_alloc_coherent(dev, size, &dma_address, GFP_KERNEL); - if (NULL == kernel_address) { - dev_warn(dev, "Failed to allocate continuous buffer, size 0x%zx. This failure means there is not a sufficient amount of CMA memory " - "(contiguous physical memory), This usually is caused by lack of general system memory. Please check you have sufficent memory.\n", size); - return -ENOBUFS; - } - - continuous_buffer->kernel_address = kernel_address; - continuous_buffer->dma_address = dma_address; - continuous_buffer->size = size; - return 0; -} - -void hailo_vdma_continuous_buffer_free(struct device *dev, - struct hailo_vdma_continuous_buffer *continuous_buffer) -{ - dma_free_coherent(dev, continuous_buffer->size, continuous_buffer->kernel_address, - continuous_buffer->dma_address); -} - -struct hailo_vdma_continuous_buffer* hailo_vdma_find_continuous_buffer(struct hailo_vdma_file_context *context, - uintptr_t buf_handle) -{ - struct hailo_vdma_continuous_buffer *cur = NULL; - list_for_each_entry(cur, &context->continuous_buffer_list, continuous_buffer_list) { - if (cur->handle == buf_handle) { - return cur; - } - } - - return NULL; -} - -void hailo_vdma_clear_continuous_buffer_list(struct hailo_vdma_file_context *context, - struct hailo_vdma_controller *controller) -{ - struct hailo_vdma_continuous_buffer *cur = NULL, *next = NULL; - list_for_each_entry_safe(cur, next, &context->continuous_buffer_list, continuous_buffer_list) { - list_del(&cur->continuous_buffer_list); - hailo_vdma_continuous_buffer_free(controller->dev, cur); - kfree(cur); - } -} - -/** - * follow_pfn - look up PFN at a user virtual address - * @vma: memory mapping - * @address: user virtual address - * @pfn: location to store found PFN - * - * Only IO mappings and raw PFN mappings are allowed. - * - * This function does not allow the caller to read the permissions - * of the PTE. Do not use it. - * - * Return: zero and the pfn at @pfn on success, -ve otherwise. - */ -#if defined(HAILO_SUPPORT_MMIO_DMA_MAPPING) -static int follow_pfn(struct vm_area_struct *vma, unsigned long address, - unsigned long *pfn) -{ - int ret = -EINVAL; - spinlock_t *ptl; - pte_t *ptep; - - if (!(vma->vm_flags & (VM_IO | VM_PFNMAP))) - return ret; - - ret = follow_pte(vma, address, &ptep, &ptl); - if (ret) - return ret; - *pfn = pte_pfn(ptep_get(ptep)); - pte_unmap_unlock(ptep, ptl); - return 0; -} -#endif - -// Assumes the provided user_address belongs to the vma and that MMIO_AND_NO_PAGES_VMA_MASK bits are set under -// vma->vm_flags. This is validated in hailo_vdma_buffer_map, and won't be checked here -#if defined(HAILO_SUPPORT_MMIO_DMA_MAPPING) -static int map_mmio_address(uintptr_t user_address, u32 size, struct vm_area_struct *vma, - struct sg_table *sgt) -{ - int ret = -EINVAL; - unsigned long i = 0; - unsigned long pfn = 0; - unsigned long next_pfn = 0; - phys_addr_t phys_addr = 0; - dma_addr_t mmio_dma_address = 0; - const uintptr_t virt_addr = user_address; - const u32 vma_size = vma->vm_end - vma->vm_start + 1; - const uintptr_t num_pages = PFN_UP(virt_addr + size) - PFN_DOWN(virt_addr); - - // Check that the vma that was marked as MMIO_AND_NO_PAGES_VMA_MASK is big enough - if (vma_size < size) { - pr_err("vma (%u bytes) smaller than provided buffer (%u bytes)\n", vma_size, size); - return -EINVAL; - } - - // Get the physical address of user_address - ret = follow_pfn(vma, virt_addr, &pfn); - if (ret) { - pr_err("follow_pfn failed with %d\n", ret); - return ret; - } - phys_addr = __pfn_to_phys(pfn) + offset_in_page(virt_addr); - - // Make sure the physical memory is contiguous - for (i = 1; i < num_pages; ++i) { - ret = follow_pfn(vma, virt_addr + (i << PAGE_SHIFT), &next_pfn); - if (ret < 0) { - pr_err("follow_pfn failed with %d\n", ret); - return ret; - } - if (next_pfn != pfn + 1) { - pr_err("non-contiguous physical memory\n"); - return -EFAULT; - } - pfn = next_pfn; - } - - // phys_addr to dma - // TODO: need dma_map_resource here? doesn't work currently (we get dma_mapping_error on the returned dma addr) - // (HRT-12521) - mmio_dma_address = (dma_addr_t)phys_addr; - - // Create a page-less scatterlist. - ret = sg_alloc_table(sgt, 1, GFP_KERNEL); - if (ret < 0) { - return ret; - } - - sg_assign_page(sgt->sgl, NULL); - sg_dma_address(sgt->sgl) = mmio_dma_address; - sg_dma_len(sgt->sgl) = size; - - return 0; -} -#else /* defined(HAILO_SUPPORT_MMIO_DMA_MAPPING) */ -static int map_mmio_address(uintptr_t user_address, u32 size, struct vm_area_struct *vma, - struct sg_table *sgt) -{ - (void) user_address; - (void) size; - (void) vma; - (void) sgt; - pr_err("MMIO DMA MAPPINGS are not supported in this kernel version\n"); - return -EINVAL; -} -#endif /* defined(HAILO_SUPPORT_MMIO_DMA_MAPPING) */ - - -static int prepare_sg_table(struct sg_table *sg_table, uintptr_t user_address, u32 size, - struct hailo_vdma_low_memory_buffer *low_mem_driver_allocated_buffer) -{ - int ret = -EINVAL; - int pinned_pages = 0; - size_t npages = 0; - struct page **pages = NULL; - int i = 0; - struct scatterlist *sg_alloc_res = NULL; - - npages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; - pages = kvmalloc_array(npages, sizeof(*pages), GFP_KERNEL); - if (!pages) { - return -ENOMEM; - } - - // Check whether mapping user allocated buffer or driver allocated low memory buffer - if (NULL == low_mem_driver_allocated_buffer) { - mmap_read_lock(current->mm); - pinned_pages = get_user_pages_compact(user_address, npages, FOLL_WRITE | FOLL_FORCE, pages); - mmap_read_unlock(current->mm); - - if (pinned_pages < 0) { - pr_err("get_user_pages failed with %d\n", pinned_pages); - ret = pinned_pages; - goto exit; - } else if (pinned_pages != npages) { - pr_err("Pinned %d out of %zu\n", pinned_pages, npages); - ret = -EINVAL; - goto release_pages; - } - } else { - // Check to make sure in case user provides wrong buffer - if (npages != low_mem_driver_allocated_buffer->pages_count) { - pr_err("Received wrong amount of pages %zu to map expected %zu\n", - npages, low_mem_driver_allocated_buffer->pages_count); - ret = -EINVAL; - goto exit; - } - - for (i = 0; i < npages; i++) { - pages[i] = virt_to_page(low_mem_driver_allocated_buffer->pages_address[i]); - get_page(pages[i]); - } - } - - sg_alloc_res = sg_alloc_table_from_pages_segment_compat(sg_table, pages, npages, - 0, size, SGL_MAX_SEGMENT_SIZE, NULL, 0, GFP_KERNEL); - if (IS_ERR(sg_alloc_res)) { - ret = PTR_ERR(sg_alloc_res); - pr_err("sg table alloc failed (err %d)..\n", ret); - goto release_pages; - } - - ret = 0; - goto exit; -release_pages: - for (i = 0; i < pinned_pages; i++) { - if (!PageReserved(pages[i])) { - SetPageDirty(pages[i]); - } - put_page(pages[i]); - } -exit: - kvfree(pages); - return ret; -} - -static void clear_sg_table(struct sg_table *sgt) -{ - struct sg_page_iter iter; - struct page *page = NULL; - - for_each_sg_page(sgt->sgl, &iter, sgt->orig_nents, 0) { - page = sg_page_iter_page(&iter); - if (page) { - if (!PageReserved(page)) { - SetPageDirty(page); - } - put_page(page); - } - } - - sg_free_table(sgt); -} diff --git a/drivers/media/pci/hailo/vdma/memory.h b/drivers/media/pci/hailo/vdma/memory.h deleted file mode 100644 index f8bffcf9143200..00000000000000 --- a/drivers/media/pci/hailo/vdma/memory.h +++ /dev/null @@ -1,56 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/** - * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. - **/ -/** - * vDMA memory utility (including allocation and mappings) - */ - -#ifndef _HAILO_VDMA_MEMORY_H_ -#define _HAILO_VDMA_MEMORY_H_ - -#include "vdma/vdma.h" - -#define SGL_MAX_SEGMENT_SIZE (0x10000) - -struct hailo_vdma_buffer *hailo_vdma_buffer_map(struct device *dev, uintptr_t user_address, size_t size, - enum dma_data_direction direction, enum hailo_dma_buffer_type buffer_type, - struct hailo_vdma_low_memory_buffer *low_mem_driver_allocated_buffer); -void hailo_vdma_buffer_get(struct hailo_vdma_buffer *buf); -void hailo_vdma_buffer_put(struct hailo_vdma_buffer *buf); - -void hailo_vdma_buffer_sync(struct hailo_vdma_controller *controller, - struct hailo_vdma_buffer *mapped_buffer, enum hailo_vdma_buffer_sync_type sync_type, - size_t offset, size_t size); -void hailo_vdma_buffer_sync_cyclic(struct hailo_vdma_controller *controller, - struct hailo_vdma_buffer *mapped_buffer, enum hailo_vdma_buffer_sync_type sync_type, - size_t offset, size_t size); - -struct hailo_vdma_buffer* hailo_vdma_find_mapped_user_buffer(struct hailo_vdma_file_context *context, - size_t buffer_handle); -void hailo_vdma_clear_mapped_user_buffer_list(struct hailo_vdma_file_context *context, - struct hailo_vdma_controller *controller); - -int hailo_desc_list_create(struct device *dev, u32 descriptors_count, u16 desc_page_size, - uintptr_t desc_handle, bool is_circular, struct hailo_descriptors_list_buffer *descriptors); -void hailo_desc_list_release(struct device *dev, struct hailo_descriptors_list_buffer *descriptors); -struct hailo_descriptors_list_buffer* hailo_vdma_find_descriptors_buffer(struct hailo_vdma_file_context *context, - uintptr_t desc_handle); -void hailo_vdma_clear_descriptors_buffer_list(struct hailo_vdma_file_context *context, - struct hailo_vdma_controller *controller); - -int hailo_vdma_low_memory_buffer_alloc(size_t size, struct hailo_vdma_low_memory_buffer *low_memory_buffer); -void hailo_vdma_low_memory_buffer_free(struct hailo_vdma_low_memory_buffer *low_memory_buffer); -struct hailo_vdma_low_memory_buffer* hailo_vdma_find_low_memory_buffer(struct hailo_vdma_file_context *context, - uintptr_t buf_handle); -void hailo_vdma_clear_low_memory_buffer_list(struct hailo_vdma_file_context *context); - -int hailo_vdma_continuous_buffer_alloc(struct device *dev, size_t size, - struct hailo_vdma_continuous_buffer *continuous_buffer); -void hailo_vdma_continuous_buffer_free(struct device *dev, - struct hailo_vdma_continuous_buffer *continuous_buffer); -struct hailo_vdma_continuous_buffer* hailo_vdma_find_continuous_buffer(struct hailo_vdma_file_context *context, - uintptr_t buf_handle); -void hailo_vdma_clear_continuous_buffer_list(struct hailo_vdma_file_context *context, - struct hailo_vdma_controller *controller); -#endif /* _HAILO_VDMA_MEMORY_H_ */ \ No newline at end of file diff --git a/drivers/media/pci/hailo/vdma/vdma.c b/drivers/media/pci/hailo/vdma/vdma.c deleted file mode 100644 index 0ad2c5016a8fc2..00000000000000 --- a/drivers/media/pci/hailo/vdma/vdma.c +++ /dev/null @@ -1,313 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/** - * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. - **/ - -#define pr_fmt(fmt) "hailo: " fmt - -#include "vdma.h" -#include "memory.h" -#include "ioctl.h" -#include "utils/logs.h" - -#include -#include - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) -#include -#else -#include -#endif - - -static struct hailo_vdma_engine* init_vdma_engines(struct device *dev, - struct hailo_resource *channel_registers_per_engine, size_t engines_count, u32 src_channels_bitmask) -{ - struct hailo_vdma_engine *engines = NULL; - u8 i = 0; - - engines = devm_kmalloc_array(dev, engines_count, sizeof(*engines), GFP_KERNEL); - if (NULL == engines) { - dev_err(dev, "Failed allocating vdma engines\n"); - return ERR_PTR(-ENOMEM); - } - - for (i = 0; i < engines_count; i++) { - hailo_vdma_engine_init(&engines[i], i, &channel_registers_per_engine[i], src_channels_bitmask); - } - - return engines; -} - -static int hailo_set_dma_mask(struct device *dev) -{ - int err = -EINVAL; - /* Check and configure DMA length */ - if (!(err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)))) { - dev_notice(dev, "Probing: Enabled 64 bit dma\n"); - } else if (!(err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48)))) { - dev_notice(dev, "Probing: Enabled 48 bit dma\n"); - } else if (!(err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(40)))) { - dev_notice(dev, "Probing: Enabled 40 bit dma\n"); - } else if (!(err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(36)))) { - dev_notice(dev, "Probing: Enabled 36 bit dma\n"); - } else if (!(err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)))) { - dev_notice(dev, "Probing: Enabled 32 bit dma\n"); - } else { - dev_err(dev, "Probing: Error enabling dma %d\n", err); - return err; - } - - return 0; -} - -int hailo_vdma_controller_init(struct hailo_vdma_controller *controller, - struct device *dev, struct hailo_vdma_hw *vdma_hw, - struct hailo_vdma_controller_ops *ops, - struct hailo_resource *channel_registers_per_engine, size_t engines_count) -{ - int err = 0; - controller->hw = vdma_hw; - controller->ops = ops; - controller->dev = dev; - - controller->vdma_engines_count = engines_count; - controller->vdma_engines = init_vdma_engines(dev, channel_registers_per_engine, engines_count, - vdma_hw->src_channels_bitmask); - if (IS_ERR(controller->vdma_engines)) { - dev_err(dev, "Failed initialized vdma engines\n"); - return PTR_ERR(controller->vdma_engines); - } - - controller->used_by_filp = NULL; - spin_lock_init(&controller->interrupts_lock); - init_waitqueue_head(&controller->interrupts_wq); - - /* Check and configure DMA length */ - err = hailo_set_dma_mask(dev); - if (0 > err) { - return err; - } - - if (get_dma_ops(controller->dev)) { - hailo_dev_notice(controller->dev, "Probing: Using specialized dma_ops=%ps", get_dma_ops(controller->dev)); - } - - return 0; -} - -void hailo_vdma_file_context_init(struct hailo_vdma_file_context *context) -{ - atomic_set(&context->last_vdma_user_buffer_handle, 0); - INIT_LIST_HEAD(&context->mapped_user_buffer_list); - - atomic_set(&context->last_vdma_handle, 0); - INIT_LIST_HEAD(&context->descriptors_buffer_list); - INIT_LIST_HEAD(&context->vdma_low_memory_buffer_list); - INIT_LIST_HEAD(&context->continuous_buffer_list); - - BUILD_BUG_ON_MSG(MAX_VDMA_CHANNELS_PER_ENGINE > sizeof(context->enabled_channels_bitmap[0]) * BITS_IN_BYTE, - "Unexpected amount of VDMA channels per engine"); -} - -void hailo_vdma_update_interrupts_mask(struct hailo_vdma_controller *controller, - size_t engine_index) -{ - struct hailo_vdma_engine *engine = &controller->vdma_engines[engine_index]; - controller->ops->update_channel_interrupts(controller, engine_index, engine->enabled_channels); -} - -void hailo_vdma_file_context_finalize(struct hailo_vdma_file_context *context, - struct hailo_vdma_controller *controller, struct file *filp) -{ - size_t engine_index = 0; - struct hailo_vdma_engine *engine = NULL; - unsigned long irq_saved_flags = 0; - // In case of FLR, the vdma registers will be NULL - const bool is_device_up = (NULL != controller->dev); - - for_each_vdma_engine(controller, engine, engine_index) { - if (context->enabled_channels_bitmap[engine_index]) { - hailo_dev_info(controller->dev, "Disabling channels for engine %zu, channels bitmap 0x%x\n", engine_index, - context->enabled_channels_bitmap[engine_index]); - hailo_vdma_engine_disable_channels(engine, context->enabled_channels_bitmap[engine_index]); - - if (is_device_up) { - hailo_vdma_update_interrupts_mask(controller, engine_index); - } - - spin_lock_irqsave(&controller->interrupts_lock, irq_saved_flags); - hailo_vdma_engine_clear_channel_interrupts(engine, context->enabled_channels_bitmap[engine_index]); - spin_unlock_irqrestore(&controller->interrupts_lock, irq_saved_flags); - } - } - - hailo_vdma_clear_mapped_user_buffer_list(context, controller); - hailo_vdma_clear_descriptors_buffer_list(context, controller); - hailo_vdma_clear_low_memory_buffer_list(context); - hailo_vdma_clear_continuous_buffer_list(context, controller); - - if (filp == controller->used_by_filp) { - controller->used_by_filp = NULL; - } -} - -void hailo_vdma_wakeup_interrupts(struct hailo_vdma_controller *controller, struct hailo_vdma_engine *engine, - u32 channels_bitmap) -{ - unsigned long irq_saved_flags = 0; - - spin_lock_irqsave(&controller->interrupts_lock, irq_saved_flags); - hailo_vdma_engine_set_channel_interrupts(engine, channels_bitmap); - spin_unlock_irqrestore(&controller->interrupts_lock, irq_saved_flags); - - wake_up_interruptible_all(&controller->interrupts_wq); -} - -void hailo_vdma_irq_handler(struct hailo_vdma_controller *controller, - size_t engine_index, u32 channels_bitmap) -{ - struct hailo_vdma_engine *engine = NULL; - - BUG_ON(engine_index >= controller->vdma_engines_count); - engine = &controller->vdma_engines[engine_index]; - - hailo_vdma_engine_push_timestamps(engine, channels_bitmap); - - hailo_vdma_wakeup_interrupts(controller, engine, channels_bitmap); -} - -long hailo_vdma_ioctl(struct hailo_vdma_file_context *context, struct hailo_vdma_controller *controller, - unsigned int cmd, unsigned long arg, struct file *filp, struct semaphore *mutex, bool *should_up_board_mutex) -{ - switch (cmd) { - case HAILO_VDMA_ENABLE_CHANNELS: - return hailo_vdma_enable_channels_ioctl(controller, arg, context); - case HAILO_VDMA_DISABLE_CHANNELS: - return hailo_vdma_disable_channels_ioctl(controller, arg, context); - case HAILO_VDMA_INTERRUPTS_WAIT: - return hailo_vdma_interrupts_wait_ioctl(controller, arg, mutex, should_up_board_mutex); - case HAILO_VDMA_INTERRUPTS_READ_TIMESTAMPS: - return hailo_vdma_interrupts_read_timestamps_ioctl(controller, arg); - case HAILO_VDMA_BUFFER_MAP: - return hailo_vdma_buffer_map_ioctl(context, controller, arg); - case HAILO_VDMA_BUFFER_UNMAP: - return hailo_vdma_buffer_unmap_ioctl(context, controller, arg); - case HAILO_VDMA_BUFFER_SYNC: - return hailo_vdma_buffer_sync_ioctl(context, controller, arg); - case HAILO_DESC_LIST_CREATE: - return hailo_desc_list_create_ioctl(context, controller, arg); - case HAILO_DESC_LIST_RELEASE: - return hailo_desc_list_release_ioctl(context, controller, arg); - case HAILO_DESC_LIST_PROGRAM: - return hailo_desc_list_program_ioctl(context, controller, arg); - case HAILO_VDMA_LOW_MEMORY_BUFFER_ALLOC: - return hailo_vdma_low_memory_buffer_alloc_ioctl(context, controller, arg); - case HAILO_VDMA_LOW_MEMORY_BUFFER_FREE: - return hailo_vdma_low_memory_buffer_free_ioctl(context, controller, arg); - case HAILO_MARK_AS_IN_USE: - return hailo_mark_as_in_use(controller, arg, filp); - case HAILO_VDMA_CONTINUOUS_BUFFER_ALLOC: - return hailo_vdma_continuous_buffer_alloc_ioctl(context, controller, arg); - case HAILO_VDMA_CONTINUOUS_BUFFER_FREE: - return hailo_vdma_continuous_buffer_free_ioctl(context, controller, arg); - case HAILO_VDMA_LAUNCH_TRANSFER: - return hailo_vdma_launch_transfer_ioctl(context, controller, arg); - default: - hailo_dev_err(controller->dev, "Invalid vDMA ioctl code 0x%x (nr: %d)\n", cmd, _IOC_NR(cmd)); - return -ENOTTY; - } -} - -static int low_memory_buffer_mmap(struct hailo_vdma_controller *controller, - struct hailo_vdma_low_memory_buffer *vdma_buffer, struct vm_area_struct *vma) -{ - int err = 0; - size_t i = 0; - unsigned long vsize = vma->vm_end - vma->vm_start; - unsigned long orig_vm_start = vma->vm_start; - unsigned long orig_vm_end = vma->vm_end; - unsigned long page_fn = 0; - - if (vsize != vdma_buffer->pages_count * PAGE_SIZE) { - hailo_dev_err(controller->dev, "mmap size should be %lu (given %lu)\n", - vdma_buffer->pages_count * PAGE_SIZE, vsize); - return -EINVAL; - } - - for (i = 0 ; i < vdma_buffer->pages_count ; i++) { - if (i > 0) { - vma->vm_start = vma->vm_end; - } - vma->vm_end = vma->vm_start + PAGE_SIZE; - - page_fn = virt_to_phys(vdma_buffer->pages_address[i]) >> PAGE_SHIFT ; - err = remap_pfn_range(vma, vma->vm_start, page_fn, PAGE_SIZE, vma->vm_page_prot); - - if (err != 0) { - hailo_dev_err(controller->dev, " fops_mmap failed mapping kernel page %d\n", err); - return err; - } - } - - vma->vm_start = orig_vm_start; - vma->vm_end = orig_vm_end; - - return 0; -} - -static int continuous_buffer_mmap(struct hailo_vdma_controller *controller, - struct hailo_vdma_continuous_buffer *buffer, struct vm_area_struct *vma) -{ - int err = 0; - const unsigned long vsize = vma->vm_end - vma->vm_start; - - if (vsize > buffer->size) { - hailo_dev_err(controller->dev, "mmap size should be less than %zu (given %lu)\n", - buffer->size, vsize); - return -EINVAL; - } - - err = dma_mmap_coherent(controller->dev, vma, buffer->kernel_address, - buffer->dma_address, vsize); - if (err < 0) { - hailo_dev_err(controller->dev, " vdma_mmap failed dma_mmap_coherent %d\n", err); - return err; - } - - return 0; -} - -int hailo_vdma_mmap(struct hailo_vdma_file_context *context, struct hailo_vdma_controller *controller, - struct vm_area_struct *vma, uintptr_t vdma_handle) -{ - struct hailo_vdma_low_memory_buffer *low_memory_buffer = NULL; - struct hailo_vdma_continuous_buffer *continuous_buffer = NULL; - - hailo_dev_info(controller->dev, "Map vdma_handle %llu\n", (u64)vdma_handle); - if (NULL != (low_memory_buffer = hailo_vdma_find_low_memory_buffer(context, vdma_handle))) { - return low_memory_buffer_mmap(controller, low_memory_buffer, vma); - } - else if (NULL != (continuous_buffer = hailo_vdma_find_continuous_buffer(context, vdma_handle))) { - return continuous_buffer_mmap(controller, continuous_buffer, vma); - } - else { - hailo_dev_err(controller->dev, "Can't mmap vdma handle: %llu (not existing)\n", (u64)vdma_handle); - return -EINVAL; - } -} - -enum dma_data_direction get_dma_direction(enum hailo_dma_data_direction hailo_direction) -{ - switch (hailo_direction) { - case HAILO_DMA_BIDIRECTIONAL: - return DMA_BIDIRECTIONAL; - case HAILO_DMA_TO_DEVICE: - return DMA_TO_DEVICE; - case HAILO_DMA_FROM_DEVICE: - return DMA_FROM_DEVICE; - default: - pr_err("Invalid hailo direction %d\n", hailo_direction); - return DMA_NONE; - } -} diff --git a/drivers/media/pci/hailo/vdma/vdma.h b/drivers/media/pci/hailo/vdma/vdma.h deleted file mode 100644 index d0e693e879c346..00000000000000 --- a/drivers/media/pci/hailo/vdma/vdma.h +++ /dev/null @@ -1,164 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/** - * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. - **/ -/** - * Hailo vdma engine definitions - */ - -#ifndef _HAILO_VDMA_VDMA_H_ -#define _HAILO_VDMA_VDMA_H_ - -#include "hailo_ioctl_common.h" -#include "hailo_resource.h" -#include "vdma_common.h" - -#include -#include -#include -#include -#include - -#define VDMA_CHANNEL_CONTROL_REG_OFFSET(channel_index, direction) (((direction) == DMA_TO_DEVICE) ? \ - (((channel_index) << 5) + 0x0) : (((channel_index) << 5) + 0x10)) -#define VDMA_CHANNEL_CONTROL_REG_ADDRESS(vdma_registers, channel_index, direction) \ - ((u8*)((vdma_registers)->address) + VDMA_CHANNEL_CONTROL_REG_OFFSET(channel_index, direction)) - -#define VDMA_CHANNEL_NUM_PROC_OFFSET(channel_index, direction) (((direction) == DMA_TO_DEVICE) ? \ - (((channel_index) << 5) + 0x4) : (((channel_index) << 5) + 0x14)) -#define VDMA_CHANNEL_NUM_PROC_ADDRESS(vdma_registers, channel_index, direction) \ - ((u8*)((vdma_registers)->address) + VDMA_CHANNEL_NUM_PROC_OFFSET(channel_index, direction)) - - -// dmabuf is supported from linux kernel version 3.3 -#if LINUX_VERSION_CODE < KERNEL_VERSION( 3, 3, 0 ) -// Make dummy struct with one byte (C standards does not allow empty struct) - in order to not have to ifdef everywhere -struct hailo_dmabuf_info { - uint8_t dummy; -}; -#else -// dmabuf_sg_table is needed because in dma_buf_unmap_attachment() the sg_table's address has to match the -// The one returned from dma_buf_map_attachment() - otherwise we would need to malloc each time -struct hailo_dmabuf_info { - struct dma_buf *dmabuf; - struct dma_buf_attachment *dmabuf_attachment; - struct sg_table *dmabuf_sg_table; -}; -#endif // LINUX_VERSION_CODE < KERNEL_VERSION( 3, 3, 0 ) - -struct hailo_vdma_buffer { - struct list_head mapped_user_buffer_list; - size_t handle; - - struct kref kref; - struct device *device; - - uintptr_t user_address; - u32 size; - enum dma_data_direction data_direction; - struct sg_table sg_table; - - // If this flag is set, the buffer pointed by sg_table is not backed by - // 'struct page' (only by pure pfn). On this case, accessing to the page, - // or calling APIs that access the page (e.g. dma_sync_sg_for_cpu) is not - // allowed. - bool is_mmio; - - // Relevant paramaters that need to be saved in case of dmabuf - otherwise struct pointers will be NULL - struct hailo_dmabuf_info dmabuf_info; -}; - -// Continuous buffer that holds a descriptor list. -struct hailo_descriptors_list_buffer { - struct list_head descriptors_buffer_list; - uintptr_t handle; - void *kernel_address; - dma_addr_t dma_address; - u32 buffer_size; - struct hailo_vdma_descriptors_list desc_list; -}; - -struct hailo_vdma_low_memory_buffer { - struct list_head vdma_low_memory_buffer_list; - uintptr_t handle; - size_t pages_count; - void **pages_address; -}; - -struct hailo_vdma_continuous_buffer { - struct list_head continuous_buffer_list; - uintptr_t handle; - void *kernel_address; - dma_addr_t dma_address; - size_t size; -}; - -struct hailo_vdma_controller; -struct hailo_vdma_controller_ops { - void (*update_channel_interrupts)(struct hailo_vdma_controller *controller, size_t engine_index, - u32 channels_bitmap); -}; - -struct hailo_vdma_controller { - struct hailo_vdma_hw *hw; - struct hailo_vdma_controller_ops *ops; - struct device *dev; - - size_t vdma_engines_count; - struct hailo_vdma_engine *vdma_engines; - - spinlock_t interrupts_lock; - wait_queue_head_t interrupts_wq; - - struct file *used_by_filp; - - // Putting big IOCTL structures here to avoid stack allocation. - struct hailo_vdma_interrupts_read_timestamp_params read_interrupt_timestamps_params; -}; - -#define for_each_vdma_engine(controller, engine, engine_index) \ - _for_each_element_array(controller->vdma_engines, controller->vdma_engines_count, \ - engine, engine_index) - -struct hailo_vdma_file_context { - atomic_t last_vdma_user_buffer_handle; - struct list_head mapped_user_buffer_list; - - // Last_vdma_handle works as a handle for vdma decriptor list and for the vdma buffer - - // there will be no collisions between the two - atomic_t last_vdma_handle; - struct list_head descriptors_buffer_list; - struct list_head vdma_low_memory_buffer_list; - struct list_head continuous_buffer_list; - u32 enabled_channels_bitmap[MAX_VDMA_ENGINES]; -}; - - -int hailo_vdma_controller_init(struct hailo_vdma_controller *controller, - struct device *dev, struct hailo_vdma_hw *vdma_hw, - struct hailo_vdma_controller_ops *ops, - struct hailo_resource *channel_registers_per_engine, size_t engines_count); - -void hailo_vdma_update_interrupts_mask(struct hailo_vdma_controller *controller, - size_t engine_index); - -void hailo_vdma_file_context_init(struct hailo_vdma_file_context *context); -void hailo_vdma_file_context_finalize(struct hailo_vdma_file_context *context, - struct hailo_vdma_controller *controller, struct file *filp); - -void hailo_vdma_wakeup_interrupts(struct hailo_vdma_controller *controller, struct hailo_vdma_engine *engine, - u32 channels_bitmap); -void hailo_vdma_irq_handler(struct hailo_vdma_controller *controller, size_t engine_index, - u32 channels_bitmap); - -// TODO: reduce params count -long hailo_vdma_ioctl(struct hailo_vdma_file_context *context, struct hailo_vdma_controller *controller, - unsigned int cmd, unsigned long arg, struct file *filp, struct semaphore *mutex, bool *should_up_board_mutex); - -int hailo_vdma_mmap(struct hailo_vdma_file_context *context, struct hailo_vdma_controller *controller, - struct vm_area_struct *vma, uintptr_t vdma_handle); - -enum dma_data_direction get_dma_direction(enum hailo_dma_data_direction hailo_direction); -void hailo_vdma_disable_vdma_channels(struct hailo_vdma_controller *controller, const bool should_close_channels); - -#endif /* _HAILO_VDMA_VDMA_H_ */ \ No newline at end of file From 2c6e5904c5bdbac8e0eadee40f70c42bb83f6dc6 Mon Sep 17 00:00:00 2001 From: Shuicheng Lin Date: Fri, 10 Oct 2025 17:25:29 +0000 Subject: [PATCH 006/143] drm/xe/guc: Check GuC running state before deregistering exec queue commit 9f64b3cd051b825de0a2a9f145c8e003200cedd5 upstream. In normal operation, a registered exec queue is disabled and deregistered through the GuC, and freed only after the GuC confirms completion. However, if the driver is forced to unbind while the exec queue is still running, the user may call exec_destroy() after the GuC has already been stopped and CT communication disabled. In this case, the driver cannot receive a response from the GuC, preventing proper cleanup of exec queue resources. Fix this by directly releasing the resources when GuC is not running. Here is the failure dmesg log: " [ 468.089581] ---[ end trace 0000000000000000 ]--- [ 468.089608] pci 0000:03:00.0: [drm] *ERROR* GT0: GUC ID manager unclean (1/65535) [ 468.090558] pci 0000:03:00.0: [drm] GT0: total 65535 [ 468.090562] pci 0000:03:00.0: [drm] GT0: used 1 [ 468.090564] pci 0000:03:00.0: [drm] GT0: range 1..1 (1) [ 468.092716] ------------[ cut here ]------------ [ 468.092719] WARNING: CPU: 14 PID: 4775 at drivers/gpu/drm/xe/xe_ttm_vram_mgr.c:298 ttm_vram_mgr_fini+0xf8/0x130 [xe] " v2: use xe_uc_fw_is_running() instead of xe_guc_ct_enabled(). As CT may go down and come back during VF migration. Fixes: dd08ebf6c352 ("drm/xe: Introduce a new DRM driver for Intel GPUs") Cc: stable@vger.kernel.org Cc: Matthew Brost Signed-off-by: Shuicheng Lin Reviewed-by: Matthew Brost Signed-off-by: Matthew Brost Link: https://lore.kernel.org/r/20251010172529.2967639-2-shuicheng.lin@intel.com (cherry picked from commit 9b42321a02c50a12b2beb6ae9469606257fbecea) Signed-off-by: Lucas De Marchi Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/xe/xe_guc_submit.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c index cf6946424fc357..03d674e9e80753 100644 --- a/drivers/gpu/drm/xe/xe_guc_submit.c +++ b/drivers/gpu/drm/xe/xe_guc_submit.c @@ -41,6 +41,7 @@ #include "xe_ring_ops_types.h" #include "xe_sched_job.h" #include "xe_trace.h" +#include "xe_uc_fw.h" #include "xe_vm.h" static struct xe_guc * @@ -1285,7 +1286,17 @@ static void __guc_exec_queue_process_msg_cleanup(struct xe_sched_msg *msg) xe_assert(xe, !(q->flags & EXEC_QUEUE_FLAG_PERMANENT)); trace_xe_exec_queue_cleanup_entity(q); - if (exec_queue_registered(q)) + /* + * Expected state transitions for cleanup: + * - If the exec queue is registered and GuC firmware is running, we must first + * disable scheduling and deregister the queue to ensure proper teardown and + * resource release in the GuC, then destroy the exec queue on driver side. + * - If the GuC is already stopped (e.g., during driver unload or GPU reset), + * we cannot expect a response for the deregister request. In this case, + * it is safe to directly destroy the exec queue on driver side, as the GuC + * will not process further requests and all resources must be cleaned up locally. + */ + if (exec_queue_registered(q) && xe_uc_fw_is_running(&guc->fw)) disable_scheduling_deregister(guc, q); else __guc_exec_queue_fini(guc, q); From dc15450a5b85577f99b9dcf5053f9ff7c53ae25a Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Mon, 8 Sep 2025 14:12:35 +0100 Subject: [PATCH 007/143] rust: cfi: only 64-bit arm and x86 support CFI_CLANG commit 812258ff4166bcd41c7d44707e0591f9ae32ac8c upstream. The kernel uses the standard rustc targets for non-x86 targets, and out of those only 64-bit arm's target has kcfi support enabled. For x86, the custom 64-bit target enables kcfi. The HAVE_CFI_ICALL_NORMALIZE_INTEGERS_RUSTC config option that allows CFI_CLANG to be used in combination with RUST does not check whether the rustc target supports kcfi. This breaks the build on riscv (and presumably 32-bit arm) when CFI_CLANG and RUST are enabled at the same time. Ordinarily, a rustc-option check would be used to detect target support but unfortunately rustc-option filters out the target for reasons given in commit 46e24a545cdb4 ("rust: kasan/kbuild: fix missing flags on first build"). As a result, if the host supports kcfi but the target does not, e.g. when building for riscv on x86_64, the build would remain broken. Instead, make HAVE_CFI_ICALL_NORMALIZE_INTEGERS_RUSTC depend on the only two architectures where the target used supports it to fix the build. CC: stable@vger.kernel.org Fixes: ca627e636551e ("rust: cfi: add support for CFI_CLANG with Rust") Signed-off-by: Conor Dooley Acked-by: Miguel Ojeda Reviewed-by: Alice Ryhl Link: https://lore.kernel.org/r/20250908-distill-lint-1ae78bcf777c@spud Signed-off-by: Paul Walmsley Signed-off-by: Greg Kroah-Hartman --- arch/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/Kconfig b/arch/Kconfig index bd9f095d69fa03..593452b43dd499 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -861,6 +861,7 @@ config HAVE_CFI_ICALL_NORMALIZE_INTEGERS_RUSTC def_bool y depends on HAVE_CFI_ICALL_NORMALIZE_INTEGERS_CLANG depends on RUSTC_VERSION >= 107900 + depends on ARM64 || X86_64 # With GCOV/KASAN we need this fix: https://github.com/rust-lang/rust/pull/129373 depends on (RUSTC_LLVM_VERSION >= 190103 && RUSTC_VERSION >= 108200) || \ (!GCOV_KERNEL && !KASAN_GENERIC && !KASAN_SW_TAGS) From e15605b68b490186da2ad8029c0351a9cfb0b9af Mon Sep 17 00:00:00 2001 From: Shuhao Fu Date: Thu, 16 Oct 2025 02:52:55 +0000 Subject: [PATCH 008/143] smb: client: Fix refcount leak for cifs_sb_tlink commit c2b77f42205ef485a647f62082c442c1cd69d3fc upstream. Fix three refcount inconsistency issues related to `cifs_sb_tlink`. Comments for `cifs_sb_tlink` state that `cifs_put_tlink()` needs to be called after successful calls to `cifs_sb_tlink()`. Three calls fail to update refcount accordingly, leading to possible resource leaks. Fixes: 8ceb98437946 ("CIFS: Move rename to ops struct") Fixes: 2f1afe25997f ("cifs: Use smb 2 - 3 and cifsacl mount options getacl functions") Fixes: 366ed846df60 ("cifs: Use smb 2 - 3 and cifsacl mount options setacl function") Cc: stable@vger.kernel.org Signed-off-by: Shuhao Fu Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/inode.c | 6 ++++-- fs/smb/client/smb2ops.c | 8 ++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c index e06d02b68c5387..4862a9518a3215 100644 --- a/fs/smb/client/inode.c +++ b/fs/smb/client/inode.c @@ -2381,8 +2381,10 @@ cifs_do_rename(const unsigned int xid, struct dentry *from_dentry, tcon = tlink_tcon(tlink); server = tcon->ses->server; - if (!server->ops->rename) - return -ENOSYS; + if (!server->ops->rename) { + rc = -ENOSYS; + goto do_rename_exit; + } /* try path-based rename first */ rc = server->ops->rename(xid, tcon, from_dentry, diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index 1b30035d02bc51..35299967737f11 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -3134,8 +3134,7 @@ get_smb2_acl_by_path(struct cifs_sb_info *cifs_sb, utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); if (!utf16_path) { rc = -ENOMEM; - free_xid(xid); - return ERR_PTR(rc); + goto put_tlink; } oparms = (struct cifs_open_parms) { @@ -3167,6 +3166,7 @@ get_smb2_acl_by_path(struct cifs_sb_info *cifs_sb, SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); } +put_tlink: cifs_put_tlink(tlink); free_xid(xid); @@ -3207,8 +3207,7 @@ set_smb2_acl(struct smb_ntsd *pnntsd, __u32 acllen, utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); if (!utf16_path) { rc = -ENOMEM; - free_xid(xid); - return rc; + goto put_tlink; } oparms = (struct cifs_open_parms) { @@ -3229,6 +3228,7 @@ set_smb2_acl(struct smb_ntsd *pnntsd, __u32 acllen, SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); } +put_tlink: cifs_put_tlink(tlink); free_xid(xid); return rc; From 4772e7f18ac2fcada7947d5d55a2fa2d845b8e27 Mon Sep 17 00:00:00 2001 From: Hao Ge Date: Wed, 15 Oct 2025 22:16:42 +0800 Subject: [PATCH 009/143] slab: reset slab->obj_ext when freeing and it is OBJEXTS_ALLOC_FAIL commit 86f54f9b6c17d6567c69e3a6fed52fdf5d7dbe93 upstream. If obj_exts allocation failed, slab->obj_exts is set to OBJEXTS_ALLOC_FAIL, But we do not clear it when freeing the slab. Since OBJEXTS_ALLOC_FAIL and MEMCG_DATA_OBJEXTS currently share the same bit position, during the release of the associated folio, a VM_BUG_ON_FOLIO() check in folio_memcg_kmem() is triggered because the OBJEXTS_ALLOC_FAIL flag was not cleared, causing it to be interpreted as a kmem folio (non-slab) with MEMCG_OBJEXTS_DATA flag set, which is invalid because MEMCG_OBJEXTS_DATA is supposed to be set only on slabs. Another problem that predates sharing the OBJEXTS_ALLOC_FAIL and MEMCG_DATA_OBJEXTS bits is that on configurations with is_check_pages_enabled(), the non-cleared bit in page->memcg_data will trigger a free_page_is_bad() failure "page still charged to cgroup" When freeing a slab, we clear slab->obj_exts if the obj_ext array has been successfully allocated. So let's clear it also when the allocation has failed. Fixes: 09c46563ff6d ("codetag: debug: introduce OBJEXTS_ALLOC_FAIL to mark failed slab_ext allocations") Fixes: 7612833192d5 ("slab: Reuse first bit for OBJEXTS_ALLOC_FAIL") Link: https://lore.kernel.org/all/20251015141642.700170-1-hao.ge@linux.dev/ Cc: Signed-off-by: Hao Ge Reviewed-by: Suren Baghdasaryan Reviewed-by: Harry Yoo Signed-off-by: Vlastimil Babka Signed-off-by: Greg Kroah-Hartman --- mm/slub.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/mm/slub.c b/mm/slub.c index b75b50ad6748f0..24e65d7048ba23 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -2038,8 +2038,15 @@ static inline void free_slab_obj_exts(struct slab *slab) struct slabobj_ext *obj_exts; obj_exts = slab_obj_exts(slab); - if (!obj_exts) + if (!obj_exts) { + /* + * If obj_exts allocation failed, slab->obj_exts is set to + * OBJEXTS_ALLOC_FAIL. In this case, we end up here and should + * clear the flag. + */ + slab->obj_exts = 0; return; + } /* * obj_exts was created with __GFP_NO_OBJ_EXT flag, therefore its From d6cf1320591d95124ef96b85d8d1fac363e7acbb Mon Sep 17 00:00:00 2001 From: Yi Cong Date: Sat, 11 Oct 2025 16:24:15 +0800 Subject: [PATCH 010/143] r8152: add error handling in rtl8152_driver_init commit 75527d61d60d493d1eb064f335071a20ca581f54 upstream. rtl8152_driver_init() is missing the error handling. When rtl8152_driver registration fails, rtl8152_cfgselector_driver should be deregistered. Fixes: ec51fbd1b8a2 ("r8152: add USB device driver for config selection") Cc: stable@vger.kernel.org Signed-off-by: Yi Cong Reviewed-by: Simon Horman Link: https://patch.msgid.link/20251011082415.580740-1-yicongsrfy@163.com [pabeni@redhat.com: clarified the commit message] Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- drivers/net/usb/r8152.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 2cab046749a922..3fcd2b736c5e3e 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -10151,7 +10151,12 @@ static int __init rtl8152_driver_init(void) ret = usb_register_device_driver(&rtl8152_cfgselector_driver, THIS_MODULE); if (ret) return ret; - return usb_register(&rtl8152_driver); + + ret = usb_register(&rtl8152_driver); + if (ret) + usb_deregister_device_driver(&rtl8152_cfgselector_driver); + + return ret; } static void __exit rtl8152_driver_exit(void) From 40bf3676cb3992829f30608acf78007e99570455 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 7 Oct 2025 03:32:30 +0000 Subject: [PATCH 011/143] f2fs: fix wrong block mapping for multi-devices commit 9d5c4f5c7a2c7677e1b3942772122b032c265aae upstream. Assuming the disk layout as below, disk0: 0 --- 0x00035abfff disk1: 0x00035ac000 --- 0x00037abfff disk2: 0x00037ac000 --- 0x00037ebfff and we want to read data from offset=13568 having len=128 across the block devices, we can illustrate the block addresses like below. 0 .. 0x00037ac000 ------------------- 0x00037ebfff, 0x00037ec000 ------- | ^ ^ ^ | fofs 0 13568 13568+128 | ------------------------------------------------------ | LBA 0x37e8aa9 0x37ebfa9 0x37ec029 --- map 0x3caa9 0x3ffa9 In this example, we should give the relative map of the target block device ranging from 0x3caa9 to 0x3ffa9 where the length should be calculated by 0x37ebfff + 1 - 0x37ebfa9. In the below equation, however, map->m_pblk was supposed to be the original address instead of the one from the target block address. - map->m_len = min(map->m_len, dev->end_blk + 1 - map->m_pblk); Cc: stable@vger.kernel.org Fixes: 71f2c8206202 ("f2fs: multidevice: support direct IO") Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim Signed-off-by: Greg Kroah-Hartman --- fs/f2fs/data.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 040c06dfb8c033..bbb29b6024382a 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1508,8 +1508,8 @@ static bool f2fs_map_blocks_cached(struct inode *inode, struct f2fs_dev_info *dev = &sbi->devs[bidx]; map->m_bdev = dev->bdev; - map->m_pblk -= dev->start_blk; map->m_len = min(map->m_len, dev->end_blk + 1 - map->m_pblk); + map->m_pblk -= dev->start_blk; } else { map->m_bdev = inode->i_sb->s_bdev; } From 9f573888397795127cca48687f4141b5a6bd613f Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Tue, 16 Sep 2025 17:33:36 +0800 Subject: [PATCH 012/143] jbd2: ensure that all ongoing I/O complete before freeing blocks commit 3c652c3a71de1d30d72dc82c3bead8deb48eb749 upstream. When releasing file system metadata blocks in jbd2_journal_forget(), if this buffer has not yet been checkpointed, it may have already been written back, currently be in the process of being written back, or has not yet written back. jbd2_journal_forget() calls jbd2_journal_try_remove_checkpoint() to check the buffer's status and add it to the current transaction if it has not been written back. This buffer can only be reallocated after the transaction is committed. jbd2_journal_try_remove_checkpoint() attempts to lock the buffer and check its dirty status while holding the buffer lock. If the buffer has already been written back, everything proceeds normally. However, there are two issues. First, the function returns immediately if the buffer is locked by the write-back process. It does not wait for the write-back to complete. Consequently, until the current transaction is committed and the block is reallocated, there is no guarantee that the I/O will complete. This means that ongoing I/O could write stale metadata to the newly allocated block, potentially corrupting data. Second, the function unlocks the buffer as soon as it detects that the buffer is still dirty. If a concurrent write-back occurs immediately after this unlocking and before clear_buffer_dirty() is called in jbd2_journal_forget(), data corruption can theoretically still occur. Although these two issues are unlikely to occur in practice since the undergoing metadata writeback I/O does not take this long to complete, it's better to explicitly ensure that all ongoing I/O operations are completed. Fixes: 597599268e3b ("jbd2: discard dirty data when forgetting an un-journalled buffer") Cc: stable@kernel.org Suggested-by: Jan Kara Signed-off-by: Zhang Yi Reviewed-by: Jan Kara Message-ID: <20250916093337.3161016-2-yi.zhang@huaweicloud.com> Signed-off-by: Theodore Ts'o Signed-off-by: Greg Kroah-Hartman --- fs/jbd2/transaction.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c index f440110df93a9b..ae43920ce395c1 100644 --- a/fs/jbd2/transaction.c +++ b/fs/jbd2/transaction.c @@ -1663,6 +1663,7 @@ int jbd2_journal_forget(handle_t *handle, struct buffer_head *bh) int drop_reserve = 0; int err = 0; int was_modified = 0; + int wait_for_writeback = 0; if (is_handle_aborted(handle)) return -EROFS; @@ -1786,18 +1787,22 @@ int jbd2_journal_forget(handle_t *handle, struct buffer_head *bh) } /* - * The buffer is still not written to disk, we should - * attach this buffer to current transaction so that the - * buffer can be checkpointed only after the current - * transaction commits. + * The buffer has not yet been written to disk. We should + * either clear the buffer or ensure that the ongoing I/O + * is completed, and attach this buffer to current + * transaction so that the buffer can be checkpointed only + * after the current transaction commits. */ clear_buffer_dirty(bh); + wait_for_writeback = 1; __jbd2_journal_file_buffer(jh, transaction, BJ_Forget); spin_unlock(&journal->j_list_lock); } drop: __brelse(bh); spin_unlock(&jh->b_state_lock); + if (wait_for_writeback) + wait_on_buffer(bh); jbd2_journal_put_journal_head(jh); if (drop_reserve) { /* no need to reserve log space for this block -bzzz */ From 5b7b9a17151be9a3e2b6742f0277a529515e8c2c Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Tue, 16 Sep 2025 17:33:37 +0800 Subject: [PATCH 013/143] ext4: wait for ongoing I/O to complete before freeing blocks commit 328a782cb138029182e521c08f50eb1587db955d upstream. When freeing metadata blocks in nojournal mode, ext4_forget() calls bforget() to clear the dirty flag on the buffer_head and remvoe associated mappings. This is acceptable if the metadata has not yet begun to be written back. However, if the write-back has already started but is not yet completed, ext4_forget() will have no effect. Subsequently, ext4_mb_clear_bb() will immediately return the block to the mb allocator. This block can then be reallocated immediately, potentially causing an data corruption issue. Fix this by clearing the buffer's dirty flag and waiting for the ongoing I/O to complete, ensuring that no further writes to stale data will occur. Fixes: 16e08b14a455 ("ext4: cleanup clean_bdev_aliases() calls") Cc: stable@kernel.org Reported-by: Gao Xiang Closes: https://lore.kernel.org/linux-ext4/a9417096-9549-4441-9878-b1955b899b4e@huaweicloud.com/ Signed-off-by: Zhang Yi Reviewed-by: Jan Kara Message-ID: <20250916093337.3161016-3-yi.zhang@huaweicloud.com> Signed-off-by: Theodore Ts'o Signed-off-by: Greg Kroah-Hartman --- fs/ext4/ext4_jbd2.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c index da4a8245638364..9e7275dd901f49 100644 --- a/fs/ext4/ext4_jbd2.c +++ b/fs/ext4/ext4_jbd2.c @@ -276,9 +276,16 @@ int __ext4_forget(const char *where, unsigned int line, handle_t *handle, bh, is_metadata, inode->i_mode, test_opt(inode->i_sb, DATA_FLAGS)); - /* In the no journal case, we can just do a bforget and return */ + /* + * In the no journal case, we should wait for the ongoing buffer + * to complete and do a forget. + */ if (!ext4_handle_valid(handle)) { - bforget(bh); + if (bh) { + clear_buffer_dirty(bh); + wait_on_buffer(bh); + __bforget(bh); + } return 0; } From de985264eef64be8a90595908f2e6a87946dad34 Mon Sep 17 00:00:00 2001 From: Deepanshu Kartikey Date: Tue, 30 Sep 2025 16:58:10 +0530 Subject: [PATCH 014/143] ext4: detect invalid INLINE_DATA + EXTENTS flag combination commit 1d3ad183943b38eec2acf72a0ae98e635dc8456b upstream. syzbot reported a BUG_ON in ext4_es_cache_extent() when opening a verity file on a corrupted ext4 filesystem mounted without a journal. The issue is that the filesystem has an inode with both the INLINE_DATA and EXTENTS flags set: EXT4-fs error (device loop0): ext4_cache_extents:545: inode #15: comm syz.0.17: corrupted extent tree: lblk 0 < prev 66 Investigation revealed that the inode has both flags set: DEBUG: inode 15 - flag=1, i_inline_off=164, has_inline=1, extents_flag=1 This is an invalid combination since an inode should have either: - INLINE_DATA: data stored directly in the inode - EXTENTS: data stored in extent-mapped blocks Having both flags causes ext4_has_inline_data() to return true, skipping extent tree validation in __ext4_iget(). The unvalidated out-of-order extents then trigger a BUG_ON in ext4_es_cache_extent() due to integer underflow when calculating hole sizes. Fix this by detecting this invalid flag combination early in ext4_iget() and rejecting the corrupted inode. Cc: stable@kernel.org Reported-and-tested-by: syzbot+038b7bf43423e132b308@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=038b7bf43423e132b308 Suggested-by: Zhang Yi Signed-off-by: Deepanshu Kartikey Reviewed-by: Zhang Yi Message-ID: <20250930112810.315095-1-kartikey406@gmail.com> Signed-off-by: Theodore Ts'o Signed-off-by: Greg Kroah-Hartman --- fs/ext4/inode.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 6af57242559658..4ad34eba00a770 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -4904,6 +4904,14 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino, } ei->i_flags = le32_to_cpu(raw_inode->i_flags); ext4_set_inode_flags(inode, true); + /* Detect invalid flag combination - can't have both inline data and extents */ + if (ext4_test_inode_flag(inode, EXT4_INODE_INLINE_DATA) && + ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) { + ext4_error_inode(inode, function, line, 0, + "inode has both inline data and extents flags"); + ret = -EFSCORRUPTED; + goto bad_inode; + } inode->i_blocks = ext4_inode_blocks(raw_inode, ei); ei->i_file_acl = le32_to_cpu(raw_inode->i_file_acl_lo); if (ext4_has_feature_64bit(sb)) From d2d3902f134e0cdbbcf97d5687e4e4ccb03ac190 Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Wed, 24 Sep 2025 16:10:38 +0100 Subject: [PATCH 015/143] btrfs: fix clearing of BTRFS_FS_RELOC_RUNNING if relocation already running commit 7e5a5983edda664e8e4bb20af17b80f5135c655c upstream. When starting relocation, at reloc_chunk_start(), if we happen to find the flag BTRFS_FS_RELOC_RUNNING is already set we return an error (-EINPROGRESS) to the callers, however the callers call reloc_chunk_end() which will clear the flag BTRFS_FS_RELOC_RUNNING, which is wrong since relocation was started by another task and still running. Finding the BTRFS_FS_RELOC_RUNNING flag already set is an unexpected scenario, but still our current behaviour is not correct. Fix this by never calling reloc_chunk_end() if reloc_chunk_start() has returned an error, which is what logically makes sense, since the general widespread pattern is to have end functions called only if the counterpart start functions succeeded. This requires changing reloc_chunk_start() to clear BTRFS_FS_RELOC_RUNNING if there's a pending cancel request. Fixes: 907d2710d727 ("btrfs: add cancellable chunk relocation support") CC: stable@vger.kernel.org # 5.15+ Reviewed-by: Boris Burkov Reviewed-by: Johannes Thumshirn Reviewed-by: Qu Wenruo Signed-off-by: Filipe Manana Reviewed-by: David Sterba Signed-off-by: David Sterba Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/relocation.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index 79eb984041dd69..0d5a3846811ad3 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -3906,6 +3906,7 @@ static noinline_for_stack struct inode *create_reloc_inode( /* * Mark start of chunk relocation that is cancellable. Check if the cancellation * has been requested meanwhile and don't start in that case. + * NOTE: if this returns an error, reloc_chunk_end() must not be called. * * Return: * 0 success @@ -3922,10 +3923,8 @@ static int reloc_chunk_start(struct btrfs_fs_info *fs_info) if (atomic_read(&fs_info->reloc_cancel_req) > 0) { btrfs_info(fs_info, "chunk relocation canceled on start"); - /* - * On cancel, clear all requests but let the caller mark - * the end after cleanup operations. - */ + /* On cancel, clear all requests. */ + clear_and_wake_up_bit(BTRFS_FS_RELOC_RUNNING, &fs_info->flags); atomic_set(&fs_info->reloc_cancel_req, 0); return -ECANCELED; } @@ -3934,9 +3933,11 @@ static int reloc_chunk_start(struct btrfs_fs_info *fs_info) /* * Mark end of chunk relocation that is cancellable and wake any waiters. + * NOTE: call only if a previous call to reloc_chunk_start() succeeded. */ static void reloc_chunk_end(struct btrfs_fs_info *fs_info) { + ASSERT(test_bit(BTRFS_FS_RELOC_RUNNING, &fs_info->flags)); /* Requested after start, clear bit first so any waiters can continue */ if (atomic_read(&fs_info->reloc_cancel_req) > 0) btrfs_info(fs_info, "chunk relocation canceled during operation"); @@ -4145,9 +4146,9 @@ int btrfs_relocate_block_group(struct btrfs_fs_info *fs_info, u64 group_start) if (err && rw) btrfs_dec_block_group_ro(rc->block_group); iput(rc->data_inode); + reloc_chunk_end(fs_info); out_put_bg: btrfs_put_block_group(bg); - reloc_chunk_end(fs_info); free_reloc_control(rc); return err; } @@ -4331,8 +4332,8 @@ int btrfs_recover_relocation(struct btrfs_fs_info *fs_info) ret = ret2; out_unset: unset_reloc_control(rc); -out_end: reloc_chunk_end(fs_info); +out_end: free_reloc_control(rc); out: free_reloc_roots(&reloc_roots); From 2b039c50299ba0355ed5e33c0b4e7fad0aa7dd6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miquel=20Sabat=C3=A9=20Sol=C3=A0?= Date: Thu, 25 Sep 2025 20:41:39 +0200 Subject: [PATCH 016/143] btrfs: fix memory leak on duplicated memory in the qgroup assign ioctl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 53a4acbfc1de85fa637521ffab4f4e2ee03cbeeb upstream. On 'btrfs_ioctl_qgroup_assign' we first duplicate the argument as provided by the user, which is kfree'd in the end. But this was not the case when allocating memory for 'prealloc'. In this case, if it somehow failed, then the previous code would go directly into calling 'mnt_drop_write_file', without freeing the string duplicated from the user space. Fixes: 4addc1ffd67a ("btrfs: qgroup: preallocate memory before adding a relation") CC: stable@vger.kernel.org # 6.12+ Reviewed-by: Boris Burkov Reviewed-by: Filipe Manana Signed-off-by: Miquel Sabaté Solà Signed-off-by: Filipe Manana Reviewed-by: David Sterba Signed-off-by: David Sterba Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 1706f6d9b12e68..03c3b5d0abbe4f 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -3852,7 +3852,7 @@ static long btrfs_ioctl_qgroup_assign(struct file *file, void __user *arg) prealloc = kzalloc(sizeof(*prealloc), GFP_KERNEL); if (!prealloc) { ret = -ENOMEM; - goto drop_write; + goto out; } } From 376b9f404130fcbe42e81480e6b3fed45a858328 Mon Sep 17 00:00:00 2001 From: Boris Burkov Date: Tue, 30 Sep 2025 21:05:17 -0700 Subject: [PATCH 017/143] btrfs: fix incorrect readahead expansion length commit 8ab2fa69691b2913a67f3c54fbb991247b3755be upstream. The intent of btrfs_readahead_expand() was to expand to the length of the current compressed extent being read. However, "ram_bytes" is *not* that, in the case where a single physical compressed extent is used for multiple file extents. Consider this case with a large compressed extent C and then later two non-compressed extents N1 and N2 written over C, leaving C1 and C2 pointing to offset/len pairs of C: [ C ] [ N1 ][ C1 ][ N2 ][ C2 ] In such a case, ram_bytes for both C1 and C2 is the full uncompressed length of C. So starting readahead in C1 will expand the readahead past the end of C1, past N2, and into C2. This will then expand readahead again, to C2_start + ram_bytes, way past EOF. First of all, this is totally undesirable, we don't want to read the whole file in arbitrary chunks of the large underlying extent if it happens to exist. Secondly, it results in zeroing the range past the end of C2 up to ram_bytes. This is particularly unpleasant with fs-verity as it can zero and set uptodate pages in the verity virtual space past EOF. This incorrect readahead behavior can lead to verity verification errors, if we iterate in a way that happens to do the wrong readahead. Fix this by using em->len for readahead expansion, not em->ram_bytes, resulting in the expected behavior of stopping readahead at the extent boundary. Reported-by: Max Chernoff Link: https://bugzilla.redhat.com/show_bug.cgi?id=2399898 Fixes: 9e9ff875e417 ("btrfs: use readahead_expand() on compressed extents") CC: stable@vger.kernel.org # 6.17 Reviewed-by: Filipe Manana Signed-off-by: Boris Burkov Signed-off-by: David Sterba Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/extent_io.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index b310a07a84adfa..86a76efb21f637 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -962,7 +962,7 @@ static void btrfs_readahead_expand(struct readahead_control *ractl, { const u64 ra_pos = readahead_pos(ractl); const u64 ra_end = ra_pos + readahead_length(ractl); - const u64 em_end = em->start + em->ram_bytes; + const u64 em_end = em->start + em->len; /* No expansion for holes and inline extents. */ if (em->disk_bytenr > EXTENT_MAP_LAST_BYTE) From 187333e6d484c6630286bfdd07c79d6815a63887 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miquel=20Sabat=C3=A9=20Sol=C3=A0?= Date: Wed, 8 Oct 2025 14:18:59 +0200 Subject: [PATCH 018/143] btrfs: fix memory leaks when rejecting a non SINGLE data profile without an RST MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit fec9b9d3ced39f16be8d7afdf81f4dd2653da319 upstream. At the end of btrfs_load_block_group_zone_info() the first thing we do is to ensure that if the mapping type is not a SINGLE one and there is no RAID stripe tree, then we return early with an error. Doing that, though, prevents the code from running the last calls from this function which are about freeing memory allocated during its run. Hence, in this case, instead of returning early, we set the ret value and fall through the rest of the cleanup code. Fixes: 5906333cc4af ("btrfs: zoned: don't skip block group profile checks on conventional zones") CC: stable@vger.kernel.org # 6.8+ Reviewed-by: Johannes Thumshirn Signed-off-by: Miquel Sabaté Solà Signed-off-by: David Sterba Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/zoned.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c index 8e8edfe0c6190e..4966b4f5a7d245 100644 --- a/fs/btrfs/zoned.c +++ b/fs/btrfs/zoned.c @@ -1664,7 +1664,7 @@ int btrfs_load_block_group_zone_info(struct btrfs_block_group *cache, bool new) !fs_info->stripe_root) { btrfs_err(fs_info, "zoned: data %s needs raid-stripe-tree", btrfs_bg_type_to_raid_name(map->type)); - return -EINVAL; + ret = -EINVAL; } if (cache->alloc_offset > cache->zone_capacity) { From 3fdcfd91b93f930d87843156c7c8cc5fbcf9b144 Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Wed, 1 Oct 2025 11:08:13 +0100 Subject: [PATCH 019/143] btrfs: do not assert we found block group item when creating free space tree commit a5a51bf4e9b7354ce7cd697e610d72c1b33fd949 upstream. Currently, when building a free space tree at populate_free_space_tree(), if we are not using the block group tree feature, we always expect to find block group items (either extent items or a block group item with key type BTRFS_BLOCK_GROUP_ITEM_KEY) when we search the extent tree with btrfs_search_slot_for_read(), so we assert that we found an item. However this expectation is wrong since we can have a new block group created in the current transaction which is still empty and for which we still have not added the block group's item to the extent tree, in which case we do not have any items in the extent tree associated to the block group. The insertion of a new block group's block group item in the extent tree happens at btrfs_create_pending_block_groups() when it calls the helper insert_block_group_item(). This typically is done when a transaction handle is released, committed or when running delayed refs (either as part of a transaction commit or when serving tickets for space reservation if we are low on free space). So remove the assertion at populate_free_space_tree() even when the block group tree feature is not enabled and update the comment to mention this case. Syzbot reported this with the following stack trace: BTRFS info (device loop3 state M): rebuilding free space tree assertion failed: ret == 0 :: 0, in fs/btrfs/free-space-tree.c:1115 ------------[ cut here ]------------ kernel BUG at fs/btrfs/free-space-tree.c:1115! Oops: invalid opcode: 0000 [#1] SMP KASAN PTI CPU: 1 UID: 0 PID: 6352 Comm: syz.3.25 Not tainted syzkaller #0 PREEMPT(full) Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 08/18/2025 RIP: 0010:populate_free_space_tree+0x700/0x710 fs/btrfs/free-space-tree.c:1115 Code: ff ff e8 d3 (...) RSP: 0018:ffffc9000430f780 EFLAGS: 00010246 RAX: 0000000000000043 RBX: ffff88805b709630 RCX: fea61d0e2e79d000 RDX: 0000000000000000 RSI: 0000000080000000 RDI: 0000000000000000 RBP: ffffc9000430f8b0 R08: ffffc9000430f4a7 R09: 1ffff92000861e94 R10: dffffc0000000000 R11: fffff52000861e95 R12: 0000000000000001 R13: 1ffff92000861f00 R14: dffffc0000000000 R15: 0000000000000000 FS: 00007f424d9fe6c0(0000) GS:ffff888125afc000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007fd78ad212c0 CR3: 0000000076d68000 CR4: 00000000003526f0 Call Trace: btrfs_rebuild_free_space_tree+0x1ba/0x6d0 fs/btrfs/free-space-tree.c:1364 btrfs_start_pre_rw_mount+0x128f/0x1bf0 fs/btrfs/disk-io.c:3062 btrfs_remount_rw fs/btrfs/super.c:1334 [inline] btrfs_reconfigure+0xaed/0x2160 fs/btrfs/super.c:1559 reconfigure_super+0x227/0x890 fs/super.c:1076 do_remount fs/namespace.c:3279 [inline] path_mount+0xd1a/0xfe0 fs/namespace.c:4027 do_mount fs/namespace.c:4048 [inline] __do_sys_mount fs/namespace.c:4236 [inline] __se_sys_mount+0x313/0x410 fs/namespace.c:4213 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0xfa/0xfa0 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x7f424e39066a Code: d8 64 89 02 (...) RSP: 002b:00007f424d9fde68 EFLAGS: 00000246 ORIG_RAX: 00000000000000a5 RAX: ffffffffffffffda RBX: 00007f424d9fdef0 RCX: 00007f424e39066a RDX: 0000200000000180 RSI: 0000200000000380 RDI: 0000000000000000 RBP: 0000200000000180 R08: 00007f424d9fdef0 R09: 0000000000000020 R10: 0000000000000020 R11: 0000000000000246 R12: 0000200000000380 R13: 00007f424d9fdeb0 R14: 0000000000000000 R15: 00002000000002c0 Modules linked in: ---[ end trace 0000000000000000 ]--- Reported-by: syzbot+884dc4621377ba579a6f@syzkaller.appspotmail.com Link: https://lore.kernel.org/linux-btrfs/68dc3dab.a00a0220.102ee.004e.GAE@google.com/ Fixes: a5ed91828518 ("Btrfs: implement the free space B-tree") CC: # 6.1.x: 1961d20f6fa8: btrfs: fix assertion when building free space tree CC: # 6.1.x Reviewed-by: Qu Wenruo Signed-off-by: Filipe Manana Signed-off-by: David Sterba Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/free-space-tree.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/fs/btrfs/free-space-tree.c b/fs/btrfs/free-space-tree.c index 51f286d5d00ab3..8e31af036e75e1 100644 --- a/fs/btrfs/free-space-tree.c +++ b/fs/btrfs/free-space-tree.c @@ -1108,14 +1108,15 @@ static int populate_free_space_tree(struct btrfs_trans_handle *trans, * If ret is 1 (no key found), it means this is an empty block group, * without any extents allocated from it and there's no block group * item (key BTRFS_BLOCK_GROUP_ITEM_KEY) located in the extent tree - * because we are using the block group tree feature, so block group - * items are stored in the block group tree. It also means there are no - * extents allocated for block groups with a start offset beyond this - * block group's end offset (this is the last, highest, block group). + * because we are using the block group tree feature (so block group + * items are stored in the block group tree) or this is a new block + * group created in the current transaction and its block group item + * was not yet inserted in the extent tree (that happens in + * btrfs_create_pending_block_groups() -> insert_block_group_item()). + * It also means there are no extents allocated for block groups with a + * start offset beyond this block group's end offset (this is the last, + * highest, block group). */ - if (!btrfs_fs_compat_ro(trans->fs_info, BLOCK_GROUP_TREE)) - ASSERT(ret == 0); - start = block_group->start; end = block_group->start + block_group->length; while (ret == 0) { From 52eb720e5bfd706f28465ef8709ee835feecb9bc Mon Sep 17 00:00:00 2001 From: Celeste Liu Date: Tue, 30 Sep 2025 14:53:39 +0800 Subject: [PATCH 020/143] can: gs_usb: gs_make_candev(): populate net_device->dev_port commit a12f0bc764da3781da2019c60826f47a6d7ed64f upstream. The gs_usb driver supports USB devices with more than 1 CAN channel. In old kernel before 3.15, it uses net_device->dev_id to distinguish different channel in userspace, which was done in commit acff76fa45b4 ("can: gs_usb: gs_make_candev(): set netdev->dev_id"). But since 3.15, the correct way is populating net_device->dev_port. And according to documentation, if network device support multiple interface, lack of net_device->dev_port SHALL be treated as a bug. Fixes: acff76fa45b4 ("can: gs_usb: gs_make_candev(): set netdev->dev_id") Cc: stable@vger.kernel.org Signed-off-by: Celeste Liu Link: https://patch.msgid.link/20250930-gs-usb-populate-net_device-dev_port-v1-1-68a065de6937@coelacanthus.name Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/usb/gs_usb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/can/usb/gs_usb.c b/drivers/net/can/usb/gs_usb.c index b6f4de375df75d..f39f1bd0a6b4a1 100644 --- a/drivers/net/can/usb/gs_usb.c +++ b/drivers/net/can/usb/gs_usb.c @@ -1246,6 +1246,7 @@ static struct gs_can *gs_make_candev(unsigned int channel, netdev->flags |= IFF_ECHO; /* we support full roundtrip echo */ netdev->dev_id = channel; + netdev->dev_port = channel; /* dev setup */ strcpy(dev->bt_const.name, KBUILD_MODNAME); From cc87d3d0f4affb9d704c4aca7c85b65522d9892d Mon Sep 17 00:00:00 2001 From: Celeste Liu Date: Tue, 30 Sep 2025 19:34:28 +0800 Subject: [PATCH 021/143] can: gs_usb: increase max interface to U8_MAX commit 2a27f6a8fb5722223d526843040f747e9b0e8060 upstream. This issue was found by Runcheng Lu when develop HSCanT USB to CAN FD converter[1]. The original developers may have only 3 interfaces device to test so they write 3 here and wait for future change. During the HSCanT development, we actually used 4 interfaces, so the limitation of 3 is not enough now. But just increase one is not future-proofed. Since the channel index type in gs_host_frame is u8, just make canch[] become a flexible array with a u8 index, so it naturally constraint by U8_MAX and avoid statically allocate 256 pointer for every gs_usb device. [1]: https://github.com/cherry-embedded/HSCanT-hardware Fixes: d08e973a77d1 ("can: gs_usb: Added support for the GS_USB CAN devices") Reported-by: Runcheng Lu Cc: stable@vger.kernel.org Reviewed-by: Vincent Mailhol Signed-off-by: Celeste Liu Link: https://patch.msgid.link/20250930-gs-usb-max-if-v5-1-863330bf6666@coelacanthus.name Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/usb/gs_usb.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/drivers/net/can/usb/gs_usb.c b/drivers/net/can/usb/gs_usb.c index f39f1bd0a6b4a1..fb904dc28b8eea 100644 --- a/drivers/net/can/usb/gs_usb.c +++ b/drivers/net/can/usb/gs_usb.c @@ -286,11 +286,6 @@ struct gs_host_frame { #define GS_MAX_RX_URBS 30 #define GS_NAPI_WEIGHT 32 -/* Maximum number of interfaces the driver supports per device. - * Current hardware only supports 3 interfaces. The future may vary. - */ -#define GS_MAX_INTF 3 - struct gs_tx_context { struct gs_can *dev; unsigned int echo_id; @@ -321,7 +316,6 @@ struct gs_can { /* usb interface struct */ struct gs_usb { - struct gs_can *canch[GS_MAX_INTF]; struct usb_anchor rx_submitted; struct usb_device *udev; @@ -333,9 +327,11 @@ struct gs_usb { unsigned int hf_size_rx; u8 active_channels; + u8 channel_cnt; unsigned int pipe_in; unsigned int pipe_out; + struct gs_can *canch[] __counted_by(channel_cnt); }; /* 'allocate' a tx context. @@ -596,7 +592,7 @@ static void gs_usb_receive_bulk_callback(struct urb *urb) } /* device reports out of range channel id */ - if (hf->channel >= GS_MAX_INTF) + if (hf->channel >= parent->channel_cnt) goto device_detach; dev = parent->canch[hf->channel]; @@ -696,7 +692,7 @@ static void gs_usb_receive_bulk_callback(struct urb *urb) /* USB failure take down all interfaces */ if (rc == -ENODEV) { device_detach: - for (rc = 0; rc < GS_MAX_INTF; rc++) { + for (rc = 0; rc < parent->channel_cnt; rc++) { if (parent->canch[rc]) netif_device_detach(parent->canch[rc]->netdev); } @@ -1458,17 +1454,19 @@ static int gs_usb_probe(struct usb_interface *intf, icount = dconf.icount + 1; dev_info(&intf->dev, "Configuring for %u interfaces\n", icount); - if (icount > GS_MAX_INTF) { + if (icount > type_max(parent->channel_cnt)) { dev_err(&intf->dev, "Driver cannot handle more that %u CAN interfaces\n", - GS_MAX_INTF); + type_max(parent->channel_cnt)); return -EINVAL; } - parent = kzalloc(sizeof(*parent), GFP_KERNEL); + parent = kzalloc(struct_size(parent, canch, icount), GFP_KERNEL); if (!parent) return -ENOMEM; + parent->channel_cnt = icount; + init_usb_anchor(&parent->rx_submitted); usb_set_intfdata(intf, parent); @@ -1529,7 +1527,7 @@ static void gs_usb_disconnect(struct usb_interface *intf) return; } - for (i = 0; i < GS_MAX_INTF; i++) + for (i = 0; i < parent->channel_cnt; i++) if (parent->canch[i]) gs_destroy_candev(parent->canch[i]); From 8bc4a8d39bac23d8b044fd3e2dbfd965f1d9b058 Mon Sep 17 00:00:00 2001 From: Eugene Korenevsky Date: Mon, 13 Oct 2025 21:39:30 +0300 Subject: [PATCH 022/143] cifs: parse_dfs_referrals: prevent oob on malformed input commit 6447b0e355562a1ff748c4a2ffb89aae7e84d2c9 upstream. Malicious SMB server can send invalid reply to FSCTL_DFS_GET_REFERRALS - reply smaller than sizeof(struct get_dfs_referral_rsp) - reply with number of referrals smaller than NumberOfReferrals in the header Processing of such replies will cause oob. Return -EINVAL error on such replies to prevent oob-s. Signed-off-by: Eugene Korenevsky Cc: stable@vger.kernel.org Suggested-by: Nathan Chancellor Acked-by: Paulo Alcantara (Red Hat) Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/misc.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c index 499f791df77998..3f3f184f7fb97b 100644 --- a/fs/smb/client/misc.c +++ b/fs/smb/client/misc.c @@ -913,6 +913,14 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size, char *data_end; struct dfs_referral_level_3 *ref; + if (rsp_size < sizeof(*rsp)) { + cifs_dbg(VFS | ONCE, + "%s: header is malformed (size is %u, must be %zu)\n", + __func__, rsp_size, sizeof(*rsp)); + rc = -EINVAL; + goto parse_DFS_referrals_exit; + } + *num_of_nodes = le16_to_cpu(rsp->NumberOfReferrals); if (*num_of_nodes < 1) { @@ -922,6 +930,15 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size, goto parse_DFS_referrals_exit; } + if (sizeof(*rsp) + *num_of_nodes * sizeof(REFERRAL3) > rsp_size) { + cifs_dbg(VFS | ONCE, + "%s: malformed buffer (size is %u, must be at least %zu)\n", + __func__, rsp_size, + sizeof(*rsp) + *num_of_nodes * sizeof(REFERRAL3)); + rc = -EINVAL; + goto parse_DFS_referrals_exit; + } + ref = (struct dfs_referral_level_3 *) &(rsp->referrals); if (ref->VersionNumber != cpu_to_le16(3)) { cifs_dbg(VFS, "Referrals of V%d version are not supported, should be V3\n", From e5e3eb2aff92994ee81ce633f1c4e73bd4b87e11 Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Wed, 15 Oct 2025 09:40:15 +0100 Subject: [PATCH 023/143] drm/sched: Fix potential double free in drm_sched_job_add_resv_dependencies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 5801e65206b065b0b2af032f7f1eef222aa2fd83 upstream. When adding dependencies with drm_sched_job_add_dependency(), that function consumes the fence reference both on success and failure, so in the latter case the dma_fence_put() on the error path (xarray failed to expand) is a double free. Interestingly this bug appears to have been present ever since commit ebd5f74255b9 ("drm/sched: Add dependency tracking"), since the code back then looked like this: drm_sched_job_add_implicit_dependencies(): ... for (i = 0; i < fence_count; i++) { ret = drm_sched_job_add_dependency(job, fences[i]); if (ret) break; } for (; i < fence_count; i++) dma_fence_put(fences[i]); Which means for the failing 'i' the dma_fence_put was already a double free. Possibly there were no users at that time, or the test cases were insufficient to hit it. The bug was then only noticed and fixed after commit 9c2ba265352a ("drm/scheduler: use new iterator in drm_sched_job_add_implicit_dependencies v2") landed, with its fixup of commit 4eaf02d6076c ("drm/scheduler: fix drm_sched_job_add_implicit_dependencies"). At that point it was a slightly different flavour of a double free, which commit 963d0b356935 ("drm/scheduler: fix drm_sched_job_add_implicit_dependencies harder") noticed and attempted to fix. But it only moved the double free from happening inside the drm_sched_job_add_dependency(), when releasing the reference not yet obtained, to the caller, when releasing the reference already released by the former in the failure case. As such it is not easy to identify the right target for the fixes tag so lets keep it simple and just continue the chain. While fixing we also improve the comment and explain the reason for taking the reference and not dropping it. Signed-off-by: Tvrtko Ursulin Fixes: 963d0b356935 ("drm/scheduler: fix drm_sched_job_add_implicit_dependencies harder") Reported-by: Dan Carpenter Closes: https://lore.kernel.org/dri-devel/aNFbXq8OeYl3QSdm@stanley.mountain/ Cc: Christian König Cc: Rob Clark Cc: Daniel Vetter Cc: Matthew Brost Cc: Danilo Krummrich Cc: Philipp Stanner Cc: Christian König Cc: dri-devel@lists.freedesktop.org Cc: stable@vger.kernel.org # v5.16+ Signed-off-by: Philipp Stanner Link: https://lore.kernel.org/r/20251015084015.6273-1-tvrtko.ursulin@igalia.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/scheduler/sched_main.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c index 416590ea0dc3d6..d5260cb1ed0ec6 100644 --- a/drivers/gpu/drm/scheduler/sched_main.c +++ b/drivers/gpu/drm/scheduler/sched_main.c @@ -952,13 +952,14 @@ int drm_sched_job_add_resv_dependencies(struct drm_sched_job *job, dma_resv_assert_held(resv); dma_resv_for_each_fence(&cursor, resv, usage, fence) { - /* Make sure to grab an additional ref on the added fence */ - dma_fence_get(fence); - ret = drm_sched_job_add_dependency(job, fence); - if (ret) { - dma_fence_put(fence); + /* + * As drm_sched_job_add_dependency always consumes the fence + * reference (even when it fails), and dma_resv_for_each_fence + * is not obtaining one, we need to grab one before calling. + */ + ret = drm_sched_job_add_dependency(job, dma_fence_get(fence)); + if (ret) return ret; - } } return 0; } From e4937f3ef9250ad79e6a972adb9cc4d7fa9bc6eb Mon Sep 17 00:00:00 2001 From: Gui-Dong Han Date: Wed, 8 Oct 2025 03:43:27 +0000 Subject: [PATCH 024/143] drm/amdgpu: use atomic functions with memory barriers for vm fault info commit 6df8e84aa6b5b1812cc2cacd6b3f5ccbb18cda2b upstream. The atomic variable vm_fault_info_updated is used to synchronize access to adev->gmc.vm_fault_info between the interrupt handler and get_vm_fault_info(). The default atomic functions like atomic_set() and atomic_read() do not provide memory barriers. This allows for CPU instruction reordering, meaning the memory accesses to vm_fault_info and the vm_fault_info_updated flag are not guaranteed to occur in the intended order. This creates a race condition that can lead to inconsistent or stale data being used. The previous implementation, which used an explicit mb(), was incomplete and inefficient. It failed to account for all potential CPU reorderings, such as the access of vm_fault_info being reordered before the atomic_read of the flag. This approach is also more verbose and less performant than using the proper atomic functions with acquire/release semantics. Fix this by switching to atomic_set_release() and atomic_read_acquire(). These functions provide the necessary acquire and release semantics, which act as memory barriers to ensure the correct order of operations. It is also more efficient and idiomatic than using explicit full memory barriers. Fixes: b97dfa27ef3a ("drm/amdgpu: save vm fault information for amdkfd") Cc: stable@vger.kernel.org Signed-off-by: Gui-Dong Han Signed-off-by: Felix Kuehling Reviewed-by: Felix Kuehling Signed-off-by: Alex Deucher Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c | 5 ++--- drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c | 7 +++---- drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c | 7 +++---- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c index 1465b3adacb0af..d349a4816e5375 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c @@ -2353,10 +2353,9 @@ void amdgpu_amdkfd_gpuvm_unmap_gtt_bo_from_kernel(struct kgd_mem *mem) int amdgpu_amdkfd_gpuvm_get_vm_fault_info(struct amdgpu_device *adev, struct kfd_vm_fault_info *mem) { - if (atomic_read(&adev->gmc.vm_fault_info_updated) == 1) { + if (atomic_read_acquire(&adev->gmc.vm_fault_info_updated) == 1) { *mem = *adev->gmc.vm_fault_info; - mb(); /* make sure read happened */ - atomic_set(&adev->gmc.vm_fault_info_updated, 0); + atomic_set_release(&adev->gmc.vm_fault_info_updated, 0); } return 0; } diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c index 994432fb57eafa..8e2f7312565049 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c @@ -1055,7 +1055,7 @@ static int gmc_v7_0_sw_init(void *handle) GFP_KERNEL); if (!adev->gmc.vm_fault_info) return -ENOMEM; - atomic_set(&adev->gmc.vm_fault_info_updated, 0); + atomic_set_release(&adev->gmc.vm_fault_info_updated, 0); return 0; } @@ -1287,7 +1287,7 @@ static int gmc_v7_0_process_interrupt(struct amdgpu_device *adev, vmid = REG_GET_FIELD(status, VM_CONTEXT1_PROTECTION_FAULT_STATUS, VMID); if (amdgpu_amdkfd_is_kfd_vmid(adev, vmid) - && !atomic_read(&adev->gmc.vm_fault_info_updated)) { + && !atomic_read_acquire(&adev->gmc.vm_fault_info_updated)) { struct kfd_vm_fault_info *info = adev->gmc.vm_fault_info; u32 protections = REG_GET_FIELD(status, VM_CONTEXT1_PROTECTION_FAULT_STATUS, @@ -1303,8 +1303,7 @@ static int gmc_v7_0_process_interrupt(struct amdgpu_device *adev, info->prot_read = protections & 0x8 ? true : false; info->prot_write = protections & 0x10 ? true : false; info->prot_exec = protections & 0x20 ? true : false; - mb(); - atomic_set(&adev->gmc.vm_fault_info_updated, 1); + atomic_set_release(&adev->gmc.vm_fault_info_updated, 1); } return 0; diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c index 86488c052f8224..5248832c04adf0 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c @@ -1168,7 +1168,7 @@ static int gmc_v8_0_sw_init(void *handle) GFP_KERNEL); if (!adev->gmc.vm_fault_info) return -ENOMEM; - atomic_set(&adev->gmc.vm_fault_info_updated, 0); + atomic_set_release(&adev->gmc.vm_fault_info_updated, 0); return 0; } @@ -1468,7 +1468,7 @@ static int gmc_v8_0_process_interrupt(struct amdgpu_device *adev, vmid = REG_GET_FIELD(status, VM_CONTEXT1_PROTECTION_FAULT_STATUS, VMID); if (amdgpu_amdkfd_is_kfd_vmid(adev, vmid) - && !atomic_read(&adev->gmc.vm_fault_info_updated)) { + && !atomic_read_acquire(&adev->gmc.vm_fault_info_updated)) { struct kfd_vm_fault_info *info = adev->gmc.vm_fault_info; u32 protections = REG_GET_FIELD(status, VM_CONTEXT1_PROTECTION_FAULT_STATUS, @@ -1484,8 +1484,7 @@ static int gmc_v8_0_process_interrupt(struct amdgpu_device *adev, info->prot_read = protections & 0x8 ? true : false; info->prot_write = protections & 0x10 ? true : false; info->prot_exec = protections & 0x20 ? true : false; - mb(); - atomic_set(&adev->gmc.vm_fault_info_updated, 1); + atomic_set_release(&adev->gmc.vm_fault_info_updated, 1); } return 0; From e5914820d35126c16c4e0a4ef36513f9679e9e45 Mon Sep 17 00:00:00 2001 From: Jonathan Kim Date: Thu, 9 Oct 2025 10:45:42 -0400 Subject: [PATCH 025/143] drm/amdgpu: fix gfx12 mes packet status return check commit d0de79f66a80eeb849033fae34bd07a69ce72235 upstream. GFX12 MES uses low 32 bits of status return for success (1 or 0) and high bits for debug information if low bits are 0. GFX11 MES doesn't do this so checking full 64-bit status return for 1 or 0 is still valid. Signed-off-by: Jonathan Kim Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/mes_v12_0.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/mes_v12_0.c b/drivers/gpu/drm/amd/amdgpu/mes_v12_0.c index e3f4f5fbbd6e75..19cbf80fa32144 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_v12_0.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_v12_0.c @@ -224,7 +224,12 @@ static int mes_v12_0_submit_pkt_and_poll_completion(struct amdgpu_mes *mes, pipe, x_pkt->header.opcode); r = amdgpu_fence_wait_polling(ring, seq, timeout); - if (r < 1 || !*status_ptr) { + + /* + * status_ptr[31:0] == 0 (fail) or status_ptr[63:0] == 1 (success). + * If status_ptr[31:0] == 0 then status_ptr[63:32] will have debug error information. + */ + if (r < 1 || !(lower_32_bits(*status_ptr))) { if (misc_op_str) dev_err(adev->dev, "MES(%d) failed to respond to msg=%s (%s)\n", From 6ddc602b1cfb9c710e9cea3d5e7d6d3581299619 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 13 Oct 2025 10:22:42 +0300 Subject: [PATCH 026/143] perf/core: Fix address filter match with backing files commit ebfc8542ad62d066771e46c8aa30f5624b89cad8 upstream. It was reported that Intel PT address filters do not work in Docker containers. That relates to the use of overlayfs. overlayfs records the backing file in struct vm_area_struct vm_file, instead of the user file that the user mmapped. In order for an address filter to match, it must compare to the user file inode. There is an existing helper file_user_inode() for that situation. Use file_user_inode() instead of file_inode() to get the inode for address filter matching. Example: Setup: # cd /root # mkdir test ; cd test ; mkdir lower upper work merged # cp `which cat` lower # mount -t overlay overlay -olowerdir=lower,upperdir=upper,workdir=work merged # perf record --buildid-mmap -e intel_pt//u --filter 'filter * @ /root/test/merged/cat' -- /root/test/merged/cat /proc/self/maps ... 55d61d246000-55d61d2e1000 r-xp 00018000 00:1a 3418 /root/test/merged/cat ... [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.015 MB perf.data ] # perf buildid-cache --add /root/test/merged/cat Before: Address filter does not match so there are no control flow packets # perf script --itrace=e # perf script --itrace=b | wc -l 0 # perf script -D | grep 'TIP.PGE' | wc -l 0 # After: Address filter does match so there are control flow packets # perf script --itrace=e # perf script --itrace=b | wc -l 235 # perf script -D | grep 'TIP.PGE' | wc -l 57 # With respect to stable kernels, overlayfs mmap function ovl_mmap() was added in v4.19 but file_user_inode() was not added until v6.8 and never back-ported to stable kernels. FMODE_BACKING that it depends on was added in v6.5. This issue has gone largely unnoticed, so back-porting before v6.8 is probably not worth it, so put 6.8 as the stable kernel prerequisite version, although in practice the next long term kernel is 6.12. Closes: https://lore.kernel.org/linux-perf-users/aBCwoq7w8ohBRQCh@fremen.lan Reported-by: Edd Barrett Signed-off-by: Adrian Hunter Signed-off-by: Peter Zijlstra (Intel) Acked-by: Amir Goldstein Cc: stable@vger.kernel.org # 6.8 Signed-off-by: Greg Kroah-Hartman --- kernel/events/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/events/core.c b/kernel/events/core.c index 3cc06ffb60c1bc..2e1283e0f145bb 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -9086,7 +9086,7 @@ static bool perf_addr_filter_match(struct perf_addr_filter *filter, if (!filter->path.dentry) return false; - if (d_inode(filter->path.dentry) != file_inode(file)) + if (d_inode(filter->path.dentry) != file_user_inode(file)) return false; if (filter->offset > offset + size) From 7024b11fb47e29909cf2f3d2e6931f9b8c42de4f Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 13 Oct 2025 10:22:43 +0300 Subject: [PATCH 027/143] perf/core: Fix MMAP event path names with backing files commit 8818f507a9391019a3ec7c57b1a32e4b386e48a5 upstream. Some file systems like FUSE-based ones or overlayfs may record the backing file in struct vm_area_struct vm_file, instead of the user file that the user mmapped. Since commit def3ae83da02f ("fs: store real path instead of fake path in backing file f_path"), file_path() no longer returns the user file path when applied to a backing file. There is an existing helper file_user_path() for that situation. Use file_user_path() instead of file_path() to get the path for MMAP and MMAP2 events. Example: Setup: # cd /root # mkdir test ; cd test ; mkdir lower upper work merged # cp `which cat` lower # mount -t overlay overlay -olowerdir=lower,upperdir=upper,workdir=work merged # perf record -e intel_pt//u -- /root/test/merged/cat /proc/self/maps ... 55b0ba399000-55b0ba434000 r-xp 00018000 00:1a 3419 /root/test/merged/cat ... [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.060 MB perf.data ] # Before: File name is wrong (/cat), so decoding fails: # perf script --no-itrace --show-mmap-events cat 367 [016] 100.491492: PERF_RECORD_MMAP2 367/367: [0x55b0ba399000(0x9b000) @ 0x18000 00:02 3419 489959280]: r-xp /cat ... # perf script --itrace=e | wc -l Warning: 19 instruction trace errors 19 # After: File name is correct (/root/test/merged/cat), so decoding is ok: # perf script --no-itrace --show-mmap-events cat 364 [016] 72.153006: PERF_RECORD_MMAP2 364/364: [0x55ce4003d000(0x9b000) @ 0x18000 00:02 3419 3132534314]: r-xp /root/test/merged/cat # perf script --itrace=e # perf script --itrace=e | wc -l 0 # Fixes: def3ae83da02f ("fs: store real path instead of fake path in backing file f_path") Signed-off-by: Adrian Hunter Signed-off-by: Peter Zijlstra (Intel) Acked-by: Amir Goldstein Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- kernel/events/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/events/core.c b/kernel/events/core.c index 2e1283e0f145bb..ee644adf10272c 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -9010,7 +9010,7 @@ static void perf_event_mmap_event(struct perf_mmap_event *mmap_event) * need to add enough zero bytes after the string to handle * the 64bit alignment we do later. */ - name = file_path(file, buf, PATH_MAX - sizeof(u64)); + name = d_path(file_user_path(file), buf, PATH_MAX - sizeof(u64)); if (IS_ERR(name)) { name = "//toolong"; goto cpy_name; From eacc4fc28dd9af61bc0008d5373ace8570b5d283 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 13 Oct 2025 10:22:44 +0300 Subject: [PATCH 028/143] perf/core: Fix MMAP2 event device with backing files commit fa4f4bae893fbce8a3edfff1ab7ece0c01dc1328 upstream. Some file systems like FUSE-based ones or overlayfs may record the backing file in struct vm_area_struct vm_file, instead of the user file that the user mmapped. That causes perf to misreport the device major/minor numbers of the file system of the file, and the generation of the file, and potentially other inode details. There is an existing helper file_user_inode() for that situation. Use file_user_inode() instead of file_inode() to get the inode for MMAP2 events. Example: Setup: # cd /root # mkdir test ; cd test ; mkdir lower upper work merged # cp `which cat` lower # mount -t overlay overlay -olowerdir=lower,upperdir=upper,workdir=work merged # perf record -e cycles:u -- /root/test/merged/cat /proc/self/maps ... 55b2c91d0000-55b2c926b000 r-xp 00018000 00:1a 3419 /root/test/merged/cat ... [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.004 MB perf.data (5 samples) ] # # stat /root/test/merged/cat File: /root/test/merged/cat Size: 1127792 Blocks: 2208 IO Block: 4096 regular file Device: 0,26 Inode: 3419 Links: 1 Access: (0755/-rwxr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root) Access: 2025-09-08 12:23:59.453309624 +0000 Modify: 2025-09-08 12:23:59.454309624 +0000 Change: 2025-09-08 12:23:59.454309624 +0000 Birth: 2025-09-08 12:23:59.453309624 +0000 Before: Device reported 00:02 differs from stat output and /proc/self/maps # perf script --show-mmap-events | grep /root/test/merged/cat cat 377 [-01] 243.078558: PERF_RECORD_MMAP2 377/377: [0x55b2c91d0000(0x9b000) @ 0x18000 00:02 3419 2068525940]: r-xp /root/test/merged/cat After: Device reported 00:1a is the same as stat output and /proc/self/maps # perf script --show-mmap-events | grep /root/test/merged/cat cat 362 [-01] 127.755167: PERF_RECORD_MMAP2 362/362: [0x55ba6e781000(0x9b000) @ 0x18000 00:1a 3419 0]: r-xp /root/test/merged/cat With respect to stable kernels, overlayfs mmap function ovl_mmap() was added in v4.19 but file_user_inode() was not added until v6.8 and never back-ported to stable kernels. FMODE_BACKING that it depends on was added in v6.5. This issue has gone largely unnoticed, so back-porting before v6.8 is probably not worth it, so put 6.8 as the stable kernel prerequisite version, although in practice the next long term kernel is 6.12. Signed-off-by: Adrian Hunter Signed-off-by: Peter Zijlstra (Intel) Acked-by: Amir Goldstein Cc: stable@vger.kernel.org # 6.8 Signed-off-by: Greg Kroah-Hartman --- kernel/events/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/events/core.c b/kernel/events/core.c index ee644adf10272c..d60d48d482b014 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -8997,7 +8997,7 @@ static void perf_event_mmap_event(struct perf_mmap_event *mmap_event) flags |= MAP_HUGETLB; if (file) { - struct inode *inode; + const struct inode *inode; dev_t dev; buf = kmalloc(PATH_MAX, GFP_KERNEL); @@ -9015,7 +9015,7 @@ static void perf_event_mmap_event(struct perf_mmap_event *mmap_event) name = "//toolong"; goto cpy_name; } - inode = file_inode(vma->vm_file); + inode = file_user_inode(vma->vm_file); dev = inode->i_sb->s_dev; ino = inode->i_ino; gen = inode->i_generation; From 03fe1647e26534d0b2c2a974f40dd5c5b208723c Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Thu, 25 Sep 2025 14:10:57 -0500 Subject: [PATCH 029/143] drm/amd: Check whether secure display TA loaded successfully commit c760bcda83571e07b72c10d9da175db5051ed971 upstream. [Why] Not all renoir hardware supports secure display. If the TA is present but the feature isn't supported it will fail to load or send commands. This shows ERR messages to the user that make it seems like there is a problem. [How] Check the resp_status of the context to see if there was an error before trying to send any secure display commands. Reviewed-by: Alex Deucher Closes: https://gitlab.freedesktop.org/drm/amd/-/issues/1415 Signed-off-by: Mario Limonciello Signed-off-by: Alex Deucher Signed-off-by: Adrian Yip Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c index 8553ac4c0ad3f1..a8358d1d1acbca 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c @@ -2171,7 +2171,7 @@ static int psp_securedisplay_initialize(struct psp_context *psp) } ret = psp_ta_load(psp, &psp->securedisplay_context.context); - if (!ret) { + if (!ret && !psp->securedisplay_context.context.resp_status) { psp->securedisplay_context.context.initialized = true; mutex_init(&psp->securedisplay_context.mutex); } else From c472088522d6fc2cf4c7306c6f1988b8270e759a Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Sat, 18 Oct 2025 11:14:09 -0400 Subject: [PATCH 030/143] irqdomain: cdx: Switch to of_fwnode_handle() [ Upstream commit 2a87a55f2281a1096d9e77ac6309b9128c107d97 ] of_node_to_fwnode() is irqdomain's reimplementation of the "officially" defined of_fwnode_handle(). The former is in the process of being removed, so use the latter instead. Signed-off-by: Jiri Slaby (SUSE) Cc: Nipun Gupta Cc: Nikhil Agarwal Acked-by: Nipun Gupta Link: https://lore.kernel.org/r/20250415104734.106849-1-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman Stable-dep-of: 76254bc489d3 ("cdx: Fix device node reference leak in cdx_msi_domain_init") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/cdx/cdx_msi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/cdx/cdx_msi.c b/drivers/cdx/cdx_msi.c index e55f1716cfcb20..0bb32829d9084b 100644 --- a/drivers/cdx/cdx_msi.c +++ b/drivers/cdx/cdx_msi.c @@ -165,7 +165,7 @@ struct irq_domain *cdx_msi_domain_init(struct device *dev) struct device_node *parent_node; struct irq_domain *parent; - fwnode_handle = of_node_to_fwnode(np); + fwnode_handle = of_fwnode_handle(np); parent_node = of_parse_phandle(np, "msi-map", 1); if (!parent_node) { @@ -173,7 +173,7 @@ struct irq_domain *cdx_msi_domain_init(struct device *dev) return NULL; } - parent = irq_find_matching_fwnode(of_node_to_fwnode(parent_node), DOMAIN_BUS_NEXUS); + parent = irq_find_matching_fwnode(of_fwnode_handle(parent_node), DOMAIN_BUS_NEXUS); if (!parent || !msi_get_domain_info(parent)) { dev_err(dev, "unable to locate ITS domain\n"); return NULL; From 2e24713ba2db393ab318e04545732e74cc47a811 Mon Sep 17 00:00:00 2001 From: Miaoqian Lin Date: Sat, 18 Oct 2025 11:14:10 -0400 Subject: [PATCH 031/143] cdx: Fix device node reference leak in cdx_msi_domain_init [ Upstream commit 76254bc489d39dae9a3427f0984fe64213d20548 ] Add missing of_node_put() call to release the device node reference obtained via of_parse_phandle(). Fixes: 0e439ba38e61 ("cdx: add MSI support for CDX bus") Cc: stable@vger.kernel.org Signed-off-by: Miaoqian Lin Acked-by: Nipun Gupta Link: https://lore.kernel.org/r/20250902084933.2418264-1-linmq006@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/cdx/cdx_msi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/cdx/cdx_msi.c b/drivers/cdx/cdx_msi.c index 0bb32829d9084b..d7bade143998e9 100644 --- a/drivers/cdx/cdx_msi.c +++ b/drivers/cdx/cdx_msi.c @@ -174,6 +174,7 @@ struct irq_domain *cdx_msi_domain_init(struct device *dev) } parent = irq_find_matching_fwnode(of_fwnode_handle(parent_node), DOMAIN_BUS_NEXUS); + of_node_put(parent_node); if (!parent || !msi_get_domain_info(parent)) { dev_err(dev, "unable to locate ITS domain\n"); return NULL; From 3e7b89ed9f07e6864943c4237a9c86e0cf9d3f33 Mon Sep 17 00:00:00 2001 From: Akhil P Oommen Date: Fri, 17 Oct 2025 22:28:32 -0400 Subject: [PATCH 032/143] drm/msm/a6xx: Fix PDC sleep sequence [ Upstream commit f248d5d5159a88ded55329f0b1b463d0f4094228 ] Since the PDC resides out of the GPU subsystem and cannot be reset in case it enters bad state, utmost care must be taken to trigger the PDC wake/sleep routines in the correct order. The PDC wake sequence can be exercised only after a PDC sleep sequence. Additionally, GMU firmware should initialize a few registers before the KMD can trigger a PDC sleep sequence. So PDC sleep can't be done if the GMU firmware has not initialized. Track these dependencies using a new status variable and trigger PDC sleep/wake sequences appropriately. Cc: stable@vger.kernel.org Fixes: 4b565ca5a2cb ("drm/msm: Add A6XX device support") Signed-off-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/673362/ Signed-off-by: Rob Clark [ Adjust context ] Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/msm/adreno/a6xx_gmu.c | 28 ++++++++++++++++----------- drivers/gpu/drm/msm/adreno/a6xx_gmu.h | 6 ++++++ 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c index 67fa528f546d33..8609fa38058ea0 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c @@ -236,6 +236,8 @@ static int a6xx_gmu_start(struct a6xx_gmu *gmu) if (ret) DRM_DEV_ERROR(gmu->dev, "GMU firmware initialization timed out\n"); + set_bit(GMU_STATUS_FW_START, &gmu->status); + return ret; } @@ -482,6 +484,9 @@ static int a6xx_rpmh_start(struct a6xx_gmu *gmu) int ret; u32 val; + if (!test_and_clear_bit(GMU_STATUS_PDC_SLEEP, &gmu->status)) + return 0; + gmu_write(gmu, REG_A6XX_GMU_RSCC_CONTROL_REQ, BIT(1)); ret = gmu_poll_timeout(gmu, REG_A6XX_GMU_RSCC_CONTROL_ACK, val, @@ -509,6 +514,9 @@ static void a6xx_rpmh_stop(struct a6xx_gmu *gmu) int ret; u32 val; + if (test_and_clear_bit(GMU_STATUS_FW_START, &gmu->status)) + return; + gmu_write(gmu, REG_A6XX_GMU_RSCC_CONTROL_REQ, 1); ret = gmu_poll_timeout_rscc(gmu, REG_A6XX_GPU_RSCC_RSC_STATUS0_DRV0, @@ -517,6 +525,8 @@ static void a6xx_rpmh_stop(struct a6xx_gmu *gmu) DRM_DEV_ERROR(gmu->dev, "Unable to power off the GPU RSC\n"); gmu_write(gmu, REG_A6XX_GMU_RSCC_CONTROL_REQ, 0); + + set_bit(GMU_STATUS_PDC_SLEEP, &gmu->status); } static inline void pdc_write(void __iomem *ptr, u32 offset, u32 value) @@ -645,8 +655,6 @@ static void a6xx_gmu_rpmh_init(struct a6xx_gmu *gmu) /* ensure no writes happen before the uCode is fully written */ wmb(); - a6xx_rpmh_stop(gmu); - err: if (!IS_ERR_OR_NULL(pdcptr)) iounmap(pdcptr); @@ -799,19 +807,15 @@ static int a6xx_gmu_fw_start(struct a6xx_gmu *gmu, unsigned int state) else gmu_write(gmu, REG_A6XX_GMU_GENERAL_7, 1); - if (state == GMU_WARM_BOOT) { - ret = a6xx_rpmh_start(gmu); - if (ret) - return ret; - } else { + ret = a6xx_rpmh_start(gmu); + if (ret) + return ret; + + if (state == GMU_COLD_BOOT) { if (WARN(!adreno_gpu->fw[ADRENO_FW_GMU], "GMU firmware is not loaded\n")) return -ENOENT; - ret = a6xx_rpmh_start(gmu); - if (ret) - return ret; - ret = a6xx_gmu_fw_load(gmu); if (ret) return ret; @@ -980,6 +984,8 @@ static void a6xx_gmu_force_off(struct a6xx_gmu *gmu) /* Reset GPU core blocks */ a6xx_gpu_sw_reset(gpu, true); + + a6xx_rpmh_stop(gmu); } static void a6xx_gmu_set_initial_freq(struct msm_gpu *gpu, struct a6xx_gmu *gmu) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gmu.h b/drivers/gpu/drm/msm/adreno/a6xx_gmu.h index 94b6c5cab6f435..db5b3b13e7435a 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gmu.h +++ b/drivers/gpu/drm/msm/adreno/a6xx_gmu.h @@ -99,6 +99,12 @@ struct a6xx_gmu { struct completion pd_gate; struct qmp *qmp; + +/* To check if we can trigger sleep seq at PDC. Cleared in a6xx_rpmh_stop() */ +#define GMU_STATUS_FW_START 0 +/* To track if PDC sleep seq was done */ +#define GMU_STATUS_PDC_SLEEP 1 + unsigned long status; }; static inline u32 gmu_read(struct a6xx_gmu *gmu, u32 offset) From f0b75b4caaafc920b72850cc98560d1d1d01144d Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 17 Oct 2025 19:19:13 -0400 Subject: [PATCH 033/143] media: nxp: imx8-isi: Drop unused argument to mxc_isi_channel_chain() [ Upstream commit 9a21ffeade25cbf310f5db39a1f9932695dd41bb ] The bypass argument to the mxc_isi_channel_chain() function is unused. Drop it. Link: https://lore.kernel.org/r/20250813225501.20762-1-laurent.pinchart@ideasonboard.com Signed-off-by: Laurent Pinchart Reviewed-by: Frank Li Signed-off-by: Hans Verkuil Stable-dep-of: 178aa3360220 ("media: nxp: imx8-isi: m2m: Fix streaming cleanup on release") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h | 2 +- drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c | 2 +- drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c | 11 +++++------ drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c | 2 +- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h index 2810ebe9b5f75c..5a4676d5207935 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h @@ -361,7 +361,7 @@ void mxc_isi_channel_get(struct mxc_isi_pipe *pipe); void mxc_isi_channel_put(struct mxc_isi_pipe *pipe); void mxc_isi_channel_enable(struct mxc_isi_pipe *pipe); void mxc_isi_channel_disable(struct mxc_isi_pipe *pipe); -int mxc_isi_channel_chain(struct mxc_isi_pipe *pipe, bool bypass); +int mxc_isi_channel_chain(struct mxc_isi_pipe *pipe); void mxc_isi_channel_unchain(struct mxc_isi_pipe *pipe); void mxc_isi_channel_config(struct mxc_isi_pipe *pipe, diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c index 5623914f95e649..9225a7ac1c3ee7 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c @@ -587,7 +587,7 @@ void mxc_isi_channel_release(struct mxc_isi_pipe *pipe) * * TODO: Support secondary line buffer for downscaling YUV420 images. */ -int mxc_isi_channel_chain(struct mxc_isi_pipe *pipe, bool bypass) +int mxc_isi_channel_chain(struct mxc_isi_pipe *pipe) { /* Channel chaining requires both line and output buffer. */ const u8 resources = MXC_ISI_CHANNEL_RES_OUTPUT_BUF diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c index cd6c52e9d158a7..7a9bc6fda3f810 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c @@ -493,7 +493,6 @@ static int mxc_isi_m2m_streamon(struct file *file, void *fh, const struct mxc_isi_format_info *cap_info = ctx->queues.cap.info; const struct mxc_isi_format_info *out_info = ctx->queues.out.info; struct mxc_isi_m2m *m2m = ctx->m2m; - bool bypass; int ret; if (q->streaming) @@ -506,15 +505,15 @@ static int mxc_isi_m2m_streamon(struct file *file, void *fh, goto unlock; } - bypass = cap_pix->width == out_pix->width && - cap_pix->height == out_pix->height && - cap_info->encoding == out_info->encoding; - /* * Acquire the pipe and initialize the channel with the first user of * the M2M device. */ if (m2m->usage_count == 0) { + bool bypass = cap_pix->width == out_pix->width && + cap_pix->height == out_pix->height && + cap_info->encoding == out_info->encoding; + ret = mxc_isi_channel_acquire(m2m->pipe, &mxc_isi_m2m_frame_write_done, bypass); @@ -531,7 +530,7 @@ static int mxc_isi_m2m_streamon(struct file *file, void *fh, * buffer chaining. */ if (!ctx->chained && out_pix->width > MXC_ISI_MAX_WIDTH_UNCHAINED) { - ret = mxc_isi_channel_chain(m2m->pipe, bypass); + ret = mxc_isi_channel_chain(m2m->pipe); if (ret) goto deinit; diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c index d76eb58deb096b..a41c51dd9ce0f2 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c @@ -855,7 +855,7 @@ int mxc_isi_pipe_acquire(struct mxc_isi_pipe *pipe, /* Chain the channel if needed for wide resolutions. */ if (sink_fmt->width > MXC_ISI_MAX_WIDTH_UNCHAINED) { - ret = mxc_isi_channel_chain(pipe, bypass); + ret = mxc_isi_channel_chain(pipe); if (ret) mxc_isi_channel_release(pipe); } From e8b5f4d80775835cf8192d65138e9be1ff202847 Mon Sep 17 00:00:00 2001 From: Guoniu Zhou Date: Fri, 17 Oct 2025 19:19:14 -0400 Subject: [PATCH 034/143] media: nxp: imx8-isi: m2m: Fix streaming cleanup on release [ Upstream commit 178aa3360220231dd91e7dbc2eb984525886c9c1 ] If streamon/streamoff calls are imbalanced, such as when exiting an application with Ctrl+C when streaming, the m2m usage_count will never reach zero and the ISI channel won't be freed. Besides from that, if the input line width is more than 2K, it will trigger a WARN_ON(): [ 59.222120] ------------[ cut here ]------------ [ 59.226758] WARNING: drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c:631 at mxc_isi_channel_chain+0xa4/0x120, CPU#4: v4l2-ctl/654 [ 59.238569] Modules linked in: ap1302 [ 59.242231] CPU: 4 UID: 0 PID: 654 Comm: v4l2-ctl Not tainted 6.16.0-rc4-next-20250704-06511-gff0e002d480a-dirty #258 PREEMPT [ 59.253597] Hardware name: NXP i.MX95 15X15 board (DT) [ 59.258720] pstate: 80400009 (Nzcv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--) [ 59.265669] pc : mxc_isi_channel_chain+0xa4/0x120 [ 59.270358] lr : mxc_isi_channel_chain+0x44/0x120 [ 59.275047] sp : ffff8000848c3b40 [ 59.278348] x29: ffff8000848c3b40 x28: ffff0000859b4c98 x27: ffff800081939f00 [ 59.285472] x26: 000000000000000a x25: ffff0000859b4cb8 x24: 0000000000000001 [ 59.292597] x23: ffff0000816f4760 x22: ffff0000816f4258 x21: ffff000084ceb780 [ 59.299720] x20: ffff000084342ff8 x19: ffff000084340000 x18: 0000000000000000 [ 59.306845] x17: 0000000000000000 x16: 0000000000000000 x15: 0000ffffdb369e1c [ 59.313969] x14: 0000000000000000 x13: 0000000000000000 x12: 0000000000000000 [ 59.321093] x11: 0000000000000000 x10: 0000000000000000 x9 : 0000000000000000 [ 59.328217] x8 : ffff8000848c3d48 x7 : ffff800081930b30 x6 : ffff800081930b30 [ 59.335340] x5 : ffff0000859b6000 x4 : ffff80008193ae80 x3 : ffff800081022420 [ 59.342464] x2 : ffff0000852f6900 x1 : 0000000000000001 x0 : ffff000084341000 [ 59.349590] Call trace: [ 59.352025] mxc_isi_channel_chain+0xa4/0x120 (P) [ 59.356722] mxc_isi_m2m_streamon+0x160/0x20c [ 59.361072] v4l_streamon+0x24/0x30 [ 59.364556] __video_do_ioctl+0x40c/0x4a0 [ 59.368560] video_usercopy+0x2bc/0x690 [ 59.372382] video_ioctl2+0x18/0x24 [ 59.375857] v4l2_ioctl+0x40/0x60 [ 59.379168] __arm64_sys_ioctl+0xac/0x104 [ 59.383172] invoke_syscall+0x48/0x104 [ 59.386916] el0_svc_common.constprop.0+0xc0/0xe0 [ 59.391613] do_el0_svc+0x1c/0x28 [ 59.394915] el0_svc+0x34/0xf4 [ 59.397966] el0t_64_sync_handler+0xa0/0xe4 [ 59.402143] el0t_64_sync+0x198/0x19c [ 59.405801] ---[ end trace 0000000000000000 ]--- Address this issue by moving the streaming preparation and cleanup to the vb2 .prepare_streaming() and .unprepare_streaming() operations. This also simplifies the driver by allowing direct usage of the v4l2_m2m_ioctl_streamon() and v4l2_m2m_ioctl_streamoff() helpers. Fixes: cf21f328fcaf ("media: nxp: Add i.MX8 ISI driver") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20250821135123.29462-1-laurent.pinchart@ideasonboard.com Signed-off-by: Guoniu Zhou Co-developed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart Tested-by: Guoniu Zhou Reviewed-by: Frank Li Signed-off-by: Hans Verkuil [ Adjust context ] Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- .../platform/nxp/imx8-isi/imx8-isi-m2m.c | 224 +++++++----------- 1 file changed, 92 insertions(+), 132 deletions(-) diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c index 7a9bc6fda3f810..81223d28ee56e8 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c @@ -43,7 +43,6 @@ struct mxc_isi_m2m_ctx_queue_data { struct v4l2_pix_format_mplane format; const struct mxc_isi_format_info *info; u32 sequence; - bool streaming; }; struct mxc_isi_m2m_ctx { @@ -236,6 +235,65 @@ static void mxc_isi_m2m_vb2_buffer_queue(struct vb2_buffer *vb2) v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); } +static int mxc_isi_m2m_vb2_prepare_streaming(struct vb2_queue *q) +{ + struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(q); + const struct v4l2_pix_format_mplane *out_pix = &ctx->queues.out.format; + const struct v4l2_pix_format_mplane *cap_pix = &ctx->queues.cap.format; + const struct mxc_isi_format_info *cap_info = ctx->queues.cap.info; + const struct mxc_isi_format_info *out_info = ctx->queues.out.info; + struct mxc_isi_m2m *m2m = ctx->m2m; + int ret; + + guard(mutex)(&m2m->lock); + + if (m2m->usage_count == INT_MAX) + return -EOVERFLOW; + + /* + * Acquire the pipe and initialize the channel with the first user of + * the M2M device. + */ + if (m2m->usage_count == 0) { + bool bypass = cap_pix->width == out_pix->width && + cap_pix->height == out_pix->height && + cap_info->encoding == out_info->encoding; + + ret = mxc_isi_channel_acquire(m2m->pipe, + &mxc_isi_m2m_frame_write_done, + bypass); + if (ret) + return ret; + + mxc_isi_channel_get(m2m->pipe); + } + + m2m->usage_count++; + + /* + * Allocate resources for the channel, counting how many users require + * buffer chaining. + */ + if (!ctx->chained && out_pix->width > MXC_ISI_MAX_WIDTH_UNCHAINED) { + ret = mxc_isi_channel_chain(m2m->pipe); + if (ret) + goto err_deinit; + + m2m->chained_count++; + ctx->chained = true; + } + + return 0; + +err_deinit: + if (--m2m->usage_count == 0) { + mxc_isi_channel_put(m2m->pipe); + mxc_isi_channel_release(m2m->pipe); + } + + return ret; +} + static int mxc_isi_m2m_vb2_start_streaming(struct vb2_queue *q, unsigned int count) { @@ -265,6 +323,35 @@ static void mxc_isi_m2m_vb2_stop_streaming(struct vb2_queue *q) } } +static void mxc_isi_m2m_vb2_unprepare_streaming(struct vb2_queue *q) +{ + struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(q); + struct mxc_isi_m2m *m2m = ctx->m2m; + + guard(mutex)(&m2m->lock); + + /* + * If the last context is this one, reset it to make sure the device + * will be reconfigured when streaming is restarted. + */ + if (m2m->last_ctx == ctx) + m2m->last_ctx = NULL; + + /* Free the channel resources if this is the last chained context. */ + if (ctx->chained && --m2m->chained_count == 0) + mxc_isi_channel_unchain(m2m->pipe); + ctx->chained = false; + + /* Turn off the light with the last user. */ + if (--m2m->usage_count == 0) { + mxc_isi_channel_disable(m2m->pipe); + mxc_isi_channel_put(m2m->pipe); + mxc_isi_channel_release(m2m->pipe); + } + + WARN_ON(m2m->usage_count < 0); +} + static const struct vb2_ops mxc_isi_m2m_vb2_qops = { .queue_setup = mxc_isi_m2m_vb2_queue_setup, .buf_init = mxc_isi_m2m_vb2_buffer_init, @@ -272,8 +359,10 @@ static const struct vb2_ops mxc_isi_m2m_vb2_qops = { .buf_queue = mxc_isi_m2m_vb2_buffer_queue, .wait_prepare = vb2_ops_wait_prepare, .wait_finish = vb2_ops_wait_finish, + .prepare_streaming = mxc_isi_m2m_vb2_prepare_streaming, .start_streaming = mxc_isi_m2m_vb2_start_streaming, .stop_streaming = mxc_isi_m2m_vb2_stop_streaming, + .unprepare_streaming = mxc_isi_m2m_vb2_unprepare_streaming, }; static int mxc_isi_m2m_queue_init(void *priv, struct vb2_queue *src_vq, @@ -483,135 +572,6 @@ static int mxc_isi_m2m_s_fmt_vid(struct file *file, void *fh, return 0; } -static int mxc_isi_m2m_streamon(struct file *file, void *fh, - enum v4l2_buf_type type) -{ - struct mxc_isi_m2m_ctx *ctx = to_isi_m2m_ctx(fh); - struct mxc_isi_m2m_ctx_queue_data *q = mxc_isi_m2m_ctx_qdata(ctx, type); - const struct v4l2_pix_format_mplane *out_pix = &ctx->queues.out.format; - const struct v4l2_pix_format_mplane *cap_pix = &ctx->queues.cap.format; - const struct mxc_isi_format_info *cap_info = ctx->queues.cap.info; - const struct mxc_isi_format_info *out_info = ctx->queues.out.info; - struct mxc_isi_m2m *m2m = ctx->m2m; - int ret; - - if (q->streaming) - return 0; - - mutex_lock(&m2m->lock); - - if (m2m->usage_count == INT_MAX) { - ret = -EOVERFLOW; - goto unlock; - } - - /* - * Acquire the pipe and initialize the channel with the first user of - * the M2M device. - */ - if (m2m->usage_count == 0) { - bool bypass = cap_pix->width == out_pix->width && - cap_pix->height == out_pix->height && - cap_info->encoding == out_info->encoding; - - ret = mxc_isi_channel_acquire(m2m->pipe, - &mxc_isi_m2m_frame_write_done, - bypass); - if (ret) - goto unlock; - - mxc_isi_channel_get(m2m->pipe); - } - - m2m->usage_count++; - - /* - * Allocate resources for the channel, counting how many users require - * buffer chaining. - */ - if (!ctx->chained && out_pix->width > MXC_ISI_MAX_WIDTH_UNCHAINED) { - ret = mxc_isi_channel_chain(m2m->pipe); - if (ret) - goto deinit; - - m2m->chained_count++; - ctx->chained = true; - } - - /* - * Drop the lock to start the stream, as the .device_run() operation - * needs to acquire it. - */ - mutex_unlock(&m2m->lock); - ret = v4l2_m2m_ioctl_streamon(file, fh, type); - if (ret) { - /* Reacquire the lock for the cleanup path. */ - mutex_lock(&m2m->lock); - goto unchain; - } - - q->streaming = true; - - return 0; - -unchain: - if (ctx->chained && --m2m->chained_count == 0) - mxc_isi_channel_unchain(m2m->pipe); - ctx->chained = false; - -deinit: - if (--m2m->usage_count == 0) { - mxc_isi_channel_put(m2m->pipe); - mxc_isi_channel_release(m2m->pipe); - } - -unlock: - mutex_unlock(&m2m->lock); - return ret; -} - -static int mxc_isi_m2m_streamoff(struct file *file, void *fh, - enum v4l2_buf_type type) -{ - struct mxc_isi_m2m_ctx *ctx = to_isi_m2m_ctx(fh); - struct mxc_isi_m2m_ctx_queue_data *q = mxc_isi_m2m_ctx_qdata(ctx, type); - struct mxc_isi_m2m *m2m = ctx->m2m; - - v4l2_m2m_ioctl_streamoff(file, fh, type); - - if (!q->streaming) - return 0; - - mutex_lock(&m2m->lock); - - /* - * If the last context is this one, reset it to make sure the device - * will be reconfigured when streaming is restarted. - */ - if (m2m->last_ctx == ctx) - m2m->last_ctx = NULL; - - /* Free the channel resources if this is the last chained context. */ - if (ctx->chained && --m2m->chained_count == 0) - mxc_isi_channel_unchain(m2m->pipe); - ctx->chained = false; - - /* Turn off the light with the last user. */ - if (--m2m->usage_count == 0) { - mxc_isi_channel_disable(m2m->pipe); - mxc_isi_channel_put(m2m->pipe); - mxc_isi_channel_release(m2m->pipe); - } - - WARN_ON(m2m->usage_count < 0); - - mutex_unlock(&m2m->lock); - - q->streaming = false; - - return 0; -} - static const struct v4l2_ioctl_ops mxc_isi_m2m_ioctl_ops = { .vidioc_querycap = mxc_isi_m2m_querycap, @@ -632,8 +592,8 @@ static const struct v4l2_ioctl_ops mxc_isi_m2m_ioctl_ops = { .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, - .vidioc_streamon = mxc_isi_m2m_streamon, - .vidioc_streamoff = mxc_isi_m2m_streamoff, + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, From 2812c6b13bcce675d8c0e66e947cfa9f83affdbf Mon Sep 17 00:00:00 2001 From: Kaustabh Chakraborty Date: Fri, 17 Oct 2025 22:05:13 -0400 Subject: [PATCH 035/143] drm/exynos: exynos7_drm_decon: fix uninitialized crtc reference in functions [ Upstream commit d31bbacf783daf1e71fbe5c68df93550c446bf44 ] Modify the functions to accept a pointer to struct decon_context instead. Signed-off-by: Kaustabh Chakraborty Signed-off-by: Inki Dae Stable-dep-of: e1361a4f1be9 ("drm/exynos: exynos7_drm_decon: remove ctx->suspended") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/exynos/exynos7_drm_decon.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/exynos/exynos7_drm_decon.c b/drivers/gpu/drm/exynos/exynos7_drm_decon.c index 9eeba254cf45df..99cc3f6803c7a0 100644 --- a/drivers/gpu/drm/exynos/exynos7_drm_decon.c +++ b/drivers/gpu/drm/exynos/exynos7_drm_decon.c @@ -81,10 +81,8 @@ static const enum drm_plane_type decon_win_types[WINDOWS_NR] = { DRM_PLANE_TYPE_CURSOR, }; -static void decon_wait_for_vblank(struct exynos_drm_crtc *crtc) +static void decon_wait_for_vblank(struct decon_context *ctx) { - struct decon_context *ctx = crtc->ctx; - if (ctx->suspended) return; @@ -100,9 +98,8 @@ static void decon_wait_for_vblank(struct exynos_drm_crtc *crtc) DRM_DEV_DEBUG_KMS(ctx->dev, "vblank wait timed out.\n"); } -static void decon_clear_channels(struct exynos_drm_crtc *crtc) +static void decon_clear_channels(struct decon_context *ctx) { - struct decon_context *ctx = crtc->ctx; unsigned int win, ch_enabled = 0; /* Check if any channel is enabled. */ @@ -118,7 +115,7 @@ static void decon_clear_channels(struct exynos_drm_crtc *crtc) /* Wait for vsync, as disable channel takes effect at next vsync */ if (ch_enabled) - decon_wait_for_vblank(ctx->crtc); + decon_wait_for_vblank(ctx); } static int decon_ctx_initialize(struct decon_context *ctx, @@ -126,7 +123,7 @@ static int decon_ctx_initialize(struct decon_context *ctx, { ctx->drm_dev = drm_dev; - decon_clear_channels(ctx->crtc); + decon_clear_channels(ctx); return exynos_drm_register_dma(drm_dev, ctx->dev, &ctx->dma_priv); } From a02e8415156b0749863a016eb158851dd930ed80 Mon Sep 17 00:00:00 2001 From: Kaustabh Chakraborty Date: Fri, 17 Oct 2025 22:05:14 -0400 Subject: [PATCH 036/143] drm/exynos: exynos7_drm_decon: properly clear channels during bind [ Upstream commit 5f1a453974204175f20b3788824a0fe23cc36f79 ] The DECON channels are not cleared properly as the windows aren't shadow protected. When accompanied with an IOMMU, it pagefaults, and the kernel panics. Implement shadow protect/unprotect, along with a standalone update, for channel clearing to properly take effect. Signed-off-by: Kaustabh Chakraborty Signed-off-by: Inki Dae Stable-dep-of: e1361a4f1be9 ("drm/exynos: exynos7_drm_decon: remove ctx->suspended") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/exynos/exynos7_drm_decon.c | 55 +++++++++++++--------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/exynos/exynos7_drm_decon.c b/drivers/gpu/drm/exynos/exynos7_drm_decon.c index 99cc3f6803c7a0..2cc2962d4ef3a4 100644 --- a/drivers/gpu/drm/exynos/exynos7_drm_decon.c +++ b/drivers/gpu/drm/exynos/exynos7_drm_decon.c @@ -81,6 +81,28 @@ static const enum drm_plane_type decon_win_types[WINDOWS_NR] = { DRM_PLANE_TYPE_CURSOR, }; +/** + * decon_shadow_protect_win() - disable updating values from shadow registers at vsync + * + * @ctx: display and enhancement controller context + * @win: window to protect registers for + * @protect: 1 to protect (disable updates) + */ +static void decon_shadow_protect_win(struct decon_context *ctx, + unsigned int win, bool protect) +{ + u32 bits, val; + + bits = SHADOWCON_WINx_PROTECT(win); + + val = readl(ctx->regs + SHADOWCON); + if (protect) + val |= bits; + else + val &= ~bits; + writel(val, ctx->regs + SHADOWCON); +} + static void decon_wait_for_vblank(struct decon_context *ctx) { if (ctx->suspended) @@ -101,18 +123,27 @@ static void decon_wait_for_vblank(struct decon_context *ctx) static void decon_clear_channels(struct decon_context *ctx) { unsigned int win, ch_enabled = 0; + u32 val; /* Check if any channel is enabled. */ for (win = 0; win < WINDOWS_NR; win++) { - u32 val = readl(ctx->regs + WINCON(win)); + val = readl(ctx->regs + WINCON(win)); if (val & WINCONx_ENWIN) { + decon_shadow_protect_win(ctx, win, true); + val &= ~WINCONx_ENWIN; writel(val, ctx->regs + WINCON(win)); ch_enabled = 1; + + decon_shadow_protect_win(ctx, win, false); } } + val = readl(ctx->regs + DECON_UPDATE); + val |= DECON_UPDATE_STANDALONE_F; + writel(val, ctx->regs + DECON_UPDATE); + /* Wait for vsync, as disable channel takes effect at next vsync */ if (ch_enabled) decon_wait_for_vblank(ctx); @@ -340,28 +371,6 @@ static void decon_win_set_colkey(struct decon_context *ctx, unsigned int win) writel(keycon1, ctx->regs + WKEYCON1_BASE(win)); } -/** - * decon_shadow_protect_win() - disable updating values from shadow registers at vsync - * - * @ctx: display and enhancement controller context - * @win: window to protect registers for - * @protect: 1 to protect (disable updates) - */ -static void decon_shadow_protect_win(struct decon_context *ctx, - unsigned int win, bool protect) -{ - u32 bits, val; - - bits = SHADOWCON_WINx_PROTECT(win); - - val = readl(ctx->regs + SHADOWCON); - if (protect) - val |= bits; - else - val &= ~bits; - writel(val, ctx->regs + SHADOWCON); -} - static void decon_atomic_begin(struct exynos_drm_crtc *crtc) { struct decon_context *ctx = crtc->ctx; From 7138de99f7b15889034dbe205afc8bd698d347ce Mon Sep 17 00:00:00 2001 From: Kaustabh Chakraborty Date: Fri, 17 Oct 2025 22:05:15 -0400 Subject: [PATCH 037/143] drm/exynos: exynos7_drm_decon: remove ctx->suspended [ Upstream commit e1361a4f1be9cb69a662c6d7b5ce218007d6e82b ] Condition guards are found to be redundant, as the call flow is properly managed now, as also observed in the Exynos5433 DECON driver. Since state checking is no longer necessary, remove it. This also fixes an issue which prevented decon_commit() from decon_atomic_enable() due to an incorrect state change setting. Fixes: 96976c3d9aff ("drm/exynos: Add DECON driver") Cc: stable@vger.kernel.org Suggested-by: Inki Dae Signed-off-by: Kaustabh Chakraborty Signed-off-by: Inki Dae Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/exynos/exynos7_drm_decon.c | 36 ---------------------- 1 file changed, 36 deletions(-) diff --git a/drivers/gpu/drm/exynos/exynos7_drm_decon.c b/drivers/gpu/drm/exynos/exynos7_drm_decon.c index 2cc2962d4ef3a4..2a218d20584284 100644 --- a/drivers/gpu/drm/exynos/exynos7_drm_decon.c +++ b/drivers/gpu/drm/exynos/exynos7_drm_decon.c @@ -51,7 +51,6 @@ struct decon_context { void __iomem *regs; unsigned long irq_flags; bool i80_if; - bool suspended; wait_queue_head_t wait_vsync_queue; atomic_t wait_vsync_event; @@ -105,9 +104,6 @@ static void decon_shadow_protect_win(struct decon_context *ctx, static void decon_wait_for_vblank(struct decon_context *ctx) { - if (ctx->suspended) - return; - atomic_set(&ctx->wait_vsync_event, 1); /* @@ -183,9 +179,6 @@ static void decon_commit(struct exynos_drm_crtc *crtc) struct drm_display_mode *mode = &crtc->base.state->adjusted_mode; u32 val, clkdiv; - if (ctx->suspended) - return; - /* nothing to do if we haven't set the mode yet */ if (mode->htotal == 0 || mode->vtotal == 0) return; @@ -247,9 +240,6 @@ static int decon_enable_vblank(struct exynos_drm_crtc *crtc) struct decon_context *ctx = crtc->ctx; u32 val; - if (ctx->suspended) - return -EPERM; - if (!test_and_set_bit(0, &ctx->irq_flags)) { val = readl(ctx->regs + VIDINTCON0); @@ -272,9 +262,6 @@ static void decon_disable_vblank(struct exynos_drm_crtc *crtc) struct decon_context *ctx = crtc->ctx; u32 val; - if (ctx->suspended) - return; - if (test_and_clear_bit(0, &ctx->irq_flags)) { val = readl(ctx->regs + VIDINTCON0); @@ -376,9 +363,6 @@ static void decon_atomic_begin(struct exynos_drm_crtc *crtc) struct decon_context *ctx = crtc->ctx; int i; - if (ctx->suspended) - return; - for (i = 0; i < WINDOWS_NR; i++) decon_shadow_protect_win(ctx, i, true); } @@ -398,9 +382,6 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc, unsigned int cpp = fb->format->cpp[0]; unsigned int pitch = fb->pitches[0]; - if (ctx->suspended) - return; - /* * SHADOWCON/PRTCON register is used for enabling timing. * @@ -488,9 +469,6 @@ static void decon_disable_plane(struct exynos_drm_crtc *crtc, unsigned int win = plane->index; u32 val; - if (ctx->suspended) - return; - /* protect windows */ decon_shadow_protect_win(ctx, win, true); @@ -509,9 +487,6 @@ static void decon_atomic_flush(struct exynos_drm_crtc *crtc) struct decon_context *ctx = crtc->ctx; int i; - if (ctx->suspended) - return; - for (i = 0; i < WINDOWS_NR; i++) decon_shadow_protect_win(ctx, i, false); exynos_crtc_handle_event(crtc); @@ -539,9 +514,6 @@ static void decon_atomic_enable(struct exynos_drm_crtc *crtc) struct decon_context *ctx = crtc->ctx; int ret; - if (!ctx->suspended) - return; - ret = pm_runtime_resume_and_get(ctx->dev); if (ret < 0) { DRM_DEV_ERROR(ctx->dev, "failed to enable DECON device.\n"); @@ -555,8 +527,6 @@ static void decon_atomic_enable(struct exynos_drm_crtc *crtc) decon_enable_vblank(ctx->crtc); decon_commit(ctx->crtc); - - ctx->suspended = false; } static void decon_atomic_disable(struct exynos_drm_crtc *crtc) @@ -564,9 +534,6 @@ static void decon_atomic_disable(struct exynos_drm_crtc *crtc) struct decon_context *ctx = crtc->ctx; int i; - if (ctx->suspended) - return; - /* * We need to make sure that all windows are disabled before we * suspend that connector. Otherwise we might try to scan from @@ -576,8 +543,6 @@ static void decon_atomic_disable(struct exynos_drm_crtc *crtc) decon_disable_plane(crtc, &ctx->planes[i]); pm_runtime_put_sync(ctx->dev); - - ctx->suspended = true; } static const struct exynos_drm_crtc_ops decon_crtc_ops = { @@ -698,7 +663,6 @@ static int decon_probe(struct platform_device *pdev) return -ENOMEM; ctx->dev = dev; - ctx->suspended = true; i80_if_timings = of_get_child_by_name(dev->of_node, "i80-if-timings"); if (i80_if_timings) From 1a3949c3e5c3832f5db7c8124aa9e99aa4695a2f Mon Sep 17 00:00:00 2001 From: Kuen-Han Tsai Date: Fri, 17 Oct 2025 19:58:24 -0400 Subject: [PATCH 038/143] usb: gadget: Store endpoint pointer in usb_request [ Upstream commit bfb1d99d969fe3b892db30848aeebfa19d21f57f ] Gadget function drivers often have goto-based error handling in their bind paths, which can be bug-prone. Refactoring these paths to use __free() scope-based cleanup is desirable, but currently blocked. The blocker is that usb_ep_free_request(ep, req) requires two parameters, while the __free() mechanism can only pass a pointer to the request itself. Store an endpoint pointer in the struct usb_request. The pointer is populated centrally in usb_ep_alloc_request() on every successful allocation, making the request object self-contained. Signed-off-by: Kuen-Han Tsai Link: https://lore.kernel.org/r/20250916-ready-v1-1-4997bf277548@google.com Signed-off-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20250916-ready-v1-1-4997bf277548@google.com Stable-dep-of: 75a5b8d4ddd4 ("usb: gadget: f_ncm: Refactor bind path to use __free()") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/core.c | 3 +++ include/linux/usb/gadget.h | 2 ++ 2 files changed, 5 insertions(+) diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index d709e24c1fd422..e3d63b8fa0f4c1 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -194,6 +194,9 @@ struct usb_request *usb_ep_alloc_request(struct usb_ep *ep, req = ep->ops->alloc_request(ep, gfp_flags); + if (req) + req->ep = ep; + trace_usb_ep_alloc_request(ep, req, req ? 0 : -ENOMEM); return req; diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index df33333650a0d9..77554f44466510 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -32,6 +32,7 @@ struct usb_ep; /** * struct usb_request - describes one i/o request + * @ep: The associated endpoint set by usb_ep_alloc_request(). * @buf: Buffer used for data. Always provide this; some controllers * only use PIO, or don't use DMA for some endpoints. * @dma: DMA address corresponding to 'buf'. If you don't set this @@ -98,6 +99,7 @@ struct usb_ep; */ struct usb_request { + struct usb_ep *ep; void *buf; unsigned length; dma_addr_t dma; From 56b5f34542d7298b6f3790ae16cc3c8c52949f4d Mon Sep 17 00:00:00 2001 From: Kuen-Han Tsai Date: Fri, 17 Oct 2025 19:58:25 -0400 Subject: [PATCH 039/143] usb: gadget: Introduce free_usb_request helper [ Upstream commit 201c53c687f2b55a7cc6d9f4000af4797860174b ] Introduce the free_usb_request() function that frees both the request's buffer and the request itself. This function serves as the cleanup callback for DEFINE_FREE() to enable automatic, scope-based cleanup for usb_request pointers. Signed-off-by: Kuen-Han Tsai Link: https://lore.kernel.org/r/20250916-ready-v1-2-4997bf277548@google.com Signed-off-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20250916-ready-v1-2-4997bf277548@google.com Stable-dep-of: 75a5b8d4ddd4 ("usb: gadget: f_ncm: Refactor bind path to use __free()") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/gadget.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 77554f44466510..da4309f3cea3f8 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -15,6 +15,7 @@ #ifndef __LINUX_USB_GADGET_H #define __LINUX_USB_GADGET_H +#include #include #include #include @@ -293,6 +294,28 @@ static inline void usb_ep_fifo_flush(struct usb_ep *ep) /*-------------------------------------------------------------------------*/ +/** + * free_usb_request - frees a usb_request object and its buffer + * @req: the request being freed + * + * This helper function frees both the request's buffer and the request object + * itself by calling usb_ep_free_request(). Its signature is designed to be used + * with DEFINE_FREE() to enable automatic, scope-based cleanup for usb_request + * pointers. + */ +static inline void free_usb_request(struct usb_request *req) +{ + if (!req) + return; + + kfree(req->buf); + usb_ep_free_request(req->ep, req); +} + +DEFINE_FREE(free_usb_request, struct usb_request *, free_usb_request(_T)) + +/*-------------------------------------------------------------------------*/ + struct usb_dcd_config_params { __u8 bU1devExitLat; /* U1 Device exit Latency */ #define USB_DEFAULT_U1_DEV_EXIT_LAT 0x01 /* Less then 1 microsec */ From d3fe7143928d8dfa2ec7bac9f906b48bc75b98ee Mon Sep 17 00:00:00 2001 From: Kuen-Han Tsai Date: Fri, 17 Oct 2025 19:58:26 -0400 Subject: [PATCH 040/143] usb: gadget: f_ncm: Refactor bind path to use __free() [ Upstream commit 75a5b8d4ddd4eb6b16cb0b475d14ff4ae64295ef ] After an bind/unbind cycle, the ncm->notify_req is left stale. If a subsequent bind fails, the unified error label attempts to free this stale request, leading to a NULL pointer dereference when accessing ep->ops->free_request. Refactor the error handling in the bind path to use the __free() automatic cleanup mechanism. Unable to handle kernel NULL pointer dereference at virtual address 0000000000000020 Call trace: usb_ep_free_request+0x2c/0xec ncm_bind+0x39c/0x3dc usb_add_function+0xcc/0x1f0 configfs_composite_bind+0x468/0x588 gadget_bind_driver+0x104/0x270 really_probe+0x190/0x374 __driver_probe_device+0xa0/0x12c driver_probe_device+0x3c/0x218 __device_attach_driver+0x14c/0x188 bus_for_each_drv+0x10c/0x168 __device_attach+0xfc/0x198 device_initial_probe+0x14/0x24 bus_probe_device+0x94/0x11c device_add+0x268/0x48c usb_add_gadget+0x198/0x28c dwc3_gadget_init+0x700/0x858 __dwc3_set_mode+0x3cc/0x664 process_scheduled_works+0x1d8/0x488 worker_thread+0x244/0x334 kthread+0x114/0x1bc ret_from_fork+0x10/0x20 Fixes: 9f6ce4240a2b ("usb: gadget: f_ncm.c added") Cc: stable@kernel.org Signed-off-by: Kuen-Han Tsai Link: https://lore.kernel.org/r/20250916-ready-v1-3-4997bf277548@google.com Signed-off-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20250916-ready-v1-3-4997bf277548@google.com Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_ncm.c | 78 ++++++++++++----------------- 1 file changed, 33 insertions(+), 45 deletions(-) diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c index 8e761249d672c7..3afc9a622086c2 100644 --- a/drivers/usb/gadget/function/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -11,6 +11,7 @@ * Copyright (C) 2008 Nokia Corporation */ +#include #include #include #include @@ -19,6 +20,7 @@ #include #include +#include #include "u_ether.h" #include "u_ether_configfs.h" @@ -1435,18 +1437,18 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) struct usb_ep *ep; struct f_ncm_opts *ncm_opts; + struct usb_os_desc_table *os_desc_table __free(kfree) = NULL; + struct usb_request *request __free(free_usb_request) = NULL; + if (!can_support_ecm(cdev->gadget)) return -EINVAL; ncm_opts = container_of(f->fi, struct f_ncm_opts, func_inst); if (cdev->use_os_string) { - f->os_desc_table = kzalloc(sizeof(*f->os_desc_table), - GFP_KERNEL); - if (!f->os_desc_table) + os_desc_table = kzalloc(sizeof(*os_desc_table), GFP_KERNEL); + if (!os_desc_table) return -ENOMEM; - f->os_desc_n = 1; - f->os_desc_table[0].os_desc = &ncm_opts->ncm_os_desc; } mutex_lock(&ncm_opts->lock); @@ -1458,16 +1460,15 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) mutex_unlock(&ncm_opts->lock); if (status) - goto fail; + return status; ncm_opts->bound = true; us = usb_gstrings_attach(cdev, ncm_strings, ARRAY_SIZE(ncm_string_defs)); - if (IS_ERR(us)) { - status = PTR_ERR(us); - goto fail; - } + if (IS_ERR(us)) + return PTR_ERR(us); + ncm_control_intf.iInterface = us[STRING_CTRL_IDX].id; ncm_data_nop_intf.iInterface = us[STRING_DATA_IDX].id; ncm_data_intf.iInterface = us[STRING_DATA_IDX].id; @@ -1477,20 +1478,16 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) /* allocate instance-specific interface IDs */ status = usb_interface_id(c, f); if (status < 0) - goto fail; + return status; ncm->ctrl_id = status; ncm_iad_desc.bFirstInterface = status; ncm_control_intf.bInterfaceNumber = status; ncm_union_desc.bMasterInterface0 = status; - if (cdev->use_os_string) - f->os_desc_table[0].if_id = - ncm_iad_desc.bFirstInterface; - status = usb_interface_id(c, f); if (status < 0) - goto fail; + return status; ncm->data_id = status; ncm_data_nop_intf.bInterfaceNumber = status; @@ -1499,35 +1496,31 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) ecm_desc.wMaxSegmentSize = cpu_to_le16(ncm_opts->max_segment_size); - status = -ENODEV; - /* allocate instance-specific endpoints */ ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_in_desc); if (!ep) - goto fail; + return -ENODEV; ncm->port.in_ep = ep; ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_out_desc); if (!ep) - goto fail; + return -ENODEV; ncm->port.out_ep = ep; ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_notify_desc); if (!ep) - goto fail; + return -ENODEV; ncm->notify = ep; - status = -ENOMEM; - /* allocate notification request and buffer */ - ncm->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL); - if (!ncm->notify_req) - goto fail; - ncm->notify_req->buf = kmalloc(NCM_STATUS_BYTECOUNT, GFP_KERNEL); - if (!ncm->notify_req->buf) - goto fail; - ncm->notify_req->context = ncm; - ncm->notify_req->complete = ncm_notify_complete; + request = usb_ep_alloc_request(ep, GFP_KERNEL); + if (!request) + return -ENOMEM; + request->buf = kmalloc(NCM_STATUS_BYTECOUNT, GFP_KERNEL); + if (!request->buf) + return -ENOMEM; + request->context = ncm; + request->complete = ncm_notify_complete; /* * support all relevant hardware speeds... we expect that when @@ -1547,7 +1540,7 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) status = usb_assign_descriptors(f, ncm_fs_function, ncm_hs_function, ncm_ss_function, ncm_ss_function); if (status) - goto fail; + return status; /* * NOTE: all that is done without knowing or caring about @@ -1561,23 +1554,18 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) hrtimer_init(&ncm->task_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); ncm->task_timer.function = ncm_tx_timeout; + if (cdev->use_os_string) { + os_desc_table[0].os_desc = &ncm_opts->ncm_os_desc; + os_desc_table[0].if_id = ncm_iad_desc.bFirstInterface; + f->os_desc_table = no_free_ptr(os_desc_table); + f->os_desc_n = 1; + } + ncm->notify_req = no_free_ptr(request); + DBG(cdev, "CDC Network: IN/%s OUT/%s NOTIFY/%s\n", ncm->port.in_ep->name, ncm->port.out_ep->name, ncm->notify->name); return 0; - -fail: - kfree(f->os_desc_table); - f->os_desc_n = 0; - - if (ncm->notify_req) { - kfree(ncm->notify_req->buf); - usb_ep_free_request(ncm->notify, ncm->notify_req); - } - - ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); - - return status; } static inline struct f_ncm_opts *to_f_ncm_opts(struct config_item *item) From 201a66d8e6630762e760e1d78f1d149da1691e7b Mon Sep 17 00:00:00 2001 From: Kuen-Han Tsai Date: Fri, 17 Oct 2025 20:29:27 -0400 Subject: [PATCH 041/143] usb: gadget: f_acm: Refactor bind path to use __free() [ Upstream commit 47b2116e54b4a854600341487e8b55249e926324 ] After an bind/unbind cycle, the acm->notify_req is left stale. If a subsequent bind fails, the unified error label attempts to free this stale request, leading to a NULL pointer dereference when accessing ep->ops->free_request. Refactor the error handling in the bind path to use the __free() automatic cleanup mechanism. Unable to handle kernel NULL pointer dereference at virtual address 0000000000000020 Call trace: usb_ep_free_request+0x2c/0xec gs_free_req+0x30/0x44 acm_bind+0x1b8/0x1f4 usb_add_function+0xcc/0x1f0 configfs_composite_bind+0x468/0x588 gadget_bind_driver+0x104/0x270 really_probe+0x190/0x374 __driver_probe_device+0xa0/0x12c driver_probe_device+0x3c/0x218 __device_attach_driver+0x14c/0x188 bus_for_each_drv+0x10c/0x168 __device_attach+0xfc/0x198 device_initial_probe+0x14/0x24 bus_probe_device+0x94/0x11c device_add+0x268/0x48c usb_add_gadget+0x198/0x28c dwc3_gadget_init+0x700/0x858 __dwc3_set_mode+0x3cc/0x664 process_scheduled_works+0x1d8/0x488 worker_thread+0x244/0x334 kthread+0x114/0x1bc ret_from_fork+0x10/0x20 Fixes: 1f1ba11b6494 ("usb gadget: issue notifications from ACM function") Cc: stable@kernel.org Signed-off-by: Kuen-Han Tsai Link: https://lore.kernel.org/r/20250916-ready-v1-4-4997bf277548@google.com Signed-off-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20250916-ready-v1-4-4997bf277548@google.com Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_acm.c | 42 +++++++++++++---------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/drivers/usb/gadget/function/f_acm.c b/drivers/usb/gadget/function/f_acm.c index 7061720b9732e4..106046e17c4e11 100644 --- a/drivers/usb/gadget/function/f_acm.c +++ b/drivers/usb/gadget/function/f_acm.c @@ -11,12 +11,15 @@ /* #define VERBOSE_DEBUG */ +#include #include #include #include #include #include +#include + #include "u_serial.h" @@ -613,6 +616,7 @@ acm_bind(struct usb_configuration *c, struct usb_function *f) struct usb_string *us; int status; struct usb_ep *ep; + struct usb_request *request __free(free_usb_request) = NULL; /* REVISIT might want instance-specific strings to help * distinguish instances ... @@ -630,7 +634,7 @@ acm_bind(struct usb_configuration *c, struct usb_function *f) /* allocate instance-specific interface IDs, and patch descriptors */ status = usb_interface_id(c, f); if (status < 0) - goto fail; + return status; acm->ctrl_id = status; acm_iad_descriptor.bFirstInterface = status; @@ -639,43 +643,41 @@ acm_bind(struct usb_configuration *c, struct usb_function *f) status = usb_interface_id(c, f); if (status < 0) - goto fail; + return status; acm->data_id = status; acm_data_interface_desc.bInterfaceNumber = status; acm_union_desc.bSlaveInterface0 = status; acm_call_mgmt_descriptor.bDataInterface = status; - status = -ENODEV; - /* allocate instance-specific endpoints */ ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_in_desc); if (!ep) - goto fail; + return -ENODEV; acm->port.in = ep; ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_out_desc); if (!ep) - goto fail; + return -ENODEV; acm->port.out = ep; ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_notify_desc); if (!ep) - goto fail; + return -ENODEV; acm->notify = ep; acm_iad_descriptor.bFunctionProtocol = acm->bInterfaceProtocol; acm_control_interface_desc.bInterfaceProtocol = acm->bInterfaceProtocol; /* allocate notification */ - acm->notify_req = gs_alloc_req(ep, - sizeof(struct usb_cdc_notification) + 2, - GFP_KERNEL); - if (!acm->notify_req) - goto fail; + request = gs_alloc_req(ep, + sizeof(struct usb_cdc_notification) + 2, + GFP_KERNEL); + if (!request) + return -ENODEV; - acm->notify_req->complete = acm_cdc_notify_complete; - acm->notify_req->context = acm; + request->complete = acm_cdc_notify_complete; + request->context = acm; /* support all relevant hardware speeds... we expect that when * hardware is dual speed, all bulk-capable endpoints work at @@ -692,7 +694,9 @@ acm_bind(struct usb_configuration *c, struct usb_function *f) status = usb_assign_descriptors(f, acm_fs_function, acm_hs_function, acm_ss_function, acm_ss_function); if (status) - goto fail; + return status; + + acm->notify_req = no_free_ptr(request); dev_dbg(&cdev->gadget->dev, "acm ttyGS%d: IN/%s OUT/%s NOTIFY/%s\n", @@ -700,14 +704,6 @@ acm_bind(struct usb_configuration *c, struct usb_function *f) acm->port.in->name, acm->port.out->name, acm->notify->name); return 0; - -fail: - if (acm->notify_req) - gs_free_req(acm->notify, acm->notify_req); - - ERROR(cdev, "%s/%p: can't bind, err %d\n", f->name, f, status); - - return status; } static void acm_unbind(struct usb_configuration *c, struct usb_function *f) From 15b9faf53ba8719700596e7ef78879ce200e8c2e Mon Sep 17 00:00:00 2001 From: Kuen-Han Tsai Date: Fri, 17 Oct 2025 20:52:36 -0400 Subject: [PATCH 042/143] usb: gadget: f_ecm: Refactor bind path to use __free() [ Upstream commit 42988380ac67c76bb9dff8f77d7ef3eefd50b7b5 ] After an bind/unbind cycle, the ecm->notify_req is left stale. If a subsequent bind fails, the unified error label attempts to free this stale request, leading to a NULL pointer dereference when accessing ep->ops->free_request. Refactor the error handling in the bind path to use the __free() automatic cleanup mechanism. Fixes: da741b8c56d6 ("usb ethernet gadget: split CDC Ethernet function") Cc: stable@kernel.org Signed-off-by: Kuen-Han Tsai Link: https://lore.kernel.org/r/20250916-ready-v1-5-4997bf277548@google.com Signed-off-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20250916-ready-v1-5-4997bf277548@google.com Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_ecm.c | 48 ++++++++++++----------------- 1 file changed, 20 insertions(+), 28 deletions(-) diff --git a/drivers/usb/gadget/function/f_ecm.c b/drivers/usb/gadget/function/f_ecm.c index 549efc84dd8321..123c03a0850995 100644 --- a/drivers/usb/gadget/function/f_ecm.c +++ b/drivers/usb/gadget/function/f_ecm.c @@ -8,12 +8,15 @@ /* #define VERBOSE_DEBUG */ +#include #include #include #include #include #include +#include + #include "u_ether.h" #include "u_ether_configfs.h" #include "u_ecm.h" @@ -678,6 +681,7 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f) struct usb_ep *ep; struct f_ecm_opts *ecm_opts; + struct usb_request *request __free(free_usb_request) = NULL; if (!can_support_ecm(cdev->gadget)) return -EINVAL; @@ -711,7 +715,7 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f) /* allocate instance-specific interface IDs */ status = usb_interface_id(c, f); if (status < 0) - goto fail; + return status; ecm->ctrl_id = status; ecm_iad_descriptor.bFirstInterface = status; @@ -720,24 +724,22 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f) status = usb_interface_id(c, f); if (status < 0) - goto fail; + return status; ecm->data_id = status; ecm_data_nop_intf.bInterfaceNumber = status; ecm_data_intf.bInterfaceNumber = status; ecm_union_desc.bSlaveInterface0 = status; - status = -ENODEV; - /* allocate instance-specific endpoints */ ep = usb_ep_autoconfig(cdev->gadget, &fs_ecm_in_desc); if (!ep) - goto fail; + return -ENODEV; ecm->port.in_ep = ep; ep = usb_ep_autoconfig(cdev->gadget, &fs_ecm_out_desc); if (!ep) - goto fail; + return -ENODEV; ecm->port.out_ep = ep; /* NOTE: a status/notification endpoint is *OPTIONAL* but we @@ -746,20 +748,18 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f) */ ep = usb_ep_autoconfig(cdev->gadget, &fs_ecm_notify_desc); if (!ep) - goto fail; + return -ENODEV; ecm->notify = ep; - status = -ENOMEM; - /* allocate notification request and buffer */ - ecm->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL); - if (!ecm->notify_req) - goto fail; - ecm->notify_req->buf = kmalloc(ECM_STATUS_BYTECOUNT, GFP_KERNEL); - if (!ecm->notify_req->buf) - goto fail; - ecm->notify_req->context = ecm; - ecm->notify_req->complete = ecm_notify_complete; + request = usb_ep_alloc_request(ep, GFP_KERNEL); + if (!request) + return -ENOMEM; + request->buf = kmalloc(ECM_STATUS_BYTECOUNT, GFP_KERNEL); + if (!request->buf) + return -ENOMEM; + request->context = ecm; + request->complete = ecm_notify_complete; /* support all relevant hardware speeds... we expect that when * hardware is dual speed, all bulk-capable endpoints work at @@ -778,7 +778,7 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f) status = usb_assign_descriptors(f, ecm_fs_function, ecm_hs_function, ecm_ss_function, ecm_ss_function); if (status) - goto fail; + return status; /* NOTE: all that is done without knowing or caring about * the network link ... which is unavailable to this code @@ -788,20 +788,12 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f) ecm->port.open = ecm_open; ecm->port.close = ecm_close; + ecm->notify_req = no_free_ptr(request); + DBG(cdev, "CDC Ethernet: IN/%s OUT/%s NOTIFY/%s\n", ecm->port.in_ep->name, ecm->port.out_ep->name, ecm->notify->name); return 0; - -fail: - if (ecm->notify_req) { - kfree(ecm->notify_req->buf); - usb_ep_free_request(ecm->notify, ecm->notify_req); - } - - ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); - - return status; } static inline struct f_ecm_opts *to_f_ecm_opts(struct config_item *item) From 380353c3a92be7d928e6f973bd065c5b79755ac3 Mon Sep 17 00:00:00 2001 From: Kuen-Han Tsai Date: Fri, 17 Oct 2025 22:03:50 -0400 Subject: [PATCH 043/143] usb: gadget: f_rndis: Refactor bind path to use __free() [ Upstream commit 08228941436047bdcd35a612c1aec0912a29d8cd ] After an bind/unbind cycle, the rndis->notify_req is left stale. If a subsequent bind fails, the unified error label attempts to free this stale request, leading to a NULL pointer dereference when accessing ep->ops->free_request. Refactor the error handling in the bind path to use the __free() automatic cleanup mechanism. Fixes: 45fe3b8e5342 ("usb ethernet gadget: split RNDIS function") Cc: stable@kernel.org Signed-off-by: Kuen-Han Tsai Link: https://lore.kernel.org/r/20250916-ready-v1-6-4997bf277548@google.com Signed-off-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20250916-ready-v1-6-4997bf277548@google.com Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_rndis.c | 85 +++++++++++---------------- 1 file changed, 35 insertions(+), 50 deletions(-) diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c index 7cec19d65fb534..7451e7cb7a8523 100644 --- a/drivers/usb/gadget/function/f_rndis.c +++ b/drivers/usb/gadget/function/f_rndis.c @@ -19,6 +19,8 @@ #include +#include + #include "u_ether.h" #include "u_ether_configfs.h" #include "u_rndis.h" @@ -662,6 +664,8 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) struct usb_ep *ep; struct f_rndis_opts *rndis_opts; + struct usb_os_desc_table *os_desc_table __free(kfree) = NULL; + struct usb_request *request __free(free_usb_request) = NULL; if (!can_support_rndis(c)) return -EINVAL; @@ -669,12 +673,9 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) rndis_opts = container_of(f->fi, struct f_rndis_opts, func_inst); if (cdev->use_os_string) { - f->os_desc_table = kzalloc(sizeof(*f->os_desc_table), - GFP_KERNEL); - if (!f->os_desc_table) + os_desc_table = kzalloc(sizeof(*os_desc_table), GFP_KERNEL); + if (!os_desc_table) return -ENOMEM; - f->os_desc_n = 1; - f->os_desc_table[0].os_desc = &rndis_opts->rndis_os_desc; } rndis_iad_descriptor.bFunctionClass = rndis_opts->class; @@ -692,16 +693,14 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) gether_set_gadget(rndis_opts->net, cdev->gadget); status = gether_register_netdev(rndis_opts->net); if (status) - goto fail; + return status; rndis_opts->bound = true; } us = usb_gstrings_attach(cdev, rndis_strings, ARRAY_SIZE(rndis_string_defs)); - if (IS_ERR(us)) { - status = PTR_ERR(us); - goto fail; - } + if (IS_ERR(us)) + return PTR_ERR(us); rndis_control_intf.iInterface = us[0].id; rndis_data_intf.iInterface = us[1].id; rndis_iad_descriptor.iFunction = us[2].id; @@ -709,36 +708,30 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) /* allocate instance-specific interface IDs */ status = usb_interface_id(c, f); if (status < 0) - goto fail; + return status; rndis->ctrl_id = status; rndis_iad_descriptor.bFirstInterface = status; rndis_control_intf.bInterfaceNumber = status; rndis_union_desc.bMasterInterface0 = status; - if (cdev->use_os_string) - f->os_desc_table[0].if_id = - rndis_iad_descriptor.bFirstInterface; - status = usb_interface_id(c, f); if (status < 0) - goto fail; + return status; rndis->data_id = status; rndis_data_intf.bInterfaceNumber = status; rndis_union_desc.bSlaveInterface0 = status; - status = -ENODEV; - /* allocate instance-specific endpoints */ ep = usb_ep_autoconfig(cdev->gadget, &fs_in_desc); if (!ep) - goto fail; + return -ENODEV; rndis->port.in_ep = ep; ep = usb_ep_autoconfig(cdev->gadget, &fs_out_desc); if (!ep) - goto fail; + return -ENODEV; rndis->port.out_ep = ep; /* NOTE: a status/notification endpoint is, strictly speaking, @@ -747,21 +740,19 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) */ ep = usb_ep_autoconfig(cdev->gadget, &fs_notify_desc); if (!ep) - goto fail; + return -ENODEV; rndis->notify = ep; - status = -ENOMEM; - /* allocate notification request and buffer */ - rndis->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL); - if (!rndis->notify_req) - goto fail; - rndis->notify_req->buf = kmalloc(STATUS_BYTECOUNT, GFP_KERNEL); - if (!rndis->notify_req->buf) - goto fail; - rndis->notify_req->length = STATUS_BYTECOUNT; - rndis->notify_req->context = rndis; - rndis->notify_req->complete = rndis_response_complete; + request = usb_ep_alloc_request(ep, GFP_KERNEL); + if (!request) + return -ENOMEM; + request->buf = kmalloc(STATUS_BYTECOUNT, GFP_KERNEL); + if (!request->buf) + return -ENOMEM; + request->length = STATUS_BYTECOUNT; + request->context = rndis; + request->complete = rndis_response_complete; /* support all relevant hardware speeds... we expect that when * hardware is dual speed, all bulk-capable endpoints work at @@ -778,7 +769,7 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) status = usb_assign_descriptors(f, eth_fs_function, eth_hs_function, eth_ss_function, eth_ss_function); if (status) - goto fail; + return status; rndis->port.open = rndis_open; rndis->port.close = rndis_close; @@ -789,9 +780,18 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) if (rndis->manufacturer && rndis->vendorID && rndis_set_param_vendor(rndis->params, rndis->vendorID, rndis->manufacturer)) { - status = -EINVAL; - goto fail_free_descs; + usb_free_all_descriptors(f); + return -EINVAL; + } + + if (cdev->use_os_string) { + os_desc_table[0].os_desc = &rndis_opts->rndis_os_desc; + os_desc_table[0].if_id = rndis_iad_descriptor.bFirstInterface; + f->os_desc_table = no_free_ptr(os_desc_table); + f->os_desc_n = 1; + } + rndis->notify_req = no_free_ptr(request); /* NOTE: all that is done without knowing or caring about * the network link ... which is unavailable to this code @@ -802,21 +802,6 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) rndis->port.in_ep->name, rndis->port.out_ep->name, rndis->notify->name); return 0; - -fail_free_descs: - usb_free_all_descriptors(f); -fail: - kfree(f->os_desc_table); - f->os_desc_n = 0; - - if (rndis->notify_req) { - kfree(rndis->notify_req->buf); - usb_ep_free_request(rndis->notify, rndis->notify_req); - } - - ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); - - return status; } void rndis_borrow_net(struct usb_function_instance *f, struct net_device *net) From 24883bfe09c504922ff7602dce9786ec15237028 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 18 Oct 2025 12:13:41 -0400 Subject: [PATCH 044/143] cpufreq: CPPC: Avoid using CPUFREQ_ETERNAL as transition delay [ Upstream commit f965d111e68f4a993cc44d487d416e3d954eea11 ] If cppc_get_transition_latency() returns CPUFREQ_ETERNAL to indicate a failure to retrieve the transition latency value from the platform firmware, the CPPC cpufreq driver will use that value (converted to microseconds) as the policy transition delay, but it is way too large for any practical use. Address this by making the driver use the cpufreq's default transition latency value (in microseconds) as the transition delay if CPUFREQ_ETERNAL is returned by cppc_get_transition_latency(). Fixes: d4f3388afd48 ("cpufreq / CPPC: Set platform specific transition_delay_us") Cc: 5.19+ # 5.19 Signed-off-by: Rafael J. Wysocki Reviewed-by: Mario Limonciello (AMD) Reviewed-by: Jie Zhan Acked-by: Viresh Kumar Reviewed-by: Qais Yousef Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/cpufreq/cppc_cpufreq.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c index 8d5279c21e6cfe..1abedcae50b265 100644 --- a/drivers/cpufreq/cppc_cpufreq.c +++ b/drivers/cpufreq/cppc_cpufreq.c @@ -339,6 +339,16 @@ static int cppc_verify_policy(struct cpufreq_policy_data *policy) return 0; } +static unsigned int __cppc_cpufreq_get_transition_delay_us(unsigned int cpu) +{ + unsigned int transition_latency_ns = cppc_get_transition_latency(cpu); + + if (transition_latency_ns == CPUFREQ_ETERNAL) + return CPUFREQ_DEFAULT_TRANSITION_LATENCY_NS / NSEC_PER_USEC; + + return transition_latency_ns / NSEC_PER_USEC; +} + /* * The PCC subspace describes the rate at which platform can accept commands * on the shared PCC channel (including READs which do not count towards freq @@ -361,12 +371,12 @@ static unsigned int cppc_cpufreq_get_transition_delay_us(unsigned int cpu) return 10000; } } - return cppc_get_transition_latency(cpu) / NSEC_PER_USEC; + return __cppc_cpufreq_get_transition_delay_us(cpu); } #else static unsigned int cppc_cpufreq_get_transition_delay_us(unsigned int cpu) { - return cppc_get_transition_latency(cpu) / NSEC_PER_USEC; + return __cppc_cpufreq_get_transition_delay_us(cpu); } #endif From df23d9ac34552b2e4328119563a6e0dfed982f7e Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Mon, 13 Oct 2025 12:05:31 -0600 Subject: [PATCH 045/143] Revert "io_uring/rw: drop -EOPNOTSUPP check in __io_complete_rw_common()" Commit 927069c4ac2cd1a37efa468596fb5b8f86db9df0 upstream. This reverts commit 90bfb28d5fa8127a113a140c9791ea0b40ab156a. Kevin reports that this commit causes an issue for him with LVM snapshots, most likely because of turning off NOWAIT support while a snapshot is being created. This makes -EOPNOTSUPP bubble back through the completion handler, where io_uring read/write handling should just retry it. Reinstate the previous check removed by the referenced commit. Cc: stable@vger.kernel.org Fixes: 90bfb28d5fa8 ("io_uring/rw: drop -EOPNOTSUPP check in __io_complete_rw_common()") Reported-by: Salvatore Bonaccorso Reported-by: Kevin Lumik Link: https://lore.kernel.org/io-uring/cceb723c-051b-4de2-9a4c-4aa82e1619ee@kernel.dk/ Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- io_uring/rw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io_uring/rw.c b/io_uring/rw.c index 3ad104cf1e7d83..996cd4bec4821a 100644 --- a/io_uring/rw.c +++ b/io_uring/rw.c @@ -477,7 +477,7 @@ static void io_req_io_end(struct io_kiocb *req) static bool __io_complete_rw_common(struct io_kiocb *req, long res) { if (unlikely(res != req->cqe.res)) { - if (res == -EAGAIN && io_rw_should_reissue(req)) { + if ((res == -EOPNOTSUPP || res == -EAGAIN) && io_rw_should_reissue(req)) { /* * Reissue will start accounting again, finish the * current cycle. From f32fea4c0234c971c12e46d76612cdc2dd4bb046 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 8 Oct 2025 16:06:58 +0200 Subject: [PATCH 046/143] HID: multitouch: fix sticky fingers commit 46f781e0d151844589dc2125c8cce3300546f92a upstream. The sticky fingers quirk (MT_QUIRK_STICKY_FINGERS) was only considering the case when slots were not released during the last report. This can be problematic if the firmware forgets to release a finger while others are still present. This was observed on the Synaptics DLL0945 touchpad found on the Dell XPS 9310 and the Dell Inspiron 5406. Fixes: 4f4001bc76fd ("HID: multitouch: fix rare Win 8 cases when the touch up event gets missing") Cc: stable@vger.kernel.org Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/hid/hid-multitouch.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 5c424010bc025c..0667a24576fc4f 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -83,9 +83,8 @@ enum latency_mode { HID_LATENCY_HIGH = 1, }; -#define MT_IO_FLAGS_RUNNING 0 -#define MT_IO_FLAGS_ACTIVE_SLOTS 1 -#define MT_IO_FLAGS_PENDING_SLOTS 2 +#define MT_IO_SLOTS_MASK GENMASK(7, 0) /* reserve first 8 bits for slot tracking */ +#define MT_IO_FLAGS_RUNNING 32 static const bool mtrue = true; /* default for true */ static const bool mfalse; /* default for false */ @@ -160,7 +159,11 @@ struct mt_device { struct mt_class mtclass; /* our mt device class */ struct timer_list release_timer; /* to release sticky fingers */ struct hid_device *hdev; /* hid_device we're attached to */ - unsigned long mt_io_flags; /* mt flags (MT_IO_FLAGS_*) */ + unsigned long mt_io_flags; /* mt flags (MT_IO_FLAGS_RUNNING) + * first 8 bits are reserved for keeping the slot + * states, this is fine because we only support up + * to 250 slots (MT_MAX_MAXCONTACT) + */ __u8 inputmode_value; /* InputMode HID feature value */ __u8 maxcontacts; bool is_buttonpad; /* is this device a button pad? */ @@ -941,6 +944,7 @@ static void mt_release_pending_palms(struct mt_device *td, for_each_set_bit(slotnum, app->pending_palm_slots, td->maxcontacts) { clear_bit(slotnum, app->pending_palm_slots); + clear_bit(slotnum, &td->mt_io_flags); input_mt_slot(input, slotnum); input_mt_report_slot_inactive(input); @@ -972,12 +976,6 @@ static void mt_sync_frame(struct mt_device *td, struct mt_application *app, app->num_received = 0; app->left_button_state = 0; - - if (test_bit(MT_IO_FLAGS_ACTIVE_SLOTS, &td->mt_io_flags)) - set_bit(MT_IO_FLAGS_PENDING_SLOTS, &td->mt_io_flags); - else - clear_bit(MT_IO_FLAGS_PENDING_SLOTS, &td->mt_io_flags); - clear_bit(MT_IO_FLAGS_ACTIVE_SLOTS, &td->mt_io_flags); } static int mt_compute_timestamp(struct mt_application *app, __s32 value) @@ -1152,7 +1150,9 @@ static int mt_process_slot(struct mt_device *td, struct input_dev *input, input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major); input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor); - set_bit(MT_IO_FLAGS_ACTIVE_SLOTS, &td->mt_io_flags); + set_bit(slotnum, &td->mt_io_flags); + } else { + clear_bit(slotnum, &td->mt_io_flags); } return 0; @@ -1287,7 +1287,7 @@ static void mt_touch_report(struct hid_device *hid, * defect. */ if (app->quirks & MT_QUIRK_STICKY_FINGERS) { - if (test_bit(MT_IO_FLAGS_PENDING_SLOTS, &td->mt_io_flags)) + if (td->mt_io_flags & MT_IO_SLOTS_MASK) mod_timer(&td->release_timer, jiffies + msecs_to_jiffies(100)); else @@ -1734,6 +1734,7 @@ static void mt_release_contacts(struct hid_device *hid) for (i = 0; i < mt->num_slots; i++) { input_mt_slot(input_dev, i); input_mt_report_slot_inactive(input_dev); + clear_bit(i, &td->mt_io_flags); } input_mt_sync_frame(input_dev); input_sync(input_dev); @@ -1756,7 +1757,7 @@ static void mt_expired_timeout(struct timer_list *t) */ if (test_and_set_bit_lock(MT_IO_FLAGS_RUNNING, &td->mt_io_flags)) return; - if (test_bit(MT_IO_FLAGS_PENDING_SLOTS, &td->mt_io_flags)) + if (td->mt_io_flags & MT_IO_SLOTS_MASK) mt_release_contacts(hdev); clear_bit_unlock(MT_IO_FLAGS_RUNNING, &td->mt_io_flags); } From 39563a86579ae9aec0e6adddae46cfa42a1e1425 Mon Sep 17 00:00:00 2001 From: Yuezhang Mo Date: Tue, 30 Sep 2025 13:42:57 +0800 Subject: [PATCH 047/143] dax: skip read lock assertion for read-only filesystems [ Upstream commit 154d1e7ad9e5ce4b2aaefd3862b3dba545ad978d ] The commit 168316db3583("dax: assert that i_rwsem is held exclusive for writes") added lock assertions to ensure proper locking in DAX operations. However, these assertions trigger false-positive lockdep warnings since read lock is unnecessary on read-only filesystems(e.g., erofs). This patch skips the read lock assertion for read-only filesystems, eliminating the spurious warnings while maintaining the integrity checks for writable filesystems. Fixes: 168316db3583 ("dax: assert that i_rwsem is held exclusive for writes") Signed-off-by: Yuezhang Mo Reviewed-by: Friendy Su Reviewed-by: Daniel Palmer Reviewed-by: Gao Xiang Signed-off-by: Christian Brauner Signed-off-by: Sasha Levin --- fs/dax.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/dax.c b/fs/dax.c index 21b47402b3dca4..756400f2a62573 100644 --- a/fs/dax.c +++ b/fs/dax.c @@ -1578,7 +1578,7 @@ dax_iomap_rw(struct kiocb *iocb, struct iov_iter *iter, if (iov_iter_rw(iter) == WRITE) { lockdep_assert_held_write(&iomi.inode->i_rwsem); iomi.flags |= IOMAP_WRITE; - } else { + } else if (!sb_rdonly(iomi.inode->i_sb)) { lockdep_assert_held(&iomi.inode->i_rwsem); } From b4851ba36459639ae899eed2506e0804e1007954 Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Wed, 6 Aug 2025 17:46:32 +0200 Subject: [PATCH 048/143] can: m_can: m_can_plat_remove(): add missing pm_runtime_disable() [ Upstream commit ba569fb07a7e9e9b71e9282e27e993ba859295c2 ] Commit 227619c3ff7c ("can: m_can: move runtime PM enable/disable to m_can_platform") moved the PM runtime enable from the m_can core driver into the m_can_platform. That patch forgot to move the pm_runtime_disable() to m_can_plat_remove(), so that unloading the m_can_platform driver causes an "Unbalanced pm_runtime_enable!" error message. Add the missing pm_runtime_disable() to m_can_plat_remove() to fix the problem. Cc: Patrik Flykt Fixes: 227619c3ff7c ("can: m_can: move runtime PM enable/disable to m_can_platform") Reviewed-by: Markus Schneider-Pargmann Link: https://patch.msgid.link/20250929-m_can-fix-state-handling-v4-1-682b49b49d9a@pengutronix.de Signed-off-by: Marc Kleine-Budde Signed-off-by: Sasha Levin --- drivers/net/can/m_can/m_can_platform.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/can/m_can/m_can_platform.c b/drivers/net/can/m_can/m_can_platform.c index b832566efda042..057eaa7b8b4b29 100644 --- a/drivers/net/can/m_can/m_can_platform.c +++ b/drivers/net/can/m_can/m_can_platform.c @@ -180,7 +180,7 @@ static void m_can_plat_remove(struct platform_device *pdev) struct m_can_classdev *mcan_class = &priv->cdev; m_can_class_unregister(mcan_class); - + pm_runtime_disable(mcan_class->dev); m_can_class_free_dev(mcan_class->net); } From 4411ca4ca715034dfd2103a946ffb037944e80ac Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Wed, 6 Aug 2025 16:56:15 +0200 Subject: [PATCH 049/143] can: m_can: m_can_handle_state_errors(): fix CAN state transition to Error Active [ Upstream commit 3d9db29b45f970d81acf61cf91a65442efbeb997 ] The CAN Error State is determined by the receive and transmit error counters. The CAN error counters decrease when reception/transmission is successful, so that a status transition back to the Error Active status is possible. This transition is not handled by m_can_handle_state_errors(). Add the missing detection of the Error Active state to m_can_handle_state_errors() and extend the handling of this state in m_can_handle_state_change(). Fixes: e0d1f4816f2a ("can: m_can: add Bosch M_CAN controller support") Fixes: cd0d83eab2e0 ("can: m_can: m_can_handle_state_change(): fix state change") Reviewed-by: Markus Schneider-Pargmann Link: https://patch.msgid.link/20250929-m_can-fix-state-handling-v4-2-682b49b49d9a@pengutronix.de Signed-off-by: Marc Kleine-Budde Stable-dep-of: 4942c42fe184 ("can: m_can: m_can_chip_config(): bring up interface in correct state") Signed-off-by: Sasha Levin --- drivers/net/can/m_can/m_can.c | 53 +++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index dbcf17fb3ef256..8f663db3869486 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -812,6 +812,9 @@ static int m_can_handle_state_change(struct net_device *dev, u32 timestamp = 0; switch (new_state) { + case CAN_STATE_ERROR_ACTIVE: + cdev->can.state = CAN_STATE_ERROR_ACTIVE; + break; case CAN_STATE_ERROR_WARNING: /* error warning state */ cdev->can.can_stats.error_warning++; @@ -841,6 +844,12 @@ static int m_can_handle_state_change(struct net_device *dev, __m_can_get_berr_counter(dev, &bec); switch (new_state) { + case CAN_STATE_ERROR_ACTIVE: + cf->can_id |= CAN_ERR_CRTL | CAN_ERR_CNT; + cf->data[1] = CAN_ERR_CRTL_ACTIVE; + cf->data[6] = bec.txerr; + cf->data[7] = bec.rxerr; + break; case CAN_STATE_ERROR_WARNING: /* error warning state */ cf->can_id |= CAN_ERR_CRTL | CAN_ERR_CNT; @@ -877,30 +886,33 @@ static int m_can_handle_state_change(struct net_device *dev, return 1; } -static int m_can_handle_state_errors(struct net_device *dev, u32 psr) +static enum can_state +m_can_state_get_by_psr(struct m_can_classdev *cdev) { - struct m_can_classdev *cdev = netdev_priv(dev); - int work_done = 0; + u32 reg_psr; - if (psr & PSR_EW && cdev->can.state != CAN_STATE_ERROR_WARNING) { - netdev_dbg(dev, "entered error warning state\n"); - work_done += m_can_handle_state_change(dev, - CAN_STATE_ERROR_WARNING); - } + reg_psr = m_can_read(cdev, M_CAN_PSR); - if (psr & PSR_EP && cdev->can.state != CAN_STATE_ERROR_PASSIVE) { - netdev_dbg(dev, "entered error passive state\n"); - work_done += m_can_handle_state_change(dev, - CAN_STATE_ERROR_PASSIVE); - } + if (reg_psr & PSR_BO) + return CAN_STATE_BUS_OFF; + if (reg_psr & PSR_EP) + return CAN_STATE_ERROR_PASSIVE; + if (reg_psr & PSR_EW) + return CAN_STATE_ERROR_WARNING; - if (psr & PSR_BO && cdev->can.state != CAN_STATE_BUS_OFF) { - netdev_dbg(dev, "entered error bus off state\n"); - work_done += m_can_handle_state_change(dev, - CAN_STATE_BUS_OFF); - } + return CAN_STATE_ERROR_ACTIVE; +} - return work_done; +static int m_can_handle_state_errors(struct net_device *dev) +{ + struct m_can_classdev *cdev = netdev_priv(dev); + enum can_state new_state; + + new_state = m_can_state_get_by_psr(cdev); + if (new_state == cdev->can.state) + return 0; + + return m_can_handle_state_change(dev, new_state); } static void m_can_handle_other_err(struct net_device *dev, u32 irqstatus) @@ -1031,8 +1043,7 @@ static int m_can_rx_handler(struct net_device *dev, int quota, u32 irqstatus) } if (irqstatus & IR_ERR_STATE) - work_done += m_can_handle_state_errors(dev, - m_can_read(cdev, M_CAN_PSR)); + work_done += m_can_handle_state_errors(dev); if (irqstatus & IR_ERR_BUS_30X) work_done += m_can_handle_bus_errors(dev, irqstatus, From df689d75c46d5ac64c057191eeb328c9dfd4adc0 Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Wed, 6 Aug 2025 18:24:12 +0200 Subject: [PATCH 050/143] can: m_can: m_can_chip_config(): bring up interface in correct state [ Upstream commit 4942c42fe1849e6d68dfb5b36ccba344a9fac016 ] In some SoCs (observed on the STM32MP15) the M_CAN IP core keeps the CAN state and CAN error counters over an internal reset cycle. An external reset is not always possible, due to the shared reset with the other CAN core. This caused the core not always be in Error Active state when bringing up the controller. Instead of always setting the CAN state to Error Active in m_can_chip_config(), fix this by reading and decoding the Protocol Status Regitser (PSR) and set the CAN state accordingly. Fixes: e0d1f4816f2a ("can: m_can: add Bosch M_CAN controller support") Reviewed-by: Markus Schneider-Pargmann Link: https://patch.msgid.link/20250929-m_can-fix-state-handling-v4-3-682b49b49d9a@pengutronix.de Signed-off-by: Marc Kleine-Budde Signed-off-by: Sasha Levin --- drivers/net/can/m_can/m_can.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index 8f663db3869486..a7e326faca8cac 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -1617,7 +1617,7 @@ static int m_can_start(struct net_device *dev) netdev_queue_set_dql_min_limit(netdev_get_tx_queue(cdev->net, 0), cdev->tx_max_coalesced_frames); - cdev->can.state = CAN_STATE_ERROR_ACTIVE; + cdev->can.state = m_can_state_get_by_psr(cdev); m_can_enable_all_interrupts(cdev); From 6219594f665f21cd0317edc2348e069eda404fd1 Mon Sep 17 00:00:00 2001 From: Sean Nyekjaer Date: Fri, 22 Nov 2024 15:52:22 +0100 Subject: [PATCH 051/143] can: m_can: add deinit callback [ Upstream commit baa8aaf79768b72eb7a181c476ca0291613f59e6 ] This is added in preparation for calling standby mode in the tcan4x5x driver or other users of m_can. For the tcan4x5x; If Vsup 12V, standby mode will save 7-8mA, when the interface is down. Signed-off-by: Sean Nyekjaer Link: https://patch.msgid.link/20241122-tcan-standby-v3-1-90bafaf5eccd@geanix.com Signed-off-by: Marc Kleine-Budde Stable-dep-of: a9e30a22d6f2 ("can: m_can: fix CAN state in system PM") Signed-off-by: Sasha Levin --- drivers/net/can/m_can/m_can.c | 7 +++++++ drivers/net/can/m_can/m_can.h | 1 + 2 files changed, 8 insertions(+) diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index a7e326faca8cac..249263fca748d5 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -1796,6 +1796,13 @@ static void m_can_stop(struct net_device *dev) /* set the state as STOPPED */ cdev->can.state = CAN_STATE_STOPPED; + + if (cdev->ops->deinit) { + ret = cdev->ops->deinit(cdev); + if (ret) + netdev_err(dev, "failed to deinitialize: %pe\n", + ERR_PTR(ret)); + } } static int m_can_close(struct net_device *dev) diff --git a/drivers/net/can/m_can/m_can.h b/drivers/net/can/m_can/m_can.h index ef39e8e527ab67..bd4746c63af3f0 100644 --- a/drivers/net/can/m_can/m_can.h +++ b/drivers/net/can/m_can/m_can.h @@ -68,6 +68,7 @@ struct m_can_ops { int (*write_fifo)(struct m_can_classdev *cdev, int addr_offset, const void *val, size_t val_count); int (*init)(struct m_can_classdev *cdev); + int (*deinit)(struct m_can_classdev *cdev); }; struct m_can_tx_op { From b7f989b93836fbc4fe602e10dceaea6d60deaade Mon Sep 17 00:00:00 2001 From: Sean Nyekjaer Date: Fri, 22 Nov 2024 15:52:24 +0100 Subject: [PATCH 052/143] can: m_can: call deinit/init callback when going into suspend/resume [ Upstream commit ad1ddb3bfb0c9193eb19d4788192904350c7e51a ] m_can user like the tcan4x5x device, can go into standby mode. Low power RX mode is enabled to allow wake on can. Signed-off-by: Sean Nyekjaer Link: https://patch.msgid.link/20241122-tcan-standby-v3-3-90bafaf5eccd@geanix.com Signed-off-by: Marc Kleine-Budde Stable-dep-of: a9e30a22d6f2 ("can: m_can: fix CAN state in system PM") Signed-off-by: Sasha Levin --- drivers/net/can/m_can/m_can.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index 249263fca748d5..bf7996c3024260 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -2485,6 +2485,7 @@ int m_can_class_suspend(struct device *dev) { struct m_can_classdev *cdev = dev_get_drvdata(dev); struct net_device *ndev = cdev->net; + int ret = 0; if (netif_running(ndev)) { netif_stop_queue(ndev); @@ -2497,6 +2498,9 @@ int m_can_class_suspend(struct device *dev) if (cdev->pm_wake_source) { hrtimer_cancel(&cdev->hrtimer); m_can_write(cdev, M_CAN_IE, IR_RF0N); + + if (cdev->ops->deinit) + ret = cdev->ops->deinit(cdev); } else { m_can_stop(ndev); } @@ -2508,7 +2512,7 @@ int m_can_class_suspend(struct device *dev) cdev->can.state = CAN_STATE_SLEEPING; - return 0; + return ret; } EXPORT_SYMBOL_GPL(m_can_class_suspend); @@ -2516,14 +2520,13 @@ int m_can_class_resume(struct device *dev) { struct m_can_classdev *cdev = dev_get_drvdata(dev); struct net_device *ndev = cdev->net; + int ret = 0; pinctrl_pm_select_default_state(dev); cdev->can.state = CAN_STATE_ERROR_ACTIVE; if (netif_running(ndev)) { - int ret; - ret = m_can_clk_start(cdev); if (ret) return ret; @@ -2536,6 +2539,10 @@ int m_can_class_resume(struct device *dev) * again. */ cdev->active_interrupts |= IR_RF0N | IR_TEFN; + + if (cdev->ops->init) + ret = cdev->ops->init(cdev); + m_can_write(cdev, M_CAN_IE, cdev->active_interrupts); } else { ret = m_can_start(ndev); @@ -2549,7 +2556,7 @@ int m_can_class_resume(struct device *dev) netif_start_queue(ndev); } - return 0; + return ret; } EXPORT_SYMBOL_GPL(m_can_class_resume); From 7ed47a3207f59e3d9aec34323eea57020a9ef1c8 Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Tue, 12 Aug 2025 16:58:31 +0200 Subject: [PATCH 053/143] can: m_can: fix CAN state in system PM [ Upstream commit a9e30a22d6f23a2684c248871cad4c3061181639 ] A suspend/resume cycle on a down interface results in the interface coming up in Error Active state. A suspend/resume cycle on an Up interface will always result in Error Active state, regardless of the actual CAN state. During suspend, only set running interfaces to CAN_STATE_SLEEPING. During resume only touch the CAN state of running interfaces. For wakeup sources, set the CAN state depending on the Protocol Status Regitser (PSR), for non wakeup source interfaces m_can_start() will do the same. Fixes: e0d1f4816f2a ("can: m_can: add Bosch M_CAN controller support") Reviewed-by: Markus Schneider-Pargmann Link: https://patch.msgid.link/20250929-m_can-fix-state-handling-v4-4-682b49b49d9a@pengutronix.de Signed-off-by: Marc Kleine-Budde Signed-off-by: Sasha Levin --- drivers/net/can/m_can/m_can.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index bf7996c3024260..f31a91ec7a6d08 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -2506,12 +2506,11 @@ int m_can_class_suspend(struct device *dev) } m_can_clk_stop(cdev); + cdev->can.state = CAN_STATE_SLEEPING; } pinctrl_pm_select_sleep_state(dev); - cdev->can.state = CAN_STATE_SLEEPING; - return ret; } EXPORT_SYMBOL_GPL(m_can_class_suspend); @@ -2524,8 +2523,6 @@ int m_can_class_resume(struct device *dev) pinctrl_pm_select_default_state(dev); - cdev->can.state = CAN_STATE_ERROR_ACTIVE; - if (netif_running(ndev)) { ret = m_can_clk_start(cdev); if (ret) @@ -2543,6 +2540,8 @@ int m_can_class_resume(struct device *dev) if (cdev->ops->init) ret = cdev->ops->init(cdev); + cdev->can.state = m_can_state_get_by_psr(cdev); + m_can_write(cdev, M_CAN_IE, cdev->active_interrupts); } else { ret = m_can_start(ndev); From 824be3d3437f2880cebf4a38829b97f341d39e60 Mon Sep 17 00:00:00 2001 From: Yeounsu Moon Date: Fri, 10 Oct 2025 00:57:16 +0900 Subject: [PATCH 054/143] net: dlink: handle dma_map_single() failure properly [ Upstream commit 65946eac6d888d50ae527c4e5c237dbe5cc3a2f2 ] There is no error handling for `dma_map_single()` failures. Add error handling by checking `dma_mapping_error()` and freeing the `skb` using `dev_kfree_skb()` (process context) when it fails. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Yeounsu Moon Tested-on: D-Link DGE-550T Rev-A3 Suggested-by: Simon Horman Reviewed-by: Simon Horman Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/ethernet/dlink/dl2k.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/dlink/dl2k.c b/drivers/net/ethernet/dlink/dl2k.c index 92856cf387c76f..7c9658a4ec4b5f 100644 --- a/drivers/net/ethernet/dlink/dl2k.c +++ b/drivers/net/ethernet/dlink/dl2k.c @@ -498,25 +498,34 @@ static int alloc_list(struct net_device *dev) for (i = 0; i < RX_RING_SIZE; i++) { /* Allocated fixed size of skbuff */ struct sk_buff *skb; + dma_addr_t addr; skb = netdev_alloc_skb_ip_align(dev, np->rx_buf_sz); np->rx_skbuff[i] = skb; - if (!skb) { - free_list(dev); - return -ENOMEM; - } + if (!skb) + goto err_free_list; + + addr = dma_map_single(&np->pdev->dev, skb->data, + np->rx_buf_sz, DMA_FROM_DEVICE); + if (dma_mapping_error(&np->pdev->dev, addr)) + goto err_kfree_skb; np->rx_ring[i].next_desc = cpu_to_le64(np->rx_ring_dma + ((i + 1) % RX_RING_SIZE) * sizeof(struct netdev_desc)); /* Rubicon now supports 40 bits of addressing space. */ - np->rx_ring[i].fraginfo = - cpu_to_le64(dma_map_single(&np->pdev->dev, skb->data, - np->rx_buf_sz, DMA_FROM_DEVICE)); + np->rx_ring[i].fraginfo = cpu_to_le64(addr); np->rx_ring[i].fraginfo |= cpu_to_le64((u64)np->rx_buf_sz << 48); } return 0; + +err_kfree_skb: + dev_kfree_skb(np->rx_skbuff[i]); + np->rx_skbuff[i] = NULL; +err_free_list: + free_list(dev); + return -ENOMEM; } static void rio_hw_init(struct net_device *dev) From 34143a23fca850d98c2ea3f4eef875d70ca66add Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Fri, 10 Oct 2025 16:18:59 +0200 Subject: [PATCH 055/143] doc: fix seg6_flowlabel path [ Upstream commit 0b4b77eff5f8cd9be062783a1c1e198d46d0a753 ] This sysctl is not per interface; it's global per netns. Fixes: 292ecd9f5a94 ("doc: move seg6_flowlabel to seg6-sysctl.rst") Reported-by: Philippe Guibert Signed-off-by: Nicolas Dichtel Reviewed-by: Simon Horman Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- Documentation/networking/seg6-sysctl.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/networking/seg6-sysctl.rst b/Documentation/networking/seg6-sysctl.rst index 07c20e470bafe6..1b6af4779be114 100644 --- a/Documentation/networking/seg6-sysctl.rst +++ b/Documentation/networking/seg6-sysctl.rst @@ -25,6 +25,9 @@ seg6_require_hmac - INTEGER Default is 0. +/proc/sys/net/ipv6/seg6_* variables: +==================================== + seg6_flowlabel - INTEGER Controls the behaviour of computing the flowlabel of outer IPv6 header in case of SR T.encaps From 599f9faabaee67eb30a9f918641b4928cee10cd6 Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Thu, 9 Oct 2025 20:25:49 +0800 Subject: [PATCH 056/143] r8169: fix packet truncation after S4 resume on RTL8168H/RTL8111H [ Upstream commit 70f92ab97042f243e1c8da1c457ff56b9b3e49f1 ] After resume from S4 (hibernate), RTL8168H/RTL8111H truncates incoming packets. Packet captures show messages like "IP truncated-ip - 146 bytes missing!". The issue is caused by RxConfig not being properly re-initialized after resume. Re-initializing the RxConfig register before the chip re-initialization sequence avoids the truncation and restores correct packet reception. This follows the same pattern as commit ef9da46ddef0 ("r8169: fix data corruption issue on RTL8402"). Fixes: 6e1d0b898818 ("r8169:add support for RTL8168H and RTL8107E") Signed-off-by: Linmao Li Reviewed-by: Jacob Keller Reviewed-by: Heiner Kallweit Link: https://patch.msgid.link/20251009122549.3955845-1-lilinmao@kylinos.cn Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/realtek/r8169_main.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c index 7b82779e4cd5d2..80b5262d0d5727 100644 --- a/drivers/net/ethernet/realtek/r8169_main.c +++ b/drivers/net/ethernet/realtek/r8169_main.c @@ -5060,8 +5060,9 @@ static int rtl8169_resume(struct device *device) if (!device_may_wakeup(tp_to_dev(tp))) clk_prepare_enable(tp->clk); - /* Reportedly at least Asus X453MA truncates packets otherwise */ - if (tp->mac_version == RTL_GIGA_MAC_VER_37) + /* Some chip versions may truncate packets without this initialization */ + if (tp->mac_version == RTL_GIGA_MAC_VER_37 || + tp->mac_version == RTL_GIGA_MAC_VER_46) rtl_init_rxcfg(tp); return rtl8169_runtime_resume(device); From eeb4345488672584db4f8c20a1ae13a212ce31c4 Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Thu, 9 Oct 2025 16:02:19 +0100 Subject: [PATCH 057/143] net/ip6_tunnel: Prevent perpetual tunnel growth [ Upstream commit 21f4d45eba0b2dcae5dbc9e5e0ad08735c993f16 ] Similarly to ipv4 tunnel, ipv6 version updates dev->needed_headroom, too. While ipv4 tunnel headroom adjustment growth was limited in commit 5ae1e9922bbd ("net: ip_tunnel: prevent perpetual headroom growth"), ipv6 tunnel yet increases the headroom without any ceiling. Reflect ipv4 tunnel headroom adjustment limit on ipv6 version. Credits to Francesco Ruggeri, who was originally debugging this issue and wrote local Arista-specific patch and a reproducer. Fixes: 8eb30be0352d ("ipv6: Create ip6_tnl_xmit") Cc: Florian Westphal Cc: Francesco Ruggeri Signed-off-by: Dmitry Safonov Link: https://patch.msgid.link/20251009-ip6_tunnel-headroom-v2-1-8e4dbd8f7e35@arista.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- include/net/ip_tunnels.h | 15 +++++++++++++++ net/ipv4/ip_tunnel.c | 14 -------------- net/ipv6/ip6_tunnel.c | 3 +-- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h index ae83a969ae64b2..01fcf952b05de9 100644 --- a/include/net/ip_tunnels.h +++ b/include/net/ip_tunnels.h @@ -605,6 +605,21 @@ struct metadata_dst *iptunnel_metadata_reply(struct metadata_dst *md, int skb_tunnel_check_pmtu(struct sk_buff *skb, struct dst_entry *encap_dst, int headroom, bool reply); +static inline void ip_tunnel_adj_headroom(struct net_device *dev, + unsigned int headroom) +{ + /* we must cap headroom to some upperlimit, else pskb_expand_head + * will overflow header offsets in skb_headers_offset_update(). + */ + const unsigned int max_allowed = 512; + + if (headroom > max_allowed) + headroom = max_allowed; + + if (headroom > READ_ONCE(dev->needed_headroom)) + WRITE_ONCE(dev->needed_headroom, headroom); +} + int iptunnel_handle_offloads(struct sk_buff *skb, int gso_type_mask); static inline int iptunnel_pull_offloads(struct sk_buff *skb) diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index 09b73acf037ae2..7c77d06372d195 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c @@ -567,20 +567,6 @@ static int tnl_update_pmtu(struct net_device *dev, struct sk_buff *skb, return 0; } -static void ip_tunnel_adj_headroom(struct net_device *dev, unsigned int headroom) -{ - /* we must cap headroom to some upperlimit, else pskb_expand_head - * will overflow header offsets in skb_headers_offset_update(). - */ - static const unsigned int max_allowed = 512; - - if (headroom > max_allowed) - headroom = max_allowed; - - if (headroom > READ_ONCE(dev->needed_headroom)) - WRITE_ONCE(dev->needed_headroom, headroom); -} - void ip_md_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, u8 proto, int tunnel_hlen) { diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 5350c9bb2319bf..b72ca103490686 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -1257,8 +1257,7 @@ int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield, */ max_headroom = LL_RESERVED_SPACE(dst->dev) + sizeof(struct ipv6hdr) + dst->header_len + t->hlen; - if (max_headroom > READ_ONCE(dev->needed_headroom)) - WRITE_ONCE(dev->needed_headroom, max_headroom); + ip_tunnel_adj_headroom(dev, max_headroom); err = ip6_tnl_encap(skb, t, &proto, fl6); if (err) From 4f4af833c7ee74bddb4b521505b02f1ad064db75 Mon Sep 17 00:00:00 2001 From: Raju Rangoju Date: Fri, 10 Oct 2025 12:21:42 +0530 Subject: [PATCH 058/143] amd-xgbe: Avoid spurious link down messages during interface toggle [ Upstream commit 2616222e423398bb374ffcb5d23dea4ba2c3e524 ] During interface toggle operations (ifdown/ifup), the driver currently resets the local helper variable 'phy_link' to -1. This causes the link state machine to incorrectly interpret the state as a link change event, resulting in spurious "Link is down" messages being logged when the interface is brought back up. Preserve the phy_link state across interface toggles to avoid treating the -1 sentinel value as a legitimate link state transition. Fixes: 88131a812b16 ("amd-xgbe: Perform phy connect/disconnect at dev open/stop") Signed-off-by: Raju Rangoju Reviewed-by: Dawid Osuchowski Link: https://patch.msgid.link/20251010065142.1189310-1-Raju.Rangoju@amd.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/amd/xgbe/xgbe-drv.c | 1 - drivers/net/ethernet/amd/xgbe/xgbe-mdio.c | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index 8bc49259d71af1..32a6d52614242c 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -1172,7 +1172,6 @@ static void xgbe_free_rx_data(struct xgbe_prv_data *pdata) static int xgbe_phy_reset(struct xgbe_prv_data *pdata) { - pdata->phy_link = -1; pdata->phy_speed = SPEED_UNKNOWN; return pdata->phy_if.phy_reset(pdata); diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c index ed76a8df6ec6ed..75e9cb3fc7aa66 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c @@ -1664,6 +1664,7 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata) pdata->phy.duplex = DUPLEX_FULL; } + pdata->phy_link = 0; pdata->phy.link = 0; pdata->phy.pause_autoneg = pdata->pause_autoneg; From 814ec62e42f42c9be399511514139407a6814f15 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sat, 11 Oct 2025 11:57:42 +0000 Subject: [PATCH 059/143] tcp: fix tcp_tso_should_defer() vs large RTT [ Upstream commit 295ce1eb36ae47dc862d6c8a1012618a25516208 ] Neal reported that using neper tcp_stream with TCP_TX_DELAY set to 50ms would often lead to flows stuck in a small cwnd mode, regardless of the congestion control. While tcp_stream sets TCP_TX_DELAY too late after the connect(), it highlighted two kernel bugs. The following heuristic in tcp_tso_should_defer() seems wrong for large RTT: delta = tp->tcp_clock_cache - head->tstamp; /* If next ACK is likely to come too late (half srtt), do not defer */ if ((s64)(delta - (u64)NSEC_PER_USEC * (tp->srtt_us >> 4)) < 0) goto send_now; If next ACK is expected to come in more than 1 ms, we should not defer because we prefer a smooth ACK clocking. While blamed commit was a step in the good direction, it was not generic enough. Another patch fixing TCP_TX_DELAY for established flows will be proposed when net-next reopens. Fixes: 50c8339e9299 ("tcp: tso: restore IW10 after TSO autosizing") Reported-by: Neal Cardwell Signed-off-by: Eric Dumazet Reviewed-by: Neal Cardwell Tested-by: Neal Cardwell Link: https://patch.msgid.link/20251011115742.1245771-1-edumazet@google.com [pabeni@redhat.com: fixed whitespace issue] Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/ipv4/tcp_output.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 6d5387811c32ad..5e37dc45639db1 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2217,7 +2217,8 @@ static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb, u32 max_segs) { const struct inet_connection_sock *icsk = inet_csk(sk); - u32 send_win, cong_win, limit, in_flight; + u32 send_win, cong_win, limit, in_flight, threshold; + u64 srtt_in_ns, expected_ack, how_far_is_the_ack; struct tcp_sock *tp = tcp_sk(sk); struct sk_buff *head; int win_divisor; @@ -2279,9 +2280,19 @@ static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb, head = tcp_rtx_queue_head(sk); if (!head) goto send_now; - delta = tp->tcp_clock_cache - head->tstamp; - /* If next ACK is likely to come too late (half srtt), do not defer */ - if ((s64)(delta - (u64)NSEC_PER_USEC * (tp->srtt_us >> 4)) < 0) + + srtt_in_ns = (u64)(NSEC_PER_USEC >> 3) * tp->srtt_us; + /* When is the ACK expected ? */ + expected_ack = head->tstamp + srtt_in_ns; + /* How far from now is the ACK expected ? */ + how_far_is_the_ack = expected_ack - tp->tcp_clock_cache; + + /* If next ACK is likely to come too late, + * ie in more than min(1ms, half srtt), do not defer. + */ + threshold = min(srtt_in_ns >> 1, NSEC_PER_MSEC); + + if ((s64)(how_far_is_the_ack - threshold) > 0) goto send_now; /* Ok, it looks like it is advisable to defer. From 4602b8cee1481dbb896182e5cb1e8cf12910e9e7 Mon Sep 17 00:00:00 2001 From: Marios Makassikis Date: Wed, 15 Oct 2025 09:25:46 +0200 Subject: [PATCH 060/143] ksmbd: fix recursive locking in RPC handle list access [ Upstream commit 88f170814fea74911ceab798a43cbd7c5599bed4 ] Since commit 305853cce3794 ("ksmbd: Fix race condition in RPC handle list access"), ksmbd_session_rpc_method() attempts to lock sess->rpc_lock. This causes hung connections / tasks when a client attempts to open a named pipe. Using Samba's rpcclient tool: $ rpcclient //192.168.1.254 -U user%password $ rpcclient $> srvinfo Kernel side: "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. task:kworker/0:0 state:D stack:0 pid:5021 tgid:5021 ppid:2 flags:0x00200000 Workqueue: ksmbd-io handle_ksmbd_work Call trace: __schedule from schedule+0x3c/0x58 schedule from schedule_preempt_disabled+0xc/0x10 schedule_preempt_disabled from rwsem_down_read_slowpath+0x1b0/0x1d8 rwsem_down_read_slowpath from down_read+0x28/0x30 down_read from ksmbd_session_rpc_method+0x18/0x3c ksmbd_session_rpc_method from ksmbd_rpc_open+0x34/0x68 ksmbd_rpc_open from ksmbd_session_rpc_open+0x194/0x228 ksmbd_session_rpc_open from create_smb2_pipe+0x8c/0x2c8 create_smb2_pipe from smb2_open+0x10c/0x27ac smb2_open from handle_ksmbd_work+0x238/0x3dc handle_ksmbd_work from process_scheduled_works+0x160/0x25c process_scheduled_works from worker_thread+0x16c/0x1e8 worker_thread from kthread+0xa8/0xb8 kthread from ret_from_fork+0x14/0x38 Exception stack(0x8529ffb0 to 0x8529fff8) The task deadlocks because the lock is already held: ksmbd_session_rpc_open down_write(&sess->rpc_lock) ksmbd_rpc_open ksmbd_session_rpc_method down_read(&sess->rpc_lock) <-- deadlock Adjust ksmbd_session_rpc_method() callers to take the lock when necessary. Fixes: 305853cce3794 ("ksmbd: Fix race condition in RPC handle list access") Signed-off-by: Marios Makassikis Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/smb/server/mgmt/user_session.c | 7 ++----- fs/smb/server/smb2pdu.c | 9 ++++++++- fs/smb/server/transport_ipc.c | 12 ++++++++++++ 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/fs/smb/server/mgmt/user_session.c b/fs/smb/server/mgmt/user_session.c index b36d0676dbe584..00805aed0b07d9 100644 --- a/fs/smb/server/mgmt/user_session.c +++ b/fs/smb/server/mgmt/user_session.c @@ -147,14 +147,11 @@ void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id) int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id) { struct ksmbd_session_rpc *entry; - int method; - down_read(&sess->rpc_lock); + lockdep_assert_held(&sess->rpc_lock); entry = xa_load(&sess->rpc_handle_list, id); - method = entry ? entry->method : 0; - up_read(&sess->rpc_lock); - return method; + return entry ? entry->method : 0; } void ksmbd_session_destroy(struct ksmbd_session *sess) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index d2182477566a64..796235cb956771 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -4623,8 +4623,15 @@ static int smb2_get_info_file_pipe(struct ksmbd_session *sess, * pipe without opening it, checking error condition here */ id = req->VolatileFileId; - if (!ksmbd_session_rpc_method(sess, id)) + + lockdep_assert_not_held(&sess->rpc_lock); + + down_read(&sess->rpc_lock); + if (!ksmbd_session_rpc_method(sess, id)) { + up_read(&sess->rpc_lock); return -ENOENT; + } + up_read(&sess->rpc_lock); ksmbd_debug(SMB, "FileInfoClass %u, FileId 0x%llx\n", req->FileInfoClass, req->VolatileFileId); diff --git a/fs/smb/server/transport_ipc.c b/fs/smb/server/transport_ipc.c index 4454bbe3c7107d..816f136ce5c4e1 100644 --- a/fs/smb/server/transport_ipc.c +++ b/fs/smb/server/transport_ipc.c @@ -825,6 +825,9 @@ struct ksmbd_rpc_command *ksmbd_rpc_write(struct ksmbd_session *sess, int handle if (!msg) return NULL; + lockdep_assert_not_held(&sess->rpc_lock); + + down_read(&sess->rpc_lock); msg->type = KSMBD_EVENT_RPC_REQUEST; req = (struct ksmbd_rpc_command *)msg->payload; req->handle = handle; @@ -833,6 +836,7 @@ struct ksmbd_rpc_command *ksmbd_rpc_write(struct ksmbd_session *sess, int handle req->flags |= KSMBD_RPC_WRITE_METHOD; req->payload_sz = payload_sz; memcpy(req->payload, payload, payload_sz); + up_read(&sess->rpc_lock); resp = ipc_msg_send_request(msg, req->handle); ipc_msg_free(msg); @@ -849,6 +853,9 @@ struct ksmbd_rpc_command *ksmbd_rpc_read(struct ksmbd_session *sess, int handle) if (!msg) return NULL; + lockdep_assert_not_held(&sess->rpc_lock); + + down_read(&sess->rpc_lock); msg->type = KSMBD_EVENT_RPC_REQUEST; req = (struct ksmbd_rpc_command *)msg->payload; req->handle = handle; @@ -856,6 +863,7 @@ struct ksmbd_rpc_command *ksmbd_rpc_read(struct ksmbd_session *sess, int handle) req->flags |= rpc_context_flags(sess); req->flags |= KSMBD_RPC_READ_METHOD; req->payload_sz = 0; + up_read(&sess->rpc_lock); resp = ipc_msg_send_request(msg, req->handle); ipc_msg_free(msg); @@ -876,6 +884,9 @@ struct ksmbd_rpc_command *ksmbd_rpc_ioctl(struct ksmbd_session *sess, int handle if (!msg) return NULL; + lockdep_assert_not_held(&sess->rpc_lock); + + down_read(&sess->rpc_lock); msg->type = KSMBD_EVENT_RPC_REQUEST; req = (struct ksmbd_rpc_command *)msg->payload; req->handle = handle; @@ -884,6 +895,7 @@ struct ksmbd_rpc_command *ksmbd_rpc_ioctl(struct ksmbd_session *sess, int handle req->flags |= KSMBD_RPC_IOCTL_METHOD; req->payload_sz = payload_sz; memcpy(req->payload, payload, payload_sz); + up_read(&sess->rpc_lock); resp = ipc_msg_send_request(msg, req->handle); ipc_msg_free(msg); From 49683288a77c9f611c55b82f7bc49b06b523fbe4 Mon Sep 17 00:00:00 2001 From: Alexey Simakov Date: Tue, 14 Oct 2025 19:47:38 +0300 Subject: [PATCH 061/143] tg3: prevent use of uninitialized remote_adv and local_adv variables [ Upstream commit 0c3f2e62815a43628e748b1e4ad97a1c46cce703 ] Some execution paths that jump to the fiber_setup_done label could leave the remote_adv and local_adv variables uninitialized and then use it. Initialize this variables at the point of definition to avoid this. Fixes: 85730a631f0c ("tg3: Add SGMII phy support for 5719/5718 serdes") Co-developed-by: Alexandr Sapozhnikov Signed-off-by: Alexandr Sapozhnikov Signed-off-by: Alexey Simakov Reviewed-by: Pavan Chebbi Link: https://patch.msgid.link/20251014164736.5890-1-bigalex934@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/broadcom/tg3.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 717e110d23c914..dc170feee8ad7c 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -5803,7 +5803,7 @@ static int tg3_setup_fiber_mii_phy(struct tg3 *tp, bool force_reset) u32 current_speed = SPEED_UNKNOWN; u8 current_duplex = DUPLEX_UNKNOWN; bool current_link_up = false; - u32 local_adv, remote_adv, sgsr; + u32 local_adv = 0, remote_adv = 0, sgsr; if ((tg3_asic_rev(tp) == ASIC_REV_5719 || tg3_asic_rev(tp) == ASIC_REV_5720) && @@ -5944,9 +5944,6 @@ static int tg3_setup_fiber_mii_phy(struct tg3 *tp, bool force_reset) else current_duplex = DUPLEX_HALF; - local_adv = 0; - remote_adv = 0; - if (bmcr & BMCR_ANENABLE) { u32 common; From b1cf131f6df85dfb8c11272b6198389cdeaf63f0 Mon Sep 17 00:00:00 2001 From: Sabrina Dubroca Date: Tue, 14 Oct 2025 11:16:56 +0200 Subject: [PATCH 062/143] tls: trim encrypted message to match the plaintext on short splice [ Upstream commit ce5af41e3234425a40974696682163edfd21128c ] During tls_sw_sendmsg_locked, we pre-allocate the encrypted message for the size we're expecting to send during the current iteration, but we may end up sending less, for example when splicing: if we're getting the data from small fragments of memory, we may fill up all the slots in the skmsg with less data than expected. In this case, we need to trim the encrypted message to only the length we actually need, to avoid pushing uninitialized bytes down the underlying TCP socket. Fixes: fe1e81d4f73b ("tls/sw: Support MSG_SPLICE_PAGES") Reported-by: Jann Horn Signed-off-by: Sabrina Dubroca Link: https://patch.msgid.link/66a0ae99c9efc15f88e9e56c1f58f902f442ce86.1760432043.git.sd@queasysnail.net Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/tls/tls_sw.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index f46550b96061ea..aac685daf66c4c 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -1112,8 +1112,11 @@ static int tls_sw_sendmsg_locked(struct sock *sk, struct msghdr *msg, goto send_end; tls_ctx->pending_open_record_frags = true; - if (sk_msg_full(msg_pl)) + if (sk_msg_full(msg_pl)) { full_record = true; + sk_msg_trim(sk, msg_en, + msg_pl->sg.size + prot->overhead_size); + } if (full_record || eor) goto copied; From 0e2e8c4d0c372ad27490fdef3bcbaf27bd1c1e19 Mon Sep 17 00:00:00 2001 From: Sabrina Dubroca Date: Tue, 14 Oct 2025 11:16:57 +0200 Subject: [PATCH 063/143] tls: wait for async encrypt in case of error during latter iterations of sendmsg [ Upstream commit b014a4e066c555185b7c367efacdc33f16695495 ] If we hit an error during the main loop of tls_sw_sendmsg_locked (eg failed allocation), we jump to send_end and immediately return. Previous iterations may have queued async encryption requests that are still pending. We should wait for those before returning, as we could otherwise be reading from memory that userspace believes we're not using anymore, which would be a sort of use-after-free. This is similar to what tls_sw_recvmsg already does: failures during the main loop jump to the "wait for async" code, not straight to the unlock/return. Fixes: a42055e8d2c3 ("net/tls: Add support for async encryption of records for performance") Reported-by: Jann Horn Signed-off-by: Sabrina Dubroca Link: https://patch.msgid.link/c793efe9673b87f808d84fdefc0f732217030c52.1760432043.git.sd@queasysnail.net Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/tls/tls_sw.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index aac685daf66c4c..dc5a7e24d7b770 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -1054,7 +1054,7 @@ static int tls_sw_sendmsg_locked(struct sock *sk, struct msghdr *msg, if (ret == -EINPROGRESS) num_async++; else if (ret != -EAGAIN) - goto send_end; + goto end; } } @@ -1226,8 +1226,9 @@ static int tls_sw_sendmsg_locked(struct sock *sk, struct msghdr *msg, goto alloc_encrypted; } +send_end: if (!num_async) { - goto send_end; + goto end; } else if (num_zc || eor) { int err; @@ -1245,7 +1246,7 @@ static int tls_sw_sendmsg_locked(struct sock *sk, struct msghdr *msg, tls_tx_records(sk, msg->msg_flags); } -send_end: +end: ret = sk_stream_error(sk, msg->msg_flags, ret); return copied > 0 ? copied : ret; } From bea15cd6f1e2a4ce7bb103d079879d1cbc1bc5bc Mon Sep 17 00:00:00 2001 From: Sabrina Dubroca Date: Tue, 14 Oct 2025 11:16:58 +0200 Subject: [PATCH 064/143] tls: always set record_type in tls_process_cmsg [ Upstream commit b6fe4c29bb51cf239ecf48eacf72b924565cb619 ] When userspace wants to send a non-DATA record (via the TLS_SET_RECORD_TYPE cmsg), we need to send any pending data from a previous MSG_MORE send() as a separate DATA record. If that DATA record is encrypted asynchronously, tls_handle_open_record will return -EINPROGRESS. This is currently treated as an error by tls_process_cmsg, and it will skip setting record_type to the correct value, but the caller (tls_sw_sendmsg_locked) handles that return value correctly and proceeds with sending the new message with an incorrect record_type (DATA instead of whatever was requested in the cmsg). Always set record_type before handling the open record. If tls_handle_open_record returns an error, record_type will be ignored. If it succeeds, whether with synchronous crypto (returning 0) or asynchronous (returning -EINPROGRESS), the caller will proceed correctly. Fixes: a42055e8d2c3 ("net/tls: Add support for async encryption of records for performance") Reported-by: Jann Horn Signed-off-by: Sabrina Dubroca Link: https://patch.msgid.link/0457252e578a10a94e40c72ba6288b3a64f31662.1760432043.git.sd@queasysnail.net Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/tls/tls_main.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/net/tls/tls_main.c b/net/tls/tls_main.c index 0acf313deb01ff..e52e4c91c90911 100644 --- a/net/tls/tls_main.c +++ b/net/tls/tls_main.c @@ -255,12 +255,9 @@ int tls_process_cmsg(struct sock *sk, struct msghdr *msg, if (msg->msg_flags & MSG_MORE) return -EINVAL; - rc = tls_handle_open_record(sk, msg->msg_flags); - if (rc) - return rc; - *record_type = *(unsigned char *)CMSG_DATA(cmsg); - rc = 0; + + rc = tls_handle_open_record(sk, msg->msg_flags); break; default: return -EINVAL; From 39dec4ea3daf77f684308576baf483b55ca7f160 Mon Sep 17 00:00:00 2001 From: Sabrina Dubroca Date: Tue, 14 Oct 2025 11:16:59 +0200 Subject: [PATCH 065/143] tls: wait for pending async decryptions if tls_strp_msg_hold fails [ Upstream commit b8a6ff84abbcbbc445463de58704686011edc8e1 ] Async decryption calls tls_strp_msg_hold to create a clone of the input skb to hold references to the memory it uses. If we fail to allocate that clone, proceeding with async decryption can lead to various issues (UAF on the skb, writing into userspace memory after the recv() call has returned). In this case, wait for all pending decryption requests. Fixes: 84c61fe1a75b ("tls: rx: do not use the standard strparser") Reported-by: Jann Horn Signed-off-by: Sabrina Dubroca Link: https://patch.msgid.link/b9fe61dcc07dab15da9b35cf4c7d86382a98caf2.1760432043.git.sd@queasysnail.net Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/tls/tls_sw.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index dc5a7e24d7b770..bebf0dd3b95fa4 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -1637,8 +1637,10 @@ static int tls_decrypt_sg(struct sock *sk, struct iov_iter *out_iov, if (unlikely(darg->async)) { err = tls_strp_msg_hold(&ctx->strp, &ctx->async_hold); - if (err) - __skb_queue_tail(&ctx->async_hold, darg->skb); + if (err) { + err = tls_decrypt_async_wait(ctx); + darg->async = false; + } return err; } From bbcf2da067aebde915bd8e6997189d63a1e167d8 Mon Sep 17 00:00:00 2001 From: Sabrina Dubroca Date: Tue, 14 Oct 2025 11:17:00 +0200 Subject: [PATCH 066/143] tls: don't rely on tx_work during send() [ Upstream commit 7f846c65ca11e63d2409868ff039081f80e42ae4 ] With async crypto, we rely on tx_work to actually transmit records once encryption completes. But while send() is running, both the tx_lock and socket lock are held, so tx_work_handler cannot process the queue of encrypted records, and simply reschedules itself. During a large send(), this could last a long time, and use a lot of memory. Transmit any pending encrypted records before restarting the main loop of tls_sw_sendmsg_locked. Fixes: a42055e8d2c3 ("net/tls: Add support for async encryption of records for performance") Reported-by: Jann Horn Signed-off-by: Sabrina Dubroca Link: https://patch.msgid.link/8396631478f70454b44afb98352237d33f48d34d.1760432043.git.sd@queasysnail.net Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/tls/tls_sw.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index bebf0dd3b95fa4..1ff0d01bdadf0d 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -1152,6 +1152,13 @@ static int tls_sw_sendmsg_locked(struct sock *sk, struct msghdr *msg, } else if (ret != -EAGAIN) goto send_end; } + + /* Transmit if any encryptions have completed */ + if (test_and_clear_bit(BIT_TX_SCHEDULED, &ctx->tx_bitmask)) { + cancel_delayed_work(&ctx->tx_work.work); + tls_tx_records(sk, msg->msg_flags); + } + continue; rollback_iter: copied -= try_to_copy; @@ -1207,6 +1214,12 @@ static int tls_sw_sendmsg_locked(struct sock *sk, struct msghdr *msg, goto send_end; } } + + /* Transmit if any encryptions have completed */ + if (test_and_clear_bit(BIT_TX_SCHEDULED, &ctx->tx_bitmask)) { + cancel_delayed_work(&ctx->tx_work.work); + tls_tx_records(sk, msg->msg_flags); + } } continue; From 95af085073221d13b9ed15cceb269fea8a578fb5 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Tue, 14 Oct 2025 02:17:25 -0700 Subject: [PATCH 067/143] netdevsim: set the carrier when the device goes up [ Upstream commit 1a8fed52f7be14e45785e8e54d0d0b50fc17dbd8 ] Bringing a linked netdevsim device down and then up causes communication failure because both interfaces lack carrier. Basically a ifdown/ifup on the interface make the link broken. Commit 3762ec05a9fbda ("netdevsim: add NAPI support") added supported for NAPI, calling netif_carrier_off() in nsim_stop(). This patch re-enables the carrier symmetrically on nsim_open(), in case the device is linked and the peer is up. Signed-off-by: Breno Leitao Fixes: 3762ec05a9fbda ("netdevsim: add NAPI support") Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/20251014-netdevsim_fix-v2-1-53b40590dae1@debian.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/netdevsim/netdev.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c index ee2a7b2f6268de..e2d92295ad33b2 100644 --- a/drivers/net/netdevsim/netdev.c +++ b/drivers/net/netdevsim/netdev.c @@ -433,6 +433,7 @@ static void nsim_enable_napi(struct netdevsim *ns) static int nsim_open(struct net_device *dev) { struct netdevsim *ns = netdev_priv(dev); + struct netdevsim *peer; int err; err = nsim_init_napi(ns); @@ -441,6 +442,12 @@ static int nsim_open(struct net_device *dev) nsim_enable_napi(ns); + peer = rtnl_dereference(ns->peer); + if (peer && netif_running(peer->netdev)) { + netif_carrier_on(dev); + netif_carrier_on(peer->netdev); + } + return 0; } From f30f0062f609906f967f445d45baf1e3b3d25b1b Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Mon, 9 Dec 2024 14:07:42 +0100 Subject: [PATCH 068/143] net: usb: lan78xx: Add error handling to lan78xx_init_mac_address [ Upstream commit 6f31135894ec96481e2bda93a1da70712f5e57c1 ] Convert `lan78xx_init_mac_address` to return error codes and handle failures in register read and write operations. Update `lan78xx_reset` to check for errors during MAC address initialization and propagate them appropriately. Signed-off-by: Oleksij Rempel Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/20241209130751.703182-3-o.rempel@pengutronix.de Signed-off-by: Jakub Kicinski Stable-dep-of: 8d93ff40d49d ("net: usb: lan78xx: fix use of improperly initialized dev->chipid in lan78xx_reset") Signed-off-by: Sasha Levin --- drivers/net/usb/lan78xx.c | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index 2f8637224b69e3..6babe909036cfb 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -1920,13 +1920,19 @@ static const struct ethtool_ops lan78xx_ethtool_ops = { .get_regs = lan78xx_get_regs, }; -static void lan78xx_init_mac_address(struct lan78xx_net *dev) +static int lan78xx_init_mac_address(struct lan78xx_net *dev) { u32 addr_lo, addr_hi; u8 addr[6]; + int ret; + + ret = lan78xx_read_reg(dev, RX_ADDRL, &addr_lo); + if (ret < 0) + return ret; - lan78xx_read_reg(dev, RX_ADDRL, &addr_lo); - lan78xx_read_reg(dev, RX_ADDRH, &addr_hi); + ret = lan78xx_read_reg(dev, RX_ADDRH, &addr_hi); + if (ret < 0) + return ret; addr[0] = addr_lo & 0xFF; addr[1] = (addr_lo >> 8) & 0xFF; @@ -1959,14 +1965,26 @@ static void lan78xx_init_mac_address(struct lan78xx_net *dev) (addr[2] << 16) | (addr[3] << 24); addr_hi = addr[4] | (addr[5] << 8); - lan78xx_write_reg(dev, RX_ADDRL, addr_lo); - lan78xx_write_reg(dev, RX_ADDRH, addr_hi); + ret = lan78xx_write_reg(dev, RX_ADDRL, addr_lo); + if (ret < 0) + return ret; + + ret = lan78xx_write_reg(dev, RX_ADDRH, addr_hi); + if (ret < 0) + return ret; } - lan78xx_write_reg(dev, MAF_LO(0), addr_lo); - lan78xx_write_reg(dev, MAF_HI(0), addr_hi | MAF_HI_VALID_); + ret = lan78xx_write_reg(dev, MAF_LO(0), addr_lo); + if (ret < 0) + return ret; + + ret = lan78xx_write_reg(dev, MAF_HI(0), addr_hi | MAF_HI_VALID_); + if (ret < 0) + return ret; eth_hw_addr_set(dev->net, addr); + + return 0; } /* MDIO read and write wrappers for phylib */ @@ -2905,7 +2923,9 @@ static int lan78xx_reset(struct lan78xx_net *dev) } } while (buf & HW_CFG_LRST_); - lan78xx_init_mac_address(dev); + ret = lan78xx_init_mac_address(dev); + if (ret < 0) + return ret; /* save DEVID for later usage */ ret = lan78xx_read_reg(dev, ID_REV, &buf); From d8a3a530d8b325ffaab1649eb13daa2e20af6b48 Mon Sep 17 00:00:00 2001 From: I Viswanath Date: Mon, 13 Oct 2025 23:46:48 +0530 Subject: [PATCH 069/143] net: usb: lan78xx: fix use of improperly initialized dev->chipid in lan78xx_reset [ Upstream commit 8d93ff40d49d70e05c82a74beae31f883fe0eaf8 ] dev->chipid is used in lan78xx_init_mac_address before it's initialized: lan78xx_reset() { lan78xx_init_mac_address() lan78xx_read_eeprom() lan78xx_read_raw_eeprom() <- dev->chipid is used here dev->chipid = ... <- dev->chipid is initialized correctly here } Reorder initialization so that dev->chipid is set before calling lan78xx_init_mac_address(). Fixes: a0db7d10b76e ("lan78xx: Add to handle mux control per chip id") Signed-off-by: I Viswanath Reviewed-by: Vadim Fedorenko Reviewed-by: Khalid Aziz Link: https://patch.msgid.link/20251013181648.35153-1-viswanathiyyappan@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/usb/lan78xx.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index 6babe909036cfb..2da67814556f99 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -2923,10 +2923,6 @@ static int lan78xx_reset(struct lan78xx_net *dev) } } while (buf & HW_CFG_LRST_); - ret = lan78xx_init_mac_address(dev); - if (ret < 0) - return ret; - /* save DEVID for later usage */ ret = lan78xx_read_reg(dev, ID_REV, &buf); if (ret < 0) @@ -2935,6 +2931,10 @@ static int lan78xx_reset(struct lan78xx_net *dev) dev->chipid = (buf & ID_REV_CHIP_ID_MASK_) >> 16; dev->chiprev = buf & ID_REV_CHIP_REV_MASK_; + ret = lan78xx_init_mac_address(dev); + if (ret < 0) + return ret; + /* Respond to the IN token with a NAK */ ret = lan78xx_read_reg(dev, USB_CFG0, &buf); if (ret < 0) From 5a833099033dd8702be5c73c432a4da7c5c156d8 Mon Sep 17 00:00:00 2001 From: Ketil Johnsen Date: Wed, 8 Oct 2025 12:51:11 +0200 Subject: [PATCH 070/143] drm/panthor: Ensure MCU is disabled on suspend [ Upstream commit e07e10ae83bdf429f59c8c149173a8c4f29c481e ] Currently the Panthor driver needs the GPU to be powered down between suspend and resume. If this is not done, then the MCU_CONTROL register will be preserved as AUTO, which again will cause a premature FW boot on resume. The FW will go directly into fatal state in this case. This case needs to be handled as there is no guarantee that the GPU will be powered down after the suspend callback on all platforms. The fix is to call panthor_fw_stop() in "pre-reset" path to ensure the MCU_CONTROL register is cleared (set DISABLE). This matches well with the already existing call to panthor_fw_start() from the "post-reset" path. Signed-off-by: Ketil Johnsen Acked-by: Boris Brezillon Reviewed-by: Steven Price Fixes: 2718d91816ee ("drm/panthor: Add the FW logical block") Signed-off-by: Steven Price Link: https://lore.kernel.org/r/20251008105112.4077015-1-ketil.johnsen@arm.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/panthor/panthor_fw.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c index 4e2d3a02ea0689..cdd6e1c08cebdb 100644 --- a/drivers/gpu/drm/panthor/panthor_fw.c +++ b/drivers/gpu/drm/panthor/panthor_fw.c @@ -1057,6 +1057,7 @@ void panthor_fw_pre_reset(struct panthor_device *ptdev, bool on_hang) } panthor_job_irq_suspend(&ptdev->fw->irq); + panthor_fw_stop(ptdev); } /** From 3fc87107f03666348eef80a5120166d3ea930601 Mon Sep 17 00:00:00 2001 From: Amit Chaudhary Date: Fri, 26 Sep 2025 12:08:22 -0700 Subject: [PATCH 071/143] nvme-multipath: Skip nr_active increments in RETRY disposition [ Upstream commit bb642e2d300ee27dcede65cda7ffc47a7047bd69 ] For queue-depth I/O policy, this patch fixes unbalanced I/Os across nvme multipaths. Issue Description: The RETRY disposition incorrectly increments ns->ctrl->nr_active counter and reinitializes iostat start-time. In such cases nr_active counter never goes back to zero until that path disconnects and reconnects. Such a path is not chosen for new I/Os if multiple RETRY cases on a given a path cause its queue-depth counter to be artificially higher compared to other paths. This leads to unbalanced I/Os across paths. The patch skips incrementing nr_active if NVME_MPATH_CNT_ACTIVE is already set. And it skips restarting io stats if NVME_MPATH_IO_STATS is already set. base-commit: e989a3da2d371a4b6597ee8dee5c72e407b4db7a Fixes: d4d957b53d91eeb ("nvme-multipath: support io stats on the mpath device") Signed-off-by: Amit Chaudhary Reviewed-by: Randy Jennings Signed-off-by: Keith Busch Signed-off-by: Sasha Levin --- drivers/nvme/host/multipath.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c index 561dd08022c061..24cff8b0449236 100644 --- a/drivers/nvme/host/multipath.c +++ b/drivers/nvme/host/multipath.c @@ -131,12 +131,14 @@ void nvme_mpath_start_request(struct request *rq) struct nvme_ns *ns = rq->q->queuedata; struct gendisk *disk = ns->head->disk; - if (READ_ONCE(ns->head->subsys->iopolicy) == NVME_IOPOLICY_QD) { + if ((READ_ONCE(ns->head->subsys->iopolicy) == NVME_IOPOLICY_QD) && + !(nvme_req(rq)->flags & NVME_MPATH_CNT_ACTIVE)) { atomic_inc(&ns->ctrl->nr_active); nvme_req(rq)->flags |= NVME_MPATH_CNT_ACTIVE; } - if (!blk_queue_io_stat(disk->queue) || blk_rq_is_passthrough(rq)) + if (!blk_queue_io_stat(disk->queue) || blk_rq_is_passthrough(rq) || + (nvme_req(rq)->flags & NVME_MPATH_IO_STATS)) return; nvme_req(rq)->flags |= NVME_MPATH_IO_STATS; From d694f809df41166587974e0dc932ef7a6e3af2e3 Mon Sep 17 00:00:00 2001 From: Fabian Vogt Date: Wed, 10 Sep 2025 17:25:13 +0200 Subject: [PATCH 072/143] riscv: kprobes: Fix probe address validation [ Upstream commit 9e68bd803fac49274fde914466fd3b07c4d602c8 ] When adding a kprobe such as "p:probe/tcp_sendmsg _text+15392192", arch_check_kprobe would start iterating all instructions starting from _text until the probed address. Not only is this very inefficient, but literal values in there (e.g. left by function patching) are misinterpreted in a way that causes a desync. Fix this by doing it like x86: start the iteration at the closest preceding symbol instead of the given starting point. Fixes: 87f48c7ccc73 ("riscv: kprobe: Fixup kernel panic when probing an illegal position") Signed-off-by: Fabian Vogt Signed-off-by: Marvin Friedrich Acked-by: Guo Ren Link: https://lore.kernel.org/r/6191817.lOV4Wx5bFT@fvogt-thinkpad Signed-off-by: Paul Walmsley Signed-off-by: Sasha Levin --- arch/riscv/kernel/probes/kprobes.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/arch/riscv/kernel/probes/kprobes.c b/arch/riscv/kernel/probes/kprobes.c index d2dacea1aedd9e..0daba93c1a81e2 100644 --- a/arch/riscv/kernel/probes/kprobes.c +++ b/arch/riscv/kernel/probes/kprobes.c @@ -49,10 +49,15 @@ static void __kprobes arch_simulate_insn(struct kprobe *p, struct pt_regs *regs) post_kprobe_handler(p, kcb, regs); } -static bool __kprobes arch_check_kprobe(struct kprobe *p) +static bool __kprobes arch_check_kprobe(unsigned long addr) { - unsigned long tmp = (unsigned long)p->addr - p->offset; - unsigned long addr = (unsigned long)p->addr; + unsigned long tmp, offset; + + /* start iterating at the closest preceding symbol */ + if (!kallsyms_lookup_size_offset(addr, NULL, &offset)) + return false; + + tmp = addr - offset; while (tmp <= addr) { if (tmp == addr) @@ -71,7 +76,7 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p) if ((unsigned long)insn & 0x1) return -EILSEQ; - if (!arch_check_kprobe(p)) + if (!arch_check_kprobe((unsigned long)p->addr)) return -EILSEQ; /* copy instruction */ From e2a7c66261fe94929d931ece69ae441997ee0421 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sat, 11 Oct 2025 12:59:53 +0200 Subject: [PATCH 073/143] drm/bridge: lt9211: Drop check for last nibble of version register [ Upstream commit db74b04edce1bc86b9a5acc724c7ca06f427ab60 ] There is now a new LT9211 rev. U5, which reports chip ID 0x18 0x01 0xe4 . The previous LT9211 reported chip ID 0x18 0x01 0xe3 , which is what the driver checks for right now. Since there is a possibility there will be yet another revision of the LT9211 in the future, drop the last version nibble check to allow all future revisions of the chip to work with this driver. This fix makes LT9211 rev. U5 work with this driver. Fixes: 8ce4129e3de4 ("drm/bridge: lt9211: Add Lontium LT9211 bridge driver") Signed-off-by: Marek Vasut Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20251011110017.12521-1-marek.vasut@mailbox.org Signed-off-by: Dmitry Baryshkov Signed-off-by: Sasha Levin --- drivers/gpu/drm/bridge/lontium-lt9211.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/bridge/lontium-lt9211.c b/drivers/gpu/drm/bridge/lontium-lt9211.c index c8881796fba4c6..4014375f06ea13 100644 --- a/drivers/gpu/drm/bridge/lontium-lt9211.c +++ b/drivers/gpu/drm/bridge/lontium-lt9211.c @@ -120,8 +120,7 @@ static int lt9211_read_chipid(struct lt9211 *ctx) } /* Test for known Chip ID. */ - if (chipid[0] != REG_CHIPID0_VALUE || chipid[1] != REG_CHIPID1_VALUE || - chipid[2] != REG_CHIPID2_VALUE) { + if (chipid[0] != REG_CHIPID0_VALUE || chipid[1] != REG_CHIPID1_VALUE) { dev_err(ctx->dev, "Unknown Chip ID: 0x%02x 0x%02x 0x%02x\n", chipid[0], chipid[1], chipid[2]); return -EINVAL; From 70a65e2893a7b64cd8847f409784c6188437eca0 Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Thu, 9 Oct 2025 19:03:13 +0200 Subject: [PATCH 074/143] ASoC: codecs: Fix gain setting ranges for Renesas IDT821034 codec [ Upstream commit 6370a996f308ea3276030769b7482b346e7cc7c1 ] The gain ranges specified in Renesas IDT821034 codec documentation are [-3dB;+13dB] in the transmit path (ADC) and [-13dB;+3dB] in the receive path (DAC). Allthough the registers allow programming values outside those ranges, the signal S/N and distorsion are only guaranteed in the specified ranges. Set ranges to the specified ones. Fixes: e51166990e81 ("ASoC: codecs: Add support for the Renesas IDT821034 codec") Signed-off-by: Christophe Leroy Link: https://patch.msgid.link/2bd547194f3398e6182f770d7d6be711c702b4b2.1760029099.git.christophe.leroy@csgroup.eu Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/idt821034.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/idt821034.c b/sound/soc/codecs/idt821034.c index cb7a68c799f8fc..401d0897b8ab45 100644 --- a/sound/soc/codecs/idt821034.c +++ b/sound/soc/codecs/idt821034.c @@ -548,14 +548,14 @@ static int idt821034_kctrl_mute_put(struct snd_kcontrol *kcontrol, return ret; } -static const DECLARE_TLV_DB_LINEAR(idt821034_gain_in, -6520, 1306); -#define IDT821034_GAIN_IN_MIN_RAW 1 /* -65.20 dB -> 10^(-65.2/20.0) * 1820 = 1 */ -#define IDT821034_GAIN_IN_MAX_RAW 8191 /* 13.06 dB -> 10^(13.06/20.0) * 1820 = 8191 */ +static const DECLARE_TLV_DB_LINEAR(idt821034_gain_in, -300, 1300); +#define IDT821034_GAIN_IN_MIN_RAW 1288 /* -3.0 dB -> 10^(-3.0/20.0) * 1820 = 1288 */ +#define IDT821034_GAIN_IN_MAX_RAW 8130 /* 13.0 dB -> 10^(13.0/20.0) * 1820 = 8130 */ #define IDT821034_GAIN_IN_INIT_RAW 1820 /* 0dB -> 10^(0/20) * 1820 = 1820 */ -static const DECLARE_TLV_DB_LINEAR(idt821034_gain_out, -6798, 1029); -#define IDT821034_GAIN_OUT_MIN_RAW 1 /* -67.98 dB -> 10^(-67.98/20.0) * 2506 = 1*/ -#define IDT821034_GAIN_OUT_MAX_RAW 8191 /* 10.29 dB -> 10^(10.29/20.0) * 2506 = 8191 */ +static const DECLARE_TLV_DB_LINEAR(idt821034_gain_out, -1300, 300); +#define IDT821034_GAIN_OUT_MIN_RAW 561 /* -13.0 dB -> 10^(-13.0/20.0) * 2506 = 561 */ +#define IDT821034_GAIN_OUT_MAX_RAW 3540 /* 3.0 dB -> 10^(3.0/20.0) * 2506 = 3540 */ #define IDT821034_GAIN_OUT_INIT_RAW 2506 /* 0dB -> 10^(0/20) * 2506 = 2506 */ static const struct snd_kcontrol_new idt821034_controls[] = { From 8ed3d6cf03cb864e8c6c91b396e82cfff30e1494 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Fri, 3 Oct 2025 21:03:23 +0300 Subject: [PATCH 075/143] ASoC: nau8821: Cancel jdet_work before handling jack ejection [ Upstream commit 6e54919cb541fdf1063b16f3254c28d01bc9e5ff ] The microphone detection work scheduled by a prior jack insertion interrupt may still be in a pending state or under execution when a jack ejection interrupt has been fired. This might lead to a racing condition or nau8821_jdet_work() completing after nau8821_eject_jack(), which will override the currently disconnected state of the jack and incorrectly report the headphone or the headset as being connected. Cancel any pending jdet_work or wait for its execution to finish before attempting to handle the ejection interrupt. Proceed similarly before launching the eject handler as a consequence of detecting an invalid insert interrupt. Fixes: aab1ad11d69f ("ASoC: nau8821: new driver") Signed-off-by: Cristian Ciocaltea Link: https://patch.msgid.link/20251003-nau8821-jdet-fixes-v1-1-f7b0e2543f09@collabora.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/nau8821.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/nau8821.c b/sound/soc/codecs/nau8821.c index de5c4db05c8f8b..23ee515db9bdd6 100644 --- a/sound/soc/codecs/nau8821.c +++ b/sound/soc/codecs/nau8821.c @@ -1186,6 +1186,7 @@ static irqreturn_t nau8821_interrupt(int irq, void *data) if ((active_irq & NAU8821_JACK_EJECT_IRQ_MASK) == NAU8821_JACK_EJECT_DETECTED) { + cancel_work_sync(&nau8821->jdet_work); regmap_update_bits(regmap, NAU8821_R71_ANALOG_ADC_1, NAU8821_MICDET_MASK, NAU8821_MICDET_DIS); nau8821_eject_jack(nau8821); @@ -1200,11 +1201,11 @@ static irqreturn_t nau8821_interrupt(int irq, void *data) clear_irq = NAU8821_KEY_RELEASE_IRQ; } else if ((active_irq & NAU8821_JACK_INSERT_IRQ_MASK) == NAU8821_JACK_INSERT_DETECTED) { + cancel_work_sync(&nau8821->jdet_work); regmap_update_bits(regmap, NAU8821_R71_ANALOG_ADC_1, NAU8821_MICDET_MASK, NAU8821_MICDET_EN); if (nau8821_is_jack_inserted(regmap)) { /* detect microphone and jack type */ - cancel_work_sync(&nau8821->jdet_work); schedule_work(&nau8821->jdet_work); /* Turn off insertion interruption at manual mode */ regmap_update_bits(regmap, From 129cef0e37d45317e014207c77709d07fdcf6cea Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Fri, 3 Oct 2025 21:03:24 +0300 Subject: [PATCH 076/143] ASoC: nau8821: Generalize helper to clear IRQ status [ Upstream commit 9273aa85b35cc02d0953a1ba3b7bd694e5a2c10e ] Instead of adding yet another utility function for dealing with the interrupt clearing register, generalize nau8821_int_status_clear_all() by renaming it to nau8821_irq_status_clear(), whilst introducing a second parameter to allow restricting the operation scope to a single interrupt instead of the whole range of active IRQs. While at it, also fix a spelling typo in the comment block. Note this is mainly a prerequisite for subsequent patches aiming to address some deficiencies in the implementation of the interrupt handler. Thus the presence of the Fixes tag below is intentional, to facilitate backporting. Fixes: aab1ad11d69f ("ASoC: nau8821: new driver") Signed-off-by: Cristian Ciocaltea Link: https://patch.msgid.link/20251003-nau8821-jdet-fixes-v1-2-f7b0e2543f09@collabora.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/nau8821.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/nau8821.c b/sound/soc/codecs/nau8821.c index 23ee515db9bdd6..56e769446eb307 100644 --- a/sound/soc/codecs/nau8821.c +++ b/sound/soc/codecs/nau8821.c @@ -1022,12 +1022,17 @@ static bool nau8821_is_jack_inserted(struct regmap *regmap) return active_high == is_high; } -static void nau8821_int_status_clear_all(struct regmap *regmap) +static void nau8821_irq_status_clear(struct regmap *regmap, int active_irq) { - int active_irq, clear_irq, i; + int clear_irq, i; - /* Reset the intrruption status from rightmost bit if the corres- - * ponding irq event occurs. + if (active_irq) { + regmap_write(regmap, NAU8821_R11_INT_CLR_KEY_STATUS, active_irq); + return; + } + + /* Reset the interruption status from rightmost bit if the + * corresponding irq event occurs. */ regmap_read(regmap, NAU8821_R10_IRQ_STATUS, &active_irq); for (i = 0; i < NAU8821_REG_DATA_LEN; i++) { @@ -1054,7 +1059,7 @@ static void nau8821_eject_jack(struct nau8821 *nau8821) snd_soc_dapm_sync(dapm); /* Clear all interruption status */ - nau8821_int_status_clear_all(regmap); + nau8821_irq_status_clear(regmap, 0); /* Enable the insertion interruption, disable the ejection inter- * ruption, and then bypass de-bounce circuit. @@ -1523,7 +1528,7 @@ static int nau8821_resume_setup(struct nau8821 *nau8821) nau8821_configure_sysclk(nau8821, NAU8821_CLK_DIS, 0); if (nau8821->irq) { /* Clear all interruption status */ - nau8821_int_status_clear_all(regmap); + nau8821_irq_status_clear(regmap, 0); /* Enable both insertion and ejection interruptions, and then * bypass de-bounce circuit. From af66058d13f025a69585c1f711b9f7c22aa08bf8 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Fri, 3 Oct 2025 21:03:26 +0300 Subject: [PATCH 077/143] ASoC: nau8821: Add DMI quirk to bypass jack debounce circuit [ Upstream commit 2b4eda7bf7d8a4e2f7575a98f55d8336dec0f302 ] Stress testing the audio jack hotplug handling on a few Steam Deck units revealed that the debounce circuit is responsible for having a negative impact on the detection reliability, e.g. in some cases the ejection interrupt is not fired, while in other instances it goes into a kind of invalid state and generates a flood of misleading interrupts. Add new entries to the DMI table introduced via commit 1bc40efdaf4a ("ASoC: nau8821: Add DMI quirk mechanism for active-high jack-detect") and extend the quirk logic to allow bypassing the debounce circuit used for jack detection on Valve Steam Deck LCD and OLED models. While at it, rename existing NAU8821_JD_ACTIVE_HIGH quirk bitfield to NAU8821_QUIRK_JD_ACTIVE_HIGH. This should help improve code readability by differentiating from similarly named register bits. Fixes: aab1ad11d69f ("ASoC: nau8821: new driver") Signed-off-by: Cristian Ciocaltea Link: https://patch.msgid.link/20251003-nau8821-jdet-fixes-v1-4-f7b0e2543f09@collabora.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/nau8821.c | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/nau8821.c b/sound/soc/codecs/nau8821.c index 56e769446eb307..bfb719ca4c2cf2 100644 --- a/sound/soc/codecs/nau8821.c +++ b/sound/soc/codecs/nau8821.c @@ -26,7 +26,8 @@ #include #include "nau8821.h" -#define NAU8821_JD_ACTIVE_HIGH BIT(0) +#define NAU8821_QUIRK_JD_ACTIVE_HIGH BIT(0) +#define NAU8821_QUIRK_JD_DB_BYPASS BIT(1) static int nau8821_quirk; static int quirk_override = -1; @@ -1166,9 +1167,10 @@ static void nau8821_setup_inserted_irq(struct nau8821 *nau8821) regmap_update_bits(regmap, NAU8821_R1D_I2S_PCM_CTRL2, NAU8821_I2S_MS_MASK, NAU8821_I2S_MS_SLAVE); - /* Not bypass de-bounce circuit */ - regmap_update_bits(regmap, NAU8821_R0D_JACK_DET_CTRL, - NAU8821_JACK_DET_DB_BYPASS, 0); + /* Do not bypass de-bounce circuit */ + if (!(nau8821_quirk & NAU8821_QUIRK_JD_DB_BYPASS)) + regmap_update_bits(regmap, NAU8821_R0D_JACK_DET_CTRL, + NAU8821_JACK_DET_DB_BYPASS, 0); regmap_update_bits(regmap, NAU8821_R0F_INTERRUPT_MASK, NAU8821_IRQ_EJECT_EN, 0); @@ -1863,7 +1865,23 @@ static const struct dmi_system_id nau8821_quirk_table[] = { DMI_MATCH(DMI_SYS_VENDOR, "Positivo Tecnologia SA"), DMI_MATCH(DMI_BOARD_NAME, "CW14Q01P-V2"), }, - .driver_data = (void *)(NAU8821_JD_ACTIVE_HIGH), + .driver_data = (void *)(NAU8821_QUIRK_JD_ACTIVE_HIGH), + }, + { + /* Valve Steam Deck LCD */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Valve"), + DMI_MATCH(DMI_PRODUCT_NAME, "Jupiter"), + }, + .driver_data = (void *)(NAU8821_QUIRK_JD_DB_BYPASS), + }, + { + /* Valve Steam Deck OLED */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Valve"), + DMI_MATCH(DMI_PRODUCT_NAME, "Galileo"), + }, + .driver_data = (void *)(NAU8821_QUIRK_JD_DB_BYPASS), }, {} }; @@ -1905,9 +1923,12 @@ static int nau8821_i2c_probe(struct i2c_client *i2c) nau8821_check_quirks(); - if (nau8821_quirk & NAU8821_JD_ACTIVE_HIGH) + if (nau8821_quirk & NAU8821_QUIRK_JD_ACTIVE_HIGH) nau8821->jkdet_polarity = 0; + if (nau8821_quirk & NAU8821_QUIRK_JD_DB_BYPASS) + dev_dbg(dev, "Force bypassing jack detection debounce circuit\n"); + nau8821_print_device_properties(nau8821); nau8821_reset_chip(nau8821->regmap); From 657e8f9f7489a7a8ef5dbb5d66f6612347298cba Mon Sep 17 00:00:00 2001 From: Zhanjun Dong Date: Mon, 29 Sep 2025 11:29:04 -0400 Subject: [PATCH 078/143] drm/i915/guc: Skip communication warning on reset in progress [ Upstream commit 1696b0cfcf004a3af34ffe4c57a14e837ef18144 ] GuC IRQ and tasklet handler receive just single G2H message, and let other messages to be received from next tasklet. During this chained tasklet process, if reset process started, communication will be disabled. Skip warning for this condition. Fixes: 65dd4ed0f4e1 ("drm/i915/guc: Don't receive all G2H messages in irq handler") Closes: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/15018 Signed-off-by: Zhanjun Dong Reviewed-by: Vinay Belgaumkar Signed-off-by: Daniele Ceraolo Spurio Link: https://lore.kernel.org/r/20250929152904.269776-1-zhanjun.dong@intel.com (cherry picked from commit 604b5ee4a653a70979ce689dbd6a5d942eb016bf) Signed-off-by: Rodrigo Vivi Signed-off-by: Sasha Levin --- drivers/gpu/drm/i915/gt/uc/intel_guc_ct.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_ct.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_ct.c index 0d5197c0824a91..5cf3a516ccfb38 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_ct.c +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_ct.c @@ -1324,9 +1324,16 @@ static int ct_receive(struct intel_guc_ct *ct) static void ct_try_receive_message(struct intel_guc_ct *ct) { + struct intel_guc *guc = ct_to_guc(ct); int ret; - if (GEM_WARN_ON(!ct->enabled)) + if (!ct->enabled) { + GEM_WARN_ON(!guc_to_gt(guc)->uc.reset_in_progress); + return; + } + + /* When interrupt disabled, message handling is not expected */ + if (!guc->interrupts.enabled) return; ret = ct_receive(ct); From 90653d924b6bed7455fa2db73065398291647220 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 27 Jun 2025 10:12:36 -0400 Subject: [PATCH 079/143] drm/amdgpu: add ip offset support for cyan skillfish [ Upstream commit e8529dbc75cab56fc3c57830d0fd48cbd8911e6c ] For chips that don't have IP discovery tables. Signed-off-by: Alex Deucher Stable-dep-of: 357d90be2c7a ("drm/amdgpu: fix handling of harvesting for ip_discovery firmware") Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/amdgpu/Makefile | 3 +- .../drm/amd/amdgpu/cyan_skillfish_reg_init.c | 56 +++++++++++++++++++ drivers/gpu/drm/amd/amdgpu/nv.h | 1 + 3 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/amd/amdgpu/cyan_skillfish_reg_init.c diff --git a/drivers/gpu/drm/amd/amdgpu/Makefile b/drivers/gpu/drm/amd/amdgpu/Makefile index c7b18c52825d67..784651269ec551 100644 --- a/drivers/gpu/drm/amd/amdgpu/Makefile +++ b/drivers/gpu/drm/amd/amdgpu/Makefile @@ -83,7 +83,8 @@ amdgpu-y += \ vega20_reg_init.o nbio_v7_4.o nbio_v2_3.o nv.o arct_reg_init.o mxgpu_nv.o \ nbio_v7_2.o hdp_v4_0.o hdp_v5_0.o aldebaran_reg_init.o aldebaran.o soc21.o soc24.o \ sienna_cichlid.o smu_v13_0_10.o nbio_v4_3.o hdp_v6_0.o nbio_v7_7.o hdp_v5_2.o lsdma_v6_0.o \ - nbio_v7_9.o aqua_vanjaram.o nbio_v7_11.o lsdma_v7_0.o hdp_v7_0.o nbif_v6_3_1.o + nbio_v7_9.o aqua_vanjaram.o nbio_v7_11.o lsdma_v7_0.o hdp_v7_0.o nbif_v6_3_1.o \ + cyan_skillfish_reg_init.o # add DF block amdgpu-y += \ diff --git a/drivers/gpu/drm/amd/amdgpu/cyan_skillfish_reg_init.c b/drivers/gpu/drm/amd/amdgpu/cyan_skillfish_reg_init.c new file mode 100644 index 00000000000000..96616a865aac71 --- /dev/null +++ b/drivers/gpu/drm/amd/amdgpu/cyan_skillfish_reg_init.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2018 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include "amdgpu.h" +#include "nv.h" + +#include "soc15_common.h" +#include "soc15_hw_ip.h" +#include "cyan_skillfish_ip_offset.h" + +int cyan_skillfish_reg_base_init(struct amdgpu_device *adev) +{ + /* HW has more IP blocks, only initialized the blocke needed by driver */ + uint32_t i; + + adev->gfx.xcc_mask = 1; + for (i = 0 ; i < MAX_INSTANCE ; ++i) { + adev->reg_offset[GC_HWIP][i] = (uint32_t *)(&(GC_BASE.instance[i])); + adev->reg_offset[HDP_HWIP][i] = (uint32_t *)(&(HDP_BASE.instance[i])); + adev->reg_offset[MMHUB_HWIP][i] = (uint32_t *)(&(MMHUB_BASE.instance[i])); + adev->reg_offset[ATHUB_HWIP][i] = (uint32_t *)(&(ATHUB_BASE.instance[i])); + adev->reg_offset[NBIO_HWIP][i] = (uint32_t *)(&(NBIO_BASE.instance[i])); + adev->reg_offset[MP0_HWIP][i] = (uint32_t *)(&(MP0_BASE.instance[i])); + adev->reg_offset[MP1_HWIP][i] = (uint32_t *)(&(MP1_BASE.instance[i])); + adev->reg_offset[VCN_HWIP][i] = (uint32_t *)(&(UVD0_BASE.instance[i])); + adev->reg_offset[DF_HWIP][i] = (uint32_t *)(&(DF_BASE.instance[i])); + adev->reg_offset[DCE_HWIP][i] = (uint32_t *)(&(DMU_BASE.instance[i])); + adev->reg_offset[OSSSYS_HWIP][i] = (uint32_t *)(&(OSSSYS_BASE.instance[i])); + adev->reg_offset[SDMA0_HWIP][i] = (uint32_t *)(&(GC_BASE.instance[i])); + adev->reg_offset[SDMA1_HWIP][i] = (uint32_t *)(&(GC_BASE.instance[i])); + adev->reg_offset[SMUIO_HWIP][i] = (uint32_t *)(&(SMUIO_BASE.instance[i])); + adev->reg_offset[THM_HWIP][i] = (uint32_t *)(&(THM_BASE.instance[i])); + adev->reg_offset[CLK_HWIP][i] = (uint32_t *)(&(CLK_BASE.instance[i])); + } + return 0; +} diff --git a/drivers/gpu/drm/amd/amdgpu/nv.h b/drivers/gpu/drm/amd/amdgpu/nv.h index 83e9782aef39d6..8f4817404f10d0 100644 --- a/drivers/gpu/drm/amd/amdgpu/nv.h +++ b/drivers/gpu/drm/amd/amdgpu/nv.h @@ -31,5 +31,6 @@ extern const struct amdgpu_ip_block_version nv_common_ip_block; void nv_grbm_select(struct amdgpu_device *adev, u32 me, u32 pipe, u32 queue, u32 vmid); void nv_set_virt_ops(struct amdgpu_device *adev); +int cyan_skillfish_reg_base_init(struct amdgpu_device *adev); #endif From 0a77caacc1d37ff4701d3175dc3fe61dbe659096 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 27 Jun 2025 10:18:46 -0400 Subject: [PATCH 080/143] drm/amdgpu: add support for cyan skillfish without IP discovery [ Upstream commit 9e6a5cf1a23bf575e93544ae05585659063b1c18 ] For platforms without an IP discovery table. Signed-off-by: Alex Deucher Stable-dep-of: 357d90be2c7a ("drm/amdgpu: fix handling of harvesting for ip_discovery firmware") Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c index 6042956cd5c3c1..0f427314b2b481 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c @@ -2644,6 +2644,36 @@ int amdgpu_discovery_set_ip_blocks(struct amdgpu_device *adev) adev->ip_versions[UVD_HWIP][1] = IP_VERSION(2, 6, 0); adev->ip_versions[XGMI_HWIP][0] = IP_VERSION(6, 1, 0); break; + case CHIP_CYAN_SKILLFISH: + if (adev->apu_flags & AMD_APU_IS_CYAN_SKILLFISH2) { + r = amdgpu_discovery_reg_base_init(adev); + if (r) + return -EINVAL; + + amdgpu_discovery_harvest_ip(adev); + amdgpu_discovery_get_gfx_info(adev); + amdgpu_discovery_get_mall_info(adev); + amdgpu_discovery_get_vcn_info(adev); + } else { + cyan_skillfish_reg_base_init(adev); + adev->sdma.num_instances = 2; + adev->ip_versions[MMHUB_HWIP][0] = IP_VERSION(2, 0, 3); + adev->ip_versions[ATHUB_HWIP][0] = IP_VERSION(2, 0, 3); + adev->ip_versions[OSSSYS_HWIP][0] = IP_VERSION(5, 0, 1); + adev->ip_versions[HDP_HWIP][0] = IP_VERSION(5, 0, 1); + adev->ip_versions[SDMA0_HWIP][0] = IP_VERSION(5, 0, 1); + adev->ip_versions[SDMA1_HWIP][1] = IP_VERSION(5, 0, 1); + adev->ip_versions[DF_HWIP][0] = IP_VERSION(3, 5, 0); + adev->ip_versions[NBIO_HWIP][0] = IP_VERSION(2, 1, 1); + adev->ip_versions[UMC_HWIP][0] = IP_VERSION(8, 1, 1); + adev->ip_versions[MP0_HWIP][0] = IP_VERSION(11, 0, 8); + adev->ip_versions[MP1_HWIP][0] = IP_VERSION(11, 0, 8); + adev->ip_versions[THM_HWIP][0] = IP_VERSION(11, 0, 1); + adev->ip_versions[SMUIO_HWIP][0] = IP_VERSION(11, 0, 8); + adev->ip_versions[GC_HWIP][0] = IP_VERSION(10, 1, 3); + adev->ip_versions[UVD_HWIP][0] = IP_VERSION(2, 0, 3); + } + break; default: r = amdgpu_discovery_reg_base_init(adev); if (r) { From 87b634c375098f0fe721924681f0aa80f10a7339 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 26 Sep 2025 17:31:32 -0400 Subject: [PATCH 081/143] drm/amdgpu: fix handling of harvesting for ip_discovery firmware [ Upstream commit 357d90be2c7aaa526a840cddffd2b8d676fe75a6 ] Chips which use the IP discovery firmware loaded by the driver reported incorrect harvesting information in the ip discovery table in sysfs because the driver only uses the ip discovery firmware for populating sysfs and not for direct parsing for the driver itself as such, the fields that are used to print the harvesting info in sysfs report incorrect data for some IPs. Populate the relevant fields for this case as well. Fixes: 514678da56da ("drm/amdgpu/discovery: fix fw based ip discovery") Acked-by: Tom St Denis Reviewed-by: Lijo Lazar Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c index 0f427314b2b481..e00b5e45423472 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c @@ -1016,7 +1016,9 @@ static uint8_t amdgpu_discovery_get_harvest_info(struct amdgpu_device *adev, /* Until a uniform way is figured, get mask based on hwid */ switch (hw_id) { case VCN_HWID: - harvest = ((1 << inst) & adev->vcn.inst_mask) == 0; + /* VCN vs UVD+VCE */ + if (!amdgpu_ip_version(adev, VCE_HWIP, 0)) + harvest = ((1 << inst) & adev->vcn.inst_mask) == 0; break; case DMU_HWID: if (adev->harvest_ip_mask & AMD_HARVEST_IP_DMU_MASK) @@ -2462,7 +2464,9 @@ int amdgpu_discovery_set_ip_blocks(struct amdgpu_device *adev) amdgpu_discovery_init(adev); vega10_reg_base_init(adev); adev->sdma.num_instances = 2; + adev->sdma.sdma_mask = 3; adev->gmc.num_umc = 4; + adev->gfx.xcc_mask = 1; adev->ip_versions[MMHUB_HWIP][0] = IP_VERSION(9, 0, 0); adev->ip_versions[ATHUB_HWIP][0] = IP_VERSION(9, 0, 0); adev->ip_versions[OSSSYS_HWIP][0] = IP_VERSION(4, 0, 0); @@ -2489,7 +2493,9 @@ int amdgpu_discovery_set_ip_blocks(struct amdgpu_device *adev) amdgpu_discovery_init(adev); vega10_reg_base_init(adev); adev->sdma.num_instances = 2; + adev->sdma.sdma_mask = 3; adev->gmc.num_umc = 4; + adev->gfx.xcc_mask = 1; adev->ip_versions[MMHUB_HWIP][0] = IP_VERSION(9, 3, 0); adev->ip_versions[ATHUB_HWIP][0] = IP_VERSION(9, 3, 0); adev->ip_versions[OSSSYS_HWIP][0] = IP_VERSION(4, 0, 1); @@ -2516,8 +2522,10 @@ int amdgpu_discovery_set_ip_blocks(struct amdgpu_device *adev) amdgpu_discovery_init(adev); vega10_reg_base_init(adev); adev->sdma.num_instances = 1; + adev->sdma.sdma_mask = 1; adev->vcn.num_vcn_inst = 1; adev->gmc.num_umc = 2; + adev->gfx.xcc_mask = 1; if (adev->apu_flags & AMD_APU_IS_RAVEN2) { adev->ip_versions[MMHUB_HWIP][0] = IP_VERSION(9, 2, 0); adev->ip_versions[ATHUB_HWIP][0] = IP_VERSION(9, 2, 0); @@ -2560,7 +2568,9 @@ int amdgpu_discovery_set_ip_blocks(struct amdgpu_device *adev) amdgpu_discovery_init(adev); vega20_reg_base_init(adev); adev->sdma.num_instances = 2; + adev->sdma.sdma_mask = 3; adev->gmc.num_umc = 8; + adev->gfx.xcc_mask = 1; adev->ip_versions[MMHUB_HWIP][0] = IP_VERSION(9, 4, 0); adev->ip_versions[ATHUB_HWIP][0] = IP_VERSION(9, 4, 0); adev->ip_versions[OSSSYS_HWIP][0] = IP_VERSION(4, 2, 0); @@ -2588,8 +2598,10 @@ int amdgpu_discovery_set_ip_blocks(struct amdgpu_device *adev) amdgpu_discovery_init(adev); arct_reg_base_init(adev); adev->sdma.num_instances = 8; + adev->sdma.sdma_mask = 0xff; adev->vcn.num_vcn_inst = 2; adev->gmc.num_umc = 8; + adev->gfx.xcc_mask = 1; adev->ip_versions[MMHUB_HWIP][0] = IP_VERSION(9, 4, 1); adev->ip_versions[ATHUB_HWIP][0] = IP_VERSION(9, 4, 1); adev->ip_versions[OSSSYS_HWIP][0] = IP_VERSION(4, 2, 1); @@ -2621,8 +2633,10 @@ int amdgpu_discovery_set_ip_blocks(struct amdgpu_device *adev) amdgpu_discovery_init(adev); aldebaran_reg_base_init(adev); adev->sdma.num_instances = 5; + adev->sdma.sdma_mask = 0x1f; adev->vcn.num_vcn_inst = 2; adev->gmc.num_umc = 4; + adev->gfx.xcc_mask = 1; adev->ip_versions[MMHUB_HWIP][0] = IP_VERSION(9, 4, 2); adev->ip_versions[ATHUB_HWIP][0] = IP_VERSION(9, 4, 2); adev->ip_versions[OSSSYS_HWIP][0] = IP_VERSION(4, 4, 0); @@ -2657,6 +2671,8 @@ int amdgpu_discovery_set_ip_blocks(struct amdgpu_device *adev) } else { cyan_skillfish_reg_base_init(adev); adev->sdma.num_instances = 2; + adev->sdma.sdma_mask = 3; + adev->gfx.xcc_mask = 1; adev->ip_versions[MMHUB_HWIP][0] = IP_VERSION(2, 0, 3); adev->ip_versions[ATHUB_HWIP][0] = IP_VERSION(2, 0, 3); adev->ip_versions[OSSSYS_HWIP][0] = IP_VERSION(5, 0, 1); From e4628ada9b95412b41656e152bbfdd753a1af173 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Mon, 13 Oct 2025 08:06:42 +0200 Subject: [PATCH 082/143] drm/amd/powerplay: Fix CIK shutdown temperature MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 6917112af2ba36c5f19075eb9f2933ffd07e55bf ] Remove extra multiplication. CIK GPUs such as Hawaii appear to use PP_TABLE_V0 in which case the shutdown temperature is hardcoded in smu7_init_dpm_defaults and is already multiplied by 1000. The value was mistakenly multiplied another time by smu7_get_thermal_temperature_range. Fixes: 4ba082572a42 ("drm/amd/powerplay: export the thermal ranges of VI asics (V2)") Closes: https://gitlab.freedesktop.org/drm/amd/-/issues/1676 Reviewed-by: Alex Deucher Signed-off-by: Timur Kristóf Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c index 632a25957477ee..3018e294673a5b 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c @@ -5444,8 +5444,7 @@ static int smu7_get_thermal_temperature_range(struct pp_hwmgr *hwmgr, thermal_data->max = table_info->cac_dtp_table->usSoftwareShutdownTemp * PP_TEMPERATURE_UNITS_PER_CENTIGRADES; else if (hwmgr->pp_table_version == PP_TABLE_V0) - thermal_data->max = data->thermal_temp_setting.temperature_shutdown * - PP_TEMPERATURE_UNITS_PER_CENTIGRADES; + thermal_data->max = data->thermal_temp_setting.temperature_shutdown; thermal_data->sw_ctf_threshold = thermal_data->max; From 33fee60d39b7bdea7167220d49e799701b2f4897 Mon Sep 17 00:00:00 2001 From: Francesco Valla Date: Fri, 3 Oct 2025 12:33:03 +0200 Subject: [PATCH 083/143] drm/draw: fix color truncation in drm_draw_fill24 [ Upstream commit 095232711f23179053ca26bcf046ca121a91a465 ] The color parameter passed to drm_draw_fill24() was truncated to 16 bits, leading to an incorrect color drawn to the target iosys_map. Fix this behavior, widening the parameter to 32 bits. Fixes: 31fa2c1ca0b2 ("drm/panic: Move drawing functions to drm_draw") Signed-off-by: Francesco Valla Reviewed-by: Jocelyn Falempe Link: https://lore.kernel.org/r/20251003-drm_draw_fill24_fix-v1-1-8fb7c1c2a893@valla.it Signed-off-by: Jocelyn Falempe Signed-off-by: Sasha Levin --- drivers/gpu/drm/drm_draw.c | 2 +- drivers/gpu/drm/drm_draw_internal.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/drm_draw.c b/drivers/gpu/drm/drm_draw.c index d41f8ae1c14833..b61ebc5bdd5cef 100644 --- a/drivers/gpu/drm/drm_draw.c +++ b/drivers/gpu/drm/drm_draw.c @@ -125,7 +125,7 @@ EXPORT_SYMBOL(drm_draw_fill16); void drm_draw_fill24(struct iosys_map *dmap, unsigned int dpitch, unsigned int height, unsigned int width, - u16 color) + u32 color) { unsigned int y, x; diff --git a/drivers/gpu/drm/drm_draw_internal.h b/drivers/gpu/drm/drm_draw_internal.h index f121ee7339dc11..20cb404e23ea62 100644 --- a/drivers/gpu/drm/drm_draw_internal.h +++ b/drivers/gpu/drm/drm_draw_internal.h @@ -47,7 +47,7 @@ void drm_draw_fill16(struct iosys_map *dmap, unsigned int dpitch, void drm_draw_fill24(struct iosys_map *dmap, unsigned int dpitch, unsigned int height, unsigned int width, - u16 color); + u32 color); void drm_draw_fill32(struct iosys_map *dmap, unsigned int dpitch, unsigned int height, unsigned int width, From 8fecfa1c17a111eb74cbc9779e3054fb7903edd0 Mon Sep 17 00:00:00 2001 From: Alok Tiwari Date: Sun, 12 Oct 2025 07:20:01 -0700 Subject: [PATCH 084/143] drm/rockchip: vop2: use correct destination rectangle height check [ Upstream commit 7f38a1487555604bc4e210fa7cc9b1bce981c40e ] The vop2_plane_atomic_check() function incorrectly checks drm_rect_width(dest) twice instead of verifying both width and height. Fix the second condition to use drm_rect_height(dest) so that invalid destination rectangles with height < 4 are correctly rejected. Fixes: 604be85547ce ("drm/rockchip: Add VOP2 driver") Signed-off-by: Alok Tiwari Reviewed-by: Andy Yan Signed-off-by: Heiko Stuebner Link: https://lore.kernel.org/r/20251012142005.660727-1-alok.a.tiwari@oracle.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index 5d7df4c3b08c47..a551458ad43404 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -1118,7 +1118,7 @@ static int vop2_plane_atomic_check(struct drm_plane *plane, return format; if (drm_rect_width(src) >> 16 < 4 || drm_rect_height(src) >> 16 < 4 || - drm_rect_width(dest) < 4 || drm_rect_width(dest) < 4) { + drm_rect_width(dest) < 4 || drm_rect_height(dest) < 4) { drm_err(vop2->drm, "Invalid size: %dx%d->%dx%d, min size is 4x4\n", drm_rect_width(src) >> 16, drm_rect_height(src) >> 16, drm_rect_width(dest), drm_rect_height(dest)); From cb4c8439cf6d10ba6ae45a14c108c657d073cfdd Mon Sep 17 00:00:00 2001 From: Vincent Guittot Date: Wed, 8 Oct 2025 15:12:14 +0200 Subject: [PATCH 085/143] sched/fair: Fix pelt lost idle time detection [ Upstream commit 17e3e88ed0b6318fde0d1c14df1a804711cab1b5 ] The check for some lost idle pelt time should be always done when pick_next_task_fair() fails to pick a task and not only when we call it from the fair fast-path. The case happens when the last running task on rq is a RT or DL task. When the latter goes to sleep and the /Sum of util_sum of the rq is at the max value, we don't account the lost of idle time whereas we should. Fixes: 67692435c411 ("sched: Rework pick_next_task() slow-path") Signed-off-by: Vincent Guittot Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Sasha Levin --- kernel/sched/fair.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index b3d9826e25b035..8bdcb5df0d4616 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -9059,21 +9059,21 @@ pick_next_task_fair(struct rq *rq, struct task_struct *prev, struct rq_flags *rf return p; idle: - if (!rf) - return NULL; - - new_tasks = sched_balance_newidle(rq, rf); + if (rf) { + new_tasks = sched_balance_newidle(rq, rf); - /* - * Because sched_balance_newidle() releases (and re-acquires) rq->lock, it is - * possible for any higher priority task to appear. In that case we - * must re-start the pick_next_entity() loop. - */ - if (new_tasks < 0) - return RETRY_TASK; + /* + * Because sched_balance_newidle() releases (and re-acquires) + * rq->lock, it is possible for any higher priority task to + * appear. In that case we must re-start the pick_next_entity() + * loop. + */ + if (new_tasks < 0) + return RETRY_TASK; - if (new_tasks > 0) - goto again; + if (new_tasks > 0) + goto again; + } /* * rq is about to be idle, check if we need to update the From e15f6ac84445d5604cda22d9cc5891b8fec56ec2 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 13 Oct 2025 20:05:52 -0700 Subject: [PATCH 086/143] ALSA: firewire: amdtp-stream: fix enum kernel-doc warnings [ Upstream commit d41f68dff783d181a8fd462e612bda0fbab7f735 ] Fix spelling of CIP_NO_HEADER to prevent a kernel-doc warning. Warning: amdtp-stream.h:57 Enum value 'CIP_NO_HEADER' not described in enum 'cip_flags' Warning: amdtp-stream.h:57 Excess enum value '%CIP_NO_HEADERS' description in 'cip_flags' Fixes: 3b196c394dd9f ("ALSA: firewire-lib: add no-header packet processing") Signed-off-by: Randy Dunlap Reviewed-by: Takashi Sakamoto Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/firewire/amdtp-stream.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index 775db3fc4959f5..ec10270c2cce3d 100644 --- a/sound/firewire/amdtp-stream.h +++ b/sound/firewire/amdtp-stream.h @@ -32,7 +32,7 @@ * allows 5 times as large as IEC 61883-6 defines. * @CIP_HEADER_WITHOUT_EOH: Only for in-stream. CIP Header doesn't include * valid EOH. - * @CIP_NO_HEADERS: a lack of headers in packets + * @CIP_NO_HEADER: a lack of headers in packets * @CIP_UNALIGHED_DBC: Only for in-stream. The value of dbc is not alighed to * the value of current SYT_INTERVAL; e.g. initial value is not zero. * @CIP_UNAWARE_SYT: For outgoing packet, the value in SYT field of CIP is 0xffff. From 646868e6962b14e25ae7462fdd1fb061b40c1f16 Mon Sep 17 00:00:00 2001 From: Jeffrey Hugo Date: Tue, 7 Oct 2025 13:57:50 +0200 Subject: [PATCH 087/143] accel/qaic: Fix bootlog initialization ordering [ Upstream commit fd6e385528d8f85993b7bfc6430576136bb14c65 ] As soon as we queue MHI buffers to receive the bootlog from the device, we could be receiving data. Therefore all the resources needed to process that data need to be setup prior to queuing the buffers. We currently initialize some of the resources after queuing the buffers which creates a race between the probe() and any data that comes back from the device. If the uninitialized resources are accessed, we could see page faults. Fix the init ordering to close the race. Fixes: 5f8df5c6def6 ("accel/qaic: Add bootlog debugfs") Signed-off-by: Jeffrey Hugo Signed-off-by: Youssef Samir Reviewed-by: Jeff Hugo Reviewed-by: Carl Vanderlip Signed-off-by: Jeff Hugo Link: https://lore.kernel.org/r/20251007115750.332169-1-youssef.abdulrahman@oss.qualcomm.com Signed-off-by: Sasha Levin --- drivers/accel/qaic/qaic_debugfs.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/accel/qaic/qaic_debugfs.c b/drivers/accel/qaic/qaic_debugfs.c index 20b653d99e524a..5ed49daaf541f4 100644 --- a/drivers/accel/qaic/qaic_debugfs.c +++ b/drivers/accel/qaic/qaic_debugfs.c @@ -251,6 +251,9 @@ static int qaic_bootlog_mhi_probe(struct mhi_device *mhi_dev, const struct mhi_d if (ret) goto destroy_workqueue; + dev_set_drvdata(&mhi_dev->dev, qdev); + qdev->bootlog_ch = mhi_dev; + for (i = 0; i < BOOTLOG_POOL_SIZE; i++) { msg = devm_kzalloc(&qdev->pdev->dev, sizeof(*msg), GFP_KERNEL); if (!msg) { @@ -266,8 +269,6 @@ static int qaic_bootlog_mhi_probe(struct mhi_device *mhi_dev, const struct mhi_d goto mhi_unprepare; } - dev_set_drvdata(&mhi_dev->dev, qdev); - qdev->bootlog_ch = mhi_dev; return 0; mhi_unprepare: From 551f1dfbcb7f3e6ed07f9d6c8c1c64337fcd0ede Mon Sep 17 00:00:00 2001 From: Youssef Samir Date: Tue, 7 Oct 2025 14:23:20 +0200 Subject: [PATCH 088/143] accel/qaic: Treat remaining == 0 as error in find_and_map_user_pages() [ Upstream commit 11f08c30a3e4157305ba692f1d44cca5fc9a8fca ] Currently, if find_and_map_user_pages() takes a DMA xfer request from the user with a length field set to 0, or in a rare case, the host receives QAIC_TRANS_DMA_XFER_CONT from the device where resources->xferred_dma_size is equal to the requested transaction size, the function will return 0 before allocating an sgt or setting the fields of the dma_xfer struct. In that case, encode_addr_size_pairs() will try to access the sgt which will lead to a general protection fault. Return an EINVAL in case the user provides a zero-sized ALP, or the device requests continuation after all of the bytes have been transferred. Fixes: 96d3c1cadedb ("accel/qaic: Clean up integer overflow checking in map_user_pages()") Signed-off-by: Youssef Samir Signed-off-by: Youssef Samir Reviewed-by: Jeff Hugo Reviewed-by: Carl Vanderlip Signed-off-by: Jeff Hugo Link: https://lore.kernel.org/r/20251007122320.339654-1-youssef.abdulrahman@oss.qualcomm.com Signed-off-by: Sasha Levin --- drivers/accel/qaic/qaic_control.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/accel/qaic/qaic_control.c b/drivers/accel/qaic/qaic_control.c index d8bdab69f80095..b86a8e48e731b7 100644 --- a/drivers/accel/qaic/qaic_control.c +++ b/drivers/accel/qaic/qaic_control.c @@ -407,7 +407,7 @@ static int find_and_map_user_pages(struct qaic_device *qdev, return -EINVAL; remaining = in_trans->size - resources->xferred_dma_size; if (remaining == 0) - return 0; + return -EINVAL; if (check_add_overflow(xfer_start_addr, remaining, &end)) return -EINVAL; From 21ba0445e422ffe469b308deaaa9bdc33b20743a Mon Sep 17 00:00:00 2001 From: Pranjal Ramajor Asha Kanojiya Date: Tue, 7 Oct 2025 08:18:37 +0200 Subject: [PATCH 089/143] accel/qaic: Synchronize access to DBC request queue head & tail pointer [ Upstream commit 52e59f7740ba23bbb664914967df9a00208ca10c ] Two threads of the same process can potential read and write parallelly to head and tail pointers of the same DBC request queue. This could lead to a race condition and corrupt the DBC request queue. Fixes: ff13be830333 ("accel/qaic: Add datapath") Signed-off-by: Pranjal Ramajor Asha Kanojiya Signed-off-by: Youssef Samir Reviewed-by: Jeff Hugo Reviewed-by: Carl Vanderlip [jhugo: Add fixes tag] Signed-off-by: Jeff Hugo Link: https://lore.kernel.org/r/20251007061837.206132-1-youssef.abdulrahman@oss.qualcomm.com Signed-off-by: Sasha Levin --- drivers/accel/qaic/qaic.h | 2 ++ drivers/accel/qaic/qaic_data.c | 12 ++++++++++-- drivers/accel/qaic/qaic_drv.c | 3 +++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/accel/qaic/qaic.h b/drivers/accel/qaic/qaic.h index 02561b6cecc64b..2d7b3af09e2846 100644 --- a/drivers/accel/qaic/qaic.h +++ b/drivers/accel/qaic/qaic.h @@ -91,6 +91,8 @@ struct dma_bridge_chan { * response queue's head and tail pointer of this DBC. */ void __iomem *dbc_base; + /* Synchronizes access to Request queue's head and tail pointer */ + struct mutex req_lock; /* Head of list where each node is a memory handle queued in request queue */ struct list_head xfer_list; /* Synchronizes DBC readers during cleanup */ diff --git a/drivers/accel/qaic/qaic_data.c b/drivers/accel/qaic/qaic_data.c index 43aba57b48f05f..265eeb4e156fc6 100644 --- a/drivers/accel/qaic/qaic_data.c +++ b/drivers/accel/qaic/qaic_data.c @@ -1357,13 +1357,17 @@ static int __qaic_execute_bo_ioctl(struct drm_device *dev, void *data, struct dr goto release_ch_rcu; } + ret = mutex_lock_interruptible(&dbc->req_lock); + if (ret) + goto release_ch_rcu; + head = readl(dbc->dbc_base + REQHP_OFF); tail = readl(dbc->dbc_base + REQTP_OFF); if (head == U32_MAX || tail == U32_MAX) { /* PCI link error */ ret = -ENODEV; - goto release_ch_rcu; + goto unlock_req_lock; } queue_level = head <= tail ? tail - head : dbc->nelem - (head - tail); @@ -1371,11 +1375,12 @@ static int __qaic_execute_bo_ioctl(struct drm_device *dev, void *data, struct dr ret = send_bo_list_to_device(qdev, file_priv, exec, args->hdr.count, is_partial, dbc, head, &tail); if (ret) - goto release_ch_rcu; + goto unlock_req_lock; /* Finalize commit to hardware */ submit_ts = ktime_get_ns(); writel(tail, dbc->dbc_base + REQTP_OFF); + mutex_unlock(&dbc->req_lock); update_profiling_data(file_priv, exec, args->hdr.count, is_partial, received_ts, submit_ts, queue_level); @@ -1383,6 +1388,9 @@ static int __qaic_execute_bo_ioctl(struct drm_device *dev, void *data, struct dr if (datapath_polling) schedule_work(&dbc->poll_work); +unlock_req_lock: + if (ret) + mutex_unlock(&dbc->req_lock); release_ch_rcu: srcu_read_unlock(&dbc->ch_lock, rcu_id); unlock_dev_srcu: diff --git a/drivers/accel/qaic/qaic_drv.c b/drivers/accel/qaic/qaic_drv.c index 10e711c96a6706..cb606c4bb85116 100644 --- a/drivers/accel/qaic/qaic_drv.c +++ b/drivers/accel/qaic/qaic_drv.c @@ -422,6 +422,9 @@ static struct qaic_device *create_qdev(struct pci_dev *pdev, const struct pci_de return NULL; init_waitqueue_head(&qdev->dbc[i].dbc_release); INIT_LIST_HEAD(&qdev->dbc[i].bo_lists); + ret = drmm_mutex_init(drm, &qdev->dbc[i].req_lock); + if (ret) + return NULL; } return qdev; From c1bcd7205ac365a341149d0c5d73c8152289caf1 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Tue, 14 Oct 2025 13:20:37 -0700 Subject: [PATCH 090/143] selftests/bpf: make arg_parsing.c more robust to crashes [ Upstream commit e603a342cf7ecd64ef8f36207dfe1caacb9e2583 ] We started getting a crash in BPF CI, which seems to originate from test_parse_test_list_file() test and is happening at this line: ASSERT_OK(strcmp("test_with_spaces", set.tests[0].name), "test 0 name"); One way we can crash there is if set.cnt zero, which is checked for with ASSERT_EQ() above, but we proceed after this regardless of the outcome. Instead of crashing, we should bail out with test failure early. Similarly, if parse_test_list_file() fails, we shouldn't be even looking at set, so bail even earlier if ASSERT_OK() fails. Fixes: 64276f01dce8 ("selftests/bpf: Test_progs can read test lists from file") Signed-off-by: Andrii Nakryiko Tested-by: Ihor Solodrai Link: https://lore.kernel.org/r/20251014202037.72922-1-andrii@kernel.org Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- tools/testing/selftests/bpf/prog_tests/arg_parsing.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/arg_parsing.c b/tools/testing/selftests/bpf/prog_tests/arg_parsing.c index bb143de68875cc..fbf0d9c2f58b3f 100644 --- a/tools/testing/selftests/bpf/prog_tests/arg_parsing.c +++ b/tools/testing/selftests/bpf/prog_tests/arg_parsing.c @@ -146,9 +146,12 @@ static void test_parse_test_list_file(void) init_test_filter_set(&set); - ASSERT_OK(parse_test_list_file(tmpfile, &set, true), "parse file"); + if (!ASSERT_OK(parse_test_list_file(tmpfile, &set, true), "parse file")) + goto out_fclose; + + if (!ASSERT_EQ(set.cnt, 4, "test count")) + goto out_free_set; - ASSERT_EQ(set.cnt, 4, "test count"); ASSERT_OK(strcmp("test_with_spaces", set.tests[0].name), "test 0 name"); ASSERT_EQ(set.tests[0].subtest_cnt, 0, "test 0 subtest count"); ASSERT_OK(strcmp("testA", set.tests[1].name), "test 1 name"); @@ -158,8 +161,8 @@ static void test_parse_test_list_file(void) ASSERT_OK(strcmp("testB", set.tests[2].name), "test 2 name"); ASSERT_OK(strcmp("testC_no_eof_newline", set.tests[3].name), "test 3 name"); +out_free_set: free_test_filter_set(&set); - out_fclose: fclose(fp); out_remove: From bba7208765d26e5e36b87f21dacc2780b064f41f Mon Sep 17 00:00:00 2001 From: Jiaming Zhang Date: Wed, 15 Oct 2025 13:16:45 +0800 Subject: [PATCH 091/143] ALSA: usb-audio: Fix NULL pointer deference in try_to_register_card [ Upstream commit 28412b489b088fb88dff488305fd4e56bd47f6e4 ] In try_to_register_card(), the return value of usb_ifnum_to_if() is passed directly to usb_interface_claimed() without a NULL check, which will lead to a NULL pointer dereference when creating an invalid USB audio device. Fix this by adding a check to ensure the interface pointer is valid before passing it to usb_interface_claimed(). Fixes: 39efc9c8a973 ("ALSA: usb-audio: Fix last interface check for registration") Closes: https://lore.kernel.org/all/CANypQFYtQxHL5ghREs-BujZG413RPJGnO5TH=xjFBKpPts33tA@mail.gmail.com/ Signed-off-by: Jiaming Zhang Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/usb/card.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/sound/usb/card.c b/sound/usb/card.c index 9c411b82a218dc..d0a42859208aaa 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -760,10 +760,16 @@ get_alias_quirk(struct usb_device *dev, unsigned int id) */ static int try_to_register_card(struct snd_usb_audio *chip, int ifnum) { + struct usb_interface *iface; + if (check_delayed_register_option(chip) == ifnum || - chip->last_iface == ifnum || - usb_interface_claimed(usb_ifnum_to_if(chip->dev, chip->last_iface))) + chip->last_iface == ifnum) + return snd_card_register(chip->card); + + iface = usb_ifnum_to_if(chip->dev, chip->last_iface); + if (iface && usb_interface_claimed(iface)) return snd_card_register(chip->card); + return 0; } From 560024035fe7840d6034b7a0934fea6a46e3bff9 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 14 Oct 2025 21:28:44 -0700 Subject: [PATCH 092/143] HID: hid-input: only ignore 0 battery events for digitizers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 0187c08058da3e7f11b356ac27e0c427d36f33f2 ] Commit 581c4484769e ("HID: input: map digitizer battery usage") added handling of battery events for digitizers (typically for batteries presented in stylii). Digitizers typically report correct battery levels only when stylus is actively touching the surface, and in other cases they may report battery level of 0. To avoid confusing consumers of the battery information the code was added to filer out reports with 0 battery levels. However there exist other kinds of devices that may legitimately report 0 battery levels. Fix this by filtering out 0-level reports only for digitizer usages, and continue reporting them for other kinds of devices (Smart Batteries, etc). Reported-by: 卢国宏 Fixes: 581c4484769e ("HID: input: map digitizer battery usage") Signed-off-by: Dmitry Torokhov Signed-off-by: Jiri Kosina Signed-off-by: Sasha Levin --- drivers/hid/hid-input.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index f5c217ac4bfaa7..f073d5621050a1 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -622,7 +622,10 @@ static void hidinput_update_battery(struct hid_device *dev, unsigned int usage, return; } - if (value == 0 || value < dev->battery_min || value > dev->battery_max) + if ((usage & HID_USAGE_PAGE) == HID_UP_DIGITIZER && value == 0) + return; + + if (value < dev->battery_min || value > dev->battery_max) return; capacity = hidinput_scale_battery_capacity(dev, value); From 9ab3e03765b93679aafeef6c0302b6a4c1f27623 Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Wed, 8 Oct 2025 09:40:33 -0300 Subject: [PATCH 093/143] HID: multitouch: fix name of Stylus input devices [ Upstream commit aa4daea418ee4215dca5c8636090660c545cb233 ] HID_DG_PEN devices should have a suffix of "Stylus", as pointed out by commit c0ee1d571626 ("HID: hid-input: Add suffix also for HID_DG_PEN"). However, on multitouch devices, these suffixes may be overridden. Before that commit, HID_DG_PEN devices would get the "Stylus" suffix, but after that, multitouch would override them to have an "UNKNOWN" suffix. Just add HID_DG_PEN to the list of non-overriden suffixes in multitouch. Before this fix: [ 0.470981] input: ELAN9008:00 04F3:2E14 UNKNOWN as /devices/pci0000:00/0000:00:15.1/i2c_designware.1/i2c-16/i2c-ELAN9008:00/0018:04F3:2E14.0001/input/input8 ELAN9008:00 04F3:2E14 UNKNOWN After this fix: [ 0.474332] input: ELAN9008:00 04F3:2E14 Stylus as /devices/pci0000:00/0000:00:15.1/i2c_designware.1/i2c-16/i2c-ELAN9008:00/0018:04F3:2E14.0001/input/input8 ELAN9008:00 04F3:2E14 Stylus Fixes: c0ee1d571626 ("HID: hid-input: Add suffix also for HID_DG_PEN") Signed-off-by: Thadeu Lima de Souza Cascardo Reviewed-by: Mika Westerberg Signed-off-by: Jiri Kosina Signed-off-by: Sasha Levin --- drivers/hid/hid-multitouch.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 0667a24576fc4f..0e4cb0e668eb5c 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -1663,6 +1663,7 @@ static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi) case HID_CP_CONSUMER_CONTROL: case HID_GD_WIRELESS_RADIO_CTLS: case HID_GD_SYSTEM_MULTIAXIS: + case HID_DG_PEN: /* already handled by hid core */ break; case HID_DG_TOUCHSCREEN: From 095d692e5997ece300c89f10d903d5230090e6a0 Mon Sep 17 00:00:00 2001 From: Li Qiang Date: Wed, 15 Oct 2025 15:55:30 +0800 Subject: [PATCH 094/143] ASoC: amd/sdw_utils: avoid NULL deref when devm_kasprintf() fails [ Upstream commit 5726b68473f7153a7f6294185e5998b7e2a230a2 ] devm_kasprintf() may return NULL on memory allocation failure, but the debug message prints cpus->dai_name before checking it. Move the dev_dbg() call after the NULL check to prevent potential NULL pointer dereference. Fixes: cb8ea62e64020 ("ASoC: amd/sdw_utils: add sof based soundwire generic machine driver") Signed-off-by: Li Qiang Link: https://patch.msgid.link/20251015075530.146851-1-liqiang01@kylinos.cn Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/amd/acp/acp-sdw-sof-mach.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/amd/acp/acp-sdw-sof-mach.c b/sound/soc/amd/acp/acp-sdw-sof-mach.c index 99a244f495bd30..876f0b7fcd3dee 100644 --- a/sound/soc/amd/acp/acp-sdw-sof-mach.c +++ b/sound/soc/amd/acp/acp-sdw-sof-mach.c @@ -216,9 +216,9 @@ static int create_sdw_dailink(struct snd_soc_card *card, cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SDW%d Pin%d", link_num, cpu_pin_id); - dev_dbg(dev, "cpu->dai_name:%s\n", cpus->dai_name); if (!cpus->dai_name) return -ENOMEM; + dev_dbg(dev, "cpu->dai_name:%s\n", cpus->dai_name); codec_maps[j].cpu = 0; codec_maps[j].codec = j; From 2a87a1c5866c3677d79e641943ee2ec19444a932 Mon Sep 17 00:00:00 2001 From: Xing Guo Date: Thu, 16 Oct 2025 11:53:30 +0800 Subject: [PATCH 095/143] selftests: arg_parsing: Ensure data is flushed to disk before reading. [ Upstream commit 0c1999ed33722f85476a248186d6e0eb2bf3dd2a ] test_parse_test_list_file writes some data to /tmp/bpf_arg_parsing_test.XXXXXX and parse_test_list_file() will read the data back. However, after writing data to that file, we forget to call fsync() and it's causing testing failure in my laptop. This patch helps fix it by adding the missing fsync() call. Fixes: 64276f01dce8 ("selftests/bpf: Test_progs can read test lists from file") Signed-off-by: Xing Guo Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20251016035330.3217145-1-higuoxing@gmail.com Signed-off-by: Sasha Levin --- tools/testing/selftests/bpf/prog_tests/arg_parsing.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/testing/selftests/bpf/prog_tests/arg_parsing.c b/tools/testing/selftests/bpf/prog_tests/arg_parsing.c index fbf0d9c2f58b3f..e27d66b75fb1fc 100644 --- a/tools/testing/selftests/bpf/prog_tests/arg_parsing.c +++ b/tools/testing/selftests/bpf/prog_tests/arg_parsing.c @@ -144,6 +144,9 @@ static void test_parse_test_list_file(void) if (!ASSERT_OK(ferror(fp), "prepare tmp")) goto out_fclose; + if (!ASSERT_OK(fsync(fileno(fp)), "fsync tmp")) + goto out_fclose; + init_test_filter_set(&set); if (!ASSERT_OK(parse_test_list_file(tmpfile, &set, true), "parse file")) From f3fe1abdeb2c38587215b9585dc17219ad984be0 Mon Sep 17 00:00:00 2001 From: Wilfred Mallawa Date: Fri, 10 Oct 2025 17:19:42 +1000 Subject: [PATCH 096/143] nvme/tcp: handle tls partially sent records in write_space() [ Upstream commit 5a869d017793399fd1d2609ff27e900534173eb3 ] With TLS enabled, records that are encrypted and appended to TLS TX list can fail to see a retry if the underlying TCP socket is busy, for example, hitting an EAGAIN from tcp_sendmsg_locked(). This is not known to the NVMe TCP driver, as the TLS layer successfully generated a record. Typically, the TLS write_space() callback would ensure such records are retried, but in the NVMe TCP Host driver, write_space() invokes nvme_tcp_write_space(). This causes a partially sent record in the TLS TX list to timeout after not being retried. This patch fixes the above by calling queue->write_space(), which calls into the TLS layer to retry any pending records. Fixes: be8e82caa685 ("nvme-tcp: enable TLS handshake upcall") Signed-off-by: Wilfred Mallawa Reviewed-by: Hannes Reinecke Signed-off-by: Keith Busch Signed-off-by: Sasha Levin --- drivers/nvme/host/tcp.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c index 83a6b18b01ada0..77df3432dfb78e 100644 --- a/drivers/nvme/host/tcp.c +++ b/drivers/nvme/host/tcp.c @@ -1075,6 +1075,9 @@ static void nvme_tcp_write_space(struct sock *sk) queue = sk->sk_user_data; if (likely(queue && sk_stream_is_writeable(sk))) { clear_bit(SOCK_NOSPACE, &sk->sk_socket->flags); + /* Ensure pending TLS partial records are retried */ + if (nvme_tcp_queue_tls(queue)) + queue->write_space(sk); queue_work_on(queue->io_cpu, nvme_tcp_wq, &queue->io_work); } read_unlock_bh(&sk->sk_callback_lock); From 586c75dfd1d265c4150f6529debb85c9d62e101f Mon Sep 17 00:00:00 2001 From: Viacheslav Dubeyko Date: Fri, 19 Sep 2025 12:12:44 -0700 Subject: [PATCH 097/143] hfsplus: fix slab-out-of-bounds read in hfsplus_strcasecmp() commit 42520df65bf67189541a425f7d36b0b3e7bd7844 upstream. The hfsplus_strcasecmp() logic can trigger the issue: [ 117.317703][ T9855] ================================================================== [ 117.318353][ T9855] BUG: KASAN: slab-out-of-bounds in hfsplus_strcasecmp+0x1bc/0x490 [ 117.318991][ T9855] Read of size 2 at addr ffff88802160f40c by task repro/9855 [ 117.319577][ T9855] [ 117.319773][ T9855] CPU: 0 UID: 0 PID: 9855 Comm: repro Not tainted 6.17.0-rc6 #33 PREEMPT(full) [ 117.319780][ T9855] Hardware name: QEMU Ubuntu 24.04 PC (i440FX + PIIX, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 [ 117.319783][ T9855] Call Trace: [ 117.319785][ T9855] [ 117.319788][ T9855] dump_stack_lvl+0x1c1/0x2a0 [ 117.319795][ T9855] ? __virt_addr_valid+0x1c8/0x5c0 [ 117.319803][ T9855] ? __pfx_dump_stack_lvl+0x10/0x10 [ 117.319808][ T9855] ? rcu_is_watching+0x15/0xb0 [ 117.319816][ T9855] ? lock_release+0x4b/0x3e0 [ 117.319821][ T9855] ? __kasan_check_byte+0x12/0x40 [ 117.319828][ T9855] ? __virt_addr_valid+0x1c8/0x5c0 [ 117.319835][ T9855] ? __virt_addr_valid+0x4a5/0x5c0 [ 117.319842][ T9855] print_report+0x17e/0x7e0 [ 117.319848][ T9855] ? __virt_addr_valid+0x1c8/0x5c0 [ 117.319855][ T9855] ? __virt_addr_valid+0x4a5/0x5c0 [ 117.319862][ T9855] ? __phys_addr+0xd3/0x180 [ 117.319869][ T9855] ? hfsplus_strcasecmp+0x1bc/0x490 [ 117.319876][ T9855] kasan_report+0x147/0x180 [ 117.319882][ T9855] ? hfsplus_strcasecmp+0x1bc/0x490 [ 117.319891][ T9855] hfsplus_strcasecmp+0x1bc/0x490 [ 117.319900][ T9855] ? __pfx_hfsplus_cat_case_cmp_key+0x10/0x10 [ 117.319906][ T9855] hfs_find_rec_by_key+0xa9/0x1e0 [ 117.319913][ T9855] __hfsplus_brec_find+0x18e/0x470 [ 117.319920][ T9855] ? __pfx_hfsplus_bnode_find+0x10/0x10 [ 117.319926][ T9855] ? __pfx_hfs_find_rec_by_key+0x10/0x10 [ 117.319933][ T9855] ? __pfx___hfsplus_brec_find+0x10/0x10 [ 117.319942][ T9855] hfsplus_brec_find+0x28f/0x510 [ 117.319949][ T9855] ? __pfx_hfs_find_rec_by_key+0x10/0x10 [ 117.319956][ T9855] ? __pfx_hfsplus_brec_find+0x10/0x10 [ 117.319963][ T9855] ? __kmalloc_noprof+0x2a9/0x510 [ 117.319969][ T9855] ? hfsplus_find_init+0x8c/0x1d0 [ 117.319976][ T9855] hfsplus_brec_read+0x2b/0x120 [ 117.319983][ T9855] hfsplus_lookup+0x2aa/0x890 [ 117.319990][ T9855] ? __pfx_hfsplus_lookup+0x10/0x10 [ 117.320003][ T9855] ? d_alloc_parallel+0x2f0/0x15e0 [ 117.320008][ T9855] ? __lock_acquire+0xaec/0xd80 [ 117.320013][ T9855] ? __pfx_d_alloc_parallel+0x10/0x10 [ 117.320019][ T9855] ? __raw_spin_lock_init+0x45/0x100 [ 117.320026][ T9855] ? __init_waitqueue_head+0xa9/0x150 [ 117.320034][ T9855] __lookup_slow+0x297/0x3d0 [ 117.320039][ T9855] ? __pfx___lookup_slow+0x10/0x10 [ 117.320045][ T9855] ? down_read+0x1ad/0x2e0 [ 117.320055][ T9855] lookup_slow+0x53/0x70 [ 117.320065][ T9855] walk_component+0x2f0/0x430 [ 117.320073][ T9855] path_lookupat+0x169/0x440 [ 117.320081][ T9855] filename_lookup+0x212/0x590 [ 117.320089][ T9855] ? __pfx_filename_lookup+0x10/0x10 [ 117.320098][ T9855] ? strncpy_from_user+0x150/0x290 [ 117.320105][ T9855] ? getname_flags+0x1e5/0x540 [ 117.320112][ T9855] user_path_at+0x3a/0x60 [ 117.320117][ T9855] __x64_sys_umount+0xee/0x160 [ 117.320123][ T9855] ? __pfx___x64_sys_umount+0x10/0x10 [ 117.320129][ T9855] ? do_syscall_64+0xb7/0x3a0 [ 117.320135][ T9855] ? entry_SYSCALL_64_after_hwframe+0x77/0x7f [ 117.320141][ T9855] ? entry_SYSCALL_64_after_hwframe+0x77/0x7f [ 117.320145][ T9855] do_syscall_64+0xf3/0x3a0 [ 117.320150][ T9855] ? exc_page_fault+0x9f/0xf0 [ 117.320154][ T9855] entry_SYSCALL_64_after_hwframe+0x77/0x7f [ 117.320158][ T9855] RIP: 0033:0x7f7dd7908b07 [ 117.320163][ T9855] Code: 23 0d 00 f7 d8 64 89 01 48 83 c8 ff c3 66 0f 1f 44 00 00 31 f6 e9 09 00 00 00 66 0f 1f 84 00 00 08 [ 117.320167][ T9855] RSP: 002b:00007ffd5ebd9698 EFLAGS: 00000202 ORIG_RAX: 00000000000000a6 [ 117.320172][ T9855] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f7dd7908b07 [ 117.320176][ T9855] RDX: 0000000000000009 RSI: 0000000000000009 RDI: 00007ffd5ebd9740 [ 117.320179][ T9855] RBP: 00007ffd5ebda780 R08: 0000000000000005 R09: 00007ffd5ebd9530 [ 117.320181][ T9855] R10: 00007f7dd799bfc0 R11: 0000000000000202 R12: 000055e2008b32d0 [ 117.320184][ T9855] R13: 0000000000000000 R14: 0000000000000000 R15: 0000000000000000 [ 117.320189][ T9855] [ 117.320190][ T9855] [ 117.351311][ T9855] Allocated by task 9855: [ 117.351683][ T9855] kasan_save_track+0x3e/0x80 [ 117.352093][ T9855] __kasan_kmalloc+0x8d/0xa0 [ 117.352490][ T9855] __kmalloc_noprof+0x288/0x510 [ 117.352914][ T9855] hfsplus_find_init+0x8c/0x1d0 [ 117.353342][ T9855] hfsplus_lookup+0x19c/0x890 [ 117.353747][ T9855] __lookup_slow+0x297/0x3d0 [ 117.354148][ T9855] lookup_slow+0x53/0x70 [ 117.354514][ T9855] walk_component+0x2f0/0x430 [ 117.354921][ T9855] path_lookupat+0x169/0x440 [ 117.355325][ T9855] filename_lookup+0x212/0x590 [ 117.355740][ T9855] user_path_at+0x3a/0x60 [ 117.356115][ T9855] __x64_sys_umount+0xee/0x160 [ 117.356529][ T9855] do_syscall_64+0xf3/0x3a0 [ 117.356920][ T9855] entry_SYSCALL_64_after_hwframe+0x77/0x7f [ 117.357429][ T9855] [ 117.357636][ T9855] The buggy address belongs to the object at ffff88802160f000 [ 117.357636][ T9855] which belongs to the cache kmalloc-2k of size 2048 [ 117.358827][ T9855] The buggy address is located 0 bytes to the right of [ 117.358827][ T9855] allocated 1036-byte region [ffff88802160f000, ffff88802160f40c) [ 117.360061][ T9855] [ 117.360266][ T9855] The buggy address belongs to the physical page: [ 117.360813][ T9855] page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x21608 [ 117.361562][ T9855] head: order:3 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0 [ 117.362285][ T9855] flags: 0xfff00000000040(head|node=0|zone=1|lastcpupid=0x7ff) [ 117.362929][ T9855] page_type: f5(slab) [ 117.363282][ T9855] raw: 00fff00000000040 ffff88801a842f00 ffffea0000932000 dead000000000002 [ 117.364015][ T9855] raw: 0000000000000000 0000000080080008 00000000f5000000 0000000000000000 [ 117.364750][ T9855] head: 00fff00000000040 ffff88801a842f00 ffffea0000932000 dead000000000002 [ 117.365491][ T9855] head: 0000000000000000 0000000080080008 00000000f5000000 0000000000000000 [ 117.366232][ T9855] head: 00fff00000000003 ffffea0000858201 00000000ffffffff 00000000ffffffff [ 117.366968][ T9855] head: ffffffffffffffff 0000000000000000 00000000ffffffff 0000000000000008 [ 117.367711][ T9855] page dumped because: kasan: bad access detected [ 117.368259][ T9855] page_owner tracks the page as allocated [ 117.368745][ T9855] page last allocated via order 3, migratetype Unmovable, gfp_mask 0xd20c0(__GFP_IO|__GFP_FS|__GFP_NOWARN1 [ 117.370541][ T9855] post_alloc_hook+0x240/0x2a0 [ 117.370954][ T9855] get_page_from_freelist+0x2101/0x21e0 [ 117.371435][ T9855] __alloc_frozen_pages_noprof+0x274/0x380 [ 117.371935][ T9855] alloc_pages_mpol+0x241/0x4b0 [ 117.372360][ T9855] allocate_slab+0x8d/0x380 [ 117.372752][ T9855] ___slab_alloc+0xbe3/0x1400 [ 117.373159][ T9855] __kmalloc_cache_noprof+0x296/0x3d0 [ 117.373621][ T9855] nexthop_net_init+0x75/0x100 [ 117.374038][ T9855] ops_init+0x35c/0x5c0 [ 117.374400][ T9855] setup_net+0x10c/0x320 [ 117.374768][ T9855] copy_net_ns+0x31b/0x4d0 [ 117.375156][ T9855] create_new_namespaces+0x3f3/0x720 [ 117.375613][ T9855] unshare_nsproxy_namespaces+0x11c/0x170 [ 117.376094][ T9855] ksys_unshare+0x4ca/0x8d0 [ 117.376477][ T9855] __x64_sys_unshare+0x38/0x50 [ 117.376879][ T9855] do_syscall_64+0xf3/0x3a0 [ 117.377265][ T9855] page last free pid 9110 tgid 9110 stack trace: [ 117.377795][ T9855] __free_frozen_pages+0xbeb/0xd50 [ 117.378229][ T9855] __put_partials+0x152/0x1a0 [ 117.378625][ T9855] put_cpu_partial+0x17c/0x250 [ 117.379026][ T9855] __slab_free+0x2d4/0x3c0 [ 117.379404][ T9855] qlist_free_all+0x97/0x140 [ 117.379790][ T9855] kasan_quarantine_reduce+0x148/0x160 [ 117.380250][ T9855] __kasan_slab_alloc+0x22/0x80 [ 117.380662][ T9855] __kmalloc_noprof+0x232/0x510 [ 117.381074][ T9855] tomoyo_supervisor+0xc0a/0x1360 [ 117.381498][ T9855] tomoyo_env_perm+0x149/0x1e0 [ 117.381903][ T9855] tomoyo_find_next_domain+0x15ad/0x1b90 [ 117.382378][ T9855] tomoyo_bprm_check_security+0x11c/0x180 [ 117.382859][ T9855] security_bprm_check+0x89/0x280 [ 117.383289][ T9855] bprm_execve+0x8f1/0x14a0 [ 117.383673][ T9855] do_execveat_common+0x528/0x6b0 [ 117.384103][ T9855] __x64_sys_execve+0x94/0xb0 [ 117.384500][ T9855] [ 117.384706][ T9855] Memory state around the buggy address: [ 117.385179][ T9855] ffff88802160f300: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 117.385854][ T9855] ffff88802160f380: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 117.386534][ T9855] >ffff88802160f400: 00 04 fc fc fc fc fc fc fc fc fc fc fc fc fc fc [ 117.387204][ T9855] ^ [ 117.387566][ T9855] ffff88802160f480: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc [ 117.388243][ T9855] ffff88802160f500: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc [ 117.388918][ T9855] ================================================================== The issue takes place if the length field of struct hfsplus_unistr is bigger than HFSPLUS_MAX_STRLEN. The patch simply checks the length of comparing strings. And if the strings' length is bigger than HFSPLUS_MAX_STRLEN, then it is corrected to this value. v2 The string length correction has been added for hfsplus_strcmp(). Reported-by: Jiaming Zhang Signed-off-by: Viacheslav Dubeyko cc: John Paul Adrian Glaubitz cc: Yangtao Li cc: linux-fsdevel@vger.kernel.org cc: syzkaller@googlegroups.com Link: https://lore.kernel.org/r/20250919191243.1370388-1-slava@dubeyko.com Signed-off-by: Viacheslav Dubeyko Signed-off-by: Greg Kroah-Hartman --- fs/hfsplus/unicode.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/fs/hfsplus/unicode.c b/fs/hfsplus/unicode.c index 36b6cf2a3abba4..ebd326799f35ac 100644 --- a/fs/hfsplus/unicode.c +++ b/fs/hfsplus/unicode.c @@ -40,6 +40,18 @@ int hfsplus_strcasecmp(const struct hfsplus_unistr *s1, p1 = s1->unicode; p2 = s2->unicode; + if (len1 > HFSPLUS_MAX_STRLEN) { + len1 = HFSPLUS_MAX_STRLEN; + pr_err("invalid length %u has been corrected to %d\n", + be16_to_cpu(s1->length), len1); + } + + if (len2 > HFSPLUS_MAX_STRLEN) { + len2 = HFSPLUS_MAX_STRLEN; + pr_err("invalid length %u has been corrected to %d\n", + be16_to_cpu(s2->length), len2); + } + while (1) { c1 = c2 = 0; @@ -74,6 +86,18 @@ int hfsplus_strcmp(const struct hfsplus_unistr *s1, p1 = s1->unicode; p2 = s2->unicode; + if (len1 > HFSPLUS_MAX_STRLEN) { + len1 = HFSPLUS_MAX_STRLEN; + pr_err("invalid length %u has been corrected to %d\n", + be16_to_cpu(s1->length), len1); + } + + if (len2 > HFSPLUS_MAX_STRLEN) { + len2 = HFSPLUS_MAX_STRLEN; + pr_err("invalid length %u has been corrected to %d\n", + be16_to_cpu(s2->length), len2); + } + for (len = min(len1, len2); len > 0; len--) { c1 = be16_to_cpu(*p1); c2 = be16_to_cpu(*p2); From ab0f805bed816384fe355f87b1d666ae82d65570 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 20 Oct 2025 08:43:57 -0400 Subject: [PATCH 098/143] xfs: rename the old_crc variable in xlog_recover_process [ Upstream commit 0b737f4ac1d3ec093347241df74bbf5f54a7e16c ] old_crc is a very misleading name. Rename it to expected_crc as that described the usage much better. Signed-off-by: Christoph Hellwig Reviewed-by: Darrick J. Wong Signed-off-by: Carlos Maiolino Stable-dep-of: e747883c7d73 ("xfs: fix log CRC mismatches between i386 and other architectures") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/xfs/xfs_log_recover.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index 704aaadb61cf29..4d5af8d508eb63 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -2890,20 +2890,19 @@ xlog_recover_process( int pass, struct list_head *buffer_list) { - __le32 old_crc = rhead->h_crc; - __le32 crc; + __le32 expected_crc = rhead->h_crc, crc; crc = xlog_cksum(log, rhead, dp, be32_to_cpu(rhead->h_len)); /* * Nothing else to do if this is a CRC verification pass. Just return * if this a record with a non-zero crc. Unfortunately, mkfs always - * sets old_crc to 0 so we must consider this valid even on v5 supers. - * Otherwise, return EFSBADCRC on failure so the callers up the stack - * know precisely what failed. + * sets expected_crc to 0 so we must consider this valid even on v5 + * supers. Otherwise, return EFSBADCRC on failure so the callers up the + * stack know precisely what failed. */ if (pass == XLOG_RECOVER_CRCPASS) { - if (old_crc && crc != old_crc) + if (expected_crc && crc != expected_crc) return -EFSBADCRC; return 0; } @@ -2914,11 +2913,11 @@ xlog_recover_process( * zero CRC check prevents warnings from being emitted when upgrading * the kernel from one that does not add CRCs by default. */ - if (crc != old_crc) { - if (old_crc || xfs_has_crc(log->l_mp)) { + if (crc != expected_crc) { + if (expected_crc || xfs_has_crc(log->l_mp)) { xfs_alert(log->l_mp, "log record CRC mismatch: found 0x%x, expected 0x%x.", - le32_to_cpu(old_crc), + le32_to_cpu(expected_crc), le32_to_cpu(crc)); xfs_hex_dump(dp, 32); } From 9909b28175c15eae4b4a337167eba9cb501a63eb Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 20 Oct 2025 08:43:58 -0400 Subject: [PATCH 099/143] xfs: fix log CRC mismatches between i386 and other architectures [ Upstream commit e747883c7d7306acb4d683038d881528fbfbe749 ] When mounting file systems with a log that was dirtied on i386 on other architectures or vice versa, log recovery is unhappy: [ 11.068052] XFS (vdb): Torn write (CRC failure) detected at log block 0x2. Truncating head block from 0xc. This is because the CRCs generated by i386 and other architectures always diff. The reason for that is that sizeof(struct xlog_rec_header) returns different values for i386 vs the rest (324 vs 328), because the struct is not sizeof(uint64_t) aligned, and i386 has odd struct size alignment rules. This issue goes back to commit 13cdc853c519 ("Add log versioning, and new super block field for the log stripe") in the xfs-import tree, which adds log v2 support and the h_size field that causes the unaligned size. At that time it only mattered for the crude debug only log header checksum, but with commit 0e446be44806 ("xfs: add CRC checks to the log") it became a real issue for v5 file system, because now there is a proper CRC, and regular builds actually expect it match. Fix this by allowing checksums with and without the padding. Fixes: 0e446be44806 ("xfs: add CRC checks to the log") Cc: # v3.8 Signed-off-by: Christoph Hellwig Signed-off-by: Carlos Maiolino Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/xfs/libxfs/xfs_log_format.h | 30 +++++++++++++++++++++++++++++- fs/xfs/libxfs/xfs_ondisk.h | 2 ++ fs/xfs/xfs_log.c | 8 ++++---- fs/xfs/xfs_log_priv.h | 4 ++-- fs/xfs/xfs_log_recover.c | 19 +++++++++++++++++-- 5 files changed, 54 insertions(+), 9 deletions(-) diff --git a/fs/xfs/libxfs/xfs_log_format.h b/fs/xfs/libxfs/xfs_log_format.h index 3e6682ed656b30..a151ea6c0f7215 100644 --- a/fs/xfs/libxfs/xfs_log_format.h +++ b/fs/xfs/libxfs/xfs_log_format.h @@ -174,12 +174,40 @@ typedef struct xlog_rec_header { __be32 h_prev_block; /* block number to previous LR : 4 */ __be32 h_num_logops; /* number of log operations in this LR : 4 */ __be32 h_cycle_data[XLOG_HEADER_CYCLE_SIZE / BBSIZE]; - /* new fields */ + + /* fields added by the Linux port: */ __be32 h_fmt; /* format of log record : 4 */ uuid_t h_fs_uuid; /* uuid of FS : 16 */ + + /* fields added for log v2: */ __be32 h_size; /* iclog size : 4 */ + + /* + * When h_size added for log v2 support, it caused structure to have + * a different size on i386 vs all other architectures because the + * sum of the size ofthe member is not aligned by that of the largest + * __be64-sized member, and i386 has really odd struct alignment rules. + * + * Due to the way the log headers are placed out on-disk that alone is + * not a problem becaue the xlog_rec_header always sits alone in a + * BBSIZEs area, and the rest of that area is padded with zeroes. + * But xlog_cksum used to calculate the checksum based on the structure + * size, and thus gives different checksums for i386 vs the rest. + * We now do two checksum validation passes for both sizes to allow + * moving v5 file systems with unclean logs between i386 and other + * (little-endian) architectures. + */ + __u32 h_pad0; } xlog_rec_header_t; +#ifdef __i386__ +#define XLOG_REC_SIZE offsetofend(struct xlog_rec_header, h_size) +#define XLOG_REC_SIZE_OTHER sizeof(struct xlog_rec_header) +#else +#define XLOG_REC_SIZE sizeof(struct xlog_rec_header) +#define XLOG_REC_SIZE_OTHER offsetofend(struct xlog_rec_header, h_size) +#endif /* __i386__ */ + typedef struct xlog_rec_ext_header { __be32 xh_cycle; /* write cycle of log : 4 */ __be32 xh_cycle_data[XLOG_HEADER_CYCLE_SIZE / BBSIZE]; /* : 256 */ diff --git a/fs/xfs/libxfs/xfs_ondisk.h b/fs/xfs/libxfs/xfs_ondisk.h index 23c133fd36f5bb..01b65d87745338 100644 --- a/fs/xfs/libxfs/xfs_ondisk.h +++ b/fs/xfs/libxfs/xfs_ondisk.h @@ -149,6 +149,8 @@ xfs_check_ondisk_structs(void) XFS_CHECK_STRUCT_SIZE(struct xfs_rud_log_format, 16); XFS_CHECK_STRUCT_SIZE(struct xfs_map_extent, 32); XFS_CHECK_STRUCT_SIZE(struct xfs_phys_extent, 16); + XFS_CHECK_STRUCT_SIZE(struct xlog_rec_header, 328); + XFS_CHECK_STRUCT_SIZE(struct xlog_rec_ext_header, 260); XFS_CHECK_OFFSET(struct xfs_bui_log_format, bui_extents, 16); XFS_CHECK_OFFSET(struct xfs_cui_log_format, cui_extents, 16); diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c index 26b2f5887b8819..a4c00decd97ba8 100644 --- a/fs/xfs/xfs_log.c +++ b/fs/xfs/xfs_log.c @@ -1567,13 +1567,13 @@ xlog_cksum( struct xlog *log, struct xlog_rec_header *rhead, char *dp, - int size) + unsigned int hdrsize, + unsigned int size) { uint32_t crc; /* first generate the crc for the record header ... */ - crc = xfs_start_cksum_update((char *)rhead, - sizeof(struct xlog_rec_header), + crc = xfs_start_cksum_update((char *)rhead, hdrsize, offsetof(struct xlog_rec_header, h_crc)); /* ... then for additional cycle data for v2 logs ... */ @@ -1837,7 +1837,7 @@ xlog_sync( /* calculcate the checksum */ iclog->ic_header.h_crc = xlog_cksum(log, &iclog->ic_header, - iclog->ic_datap, size); + iclog->ic_datap, XLOG_REC_SIZE, size); /* * Intentionally corrupt the log record CRC based on the error injection * frequency, if defined. This facilitates testing log recovery in the diff --git a/fs/xfs/xfs_log_priv.h b/fs/xfs/xfs_log_priv.h index b8778a4fd6b64e..89a75b7cbd6a47 100644 --- a/fs/xfs/xfs_log_priv.h +++ b/fs/xfs/xfs_log_priv.h @@ -498,8 +498,8 @@ xlog_recover_finish( extern void xlog_recover_cancel(struct xlog *); -extern __le32 xlog_cksum(struct xlog *log, struct xlog_rec_header *rhead, - char *dp, int size); +__le32 xlog_cksum(struct xlog *log, struct xlog_rec_header *rhead, + char *dp, unsigned int hdrsize, unsigned int size); extern struct kmem_cache *xfs_log_ticket_cache; struct xlog_ticket *xlog_ticket_alloc(struct xlog *log, int unit_bytes, diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index 4d5af8d508eb63..64338ce501c996 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -2890,9 +2890,24 @@ xlog_recover_process( int pass, struct list_head *buffer_list) { - __le32 expected_crc = rhead->h_crc, crc; + __le32 expected_crc = rhead->h_crc, crc, other_crc; - crc = xlog_cksum(log, rhead, dp, be32_to_cpu(rhead->h_len)); + crc = xlog_cksum(log, rhead, dp, XLOG_REC_SIZE, + be32_to_cpu(rhead->h_len)); + + /* + * Look at the end of the struct xlog_rec_header definition in + * xfs_log_format.h for the glory details. + */ + if (expected_crc && crc != expected_crc) { + other_crc = xlog_cksum(log, rhead, dp, XLOG_REC_SIZE_OTHER, + be32_to_cpu(rhead->h_len)); + if (other_crc == expected_crc) { + xfs_notice_once(log->l_mp, + "Fixing up incorrect CRC due to padding."); + crc = other_crc; + } + } /* * Nothing else to do if this is a CRC verification pass. Just return From f9ad5c7c472f3bb2437d7b26c707b77a6a5b0f28 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Mon, 20 Oct 2025 08:59:05 -0400 Subject: [PATCH 100/143] phy: cdns-dphy: Store hs_clk_rate and return it [ Upstream commit 689a54acb56858c85de8c7285db82b8ae6dbf683 ] The DPHY driver does not return the actual hs_clk_rate, so the DSI driver has no idea what clock was actually achieved. Set the realized hs_clk_rate to the opts struct, so that the DSI driver gets it back. Reviewed-by: Aradhya Bhatia Tested-by: Parth Pancholi Tested-by: Jayesh Choudhary Acked-by: Vinod Koul Reviewed-by: Devarsh Thakkar Signed-off-by: Tomi Valkeinen Link: https://lore.kernel.org/r/20250723-cdns-dphy-hs-clk-rate-fix-v1-1-d4539d44cbe7@ideasonboard.com Signed-off-by: Vinod Koul Stable-dep-of: 284fb19a3ffb ("phy: cadence: cdns-dphy: Fix PLL lock and O_CMN_READY polling") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/phy/cadence/cdns-dphy.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/phy/cadence/cdns-dphy.c b/drivers/phy/cadence/cdns-dphy.c index dddb66de6dba15..7e4a45085a66ac 100644 --- a/drivers/phy/cadence/cdns-dphy.c +++ b/drivers/phy/cadence/cdns-dphy.c @@ -79,6 +79,7 @@ struct cdns_dphy_cfg { u8 pll_ipdiv; u8 pll_opdiv; u16 pll_fbdiv; + u32 hs_clk_rate; unsigned int nlanes; }; @@ -154,6 +155,9 @@ static int cdns_dsi_get_dphy_pll_cfg(struct cdns_dphy *dphy, cfg->pll_ipdiv, pll_ref_hz); + cfg->hs_clk_rate = div_u64((u64)pll_ref_hz * cfg->pll_fbdiv, + 2 * cfg->pll_opdiv * cfg->pll_ipdiv); + return 0; } @@ -297,6 +301,7 @@ static int cdns_dphy_config_from_opts(struct phy *phy, if (ret) return ret; + opts->hs_clk_rate = cfg->hs_clk_rate; opts->wakeup = cdns_dphy_get_wakeup_time_ns(dphy) / 1000; return 0; From 4d1422bfef2d847e70eb0ca1c77f35592e312b55 Mon Sep 17 00:00:00 2001 From: Devarsh Thakkar Date: Mon, 20 Oct 2025 08:59:06 -0400 Subject: [PATCH 101/143] phy: cadence: cdns-dphy: Fix PLL lock and O_CMN_READY polling [ Upstream commit 284fb19a3ffb1083c3ad9c00d29749d09dddb99c ] PLL lockup and O_CMN_READY assertion can only happen after common state machine gets enabled by programming DPHY_CMN_SSM register, but driver was polling them before the common state machine was enabled which is incorrect. This is as per the DPHY initialization sequence as mentioned in J721E TRM [1] at section "12.7.2.4.1.2.1 Start-up Sequence Timing Diagram". It shows O_CMN_READY polling at the end after common configuration pin setup where the common configuration pin setup step enables state machine as referenced in "Table 12-1533. Common Configuration-Related Setup mentions state machine" To fix this : - Add new function callbacks for polling on PLL lock and O_CMN_READY assertion. - As state machine and clocks get enabled in power_on callback only, move the clock related programming part from configure callback to power_on callback and poll for the PLL lockup and O_CMN_READY assertion after state machine gets enabled. - The configure callback only saves the PLL configuration received from the client driver which will be applied later on in power_on callback. - Add checks to ensure configure is called before power_on and state machine is in disabled state before power_on callback is called. - Disable state machine in power_off so that client driver can re-configure the PLL by following up a power_off, configure, power_on sequence. [1]: https://www.ti.com/lit/zip/spruil1 Cc: stable@vger.kernel.org Fixes: 7a343c8bf4b5 ("phy: Add Cadence D-PHY support") Signed-off-by: Devarsh Thakkar Tested-by: Harikrishna Shenoy Reviewed-by: Tomi Valkeinen Link: https://lore.kernel.org/r/20250704125915.1224738-2-devarsht@ti.com Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/phy/cadence/cdns-dphy.c | 124 +++++++++++++++++++++++--------- 1 file changed, 92 insertions(+), 32 deletions(-) diff --git a/drivers/phy/cadence/cdns-dphy.c b/drivers/phy/cadence/cdns-dphy.c index 7e4a45085a66ac..a5d6d870034232 100644 --- a/drivers/phy/cadence/cdns-dphy.c +++ b/drivers/phy/cadence/cdns-dphy.c @@ -100,6 +100,8 @@ struct cdns_dphy_ops { void (*set_pll_cfg)(struct cdns_dphy *dphy, const struct cdns_dphy_cfg *cfg); unsigned long (*get_wakeup_time_ns)(struct cdns_dphy *dphy); + int (*wait_for_pll_lock)(struct cdns_dphy *dphy); + int (*wait_for_cmn_ready)(struct cdns_dphy *dphy); }; struct cdns_dphy { @@ -109,6 +111,8 @@ struct cdns_dphy { struct clk *pll_ref_clk; const struct cdns_dphy_ops *ops; struct phy *phy; + bool is_configured; + bool is_powered; }; /* Order of bands is important since the index is the band number. */ @@ -195,6 +199,16 @@ static unsigned long cdns_dphy_get_wakeup_time_ns(struct cdns_dphy *dphy) return dphy->ops->get_wakeup_time_ns(dphy); } +static int cdns_dphy_wait_for_pll_lock(struct cdns_dphy *dphy) +{ + return dphy->ops->wait_for_pll_lock ? dphy->ops->wait_for_pll_lock(dphy) : 0; +} + +static int cdns_dphy_wait_for_cmn_ready(struct cdns_dphy *dphy) +{ + return dphy->ops->wait_for_cmn_ready ? dphy->ops->wait_for_cmn_ready(dphy) : 0; +} + static unsigned long cdns_dphy_ref_get_wakeup_time_ns(struct cdns_dphy *dphy) { /* Default wakeup time is 800 ns (in a simulated environment). */ @@ -236,7 +250,6 @@ static unsigned long cdns_dphy_j721e_get_wakeup_time_ns(struct cdns_dphy *dphy) static void cdns_dphy_j721e_set_pll_cfg(struct cdns_dphy *dphy, const struct cdns_dphy_cfg *cfg) { - u32 status; /* * set the PWM and PLL Byteclk divider settings to recommended values @@ -253,13 +266,6 @@ static void cdns_dphy_j721e_set_pll_cfg(struct cdns_dphy *dphy, writel(DPHY_TX_J721E_WIZ_LANE_RSTB, dphy->regs + DPHY_TX_J721E_WIZ_RST_CTRL); - - readl_poll_timeout(dphy->regs + DPHY_TX_J721E_WIZ_PLL_CTRL, status, - (status & DPHY_TX_WIZ_PLL_LOCK), 0, POLL_TIMEOUT_US); - - readl_poll_timeout(dphy->regs + DPHY_TX_J721E_WIZ_STATUS, status, - (status & DPHY_TX_WIZ_O_CMN_READY), 0, - POLL_TIMEOUT_US); } static void cdns_dphy_j721e_set_psm_div(struct cdns_dphy *dphy, u8 div) @@ -267,6 +273,23 @@ static void cdns_dphy_j721e_set_psm_div(struct cdns_dphy *dphy, u8 div) writel(div, dphy->regs + DPHY_TX_J721E_WIZ_PSM_FREQ); } +static int cdns_dphy_j721e_wait_for_pll_lock(struct cdns_dphy *dphy) +{ + u32 status; + + return readl_poll_timeout(dphy->regs + DPHY_TX_J721E_WIZ_PLL_CTRL, status, + status & DPHY_TX_WIZ_PLL_LOCK, 0, POLL_TIMEOUT_US); +} + +static int cdns_dphy_j721e_wait_for_cmn_ready(struct cdns_dphy *dphy) +{ + u32 status; + + return readl_poll_timeout(dphy->regs + DPHY_TX_J721E_WIZ_STATUS, status, + status & DPHY_TX_WIZ_O_CMN_READY, 0, + POLL_TIMEOUT_US); +} + /* * This is the reference implementation of DPHY hooks. Specific integration of * this IP may have to re-implement some of them depending on how they decided @@ -282,6 +305,8 @@ static const struct cdns_dphy_ops j721e_dphy_ops = { .get_wakeup_time_ns = cdns_dphy_j721e_get_wakeup_time_ns, .set_pll_cfg = cdns_dphy_j721e_set_pll_cfg, .set_psm_div = cdns_dphy_j721e_set_psm_div, + .wait_for_pll_lock = cdns_dphy_j721e_wait_for_pll_lock, + .wait_for_cmn_ready = cdns_dphy_j721e_wait_for_cmn_ready, }; static int cdns_dphy_config_from_opts(struct phy *phy, @@ -339,21 +364,36 @@ static int cdns_dphy_validate(struct phy *phy, enum phy_mode mode, int submode, static int cdns_dphy_configure(struct phy *phy, union phy_configure_opts *opts) { struct cdns_dphy *dphy = phy_get_drvdata(phy); - struct cdns_dphy_cfg cfg = { 0 }; - int ret, band_ctrl; - unsigned int reg; + int ret; - ret = cdns_dphy_config_from_opts(phy, &opts->mipi_dphy, &cfg); - if (ret) - return ret; + ret = cdns_dphy_config_from_opts(phy, &opts->mipi_dphy, &dphy->cfg); + if (!ret) + dphy->is_configured = true; + + return ret; +} + +static int cdns_dphy_power_on(struct phy *phy) +{ + struct cdns_dphy *dphy = phy_get_drvdata(phy); + int ret; + u32 reg; + + if (!dphy->is_configured || dphy->is_powered) + return -EINVAL; + + clk_prepare_enable(dphy->psm_clk); + clk_prepare_enable(dphy->pll_ref_clk); /* * Configure the internal PSM clk divider so that the DPHY has a * 1MHz clk (or something close). */ ret = cdns_dphy_setup_psm(dphy); - if (ret) - return ret; + if (ret) { + dev_err(&dphy->phy->dev, "Failed to setup PSM with error %d\n", ret); + goto err_power_on; + } /* * Configure attach clk lanes to data lanes: the DPHY has 2 clk lanes @@ -368,40 +408,60 @@ static int cdns_dphy_configure(struct phy *phy, union phy_configure_opts *opts) * Configure the DPHY PLL that will be used to generate the TX byte * clk. */ - cdns_dphy_set_pll_cfg(dphy, &cfg); + cdns_dphy_set_pll_cfg(dphy, &dphy->cfg); - band_ctrl = cdns_dphy_tx_get_band_ctrl(opts->mipi_dphy.hs_clk_rate); - if (band_ctrl < 0) - return band_ctrl; + ret = cdns_dphy_tx_get_band_ctrl(dphy->cfg.hs_clk_rate); + if (ret < 0) { + dev_err(&dphy->phy->dev, "Failed to get band control value with error %d\n", ret); + goto err_power_on; + } - reg = FIELD_PREP(DPHY_BAND_CFG_LEFT_BAND, band_ctrl) | - FIELD_PREP(DPHY_BAND_CFG_RIGHT_BAND, band_ctrl); + reg = FIELD_PREP(DPHY_BAND_CFG_LEFT_BAND, ret) | + FIELD_PREP(DPHY_BAND_CFG_RIGHT_BAND, ret); writel(reg, dphy->regs + DPHY_BAND_CFG); - return 0; -} - -static int cdns_dphy_power_on(struct phy *phy) -{ - struct cdns_dphy *dphy = phy_get_drvdata(phy); - - clk_prepare_enable(dphy->psm_clk); - clk_prepare_enable(dphy->pll_ref_clk); - /* Start TX state machine. */ writel(DPHY_CMN_SSM_EN | DPHY_CMN_TX_MODE_EN, dphy->regs + DPHY_CMN_SSM); + ret = cdns_dphy_wait_for_pll_lock(dphy); + if (ret) { + dev_err(&dphy->phy->dev, "Failed to lock PLL with error %d\n", ret); + goto err_power_on; + } + + ret = cdns_dphy_wait_for_cmn_ready(dphy); + if (ret) { + dev_err(&dphy->phy->dev, "O_CMN_READY signal failed to assert with error %d\n", + ret); + goto err_power_on; + } + + dphy->is_powered = true; + return 0; + +err_power_on: + clk_disable_unprepare(dphy->pll_ref_clk); + clk_disable_unprepare(dphy->psm_clk); + + return ret; } static int cdns_dphy_power_off(struct phy *phy) { struct cdns_dphy *dphy = phy_get_drvdata(phy); + u32 reg; clk_disable_unprepare(dphy->pll_ref_clk); clk_disable_unprepare(dphy->psm_clk); + /* Stop TX state machine. */ + reg = readl(dphy->regs + DPHY_CMN_SSM); + writel(reg & ~DPHY_CMN_SSM_EN, dphy->regs + DPHY_CMN_SSM); + + dphy->is_powered = false; + return 0; } From 69a837b75edcea8861ea3a851d10fd138e7f7406 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bence=20Cs=C3=B3k=C3=A1s?= Date: Mon, 20 Oct 2025 09:02:38 -0400 Subject: [PATCH 102/143] PM: runtime: Add new devm functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 73db799bf5efc5a04654bb3ff6c9bf63a0dfa473 ] Add `devm_pm_runtime_set_active_enabled()` and `devm_pm_runtime_get_noresume()` for simplifying common cases in drivers. Signed-off-by: Bence Csókás Link: https://patch.msgid.link/20250327195928.680771-3-csokas.bence@prolan.hu Signed-off-by: Rafael J. Wysocki Stable-dep-of: 0792c1984a45 ("iio: imu: inv_icm42600: Simplify pm_runtime setup") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/base/power/runtime.c | 44 ++++++++++++++++++++++++++++++++++++ include/linux/pm_runtime.h | 4 ++++ 2 files changed, 48 insertions(+) diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index c7ec69597a955f..d8aaa5b61628b8 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -1554,6 +1554,32 @@ void pm_runtime_enable(struct device *dev) } EXPORT_SYMBOL_GPL(pm_runtime_enable); +static void pm_runtime_set_suspended_action(void *data) +{ + pm_runtime_set_suspended(data); +} + +/** + * devm_pm_runtime_set_active_enabled - set_active version of devm_pm_runtime_enable. + * + * @dev: Device to handle. + */ +int devm_pm_runtime_set_active_enabled(struct device *dev) +{ + int err; + + err = pm_runtime_set_active(dev); + if (err) + return err; + + err = devm_add_action_or_reset(dev, pm_runtime_set_suspended_action, dev); + if (err) + return err; + + return devm_pm_runtime_enable(dev); +} +EXPORT_SYMBOL_GPL(devm_pm_runtime_set_active_enabled); + static void pm_runtime_disable_action(void *data) { pm_runtime_dont_use_autosuspend(data); @@ -1576,6 +1602,24 @@ int devm_pm_runtime_enable(struct device *dev) } EXPORT_SYMBOL_GPL(devm_pm_runtime_enable); +static void pm_runtime_put_noidle_action(void *data) +{ + pm_runtime_put_noidle(data); +} + +/** + * devm_pm_runtime_get_noresume - devres-enabled version of pm_runtime_get_noresume. + * + * @dev: Device to handle. + */ +int devm_pm_runtime_get_noresume(struct device *dev) +{ + pm_runtime_get_noresume(dev); + + return devm_add_action_or_reset(dev, pm_runtime_put_noidle_action, dev); +} +EXPORT_SYMBOL_GPL(devm_pm_runtime_get_noresume); + /** * pm_runtime_forbid - Block runtime PM of a device. * @dev: Device to handle. diff --git a/include/linux/pm_runtime.h b/include/linux/pm_runtime.h index d0b29cd1fd204e..142ab39a3b386d 100644 --- a/include/linux/pm_runtime.h +++ b/include/linux/pm_runtime.h @@ -94,7 +94,9 @@ extern void pm_runtime_new_link(struct device *dev); extern void pm_runtime_drop_link(struct device_link *link); extern void pm_runtime_release_supplier(struct device_link *link); +int devm_pm_runtime_set_active_enabled(struct device *dev); extern int devm_pm_runtime_enable(struct device *dev); +int devm_pm_runtime_get_noresume(struct device *dev); /** * pm_suspend_ignore_children - Set runtime PM behavior regarding children. @@ -278,7 +280,9 @@ static inline void __pm_runtime_disable(struct device *dev, bool c) {} static inline void pm_runtime_allow(struct device *dev) {} static inline void pm_runtime_forbid(struct device *dev) {} +static inline int devm_pm_runtime_set_active_enabled(struct device *dev) { return 0; } static inline int devm_pm_runtime_enable(struct device *dev) { return 0; } +static inline int devm_pm_runtime_get_noresume(struct device *dev) { return 0; } static inline void pm_suspend_ignore_children(struct device *dev, bool enable) {} static inline void pm_runtime_get_noresume(struct device *dev) {} From 29c57a688bb486b7cc87d31fc886b1879fd96143 Mon Sep 17 00:00:00 2001 From: Sean Nyekjaer Date: Mon, 20 Oct 2025 09:02:39 -0400 Subject: [PATCH 103/143] iio: imu: inv_icm42600: Simplify pm_runtime setup [ Upstream commit 0792c1984a45ccd7a296d6b8cb78088bc99a212e ] Rework the power management in inv_icm42600_core_probe() to use devm_pm_runtime_set_active_enabled(), which simplifies the runtime PM setup by handling activation and enabling in one step. Remove the separate inv_icm42600_disable_pm callback, as it's no longer needed with the devm-managed approach. Using devm_pm_runtime_enable() also fixes the missing disable of autosuspend. Update inv_icm42600_disable_vddio_reg() to only disable the regulator if the device is not suspended i.e. powered-down, preventing unbalanced disables. Also remove redundant error msg on regulator_disable(), the regulator framework already emits an error message when regulator_disable() fails. This simplifies the PM setup and avoids manipulating the usage counter unnecessarily. Fixes: 31c24c1e93c3 ("iio: imu: inv_icm42600: add core of new inv_icm42600 driver") Signed-off-by: Sean Nyekjaer Link: https://patch.msgid.link/20250901-icm42pmreg-v3-1-ef1336246960@geanix.com Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- .../iio/imu/inv_icm42600/inv_icm42600_core.c | 24 ++++++------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c index 790bc8fbf21da3..f7acf2def71b23 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c @@ -667,20 +667,12 @@ static void inv_icm42600_disable_vdd_reg(void *_data) static void inv_icm42600_disable_vddio_reg(void *_data) { struct inv_icm42600_state *st = _data; - const struct device *dev = regmap_get_device(st->map); - int ret; - - ret = regulator_disable(st->vddio_supply); - if (ret) - dev_err(dev, "failed to disable vddio error %d\n", ret); -} + struct device *dev = regmap_get_device(st->map); -static void inv_icm42600_disable_pm(void *_data) -{ - struct device *dev = _data; + if (pm_runtime_status_suspended(dev)) + return; - pm_runtime_put_sync(dev); - pm_runtime_disable(dev); + regulator_disable(st->vddio_supply); } int inv_icm42600_core_probe(struct regmap *regmap, int chip, int irq, @@ -777,16 +769,14 @@ int inv_icm42600_core_probe(struct regmap *regmap, int chip, int irq, return ret; /* setup runtime power management */ - ret = pm_runtime_set_active(dev); + ret = devm_pm_runtime_set_active_enabled(dev); if (ret) return ret; - pm_runtime_get_noresume(dev); - pm_runtime_enable(dev); + pm_runtime_set_autosuspend_delay(dev, INV_ICM42600_SUSPEND_DELAY_MS); pm_runtime_use_autosuspend(dev); - pm_runtime_put(dev); - return devm_add_action_or_reset(dev, inv_icm42600_disable_pm, dev); + return ret; } EXPORT_SYMBOL_NS_GPL(inv_icm42600_core_probe, IIO_ICM42600); From 7e708dbee2e828867d0858fe91f3426b5c9f1a76 Mon Sep 17 00:00:00 2001 From: Sean Nyekjaer Date: Mon, 20 Oct 2025 09:08:10 -0400 Subject: [PATCH 104/143] iio: imu: inv_icm42600: Avoid configuring if already pm_runtime suspended [ Upstream commit 466f7a2fef2a4e426f809f79845a1ec1aeb558f4 ] Do as in suspend, skip resume configuration steps if the device is already pm_runtime suspended. This avoids reconfiguring a device that is already in the correct low-power state and ensures that pm_runtime handles the power state transitions properly. Fixes: 31c24c1e93c3 ("iio: imu: inv_icm42600: add core of new inv_icm42600 driver") Signed-off-by: Sean Nyekjaer Link: https://patch.msgid.link/20250901-icm42pmreg-v3-3-ef1336246960@geanix.com Cc: Signed-off-by: Jonathan Cameron [ removed apex/wakeup variable declarations ] Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/iio/imu/inv_icm42600/inv_icm42600_core.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c index f7acf2def71b23..9f88d8ca6d1b1f 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c @@ -787,17 +787,15 @@ EXPORT_SYMBOL_NS_GPL(inv_icm42600_core_probe, IIO_ICM42600); static int inv_icm42600_suspend(struct device *dev) { struct inv_icm42600_state *st = dev_get_drvdata(dev); - int ret; + int ret = 0; mutex_lock(&st->lock); st->suspended.gyro = st->conf.gyro.mode; st->suspended.accel = st->conf.accel.mode; st->suspended.temp = st->conf.temp_en; - if (pm_runtime_suspended(dev)) { - ret = 0; + if (pm_runtime_suspended(dev)) goto out_unlock; - } /* disable FIFO data streaming */ if (st->fifo.on) { @@ -829,10 +827,13 @@ static int inv_icm42600_resume(struct device *dev) struct inv_icm42600_state *st = dev_get_drvdata(dev); struct inv_icm42600_sensor_state *gyro_st = iio_priv(st->indio_gyro); struct inv_icm42600_sensor_state *accel_st = iio_priv(st->indio_accel); - int ret; + int ret = 0; mutex_lock(&st->lock); + if (pm_runtime_suspended(dev)) + goto out_unlock; + ret = inv_icm42600_enable_regulator_vddio(st); if (ret) goto out_unlock; From 434b399044aeba3eb6536578bc1c2898b2b5c016 Mon Sep 17 00:00:00 2001 From: Sergey Bashirov Date: Mon, 20 Oct 2025 08:52:59 -0400 Subject: [PATCH 105/143] nfsd: Use correct error code when decoding extents [ Upstream commit 26d05e1c37d276905bc921384b5a75158fca284b ] Update error codes in decoding functions of block and scsi layout drivers to match the core nfsd code. NFS4ERR_EINVAL means that the server was able to decode the request, but the decoded values are invalid. Use NFS4ERR_BADXDR instead to indicate a decoding error. And ENOMEM is changed to nfs code NFS4ERR_DELAY. Signed-off-by: Sergey Bashirov Reviewed-by: Christoph Hellwig Signed-off-by: Chuck Lever Stable-dep-of: d68886bae76a ("NFSD: Fix last write offset handling in layoutcommit") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/blocklayout.c | 20 ++++++----- fs/nfsd/blocklayoutxdr.c | 71 +++++++++++++++++++++++++++++++--------- fs/nfsd/blocklayoutxdr.h | 8 ++--- fs/nfsd/nfsd.h | 1 + 4 files changed, 73 insertions(+), 27 deletions(-) diff --git a/fs/nfsd/blocklayout.c b/fs/nfsd/blocklayout.c index 08a20e5bcf7fee..19078a043e85c5 100644 --- a/fs/nfsd/blocklayout.c +++ b/fs/nfsd/blocklayout.c @@ -178,11 +178,13 @@ nfsd4_block_proc_layoutcommit(struct inode *inode, { struct iomap *iomaps; int nr_iomaps; + __be32 nfserr; - nr_iomaps = nfsd4_block_decode_layoutupdate(lcp->lc_up_layout, - lcp->lc_up_len, &iomaps, i_blocksize(inode)); - if (nr_iomaps < 0) - return nfserrno(nr_iomaps); + nfserr = nfsd4_block_decode_layoutupdate(lcp->lc_up_layout, + lcp->lc_up_len, &iomaps, &nr_iomaps, + i_blocksize(inode)); + if (nfserr != nfs_ok) + return nfserr; return nfsd4_block_commit_blocks(inode, lcp, iomaps, nr_iomaps); } @@ -316,11 +318,13 @@ nfsd4_scsi_proc_layoutcommit(struct inode *inode, { struct iomap *iomaps; int nr_iomaps; + __be32 nfserr; - nr_iomaps = nfsd4_scsi_decode_layoutupdate(lcp->lc_up_layout, - lcp->lc_up_len, &iomaps, i_blocksize(inode)); - if (nr_iomaps < 0) - return nfserrno(nr_iomaps); + nfserr = nfsd4_scsi_decode_layoutupdate(lcp->lc_up_layout, + lcp->lc_up_len, &iomaps, &nr_iomaps, + i_blocksize(inode)); + if (nfserr != nfs_ok) + return nfserr; return nfsd4_block_commit_blocks(inode, lcp, iomaps, nr_iomaps); } diff --git a/fs/nfsd/blocklayoutxdr.c b/fs/nfsd/blocklayoutxdr.c index ce78f74715eead..669ff8e6e966e3 100644 --- a/fs/nfsd/blocklayoutxdr.c +++ b/fs/nfsd/blocklayoutxdr.c @@ -112,34 +112,54 @@ nfsd4_block_encode_getdeviceinfo(struct xdr_stream *xdr, return 0; } -int +/** + * nfsd4_block_decode_layoutupdate - decode the block layout extent array + * @p: pointer to the xdr data + * @len: number of bytes to decode + * @iomapp: pointer to store the decoded extent array + * @nr_iomapsp: pointer to store the number of extents + * @block_size: alignment of extent offset and length + * + * This function decodes the opaque field of the layoutupdate4 structure + * in a layoutcommit request for the block layout driver. The field is + * actually an array of extents sent by the client. It also checks that + * the file offset, storage offset and length of each extent are aligned + * by @block_size. + * + * Return values: + * %nfs_ok: Successful decoding, @iomapp and @nr_iomapsp are valid + * %nfserr_bad_xdr: The encoded array in @p is invalid + * %nfserr_inval: An unaligned extent found + * %nfserr_delay: Failed to allocate memory for @iomapp + */ +__be32 nfsd4_block_decode_layoutupdate(__be32 *p, u32 len, struct iomap **iomapp, - u32 block_size) + int *nr_iomapsp, u32 block_size) { struct iomap *iomaps; u32 nr_iomaps, i; if (len < sizeof(u32)) { dprintk("%s: extent array too small: %u\n", __func__, len); - return -EINVAL; + return nfserr_bad_xdr; } len -= sizeof(u32); if (len % PNFS_BLOCK_EXTENT_SIZE) { dprintk("%s: extent array invalid: %u\n", __func__, len); - return -EINVAL; + return nfserr_bad_xdr; } nr_iomaps = be32_to_cpup(p++); if (nr_iomaps != len / PNFS_BLOCK_EXTENT_SIZE) { dprintk("%s: extent array size mismatch: %u/%u\n", __func__, len, nr_iomaps); - return -EINVAL; + return nfserr_bad_xdr; } iomaps = kcalloc(nr_iomaps, sizeof(*iomaps), GFP_KERNEL); if (!iomaps) { dprintk("%s: failed to allocate extent array\n", __func__); - return -ENOMEM; + return nfserr_delay; } for (i = 0; i < nr_iomaps; i++) { @@ -178,22 +198,42 @@ nfsd4_block_decode_layoutupdate(__be32 *p, u32 len, struct iomap **iomapp, } *iomapp = iomaps; - return nr_iomaps; + *nr_iomapsp = nr_iomaps; + return nfs_ok; fail: kfree(iomaps); - return -EINVAL; + return nfserr_inval; } -int +/** + * nfsd4_scsi_decode_layoutupdate - decode the scsi layout extent array + * @p: pointer to the xdr data + * @len: number of bytes to decode + * @iomapp: pointer to store the decoded extent array + * @nr_iomapsp: pointer to store the number of extents + * @block_size: alignment of extent offset and length + * + * This function decodes the opaque field of the layoutupdate4 structure + * in a layoutcommit request for the scsi layout driver. The field is + * actually an array of extents sent by the client. It also checks that + * the offset and length of each extent are aligned by @block_size. + * + * Return values: + * %nfs_ok: Successful decoding, @iomapp and @nr_iomapsp are valid + * %nfserr_bad_xdr: The encoded array in @p is invalid + * %nfserr_inval: An unaligned extent found + * %nfserr_delay: Failed to allocate memory for @iomapp + */ +__be32 nfsd4_scsi_decode_layoutupdate(__be32 *p, u32 len, struct iomap **iomapp, - u32 block_size) + int *nr_iomapsp, u32 block_size) { struct iomap *iomaps; u32 nr_iomaps, expected, i; if (len < sizeof(u32)) { dprintk("%s: extent array too small: %u\n", __func__, len); - return -EINVAL; + return nfserr_bad_xdr; } nr_iomaps = be32_to_cpup(p++); @@ -201,13 +241,13 @@ nfsd4_scsi_decode_layoutupdate(__be32 *p, u32 len, struct iomap **iomapp, if (len != expected) { dprintk("%s: extent array size mismatch: %u/%u\n", __func__, len, expected); - return -EINVAL; + return nfserr_bad_xdr; } iomaps = kcalloc(nr_iomaps, sizeof(*iomaps), GFP_KERNEL); if (!iomaps) { dprintk("%s: failed to allocate extent array\n", __func__); - return -ENOMEM; + return nfserr_delay; } for (i = 0; i < nr_iomaps; i++) { @@ -229,8 +269,9 @@ nfsd4_scsi_decode_layoutupdate(__be32 *p, u32 len, struct iomap **iomapp, } *iomapp = iomaps; - return nr_iomaps; + *nr_iomapsp = nr_iomaps; + return nfs_ok; fail: kfree(iomaps); - return -EINVAL; + return nfserr_inval; } diff --git a/fs/nfsd/blocklayoutxdr.h b/fs/nfsd/blocklayoutxdr.h index 4e28ac8f112797..15b3569f3d9ad3 100644 --- a/fs/nfsd/blocklayoutxdr.h +++ b/fs/nfsd/blocklayoutxdr.h @@ -54,9 +54,9 @@ __be32 nfsd4_block_encode_getdeviceinfo(struct xdr_stream *xdr, const struct nfsd4_getdeviceinfo *gdp); __be32 nfsd4_block_encode_layoutget(struct xdr_stream *xdr, const struct nfsd4_layoutget *lgp); -int nfsd4_block_decode_layoutupdate(__be32 *p, u32 len, struct iomap **iomapp, - u32 block_size); -int nfsd4_scsi_decode_layoutupdate(__be32 *p, u32 len, struct iomap **iomapp, - u32 block_size); +__be32 nfsd4_block_decode_layoutupdate(__be32 *p, u32 len, + struct iomap **iomapp, int *nr_iomapsp, u32 block_size); +__be32 nfsd4_scsi_decode_layoutupdate(__be32 *p, u32 len, + struct iomap **iomapp, int *nr_iomapsp, u32 block_size); #endif /* _NFSD_BLOCKLAYOUTXDR_H */ diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h index 4b56ba1e8e48d0..ae435444e8b3b1 100644 --- a/fs/nfsd/nfsd.h +++ b/fs/nfsd/nfsd.h @@ -286,6 +286,7 @@ void nfsd_lockd_shutdown(void); #define nfserr_cb_path_down cpu_to_be32(NFSERR_CB_PATH_DOWN) #define nfserr_locked cpu_to_be32(NFSERR_LOCKED) #define nfserr_wrongsec cpu_to_be32(NFSERR_WRONGSEC) +#define nfserr_delay cpu_to_be32(NFS4ERR_DELAY) #define nfserr_badiomode cpu_to_be32(NFS4ERR_BADIOMODE) #define nfserr_badlayout cpu_to_be32(NFS4ERR_BADLAYOUT) #define nfserr_bad_session_digest cpu_to_be32(NFS4ERR_BAD_SESSION_DIGEST) From 5def53c55a1ee48de4e68ceb2a958ed5b9ba2c93 Mon Sep 17 00:00:00 2001 From: Sergey Bashirov Date: Mon, 20 Oct 2025 08:53:00 -0400 Subject: [PATCH 106/143] nfsd: Drop dprintk in blocklayout xdr functions [ Upstream commit e339967eecf1305557f7c697e1bc10b5cc495454 ] Minor clean up. Instead of dprintk there are appropriate error codes. Signed-off-by: Sergey Bashirov Reviewed-by: Jeff Layton Reviewed-by: Christoph Hellwig Signed-off-by: Chuck Lever Stable-dep-of: d68886bae76a ("NFSD: Fix last write offset handling in layoutcommit") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/blocklayoutxdr.c | 40 +++++++--------------------------------- 1 file changed, 7 insertions(+), 33 deletions(-) diff --git a/fs/nfsd/blocklayoutxdr.c b/fs/nfsd/blocklayoutxdr.c index 669ff8e6e966e3..bcf21fde912077 100644 --- a/fs/nfsd/blocklayoutxdr.c +++ b/fs/nfsd/blocklayoutxdr.c @@ -139,28 +139,19 @@ nfsd4_block_decode_layoutupdate(__be32 *p, u32 len, struct iomap **iomapp, struct iomap *iomaps; u32 nr_iomaps, i; - if (len < sizeof(u32)) { - dprintk("%s: extent array too small: %u\n", __func__, len); + if (len < sizeof(u32)) return nfserr_bad_xdr; - } len -= sizeof(u32); - if (len % PNFS_BLOCK_EXTENT_SIZE) { - dprintk("%s: extent array invalid: %u\n", __func__, len); + if (len % PNFS_BLOCK_EXTENT_SIZE) return nfserr_bad_xdr; - } nr_iomaps = be32_to_cpup(p++); - if (nr_iomaps != len / PNFS_BLOCK_EXTENT_SIZE) { - dprintk("%s: extent array size mismatch: %u/%u\n", - __func__, len, nr_iomaps); + if (nr_iomaps != len / PNFS_BLOCK_EXTENT_SIZE) return nfserr_bad_xdr; - } iomaps = kcalloc(nr_iomaps, sizeof(*iomaps), GFP_KERNEL); - if (!iomaps) { - dprintk("%s: failed to allocate extent array\n", __func__); + if (!iomaps) return nfserr_delay; - } for (i = 0; i < nr_iomaps; i++) { struct pnfs_block_extent bex; @@ -170,26 +161,18 @@ nfsd4_block_decode_layoutupdate(__be32 *p, u32 len, struct iomap **iomapp, p = xdr_decode_hyper(p, &bex.foff); if (bex.foff & (block_size - 1)) { - dprintk("%s: unaligned offset 0x%llx\n", - __func__, bex.foff); goto fail; } p = xdr_decode_hyper(p, &bex.len); if (bex.len & (block_size - 1)) { - dprintk("%s: unaligned length 0x%llx\n", - __func__, bex.foff); goto fail; } p = xdr_decode_hyper(p, &bex.soff); if (bex.soff & (block_size - 1)) { - dprintk("%s: unaligned disk offset 0x%llx\n", - __func__, bex.soff); goto fail; } bex.es = be32_to_cpup(p++); if (bex.es != PNFS_BLOCK_READWRITE_DATA) { - dprintk("%s: incorrect extent state %d\n", - __func__, bex.es); goto fail; } @@ -231,38 +214,29 @@ nfsd4_scsi_decode_layoutupdate(__be32 *p, u32 len, struct iomap **iomapp, struct iomap *iomaps; u32 nr_iomaps, expected, i; - if (len < sizeof(u32)) { - dprintk("%s: extent array too small: %u\n", __func__, len); + if (len < sizeof(u32)) return nfserr_bad_xdr; - } nr_iomaps = be32_to_cpup(p++); expected = sizeof(__be32) + nr_iomaps * PNFS_SCSI_RANGE_SIZE; - if (len != expected) { - dprintk("%s: extent array size mismatch: %u/%u\n", - __func__, len, expected); + if (len != expected) return nfserr_bad_xdr; - } iomaps = kcalloc(nr_iomaps, sizeof(*iomaps), GFP_KERNEL); - if (!iomaps) { - dprintk("%s: failed to allocate extent array\n", __func__); + if (!iomaps) return nfserr_delay; - } for (i = 0; i < nr_iomaps; i++) { u64 val; p = xdr_decode_hyper(p, &val); if (val & (block_size - 1)) { - dprintk("%s: unaligned offset 0x%llx\n", __func__, val); goto fail; } iomaps[i].offset = val; p = xdr_decode_hyper(p, &val); if (val & (block_size - 1)) { - dprintk("%s: unaligned length 0x%llx\n", __func__, val); goto fail; } iomaps[i].length = val; From 47c609979b085e88c18141cd9f2f4b02c09a76ec Mon Sep 17 00:00:00 2001 From: Sergey Bashirov Date: Mon, 20 Oct 2025 08:53:01 -0400 Subject: [PATCH 107/143] NFSD: Rework encoding and decoding of nfsd4_deviceid [ Upstream commit 832738e4b325b742940761e10487403f9aad13e8 ] Compilers may optimize the layout of C structures, so we should not rely on sizeof struct and memcpy to encode and decode XDR structures. The byte order of the fields should also be taken into account. This patch adds the correct functions to handle the deviceid4 structure and removes the pad field, which is currently not used by NFSD, from the runtime state. The server's byte order is preserved because the deviceid4 blob on the wire is only used as a cookie by the client. Signed-off-by: Sergey Bashirov Signed-off-by: Chuck Lever Stable-dep-of: d68886bae76a ("NFSD: Fix last write offset handling in layoutcommit") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/blocklayoutxdr.c | 7 ++----- fs/nfsd/flexfilelayoutxdr.c | 3 +-- fs/nfsd/nfs4layouts.c | 1 - fs/nfsd/nfs4xdr.c | 14 +------------- fs/nfsd/xdr4.h | 36 +++++++++++++++++++++++++++++++++++- 5 files changed, 39 insertions(+), 22 deletions(-) diff --git a/fs/nfsd/blocklayoutxdr.c b/fs/nfsd/blocklayoutxdr.c index bcf21fde912077..18de37ff289166 100644 --- a/fs/nfsd/blocklayoutxdr.c +++ b/fs/nfsd/blocklayoutxdr.c @@ -29,8 +29,7 @@ nfsd4_block_encode_layoutget(struct xdr_stream *xdr, *p++ = cpu_to_be32(len); *p++ = cpu_to_be32(1); /* we always return a single extent */ - p = xdr_encode_opaque_fixed(p, &b->vol_id, - sizeof(struct nfsd4_deviceid)); + p = svcxdr_encode_deviceid4(p, &b->vol_id); p = xdr_encode_hyper(p, b->foff); p = xdr_encode_hyper(p, b->len); p = xdr_encode_hyper(p, b->soff); @@ -156,9 +155,7 @@ nfsd4_block_decode_layoutupdate(__be32 *p, u32 len, struct iomap **iomapp, for (i = 0; i < nr_iomaps; i++) { struct pnfs_block_extent bex; - memcpy(&bex.vol_id, p, sizeof(struct nfsd4_deviceid)); - p += XDR_QUADLEN(sizeof(struct nfsd4_deviceid)); - + p = svcxdr_decode_deviceid4(p, &bex.vol_id); p = xdr_decode_hyper(p, &bex.foff); if (bex.foff & (block_size - 1)) { goto fail; diff --git a/fs/nfsd/flexfilelayoutxdr.c b/fs/nfsd/flexfilelayoutxdr.c index aeb71c10ff1b96..f9f7e38cba13fb 100644 --- a/fs/nfsd/flexfilelayoutxdr.c +++ b/fs/nfsd/flexfilelayoutxdr.c @@ -54,8 +54,7 @@ nfsd4_ff_encode_layoutget(struct xdr_stream *xdr, *p++ = cpu_to_be32(1); /* single mirror */ *p++ = cpu_to_be32(1); /* single data server */ - p = xdr_encode_opaque_fixed(p, &fl->deviceid, - sizeof(struct nfsd4_deviceid)); + p = svcxdr_encode_deviceid4(p, &fl->deviceid); *p++ = cpu_to_be32(1); /* efficiency */ diff --git a/fs/nfsd/nfs4layouts.c b/fs/nfsd/nfs4layouts.c index fbfddd3c4c943c..fc5e82eddaa1af 100644 --- a/fs/nfsd/nfs4layouts.c +++ b/fs/nfsd/nfs4layouts.c @@ -120,7 +120,6 @@ nfsd4_set_deviceid(struct nfsd4_deviceid *id, const struct svc_fh *fhp, id->fsid_idx = fhp->fh_export->ex_devid_map->idx; id->generation = device_generation; - id->pad = 0; return 0; } diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 90db900b346ce9..bd5c8720ea7e36 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -566,18 +566,6 @@ nfsd4_decode_state_owner4(struct nfsd4_compoundargs *argp, } #ifdef CONFIG_NFSD_PNFS -static __be32 -nfsd4_decode_deviceid4(struct nfsd4_compoundargs *argp, - struct nfsd4_deviceid *devid) -{ - __be32 *p; - - p = xdr_inline_decode(argp->xdr, NFS4_DEVICEID4_SIZE); - if (!p) - return nfserr_bad_xdr; - memcpy(devid, p, sizeof(*devid)); - return nfs_ok; -} static __be32 nfsd4_decode_layoutupdate4(struct nfsd4_compoundargs *argp, @@ -1762,7 +1750,7 @@ nfsd4_decode_getdeviceinfo(struct nfsd4_compoundargs *argp, __be32 status; memset(gdev, 0, sizeof(*gdev)); - status = nfsd4_decode_deviceid4(argp, &gdev->gd_devid); + status = nfsd4_decode_deviceid4(argp->xdr, &gdev->gd_devid); if (status) return status; if (xdr_stream_decode_u32(argp->xdr, &gdev->gd_layout_type) < 0) diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h index 2a21a7662e030c..83263bff27dc61 100644 --- a/fs/nfsd/xdr4.h +++ b/fs/nfsd/xdr4.h @@ -596,9 +596,43 @@ struct nfsd4_reclaim_complete { struct nfsd4_deviceid { u64 fsid_idx; u32 generation; - u32 pad; }; +static inline __be32 * +svcxdr_encode_deviceid4(__be32 *p, const struct nfsd4_deviceid *devid) +{ + __be64 *q = (__be64 *)p; + + *q = (__force __be64)devid->fsid_idx; + p += 2; + *p++ = (__force __be32)devid->generation; + *p++ = xdr_zero; + return p; +} + +static inline __be32 * +svcxdr_decode_deviceid4(__be32 *p, struct nfsd4_deviceid *devid) +{ + __be64 *q = (__be64 *)p; + + devid->fsid_idx = (__force u64)(*q); + p += 2; + devid->generation = (__force u32)(*p++); + p++; /* NFSD does not use the remaining octets */ + return p; +} + +static inline __be32 +nfsd4_decode_deviceid4(struct xdr_stream *xdr, struct nfsd4_deviceid *devid) +{ + __be32 *p = xdr_inline_decode(xdr, NFS4_DEVICEID4_SIZE); + + if (unlikely(!p)) + return nfserr_bad_xdr; + svcxdr_decode_deviceid4(p, devid); + return nfs_ok; +} + struct nfsd4_layout_seg { u32 iomode; u64 offset; From 18eee640741cbb499a67f71b9e8e061cf929a9ae Mon Sep 17 00:00:00 2001 From: Sergey Bashirov Date: Mon, 20 Oct 2025 08:53:02 -0400 Subject: [PATCH 108/143] NFSD: Minor cleanup in layoutcommit processing [ Upstream commit 274365a51d88658fb51cca637ba579034e90a799 ] Remove dprintk in nfsd4_layoutcommit. These are not needed in day to day usage, and the information is also available in Wireshark when capturing NFS traffic. Reviewed-by: Christoph Hellwig Signed-off-by: Sergey Bashirov Signed-off-by: Chuck Lever Stable-dep-of: d68886bae76a ("NFSD: Fix last write offset handling in layoutcommit") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/nfs4proc.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 8f2dc7eb4fc451..c01183ddc93f27 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -2379,18 +2379,12 @@ nfsd4_layoutcommit(struct svc_rqst *rqstp, inode = d_inode(current_fh->fh_dentry); nfserr = nfserr_inval; - if (new_size <= seg->offset) { - dprintk("pnfsd: last write before layout segment\n"); + if (new_size <= seg->offset) goto out; - } - if (new_size > seg->offset + seg->length) { - dprintk("pnfsd: last write beyond layout segment\n"); + if (new_size > seg->offset + seg->length) goto out; - } - if (!lcp->lc_newoffset && new_size > i_size_read(inode)) { - dprintk("pnfsd: layoutcommit beyond EOF\n"); + if (!lcp->lc_newoffset && new_size > i_size_read(inode)) goto out; - } nfserr = nfsd4_preprocess_layout_stateid(rqstp, cstate, &lcp->lc_sid, false, lcp->lc_layout_type, From da68bc55d5f892b0e27ca2eae40803d4621508c5 Mon Sep 17 00:00:00 2001 From: Sergey Bashirov Date: Mon, 20 Oct 2025 08:53:03 -0400 Subject: [PATCH 109/143] NFSD: Implement large extent array support in pNFS [ Upstream commit f963cf2b91a30b5614c514f3ad53ca124cb65280 ] When pNFS client in the block or scsi layout mode sends layoutcommit to MDS, a variable length array of modified extents is supplied within the request. This patch allows the server to accept such extent arrays if they do not fit within single memory page. The issue can be reproduced when writing to a 1GB file using FIO with O_DIRECT, 4K block and large I/O depth without preallocation of the file. In this case, the server returns NFSERR_BADXDR to the client. Co-developed-by: Konstantin Evtushenko Signed-off-by: Konstantin Evtushenko Signed-off-by: Sergey Bashirov Reviewed-by: Jeff Layton Reviewed-by: Christoph Hellwig Signed-off-by: Chuck Lever Stable-dep-of: d68886bae76a ("NFSD: Fix last write offset handling in layoutcommit") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/blocklayout.c | 20 ++++++---- fs/nfsd/blocklayoutxdr.c | 83 +++++++++++++++++++++++++++------------- fs/nfsd/blocklayoutxdr.h | 4 +- fs/nfsd/nfs4proc.c | 2 +- fs/nfsd/nfs4xdr.c | 11 +++--- fs/nfsd/pnfs.h | 1 + fs/nfsd/xdr4.h | 3 +- 7 files changed, 78 insertions(+), 46 deletions(-) diff --git a/fs/nfsd/blocklayout.c b/fs/nfsd/blocklayout.c index 19078a043e85c5..4c936132eb4403 100644 --- a/fs/nfsd/blocklayout.c +++ b/fs/nfsd/blocklayout.c @@ -173,16 +173,18 @@ nfsd4_block_proc_getdeviceinfo(struct super_block *sb, } static __be32 -nfsd4_block_proc_layoutcommit(struct inode *inode, +nfsd4_block_proc_layoutcommit(struct inode *inode, struct svc_rqst *rqstp, struct nfsd4_layoutcommit *lcp) { struct iomap *iomaps; int nr_iomaps; __be32 nfserr; - nfserr = nfsd4_block_decode_layoutupdate(lcp->lc_up_layout, - lcp->lc_up_len, &iomaps, &nr_iomaps, - i_blocksize(inode)); + rqstp->rq_arg = lcp->lc_up_layout; + svcxdr_init_decode(rqstp); + + nfserr = nfsd4_block_decode_layoutupdate(&rqstp->rq_arg_stream, + &iomaps, &nr_iomaps, i_blocksize(inode)); if (nfserr != nfs_ok) return nfserr; @@ -313,16 +315,18 @@ nfsd4_scsi_proc_getdeviceinfo(struct super_block *sb, return nfserrno(nfsd4_block_get_device_info_scsi(sb, clp, gdp)); } static __be32 -nfsd4_scsi_proc_layoutcommit(struct inode *inode, +nfsd4_scsi_proc_layoutcommit(struct inode *inode, struct svc_rqst *rqstp, struct nfsd4_layoutcommit *lcp) { struct iomap *iomaps; int nr_iomaps; __be32 nfserr; - nfserr = nfsd4_scsi_decode_layoutupdate(lcp->lc_up_layout, - lcp->lc_up_len, &iomaps, &nr_iomaps, - i_blocksize(inode)); + rqstp->rq_arg = lcp->lc_up_layout; + svcxdr_init_decode(rqstp); + + nfserr = nfsd4_scsi_decode_layoutupdate(&rqstp->rq_arg_stream, + &iomaps, &nr_iomaps, i_blocksize(inode)); if (nfserr != nfs_ok) return nfserr; diff --git a/fs/nfsd/blocklayoutxdr.c b/fs/nfsd/blocklayoutxdr.c index 18de37ff289166..e50afe34073719 100644 --- a/fs/nfsd/blocklayoutxdr.c +++ b/fs/nfsd/blocklayoutxdr.c @@ -113,8 +113,7 @@ nfsd4_block_encode_getdeviceinfo(struct xdr_stream *xdr, /** * nfsd4_block_decode_layoutupdate - decode the block layout extent array - * @p: pointer to the xdr data - * @len: number of bytes to decode + * @xdr: subbuf set to the encoded array * @iomapp: pointer to store the decoded extent array * @nr_iomapsp: pointer to store the number of extents * @block_size: alignment of extent offset and length @@ -127,25 +126,24 @@ nfsd4_block_encode_getdeviceinfo(struct xdr_stream *xdr, * * Return values: * %nfs_ok: Successful decoding, @iomapp and @nr_iomapsp are valid - * %nfserr_bad_xdr: The encoded array in @p is invalid + * %nfserr_bad_xdr: The encoded array in @xdr is invalid * %nfserr_inval: An unaligned extent found * %nfserr_delay: Failed to allocate memory for @iomapp */ __be32 -nfsd4_block_decode_layoutupdate(__be32 *p, u32 len, struct iomap **iomapp, +nfsd4_block_decode_layoutupdate(struct xdr_stream *xdr, struct iomap **iomapp, int *nr_iomapsp, u32 block_size) { struct iomap *iomaps; - u32 nr_iomaps, i; + u32 nr_iomaps, expected, len, i; + __be32 nfserr; - if (len < sizeof(u32)) - return nfserr_bad_xdr; - len -= sizeof(u32); - if (len % PNFS_BLOCK_EXTENT_SIZE) + if (xdr_stream_decode_u32(xdr, &nr_iomaps)) return nfserr_bad_xdr; - nr_iomaps = be32_to_cpup(p++); - if (nr_iomaps != len / PNFS_BLOCK_EXTENT_SIZE) + len = sizeof(__be32) + xdr_stream_remaining(xdr); + expected = sizeof(__be32) + nr_iomaps * PNFS_BLOCK_EXTENT_SIZE; + if (len != expected) return nfserr_bad_xdr; iomaps = kcalloc(nr_iomaps, sizeof(*iomaps), GFP_KERNEL); @@ -155,21 +153,44 @@ nfsd4_block_decode_layoutupdate(__be32 *p, u32 len, struct iomap **iomapp, for (i = 0; i < nr_iomaps; i++) { struct pnfs_block_extent bex; - p = svcxdr_decode_deviceid4(p, &bex.vol_id); - p = xdr_decode_hyper(p, &bex.foff); + if (nfsd4_decode_deviceid4(xdr, &bex.vol_id)) { + nfserr = nfserr_bad_xdr; + goto fail; + } + + if (xdr_stream_decode_u64(xdr, &bex.foff)) { + nfserr = nfserr_bad_xdr; + goto fail; + } if (bex.foff & (block_size - 1)) { + nfserr = nfserr_inval; + goto fail; + } + + if (xdr_stream_decode_u64(xdr, &bex.len)) { + nfserr = nfserr_bad_xdr; goto fail; } - p = xdr_decode_hyper(p, &bex.len); if (bex.len & (block_size - 1)) { + nfserr = nfserr_inval; + goto fail; + } + + if (xdr_stream_decode_u64(xdr, &bex.soff)) { + nfserr = nfserr_bad_xdr; goto fail; } - p = xdr_decode_hyper(p, &bex.soff); if (bex.soff & (block_size - 1)) { + nfserr = nfserr_inval; + goto fail; + } + + if (xdr_stream_decode_u32(xdr, &bex.es)) { + nfserr = nfserr_bad_xdr; goto fail; } - bex.es = be32_to_cpup(p++); if (bex.es != PNFS_BLOCK_READWRITE_DATA) { + nfserr = nfserr_inval; goto fail; } @@ -182,13 +203,12 @@ nfsd4_block_decode_layoutupdate(__be32 *p, u32 len, struct iomap **iomapp, return nfs_ok; fail: kfree(iomaps); - return nfserr_inval; + return nfserr; } /** * nfsd4_scsi_decode_layoutupdate - decode the scsi layout extent array - * @p: pointer to the xdr data - * @len: number of bytes to decode + * @xdr: subbuf set to the encoded array * @iomapp: pointer to store the decoded extent array * @nr_iomapsp: pointer to store the number of extents * @block_size: alignment of extent offset and length @@ -200,21 +220,22 @@ nfsd4_block_decode_layoutupdate(__be32 *p, u32 len, struct iomap **iomapp, * * Return values: * %nfs_ok: Successful decoding, @iomapp and @nr_iomapsp are valid - * %nfserr_bad_xdr: The encoded array in @p is invalid + * %nfserr_bad_xdr: The encoded array in @xdr is invalid * %nfserr_inval: An unaligned extent found * %nfserr_delay: Failed to allocate memory for @iomapp */ __be32 -nfsd4_scsi_decode_layoutupdate(__be32 *p, u32 len, struct iomap **iomapp, +nfsd4_scsi_decode_layoutupdate(struct xdr_stream *xdr, struct iomap **iomapp, int *nr_iomapsp, u32 block_size) { struct iomap *iomaps; - u32 nr_iomaps, expected, i; + u32 nr_iomaps, expected, len, i; + __be32 nfserr; - if (len < sizeof(u32)) + if (xdr_stream_decode_u32(xdr, &nr_iomaps)) return nfserr_bad_xdr; - nr_iomaps = be32_to_cpup(p++); + len = sizeof(__be32) + xdr_stream_remaining(xdr); expected = sizeof(__be32) + nr_iomaps * PNFS_SCSI_RANGE_SIZE; if (len != expected) return nfserr_bad_xdr; @@ -226,14 +247,22 @@ nfsd4_scsi_decode_layoutupdate(__be32 *p, u32 len, struct iomap **iomapp, for (i = 0; i < nr_iomaps; i++) { u64 val; - p = xdr_decode_hyper(p, &val); + if (xdr_stream_decode_u64(xdr, &val)) { + nfserr = nfserr_bad_xdr; + goto fail; + } if (val & (block_size - 1)) { + nfserr = nfserr_inval; goto fail; } iomaps[i].offset = val; - p = xdr_decode_hyper(p, &val); + if (xdr_stream_decode_u64(xdr, &val)) { + nfserr = nfserr_bad_xdr; + goto fail; + } if (val & (block_size - 1)) { + nfserr = nfserr_inval; goto fail; } iomaps[i].length = val; @@ -244,5 +273,5 @@ nfsd4_scsi_decode_layoutupdate(__be32 *p, u32 len, struct iomap **iomapp, return nfs_ok; fail: kfree(iomaps); - return nfserr_inval; + return nfserr; } diff --git a/fs/nfsd/blocklayoutxdr.h b/fs/nfsd/blocklayoutxdr.h index 15b3569f3d9ad3..7d25ef689671f7 100644 --- a/fs/nfsd/blocklayoutxdr.h +++ b/fs/nfsd/blocklayoutxdr.h @@ -54,9 +54,9 @@ __be32 nfsd4_block_encode_getdeviceinfo(struct xdr_stream *xdr, const struct nfsd4_getdeviceinfo *gdp); __be32 nfsd4_block_encode_layoutget(struct xdr_stream *xdr, const struct nfsd4_layoutget *lgp); -__be32 nfsd4_block_decode_layoutupdate(__be32 *p, u32 len, +__be32 nfsd4_block_decode_layoutupdate(struct xdr_stream *xdr, struct iomap **iomapp, int *nr_iomapsp, u32 block_size); -__be32 nfsd4_scsi_decode_layoutupdate(__be32 *p, u32 len, +__be32 nfsd4_scsi_decode_layoutupdate(struct xdr_stream *xdr, struct iomap **iomapp, int *nr_iomapsp, u32 block_size); #endif /* _NFSD_BLOCKLAYOUTXDR_H */ diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index c01183ddc93f27..294fede450a012 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -2407,7 +2407,7 @@ nfsd4_layoutcommit(struct svc_rqst *rqstp, lcp->lc_size_chg = false; } - nfserr = ops->proc_layoutcommit(inode, lcp); + nfserr = ops->proc_layoutcommit(inode, rqstp, lcp); nfs4_put_stid(&ls->ls_stid); out: return nfserr; diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index bd5c8720ea7e36..66383eeeed15a0 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -571,6 +571,8 @@ static __be32 nfsd4_decode_layoutupdate4(struct nfsd4_compoundargs *argp, struct nfsd4_layoutcommit *lcp) { + u32 len; + if (xdr_stream_decode_u32(argp->xdr, &lcp->lc_layout_type) < 0) return nfserr_bad_xdr; if (lcp->lc_layout_type < LAYOUT_NFSV4_1_FILES) @@ -578,13 +580,10 @@ nfsd4_decode_layoutupdate4(struct nfsd4_compoundargs *argp, if (lcp->lc_layout_type >= LAYOUT_TYPE_MAX) return nfserr_bad_xdr; - if (xdr_stream_decode_u32(argp->xdr, &lcp->lc_up_len) < 0) + if (xdr_stream_decode_u32(argp->xdr, &len) < 0) + return nfserr_bad_xdr; + if (!xdr_stream_subsegment(argp->xdr, &lcp->lc_up_layout, len)) return nfserr_bad_xdr; - if (lcp->lc_up_len > 0) { - lcp->lc_up_layout = xdr_inline_decode(argp->xdr, lcp->lc_up_len); - if (!lcp->lc_up_layout) - return nfserr_bad_xdr; - } return nfs_ok; } diff --git a/fs/nfsd/pnfs.h b/fs/nfsd/pnfs.h index 925817f669176c..dfd411d1f363fd 100644 --- a/fs/nfsd/pnfs.h +++ b/fs/nfsd/pnfs.h @@ -35,6 +35,7 @@ struct nfsd4_layout_ops { const struct nfsd4_layoutget *lgp); __be32 (*proc_layoutcommit)(struct inode *inode, + struct svc_rqst *rqstp, struct nfsd4_layoutcommit *lcp); void (*fence_client)(struct nfs4_layout_stateid *ls, diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h index 83263bff27dc61..c75b295df206ad 100644 --- a/fs/nfsd/xdr4.h +++ b/fs/nfsd/xdr4.h @@ -665,8 +665,7 @@ struct nfsd4_layoutcommit { u64 lc_last_wr; /* request */ struct timespec64 lc_mtime; /* request */ u32 lc_layout_type; /* request */ - u32 lc_up_len; /* layout length */ - void *lc_up_layout; /* decoded by callback */ + struct xdr_buf lc_up_layout; /* decoded by callback */ bool lc_size_chg; /* response */ u64 lc_newsize; /* response */ }; From c33da548fbf230a46cae97dcff2db98948bcd791 Mon Sep 17 00:00:00 2001 From: Sergey Bashirov Date: Mon, 20 Oct 2025 08:53:04 -0400 Subject: [PATCH 110/143] NFSD: Fix last write offset handling in layoutcommit [ Upstream commit d68886bae76a4b9b3484d23e5b7df086f940fa38 ] The data type of loca_last_write_offset is newoffset4 and is switched on a boolean value, no_newoffset, that indicates if a previous write occurred or not. If no_newoffset is FALSE, an offset is not given. This means that client does not try to update the file size. Thus, server should not try to calculate new file size and check if it fits into the segment range. See RFC 8881, section 12.5.4.2. Sometimes the current incorrect logic may cause clients to hang when trying to sync an inode. If layoutcommit fails, the client marks the inode as dirty again. Fixes: 9cf514ccfacb ("nfsd: implement pNFS operations") Cc: stable@vger.kernel.org Co-developed-by: Konstantin Evtushenko Signed-off-by: Konstantin Evtushenko Signed-off-by: Sergey Bashirov Reviewed-by: Christoph Hellwig Reviewed-by: Jeff Layton Signed-off-by: Chuck Lever Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/blocklayout.c | 5 ++--- fs/nfsd/nfs4proc.c | 30 +++++++++++++++--------------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/fs/nfsd/blocklayout.c b/fs/nfsd/blocklayout.c index 4c936132eb4403..0822d8a119c6fa 100644 --- a/fs/nfsd/blocklayout.c +++ b/fs/nfsd/blocklayout.c @@ -118,7 +118,6 @@ nfsd4_block_commit_blocks(struct inode *inode, struct nfsd4_layoutcommit *lcp, struct iomap *iomaps, int nr_iomaps) { struct timespec64 mtime = inode_get_mtime(inode); - loff_t new_size = lcp->lc_last_wr + 1; struct iattr iattr = { .ia_valid = 0 }; int error; @@ -128,9 +127,9 @@ nfsd4_block_commit_blocks(struct inode *inode, struct nfsd4_layoutcommit *lcp, iattr.ia_valid |= ATTR_ATIME | ATTR_CTIME | ATTR_MTIME; iattr.ia_atime = iattr.ia_ctime = iattr.ia_mtime = lcp->lc_mtime; - if (new_size > i_size_read(inode)) { + if (lcp->lc_size_chg) { iattr.ia_valid |= ATTR_SIZE; - iattr.ia_size = new_size; + iattr.ia_size = lcp->lc_newsize; } error = inode->i_sb->s_export_op->commit_blocks(inode, iomaps, diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 294fede450a012..6040de0923f851 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -2362,7 +2362,6 @@ nfsd4_layoutcommit(struct svc_rqst *rqstp, const struct nfsd4_layout_seg *seg = &lcp->lc_seg; struct svc_fh *current_fh = &cstate->current_fh; const struct nfsd4_layout_ops *ops; - loff_t new_size = lcp->lc_last_wr + 1; struct inode *inode; struct nfs4_layout_stateid *ls; __be32 nfserr; @@ -2378,13 +2377,21 @@ nfsd4_layoutcommit(struct svc_rqst *rqstp, goto out; inode = d_inode(current_fh->fh_dentry); - nfserr = nfserr_inval; - if (new_size <= seg->offset) - goto out; - if (new_size > seg->offset + seg->length) - goto out; - if (!lcp->lc_newoffset && new_size > i_size_read(inode)) - goto out; + lcp->lc_size_chg = false; + if (lcp->lc_newoffset) { + loff_t new_size = lcp->lc_last_wr + 1; + + nfserr = nfserr_inval; + if (new_size <= seg->offset) + goto out; + if (new_size > seg->offset + seg->length) + goto out; + + if (new_size > i_size_read(inode)) { + lcp->lc_size_chg = true; + lcp->lc_newsize = new_size; + } + } nfserr = nfsd4_preprocess_layout_stateid(rqstp, cstate, &lcp->lc_sid, false, lcp->lc_layout_type, @@ -2400,13 +2407,6 @@ nfsd4_layoutcommit(struct svc_rqst *rqstp, /* LAYOUTCOMMIT does not require any serialization */ mutex_unlock(&ls->ls_mutex); - if (new_size > i_size_read(inode)) { - lcp->lc_size_chg = true; - lcp->lc_newsize = new_size; - } else { - lcp->lc_size_chg = false; - } - nfserr = ops->proc_layoutcommit(inode, rqstp, lcp); nfs4_put_stid(&ls->ls_stid); out: From e9fd43b799d231319e1d00b05bd8310f0dadc99d Mon Sep 17 00:00:00 2001 From: Fedor Pchelkin Date: Mon, 20 Oct 2025 11:47:33 -0400 Subject: [PATCH 111/143] wifi: rtw89: avoid possible TX wait initialization race [ Upstream commit c24248ed78f33ea299ea61d105355ba47157d49f ] The value of skb_data->wait indicates whether skb is passed on to the core mac80211 stack or released by the driver itself. Make sure that by the time skb is added to txwd queue and becomes visible to the completing side, it has already allocated and initialized TX wait related data (in case it's needed). This is found by code review and addresses a possible race scenario described below: Waiting thread Completing thread rtw89_core_send_nullfunc() rtw89_core_tx_write_link() ... rtw89_pci_txwd_submit() skb_data->wait = NULL /* add skb to the queue */ skb_queue_tail(&txwd->queue, skb) /* another thread (e.g. rtw89_ops_tx) performs TX kick off for the same queue */ rtw89_pci_napi_poll() ... rtw89_pci_release_txwd_skb() /* get skb from the queue */ skb_unlink(skb, &txwd->queue) rtw89_pci_tx_status() rtw89_core_tx_wait_complete() /* use incorrect skb_data->wait */ rtw89_core_tx_kick_off_and_wait() /* assign skb_data->wait but too late */ Found by Linux Verification Center (linuxtesting.org). Fixes: 1ae5ca615285 ("wifi: rtw89: add function to wait for completion of TX skbs") Cc: stable@vger.kernel.org Signed-off-by: Fedor Pchelkin Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250919210852.823912-3-pchelkin@ispras.ru [ adapted rtw89_core_tx_write_link() modifications to rtw89_core_tx_write() ] Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/realtek/rtw89/core.c | 39 ++++++++++--------- drivers/net/wireless/realtek/rtw89/core.h | 6 ++- drivers/net/wireless/realtek/rtw89/mac80211.c | 2 +- drivers/net/wireless/realtek/rtw89/pci.c | 2 - 4 files changed, 26 insertions(+), 23 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 99711a4fb85df8..df8fad7a2aea63 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -978,25 +978,14 @@ void rtw89_core_tx_kick_off(struct rtw89_dev *rtwdev, u8 qsel) } int rtw89_core_tx_kick_off_and_wait(struct rtw89_dev *rtwdev, struct sk_buff *skb, - int qsel, unsigned int timeout) + struct rtw89_tx_wait_info *wait, int qsel, + unsigned int timeout) { - struct rtw89_tx_skb_data *skb_data = RTW89_TX_SKB_CB(skb); - struct rtw89_tx_wait_info *wait; unsigned long time_left; int ret = 0; lockdep_assert_wiphy(rtwdev->hw->wiphy); - wait = kzalloc(sizeof(*wait), GFP_KERNEL); - if (!wait) { - rtw89_core_tx_kick_off(rtwdev, qsel); - return 0; - } - - init_completion(&wait->completion); - wait->skb = skb; - rcu_assign_pointer(skb_data->wait, wait); - rtw89_core_tx_kick_off(rtwdev, qsel); time_left = wait_for_completion_timeout(&wait->completion, msecs_to_jiffies(timeout)); @@ -1057,10 +1046,12 @@ int rtw89_h2c_tx(struct rtw89_dev *rtwdev, } int rtw89_core_tx_write(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, struct sk_buff *skb, int *qsel) + struct ieee80211_sta *sta, struct sk_buff *skb, int *qsel, + struct rtw89_tx_wait_info *wait) { struct rtw89_sta *rtwsta = sta_to_rtwsta_safe(sta); struct rtw89_vif *rtwvif = vif_to_rtwvif(vif); + struct rtw89_tx_skb_data *skb_data = RTW89_TX_SKB_CB(skb); struct rtw89_core_tx_request tx_req = {0}; struct rtw89_sta_link *rtwsta_link = NULL; struct rtw89_vif_link *rtwvif_link; @@ -1093,6 +1084,8 @@ int rtw89_core_tx_write(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif, rtw89_core_tx_update_desc_info(rtwdev, &tx_req); rtw89_core_tx_wake(rtwdev, &tx_req); + rcu_assign_pointer(skb_data->wait, wait); + ret = rtw89_hci_tx_write(rtwdev, &tx_req); if (ret) { rtw89_err(rtwdev, "failed to transmit skb to HCI\n"); @@ -2908,7 +2901,7 @@ static void rtw89_core_txq_push(struct rtw89_dev *rtwdev, goto out; } rtw89_core_txq_check_agg(rtwdev, rtwtxq, skb); - ret = rtw89_core_tx_write(rtwdev, vif, sta, skb, NULL); + ret = rtw89_core_tx_write(rtwdev, vif, sta, skb, NULL, NULL); if (ret) { rtw89_err(rtwdev, "failed to push txq: %d\n", ret); ieee80211_free_txskb(rtwdev->hw, skb); @@ -3084,7 +3077,7 @@ static void rtw89_core_sta_pending_tx_iter(void *data, skb_queue_walk_safe(&rtwsta->roc_queue, skb, tmp) { skb_unlink(skb, &rtwsta->roc_queue); - ret = rtw89_core_tx_write(rtwdev, vif, sta, skb, &qsel); + ret = rtw89_core_tx_write(rtwdev, vif, sta, skb, &qsel, NULL); if (ret) { rtw89_warn(rtwdev, "pending tx failed with %d\n", ret); dev_kfree_skb_any(skb); @@ -3106,6 +3099,7 @@ static int rtw89_core_send_nullfunc(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, bool qos, bool ps) { struct ieee80211_vif *vif = rtwvif_link_to_vif(rtwvif_link); + struct rtw89_tx_wait_info *wait; struct ieee80211_sta *sta; struct ieee80211_hdr *hdr; struct sk_buff *skb; @@ -3114,6 +3108,12 @@ static int rtw89_core_send_nullfunc(struct rtw89_dev *rtwdev, if (vif->type != NL80211_IFTYPE_STATION || !vif->cfg.assoc) return 0; + wait = kzalloc(sizeof(*wait), GFP_KERNEL); + if (!wait) + return -ENOMEM; + + init_completion(&wait->completion); + rcu_read_lock(); sta = ieee80211_find_sta(vif, vif->cfg.ap_addr); if (!sta) { @@ -3127,11 +3127,13 @@ static int rtw89_core_send_nullfunc(struct rtw89_dev *rtwdev, goto out; } + wait->skb = skb; + hdr = (struct ieee80211_hdr *)skb->data; if (ps) hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); - ret = rtw89_core_tx_write(rtwdev, vif, sta, skb, &qsel); + ret = rtw89_core_tx_write(rtwdev, vif, sta, skb, &qsel, wait); if (ret) { rtw89_warn(rtwdev, "nullfunc transmit failed: %d\n", ret); dev_kfree_skb_any(skb); @@ -3140,10 +3142,11 @@ static int rtw89_core_send_nullfunc(struct rtw89_dev *rtwdev, rcu_read_unlock(); - return rtw89_core_tx_kick_off_and_wait(rtwdev, skb, qsel, + return rtw89_core_tx_kick_off_and_wait(rtwdev, skb, wait, qsel, RTW89_ROC_TX_TIMEOUT); out: rcu_read_unlock(); + kfree(wait); return ret; } diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index cb703588e3a4ff..7be32b83d4d64c 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -6818,12 +6818,14 @@ static inline bool rtw89_is_rtl885xb(struct rtw89_dev *rtwdev) } int rtw89_core_tx_write(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, struct sk_buff *skb, int *qsel); + struct ieee80211_sta *sta, struct sk_buff *skb, int *qsel, + struct rtw89_tx_wait_info *wait); int rtw89_h2c_tx(struct rtw89_dev *rtwdev, struct sk_buff *skb, bool fwdl); void rtw89_core_tx_kick_off(struct rtw89_dev *rtwdev, u8 qsel); int rtw89_core_tx_kick_off_and_wait(struct rtw89_dev *rtwdev, struct sk_buff *skb, - int qsel, unsigned int timeout); + struct rtw89_tx_wait_info *wait, int qsel, + unsigned int timeout); void rtw89_core_fill_txdesc(struct rtw89_dev *rtwdev, struct rtw89_tx_desc_info *desc_info, void *txdesc); diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index 3a1a2b243adf0e..d1f9bd41f8b5d0 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -36,7 +36,7 @@ static void rtw89_ops_tx(struct ieee80211_hw *hw, return; } - ret = rtw89_core_tx_write(rtwdev, vif, sta, skb, &qsel); + ret = rtw89_core_tx_write(rtwdev, vif, sta, skb, &qsel, NULL); if (ret) { rtw89_err(rtwdev, "failed to transmit skb: %d\n", ret); ieee80211_free_txskb(hw, skb); diff --git a/drivers/net/wireless/realtek/rtw89/pci.c b/drivers/net/wireless/realtek/rtw89/pci.c index 5fd5fe88e6b083..a87e1778a0d419 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.c +++ b/drivers/net/wireless/realtek/rtw89/pci.c @@ -1366,7 +1366,6 @@ static int rtw89_pci_txwd_submit(struct rtw89_dev *rtwdev, struct pci_dev *pdev = rtwpci->pdev; struct sk_buff *skb = tx_req->skb; struct rtw89_pci_tx_data *tx_data = RTW89_PCI_TX_SKB_CB(skb); - struct rtw89_tx_skb_data *skb_data = RTW89_TX_SKB_CB(skb); bool en_wd_info = desc_info->en_wd_info; u32 txwd_len; u32 txwp_len; @@ -1382,7 +1381,6 @@ static int rtw89_pci_txwd_submit(struct rtw89_dev *rtwdev, } tx_data->dma = dma; - rcu_assign_pointer(skb_data->wait, NULL); txwp_len = sizeof(*txwp_info); txwd_len = chip->txwd_body_size; From 88ad39711bfb557249c238272f8dfb0c41b3f36f Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 20 Oct 2025 11:49:51 -0400 Subject: [PATCH 112/143] xfs: use deferred intent items for reaping crosslinked blocks [ Upstream commit cd32a0c0dcdf634f2e0e71f41c272e19dece6264 ] When we're removing rmap records for crosslinked blocks, use deferred intent items so that we can try to free/unmap as many of the old data structure's blocks as we can in the same transaction as the commit. Cc: # v6.6 Fixes: 1c7ce115e52106 ("xfs: reap large AG metadata extents when possible") Signed-off-by: "Darrick J. Wong" Reviewed-by: Christoph Hellwig [ adapted xfs_refcount_free_cow_extent() and xfs_rmap_free_extent() ] Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/xfs/scrub/reap.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/fs/xfs/scrub/reap.c b/fs/xfs/scrub/reap.c index 53697f3c5e1b0b..8688edec587554 100644 --- a/fs/xfs/scrub/reap.c +++ b/fs/xfs/scrub/reap.c @@ -409,8 +409,6 @@ xreap_agextent_iter( if (crosslinked) { trace_xreap_dispose_unmap_extent(sc->sa.pag, agbno, *aglenp); - rs->force_roll = true; - if (rs->oinfo == &XFS_RMAP_OINFO_COW) { /* * If we're unmapping CoW staging extents, remove the @@ -418,11 +416,14 @@ xreap_agextent_iter( * rmap record as well. */ xfs_refcount_free_cow_extent(sc->tp, fsbno, *aglenp); + rs->force_roll = true; return 0; } - return xfs_rmap_free(sc->tp, sc->sa.agf_bp, sc->sa.pag, agbno, - *aglenp, rs->oinfo); + xfs_rmap_free_extent(sc->tp, sc->sa.pag->pag_agno, agbno, + *aglenp, rs->oinfo->oi_owner); + rs->deferred++; + return 0; } trace_xreap_dispose_free_extent(sc->sa.pag, agbno, *aglenp); From fd819637d0cf697890d4d826ee9b7e3fa13c14b0 Mon Sep 17 00:00:00 2001 From: Xiao Liang Date: Mon, 20 Oct 2025 11:37:02 -0400 Subject: [PATCH 113/143] padata: Reset next CPU when reorder sequence wraps around [ Upstream commit 501302d5cee0d8e8ec2c4a5919c37e0df9abc99b ] When seq_nr wraps around, the next reorder job with seq 0 is hashed to the first CPU in padata_do_serial(). Correspondingly, need reset pd->cpu to the first one when pd->processed wraps around. Otherwise, if the number of used CPUs is not a power of 2, padata_find_next() will be checking a wrong list, hence deadlock. Fixes: 6fc4dbcf0276 ("padata: Replace delayed timer with immediate workqueue in padata_reorder") Cc: Signed-off-by: Xiao Liang Signed-off-by: Herbert Xu [ relocated fix to padata_find_next() using pd->processed and pd->cpu structure fields ] Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- kernel/padata.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/kernel/padata.c b/kernel/padata.c index 3e0ef0753e73e1..c3810f5bd71563 100644 --- a/kernel/padata.c +++ b/kernel/padata.c @@ -290,7 +290,11 @@ static struct padata_priv *padata_find_next(struct parallel_data *pd, if (remove_object) { list_del_init(&padata->list); ++pd->processed; - pd->cpu = cpumask_next_wrap(cpu, pd->cpumask.pcpu, -1, false); + /* When sequence wraps around, reset to the first CPU. */ + if (unlikely(pd->processed == 0)) + pd->cpu = cpumask_first(pd->cpumask.pcpu); + else + pd->cpu = cpumask_next_wrap(cpu, pd->cpumask.pcpu, -1, false); } spin_unlock(&reorder->lock); From 069e7bbe4382f596232204e48a11e17ed9987ec9 Mon Sep 17 00:00:00 2001 From: John Garry Date: Mon, 20 Oct 2025 09:06:46 -0400 Subject: [PATCH 114/143] md/raid0: Handle bio_split() errors [ Upstream commit 74538fdac3e85aae55eb4ed786478ed2384cb85d ] Add proper bio_split() error handling. For any error, set bi_status, end the bio, and return. Reviewed-by: Yu Kuai Reviewed-by: Hannes Reinecke Signed-off-by: John Garry Link: https://lore.kernel.org/r/20241111112150.3756529-5-john.g.garry@oracle.com Signed-off-by: Jens Axboe Stable-dep-of: 22f166218f73 ("md: fix mssing blktrace bio split events") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/md/raid0.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c index 31bea72bcb01ad..67ec633d27e26b 100644 --- a/drivers/md/raid0.c +++ b/drivers/md/raid0.c @@ -464,6 +464,12 @@ static void raid0_handle_discard(struct mddev *mddev, struct bio *bio) struct bio *split = bio_split(bio, zone->zone_end - bio->bi_iter.bi_sector, GFP_NOIO, &mddev->bio_set); + + if (IS_ERR(split)) { + bio->bi_status = errno_to_blk_status(PTR_ERR(split)); + bio_endio(bio); + return; + } bio_chain(split, bio); submit_bio_noacct(bio); bio = split; @@ -606,6 +612,12 @@ static bool raid0_make_request(struct mddev *mddev, struct bio *bio) if (sectors < bio_sectors(bio)) { struct bio *split = bio_split(bio, sectors, GFP_NOIO, &mddev->bio_set); + + if (IS_ERR(split)) { + bio->bi_status = errno_to_blk_status(PTR_ERR(split)); + bio_endio(bio); + return true; + } bio_chain(split, bio); raid0_map_submit_bio(mddev, bio); bio = split; From 74dc8c235ad08accf5040ddb659b286ab18480d4 Mon Sep 17 00:00:00 2001 From: John Garry Date: Mon, 20 Oct 2025 09:06:47 -0400 Subject: [PATCH 115/143] md/raid1: Handle bio_split() errors [ Upstream commit b1a7ad8b5c4fa28325ee7b369a2d545d3e16ccde ] Add proper bio_split() error handling. For any error, call raid_end_bio_io() and return. For the case of an in the write path, we need to undo the increment in the rdev pending count and NULLify the r1_bio->bios[] pointers. For read path failure, we need to undo rdev pending count increment from the earlier read_balance() call. Reviewed-by: Yu Kuai Reviewed-by: Hannes Reinecke Signed-off-by: John Garry Link: https://lore.kernel.org/r/20241111112150.3756529-6-john.g.garry@oracle.com Signed-off-by: Jens Axboe Stable-dep-of: 22f166218f73 ("md: fix mssing blktrace bio split events") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/md/raid1.c | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index faccf7344ef933..31081d9e940252 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -1317,7 +1317,7 @@ static void raid1_read_request(struct mddev *mddev, struct bio *bio, struct raid1_info *mirror; struct bio *read_bio; int max_sectors; - int rdisk; + int rdisk, error; bool r1bio_existed = !!r1_bio; /* @@ -1378,6 +1378,11 @@ static void raid1_read_request(struct mddev *mddev, struct bio *bio, if (max_sectors < bio_sectors(bio)) { struct bio *split = bio_split(bio, max_sectors, gfp, &conf->bio_split); + + if (IS_ERR(split)) { + error = PTR_ERR(split); + goto err_handle; + } bio_chain(split, bio); submit_bio_noacct(bio); bio = split; @@ -1404,6 +1409,13 @@ static void raid1_read_request(struct mddev *mddev, struct bio *bio, read_bio->bi_private = r1_bio; mddev_trace_remap(mddev, read_bio, r1_bio->sector); submit_bio_noacct(read_bio); + return; + +err_handle: + atomic_dec(&mirror->rdev->nr_pending); + bio->bi_status = errno_to_blk_status(error); + set_bit(R1BIO_Uptodate, &r1_bio->state); + raid_end_bio_io(r1_bio); } static void raid1_write_request(struct mddev *mddev, struct bio *bio, @@ -1411,7 +1423,7 @@ static void raid1_write_request(struct mddev *mddev, struct bio *bio, { struct r1conf *conf = mddev->private; struct r1bio *r1_bio; - int i, disks; + int i, disks, k, error; unsigned long flags; struct md_rdev *blocked_rdev; int first_clone; @@ -1557,6 +1569,11 @@ static void raid1_write_request(struct mddev *mddev, struct bio *bio, if (max_sectors < bio_sectors(bio)) { struct bio *split = bio_split(bio, max_sectors, GFP_NOIO, &conf->bio_split); + + if (IS_ERR(split)) { + error = PTR_ERR(split); + goto err_handle; + } bio_chain(split, bio); submit_bio_noacct(bio); bio = split; @@ -1640,6 +1657,18 @@ static void raid1_write_request(struct mddev *mddev, struct bio *bio, /* In case raid1d snuck in to freeze_array */ wake_up_barrier(conf); + return; +err_handle: + for (k = 0; k < i; k++) { + if (r1_bio->bios[k]) { + rdev_dec_pending(conf->mirrors[k].rdev, mddev); + r1_bio->bios[k] = NULL; + } + } + + bio->bi_status = errno_to_blk_status(error); + set_bit(R1BIO_Uptodate, &r1_bio->state); + raid_end_bio_io(r1_bio); } static bool raid1_make_request(struct mddev *mddev, struct bio *bio) From 2d24bf9117ad21089d7f265c71037351fb3c4f07 Mon Sep 17 00:00:00 2001 From: John Garry Date: Mon, 20 Oct 2025 09:06:48 -0400 Subject: [PATCH 116/143] md/raid10: Handle bio_split() errors [ Upstream commit 4cf58d9529097328b669e3c8693ed21e3a041903 ] Add proper bio_split() error handling. For any error, call raid_end_bio_io() and return. Except for discard, where we end the bio directly. Reviewed-by: Yu Kuai Reviewed-by: Hannes Reinecke Signed-off-by: John Garry Link: https://lore.kernel.org/r/20241111112150.3756529-7-john.g.garry@oracle.com Signed-off-by: Jens Axboe Stable-dep-of: 22f166218f73 ("md: fix mssing blktrace bio split events") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/md/raid10.c | 47 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 6579bbb6a39a5d..d02bd096824c80 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1153,6 +1153,7 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio, int slot = r10_bio->read_slot; struct md_rdev *err_rdev = NULL; gfp_t gfp = GFP_NOIO; + int error; if (slot >= 0 && r10_bio->devs[slot].rdev) { /* @@ -1203,6 +1204,10 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio, if (max_sectors < bio_sectors(bio)) { struct bio *split = bio_split(bio, max_sectors, gfp, &conf->bio_split); + if (IS_ERR(split)) { + error = PTR_ERR(split); + goto err_handle; + } bio_chain(split, bio); allow_barrier(conf); submit_bio_noacct(bio); @@ -1233,6 +1238,11 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio, mddev_trace_remap(mddev, read_bio, r10_bio->sector); submit_bio_noacct(read_bio); return; +err_handle: + atomic_dec(&rdev->nr_pending); + bio->bi_status = errno_to_blk_status(error); + set_bit(R10BIO_Uptodate, &r10_bio->state); + raid_end_bio_io(r10_bio); } static void raid10_write_one_disk(struct mddev *mddev, struct r10bio *r10_bio, @@ -1341,9 +1351,10 @@ static void raid10_write_request(struct mddev *mddev, struct bio *bio, struct r10bio *r10_bio) { struct r10conf *conf = mddev->private; - int i; + int i, k; sector_t sectors; int max_sectors; + int error; if ((mddev_is_clustered(mddev) && md_cluster_ops->area_resyncing(mddev, WRITE, @@ -1469,6 +1480,10 @@ static void raid10_write_request(struct mddev *mddev, struct bio *bio, if (r10_bio->sectors < bio_sectors(bio)) { struct bio *split = bio_split(bio, r10_bio->sectors, GFP_NOIO, &conf->bio_split); + if (IS_ERR(split)) { + error = PTR_ERR(split); + goto err_handle; + } bio_chain(split, bio); allow_barrier(conf); submit_bio_noacct(bio); @@ -1488,6 +1503,26 @@ static void raid10_write_request(struct mddev *mddev, struct bio *bio, raid10_write_one_disk(mddev, r10_bio, bio, true, i); } one_write_done(r10_bio); + return; +err_handle: + for (k = 0; k < i; k++) { + int d = r10_bio->devs[k].devnum; + struct md_rdev *rdev = conf->mirrors[d].rdev; + struct md_rdev *rrdev = conf->mirrors[d].replacement; + + if (r10_bio->devs[k].bio) { + rdev_dec_pending(rdev, mddev); + r10_bio->devs[k].bio = NULL; + } + if (r10_bio->devs[k].repl_bio) { + rdev_dec_pending(rrdev, mddev); + r10_bio->devs[k].repl_bio = NULL; + } + } + + bio->bi_status = errno_to_blk_status(error); + set_bit(R10BIO_Uptodate, &r10_bio->state); + raid_end_bio_io(r10_bio); } static void __make_request(struct mddev *mddev, struct bio *bio, int sectors) @@ -1629,6 +1664,11 @@ static int raid10_handle_discard(struct mddev *mddev, struct bio *bio) if (remainder) { split_size = stripe_size - remainder; split = bio_split(bio, split_size, GFP_NOIO, &conf->bio_split); + if (IS_ERR(split)) { + bio->bi_status = errno_to_blk_status(PTR_ERR(split)); + bio_endio(bio); + return 0; + } bio_chain(split, bio); allow_barrier(conf); /* Resend the fist split part */ @@ -1639,6 +1679,11 @@ static int raid10_handle_discard(struct mddev *mddev, struct bio *bio) if (remainder) { split_size = bio_sectors(bio) - remainder; split = bio_split(bio, split_size, GFP_NOIO, &conf->bio_split); + if (IS_ERR(split)) { + bio->bi_status = errno_to_blk_status(PTR_ERR(split)); + bio_endio(bio); + return 0; + } bio_chain(split, bio); allow_barrier(conf); /* Resend the second split part */ From 78a2d39e2eefff3dc37952cb280436e1b69f66d7 Mon Sep 17 00:00:00 2001 From: Yu Kuai Date: Mon, 20 Oct 2025 09:06:49 -0400 Subject: [PATCH 117/143] md: fix mssing blktrace bio split events [ Upstream commit 22f166218f7313e8fe2d19213b5f4b3265f8c39e ] If bio is split by internal handling like chunksize or badblocks, the corresponding trace_block_split() is missing, resulting in blktrace inability to catch BIO split events and making it harder to analyze the BIO sequence. Cc: stable@vger.kernel.org Fixes: 4b1faf931650 ("block: Kill bio_pair_split()") Signed-off-by: Yu Kuai Reviewed-by: Damien Le Moal Reviewed-by: Christoph Hellwig Signed-off-by: Jens Axboe Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/md/md-linear.c | 1 + drivers/md/raid0.c | 4 ++++ drivers/md/raid1.c | 4 ++++ drivers/md/raid10.c | 8 ++++++++ drivers/md/raid5.c | 2 ++ 5 files changed, 19 insertions(+) diff --git a/drivers/md/md-linear.c b/drivers/md/md-linear.c index 369aed044b409f..d733ebee624b7e 100644 --- a/drivers/md/md-linear.c +++ b/drivers/md/md-linear.c @@ -267,6 +267,7 @@ static bool linear_make_request(struct mddev *mddev, struct bio *bio) } bio_chain(split, bio); + trace_block_split(split, bio->bi_iter.bi_sector); submit_bio_noacct(bio); bio = split; } diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c index 67ec633d27e26b..db1ab214250f91 100644 --- a/drivers/md/raid0.c +++ b/drivers/md/raid0.c @@ -470,7 +470,9 @@ static void raid0_handle_discard(struct mddev *mddev, struct bio *bio) bio_endio(bio); return; } + bio_chain(split, bio); + trace_block_split(split, bio->bi_iter.bi_sector); submit_bio_noacct(bio); bio = split; end = zone->zone_end; @@ -618,7 +620,9 @@ static bool raid0_make_request(struct mddev *mddev, struct bio *bio) bio_endio(bio); return true; } + bio_chain(split, bio); + trace_block_split(split, bio->bi_iter.bi_sector); raid0_map_submit_bio(mddev, bio); bio = split; } diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 31081d9e940252..4c6b1bd6da9bb1 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -1383,7 +1383,9 @@ static void raid1_read_request(struct mddev *mddev, struct bio *bio, error = PTR_ERR(split); goto err_handle; } + bio_chain(split, bio); + trace_block_split(split, bio->bi_iter.bi_sector); submit_bio_noacct(bio); bio = split; r1_bio->master_bio = bio; @@ -1574,7 +1576,9 @@ static void raid1_write_request(struct mddev *mddev, struct bio *bio, error = PTR_ERR(split); goto err_handle; } + bio_chain(split, bio); + trace_block_split(split, bio->bi_iter.bi_sector); submit_bio_noacct(bio); bio = split; r1_bio->master_bio = bio; diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index d02bd096824c80..b0062ad9b1d95f 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1208,7 +1208,9 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio, error = PTR_ERR(split); goto err_handle; } + bio_chain(split, bio); + trace_block_split(split, bio->bi_iter.bi_sector); allow_barrier(conf); submit_bio_noacct(bio); wait_barrier(conf, false); @@ -1484,7 +1486,9 @@ static void raid10_write_request(struct mddev *mddev, struct bio *bio, error = PTR_ERR(split); goto err_handle; } + bio_chain(split, bio); + trace_block_split(split, bio->bi_iter.bi_sector); allow_barrier(conf); submit_bio_noacct(bio); wait_barrier(conf, false); @@ -1669,7 +1673,9 @@ static int raid10_handle_discard(struct mddev *mddev, struct bio *bio) bio_endio(bio); return 0; } + bio_chain(split, bio); + trace_block_split(split, bio->bi_iter.bi_sector); allow_barrier(conf); /* Resend the fist split part */ submit_bio_noacct(split); @@ -1684,7 +1690,9 @@ static int raid10_handle_discard(struct mddev *mddev, struct bio *bio) bio_endio(bio); return 0; } + bio_chain(split, bio); + trace_block_split(split, bio->bi_iter.bi_sector); allow_barrier(conf); /* Resend the second split part */ submit_bio_noacct(bio); diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 39e7596e78c0b0..4fae8ade24090f 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -5484,8 +5484,10 @@ static struct bio *chunk_aligned_read(struct mddev *mddev, struct bio *raid_bio) if (sectors < bio_sectors(raid_bio)) { struct r5conf *conf = mddev->private; + split = bio_split(raid_bio, sectors, GFP_NOIO, &conf->bio_split); bio_chain(split, raid_bio); + trace_block_split(split, raid_bio->bi_iter.bi_sector); submit_bio_noacct(raid_bio); raid_bio = split; } From 12e3db99bc4ea4037cf03c23148f17fb572b5902 Mon Sep 17 00:00:00 2001 From: Babu Moger Date: Mon, 20 Oct 2025 12:38:52 -0400 Subject: [PATCH 118/143] x86/resctrl: Refactor resctrl_arch_rmid_read() [ Upstream commit 7c9ac605e202c4668e441fc8146a993577131ca1 ] resctrl_arch_rmid_read() adjusts the value obtained from MSR_IA32_QM_CTR to account for the overflow for MBM events and apply counter scaling for all the events. This logic is common to both reading an RMID and reading a hardware counter directly. Refactor the hardware value adjustment logic into get_corrected_val() to prepare for support of reading a hardware counter. Signed-off-by: Babu Moger Signed-off-by: Borislav Petkov (AMD) Reviewed-by: Reinette Chatre Link: https://lore.kernel.org/cover.1757108044.git.babu.moger@amd.com Stable-dep-of: 15292f1b4c55 ("x86/resctrl: Fix miscount of bandwidth event when reactivating previously unavailable RMID") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/cpu/resctrl/monitor.c | 38 ++++++++++++++++----------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/arch/x86/kernel/cpu/resctrl/monitor.c b/arch/x86/kernel/cpu/resctrl/monitor.c index 851b561850e0c2..b8d0748c4f2dbe 100644 --- a/arch/x86/kernel/cpu/resctrl/monitor.c +++ b/arch/x86/kernel/cpu/resctrl/monitor.c @@ -312,24 +312,13 @@ static u64 mbm_overflow_count(u64 prev_msr, u64 cur_msr, unsigned int width) return chunks >> shift; } -int resctrl_arch_rmid_read(struct rdt_resource *r, struct rdt_mon_domain *d, - u32 unused, u32 rmid, enum resctrl_event_id eventid, - u64 *val, void *ignored) +static u64 get_corrected_val(struct rdt_resource *r, struct rdt_mon_domain *d, + u32 rmid, enum resctrl_event_id eventid, u64 msr_val) { struct rdt_hw_mon_domain *hw_dom = resctrl_to_arch_mon_dom(d); struct rdt_hw_resource *hw_res = resctrl_to_arch_res(r); - int cpu = cpumask_any(&d->hdr.cpu_mask); struct arch_mbm_state *am; - u64 msr_val, chunks; - u32 prmid; - int ret; - - resctrl_arch_rmid_read_context_check(); - - prmid = logical_rmid_to_physical_rmid(cpu, rmid); - ret = __rmid_read_phys(prmid, eventid, &msr_val); - if (ret) - return ret; + u64 chunks; am = get_arch_mbm_state(hw_dom, rmid, eventid); if (am) { @@ -341,7 +330,26 @@ int resctrl_arch_rmid_read(struct rdt_resource *r, struct rdt_mon_domain *d, chunks = msr_val; } - *val = chunks * hw_res->mon_scale; + return chunks * hw_res->mon_scale; +} + +int resctrl_arch_rmid_read(struct rdt_resource *r, struct rdt_mon_domain *d, + u32 unused, u32 rmid, enum resctrl_event_id eventid, + u64 *val, void *ignored) +{ + int cpu = cpumask_any(&d->hdr.cpu_mask); + u64 msr_val; + u32 prmid; + int ret; + + resctrl_arch_rmid_read_context_check(); + + prmid = logical_rmid_to_physical_rmid(cpu, rmid); + ret = __rmid_read_phys(prmid, eventid, &msr_val); + if (ret) + return ret; + + *val = get_corrected_val(r, d, rmid, eventid, msr_val); return 0; } From 9ec6939a502d211ebcfb219865485276ed750081 Mon Sep 17 00:00:00 2001 From: Babu Moger Date: Mon, 20 Oct 2025 12:38:53 -0400 Subject: [PATCH 119/143] x86/resctrl: Fix miscount of bandwidth event when reactivating previously unavailable RMID [ Upstream commit 15292f1b4c55a3a7c940dbcb6cb8793871ed3d92 ] Users can create as many monitoring groups as the number of RMIDs supported by the hardware. However, on AMD systems, only a limited number of RMIDs are guaranteed to be actively tracked by the hardware. RMIDs that exceed this limit are placed in an "Unavailable" state. When a bandwidth counter is read for such an RMID, the hardware sets MSR_IA32_QM_CTR.Unavailable (bit 62). When such an RMID starts being tracked again the hardware counter is reset to zero. MSR_IA32_QM_CTR.Unavailable remains set on first read after tracking re-starts and is clear on all subsequent reads as long as the RMID is tracked. resctrl miscounts the bandwidth events after an RMID transitions from the "Unavailable" state back to being tracked. This happens because when the hardware starts counting again after resetting the counter to zero, resctrl in turn compares the new count against the counter value stored from the previous time the RMID was tracked. This results in resctrl computing an event value that is either undercounting (when new counter is more than stored counter) or a mistaken overflow (when new counter is less than stored counter). Reset the stored value (arch_mbm_state::prev_msr) of MSR_IA32_QM_CTR to zero whenever the RMID is in the "Unavailable" state to ensure accurate counting after the RMID resets to zero when it starts to be tracked again. Example scenario that results in mistaken overflow ================================================== 1. The resctrl filesystem is mounted, and a task is assigned to a monitoring group. $mount -t resctrl resctrl /sys/fs/resctrl $mkdir /sys/fs/resctrl/mon_groups/test1/ $echo 1234 > /sys/fs/resctrl/mon_groups/test1/tasks $cat /sys/fs/resctrl/mon_groups/test1/mon_data/mon_L3_*/mbm_total_bytes 21323 <- Total bytes on domain 0 "Unavailable" <- Total bytes on domain 1 Task is running on domain 0. Counter on domain 1 is "Unavailable". 2. The task runs on domain 0 for a while and then moves to domain 1. The counter starts incrementing on domain 1. $cat /sys/fs/resctrl/mon_groups/test1/mon_data/mon_L3_*/mbm_total_bytes 7345357 <- Total bytes on domain 0 4545 <- Total bytes on domain 1 3. At some point, the RMID in domain 0 transitions to the "Unavailable" state because the task is no longer executing in that domain. $cat /sys/fs/resctrl/mon_groups/test1/mon_data/mon_L3_*/mbm_total_bytes "Unavailable" <- Total bytes on domain 0 434341 <- Total bytes on domain 1 4. Since the task continues to migrate between domains, it may eventually return to domain 0. $cat /sys/fs/resctrl/mon_groups/test1/mon_data/mon_L3_*/mbm_total_bytes 17592178699059 <- Overflow on domain 0 3232332 <- Total bytes on domain 1 In this case, the RMID on domain 0 transitions from "Unavailable" state to active state. The hardware sets MSR_IA32_QM_CTR.Unavailable (bit 62) when the counter is read and begins tracking the RMID counting from 0. Subsequent reads succeed but return a value smaller than the previously saved MSR value (7345357). Consequently, the resctrl's overflow logic is triggered, it compares the previous value (7345357) with the new, smaller value and incorrectly interprets this as a counter overflow, adding a large delta. In reality, this is a false positive: the counter did not overflow but was simply reset when the RMID transitioned from "Unavailable" back to active state. Here is the text from APM [1] available from [2]. "In PQOS Version 2.0 or higher, the MBM hardware will set the U bit on the first QM_CTR read when it begins tracking an RMID that it was not previously tracking. The U bit will be zero for all subsequent reads from that RMID while it is still tracked by the hardware. Therefore, a QM_CTR read with the U bit set when that RMID is in use by a processor can be considered 0 when calculating the difference with a subsequent read." [1] AMD64 Architecture Programmer's Manual Volume 2: System Programming Publication # 24593 Revision 3.41 section 19.3.3 Monitoring L3 Memory Bandwidth (MBM). [ bp: Split commit message into smaller paragraph chunks for better consumption. ] Fixes: 4d05bf71f157d ("x86/resctrl: Introduce AMD QOS feature") Signed-off-by: Babu Moger Signed-off-by: Borislav Petkov (AMD) Reviewed-by: Reinette Chatre Tested-by: Reinette Chatre Cc: stable@vger.kernel.org # needs adjustments for <= v6.17 Link: https://bugzilla.kernel.org/show_bug.cgi?id=206537 # [2] Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/cpu/resctrl/monitor.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/arch/x86/kernel/cpu/resctrl/monitor.c b/arch/x86/kernel/cpu/resctrl/monitor.c index b8d0748c4f2dbe..c117fba4f8da92 100644 --- a/arch/x86/kernel/cpu/resctrl/monitor.c +++ b/arch/x86/kernel/cpu/resctrl/monitor.c @@ -337,7 +337,9 @@ int resctrl_arch_rmid_read(struct rdt_resource *r, struct rdt_mon_domain *d, u32 unused, u32 rmid, enum resctrl_event_id eventid, u64 *val, void *ignored) { + struct rdt_hw_mon_domain *hw_dom = resctrl_to_arch_mon_dom(d); int cpu = cpumask_any(&d->hdr.cpu_mask); + struct arch_mbm_state *am; u64 msr_val; u32 prmid; int ret; @@ -346,12 +348,16 @@ int resctrl_arch_rmid_read(struct rdt_resource *r, struct rdt_mon_domain *d, prmid = logical_rmid_to_physical_rmid(cpu, rmid); ret = __rmid_read_phys(prmid, eventid, &msr_val); - if (ret) - return ret; - *val = get_corrected_val(r, d, rmid, eventid, msr_val); + if (!ret) { + *val = get_corrected_val(r, d, rmid, eventid, msr_val); + } else if (ret == -EINVAL) { + am = get_arch_mbm_state(hw_dom, rmid, eventid); + if (am) + am->prev_msr = 0; + } - return 0; + return ret; } static void limbo_release_entry(struct rmid_entry *entry) From dc63d878146321f0d736f4cd9f0802283968ab49 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 20 Oct 2025 13:28:58 -0400 Subject: [PATCH 120/143] d_alloc_parallel(): set DCACHE_PAR_LOOKUP earlier [ Upstream commit e95db51c81f54dd12ea465b5127e4786f62a1095 ] Do that before new dentry is visible anywhere. It does create a new possible state for dentries present in ->d_children/->d_sib - DCACHE_PAR_LOOKUP present, negative, unhashed, not in in-lookup hash chains, refcount positive. Those are going to be skipped by all tree-walkers (both d_walk() callbacks in fs/dcache.c and explicit loops over children/sibling lists elsewhere) and dput() is fine with those. NOTE: dropping the final reference to a "normal" in-lookup dentry (in in-lookup hash) is a bug - somebody must've forgotten to call d_lookup_done() on it and bad things will happen. With those it's OK; if/when we get around to making __dentry_kill() complain about such breakage, remember that predicate to check should *not* be just d_in_lookup(victim) but rather a combination of that with !hlist_bl_unhashed(&victim->d_u.d_in_lookup_hash). Might be worth considering later... Reviewed-by: Christian Brauner Signed-off-by: Al Viro Stable-dep-of: 56094ad3eaa2 ("vfs: Don't leak disconnected dentries on umount") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/dcache.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/fs/dcache.c b/fs/dcache.c index 0f6b16ba30d082..d81765352cf819 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -2475,13 +2475,19 @@ struct dentry *d_alloc_parallel(struct dentry *parent, unsigned int hash = name->hash; struct hlist_bl_head *b = in_lookup_hash(parent, hash); struct hlist_bl_node *node; - struct dentry *new = d_alloc(parent, name); + struct dentry *new = __d_alloc(parent->d_sb, name); struct dentry *dentry; unsigned seq, r_seq, d_seq; if (unlikely(!new)) return ERR_PTR(-ENOMEM); + new->d_flags |= DCACHE_PAR_LOOKUP; + spin_lock(&parent->d_lock); + new->d_parent = dget_dlock(parent); + hlist_add_head(&new->d_sib, &parent->d_children); + spin_unlock(&parent->d_lock); + retry: rcu_read_lock(); seq = smp_load_acquire(&parent->d_inode->i_dir_seq); @@ -2565,8 +2571,6 @@ struct dentry *d_alloc_parallel(struct dentry *parent, return dentry; } rcu_read_unlock(); - /* we can't take ->d_lock here; it's OK, though. */ - new->d_flags |= DCACHE_PAR_LOOKUP; new->d_wait = wq; hlist_bl_add_head(&new->d_u.d_in_lookup_hash, b); hlist_bl_unlock(b); From 620f3b0ede9c5cb4976cd0457d0b04ad551e5d6b Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 20 Oct 2025 13:28:59 -0400 Subject: [PATCH 121/143] vfs: Don't leak disconnected dentries on umount [ Upstream commit 56094ad3eaa21e6621396cc33811d8f72847a834 ] When user calls open_by_handle_at() on some inode that is not cached, we will create disconnected dentry for it. If such dentry is a directory, exportfs_decode_fh_raw() will then try to connect this dentry to the dentry tree through reconnect_path(). It may happen for various reasons (such as corrupted fs or race with rename) that the call to lookup_one_unlocked() in reconnect_one() will fail to find the dentry we are trying to reconnect and instead create a new dentry under the parent. Now this dentry will not be marked as disconnected although the parent still may well be disconnected (at least in case this inconsistency happened because the fs is corrupted and .. doesn't point to the real parent directory). This creates inconsistency in disconnected flags but AFAICS it was mostly harmless. At least until commit f1ee616214cb ("VFS: don't keep disconnected dentries on d_anon") which removed adding of most disconnected dentries to sb->s_anon list. Thus after this commit cleanup of disconnected dentries implicitely relies on the fact that dput() will immediately reclaim such dentries. However when some leaf dentry isn't marked as disconnected, as in the scenario described above, the reclaim doesn't happen and the dentries are "leaked". Memory reclaim can eventually reclaim them but otherwise they stay in memory and if umount comes first, we hit infamous "Busy inodes after unmount" bug. Make sure all dentries created under a disconnected parent are marked as disconnected as well. Reported-by: syzbot+1d79ebe5383fc016cf07@syzkaller.appspotmail.com Fixes: f1ee616214cb ("VFS: don't keep disconnected dentries on d_anon") CC: stable@vger.kernel.org Signed-off-by: Jan Kara Signed-off-by: Christian Brauner Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/dcache.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/dcache.c b/fs/dcache.c index d81765352cf819..d7814142ba7db3 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -2486,6 +2486,8 @@ struct dentry *d_alloc_parallel(struct dentry *parent, spin_lock(&parent->d_lock); new->d_parent = dget_dlock(parent); hlist_add_head(&new->d_sib, &parent->d_children); + if (parent->d_flags & DCACHE_DISCONNECTED) + new->d_flags |= DCACHE_DISCONNECTED; spin_unlock(&parent->d_lock); retry: From fb151d86dc04ee9913aaba4fbd8b22fbcd8831e6 Mon Sep 17 00:00:00 2001 From: Piotr Kwapulinski Date: Mon, 20 Oct 2025 13:28:38 -0400 Subject: [PATCH 122/143] PCI: Add PCI_VDEVICE_SUB helper macro [ Upstream commit 208fff3f567e2a3c3e7e4788845e90245c3891b4 ] PCI_VDEVICE_SUB generates the pci_device_id struct layout for the specific PCI device/subdevice. Private data may follow the output. Reviewed-by: Przemek Kitszel Signed-off-by: Piotr Kwapulinski Acked-by: Bjorn Helgaas Tested-by: Rafal Romanowski Signed-off-by: Tony Nguyen Stable-dep-of: a7075f501bd3 ("ixgbevf: fix mailbox API compatibility by negotiating supported features") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- include/linux/pci.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/include/linux/pci.h b/include/linux/pci.h index 6b3fef24d60e72..452a3dca28eaa0 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1066,6 +1066,20 @@ struct pci_driver { .vendor = PCI_VENDOR_ID_##vend, .device = (dev), \ .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, 0, 0 +/** + * PCI_VDEVICE_SUB - describe a specific PCI device/subdevice in a short form + * @vend: the vendor name + * @dev: the 16 bit PCI Device ID + * @subvend: the 16 bit PCI Subvendor ID + * @subdev: the 16 bit PCI Subdevice ID + * + * Generate the pci_device_id struct layout for the specific PCI + * device/subdevice. Private data may follow the output. + */ +#define PCI_VDEVICE_SUB(vend, dev, subvend, subdev) \ + .vendor = PCI_VENDOR_ID_##vend, .device = (dev), \ + .subvendor = (subvend), .subdevice = (subdev), 0, 0 + /** * PCI_DEVICE_DATA - macro used to describe a specific PCI device in very short form * @vend: the vendor name (without PCI_VENDOR_ID_ prefix) From 8a661d63d5543a5eb1a0d1f71fc8b25516cfe4f3 Mon Sep 17 00:00:00 2001 From: Piotr Kwapulinski Date: Mon, 20 Oct 2025 13:28:39 -0400 Subject: [PATCH 123/143] ixgbevf: Add support for Intel(R) E610 device [ Upstream commit 4c44b450c69b676955c2790dcf467c1f969d80f1 ] Add support for Intel(R) E610 Series of network devices. The E610 is based on X550 but adds firmware managed link, enhanced security capabilities and support for updated server manageability Reviewed-by: Przemek Kitszel Signed-off-by: Piotr Kwapulinski Reviewed-by: Simon Horman Tested-by: Rafal Romanowski Signed-off-by: Tony Nguyen Stable-dep-of: a7075f501bd3 ("ixgbevf: fix mailbox API compatibility by negotiating supported features") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/ixgbevf/defines.h | 5 ++++- drivers/net/ethernet/intel/ixgbevf/ixgbevf.h | 6 +++++- drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c | 12 ++++++++++-- drivers/net/ethernet/intel/ixgbevf/vf.c | 12 +++++++++++- drivers/net/ethernet/intel/ixgbevf/vf.h | 4 +++- 5 files changed, 33 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbevf/defines.h b/drivers/net/ethernet/intel/ixgbevf/defines.h index 5f08779c0e4e31..a9bc96f6399dc0 100644 --- a/drivers/net/ethernet/intel/ixgbevf/defines.h +++ b/drivers/net/ethernet/intel/ixgbevf/defines.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright(c) 1999 - 2018 Intel Corporation. */ +/* Copyright(c) 1999 - 2024 Intel Corporation. */ #ifndef _IXGBEVF_DEFINES_H_ #define _IXGBEVF_DEFINES_H_ @@ -16,6 +16,9 @@ #define IXGBE_DEV_ID_X550_VF_HV 0x1564 #define IXGBE_DEV_ID_X550EM_X_VF_HV 0x15A9 +#define IXGBE_DEV_ID_E610_VF 0x57AD +#define IXGBE_SUBDEV_ID_E610_VF_HV 0x00FF + #define IXGBE_VF_IRQ_CLEAR_MASK 7 #define IXGBE_VF_MAX_TX_QUEUES 8 #define IXGBE_VF_MAX_RX_QUEUES 8 diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h b/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h index 130cb868774c40..9b37f354d78ce0 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright(c) 1999 - 2018 Intel Corporation. */ +/* Copyright(c) 1999 - 2024 Intel Corporation. */ #ifndef _IXGBEVF_H_ #define _IXGBEVF_H_ @@ -418,6 +418,8 @@ enum ixgbevf_boards { board_X550EM_x_vf, board_X550EM_x_vf_hv, board_x550em_a_vf, + board_e610_vf, + board_e610_vf_hv, }; enum ixgbevf_xcast_modes { @@ -434,11 +436,13 @@ extern const struct ixgbevf_info ixgbevf_X550EM_x_vf_info; extern const struct ixgbe_mbx_operations ixgbevf_mbx_ops; extern const struct ixgbe_mbx_operations ixgbevf_mbx_ops_legacy; extern const struct ixgbevf_info ixgbevf_x550em_a_vf_info; +extern const struct ixgbevf_info ixgbevf_e610_vf_info; extern const struct ixgbevf_info ixgbevf_82599_vf_hv_info; extern const struct ixgbevf_info ixgbevf_X540_vf_hv_info; extern const struct ixgbevf_info ixgbevf_X550_vf_hv_info; extern const struct ixgbevf_info ixgbevf_X550EM_x_vf_hv_info; +extern const struct ixgbevf_info ixgbevf_e610_vf_hv_info; extern const struct ixgbe_mbx_operations ixgbevf_hv_mbx_ops; /* needed by ethtool.c */ diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index 149911e3002a22..2829bac9af9493 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/* Copyright(c) 1999 - 2018 Intel Corporation. */ +/* Copyright(c) 1999 - 2024 Intel Corporation. */ /****************************************************************************** Copyright (c)2006 - 2007 Myricom, Inc. for some LRO specific code @@ -39,7 +39,7 @@ static const char ixgbevf_driver_string[] = "Intel(R) 10 Gigabit PCI Express Virtual Function Network Driver"; static char ixgbevf_copyright[] = - "Copyright (c) 2009 - 2018 Intel Corporation."; + "Copyright (c) 2009 - 2024 Intel Corporation."; static const struct ixgbevf_info *ixgbevf_info_tbl[] = { [board_82599_vf] = &ixgbevf_82599_vf_info, @@ -51,6 +51,8 @@ static const struct ixgbevf_info *ixgbevf_info_tbl[] = { [board_X550EM_x_vf] = &ixgbevf_X550EM_x_vf_info, [board_X550EM_x_vf_hv] = &ixgbevf_X550EM_x_vf_hv_info, [board_x550em_a_vf] = &ixgbevf_x550em_a_vf_info, + [board_e610_vf] = &ixgbevf_e610_vf_info, + [board_e610_vf_hv] = &ixgbevf_e610_vf_hv_info, }; /* ixgbevf_pci_tbl - PCI Device ID Table @@ -71,6 +73,9 @@ static const struct pci_device_id ixgbevf_pci_tbl[] = { {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_VF), board_X550EM_x_vf }, {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_VF_HV), board_X550EM_x_vf_hv}, {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_A_VF), board_x550em_a_vf }, + {PCI_VDEVICE_SUB(INTEL, IXGBE_DEV_ID_E610_VF, PCI_ANY_ID, + IXGBE_SUBDEV_ID_E610_VF_HV), board_e610_vf_hv}, + {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_E610_VF), board_e610_vf}, /* required last entry */ {0, } }; @@ -4693,6 +4698,9 @@ static int ixgbevf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) case ixgbe_mac_X540_vf: dev_info(&pdev->dev, "Intel(R) X540 Virtual Function\n"); break; + case ixgbe_mac_e610_vf: + dev_info(&pdev->dev, "Intel(R) E610 Virtual Function\n"); + break; case ixgbe_mac_82599_vf: default: dev_info(&pdev->dev, "Intel(R) 82599 Virtual Function\n"); diff --git a/drivers/net/ethernet/intel/ixgbevf/vf.c b/drivers/net/ethernet/intel/ixgbevf/vf.c index 1641d00d8ed35c..da7a72ecce7a2d 100644 --- a/drivers/net/ethernet/intel/ixgbevf/vf.c +++ b/drivers/net/ethernet/intel/ixgbevf/vf.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/* Copyright(c) 1999 - 2018 Intel Corporation. */ +/* Copyright(c) 1999 - 2024 Intel Corporation. */ #include "vf.h" #include "ixgbevf.h" @@ -1076,3 +1076,13 @@ const struct ixgbevf_info ixgbevf_x550em_a_vf_info = { .mac = ixgbe_mac_x550em_a_vf, .mac_ops = &ixgbevf_mac_ops, }; + +const struct ixgbevf_info ixgbevf_e610_vf_info = { + .mac = ixgbe_mac_e610_vf, + .mac_ops = &ixgbevf_mac_ops, +}; + +const struct ixgbevf_info ixgbevf_e610_vf_hv_info = { + .mac = ixgbe_mac_e610_vf, + .mac_ops = &ixgbevf_hv_mac_ops, +}; diff --git a/drivers/net/ethernet/intel/ixgbevf/vf.h b/drivers/net/ethernet/intel/ixgbevf/vf.h index b4eef5b6c172bd..2d791bc26ae4e7 100644 --- a/drivers/net/ethernet/intel/ixgbevf/vf.h +++ b/drivers/net/ethernet/intel/ixgbevf/vf.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright(c) 1999 - 2018 Intel Corporation. */ +/* Copyright(c) 1999 - 2024 Intel Corporation. */ #ifndef __IXGBE_VF_H__ #define __IXGBE_VF_H__ @@ -54,6 +54,8 @@ enum ixgbe_mac_type { ixgbe_mac_X550_vf, ixgbe_mac_X550EM_x_vf, ixgbe_mac_x550em_a_vf, + ixgbe_mac_e610, + ixgbe_mac_e610_vf, ixgbe_num_macs }; From 68bfddd2b3beffee41a8f9732abcdb58f9ff60bc Mon Sep 17 00:00:00 2001 From: Jedrzej Jagielski Date: Mon, 20 Oct 2025 13:28:40 -0400 Subject: [PATCH 124/143] ixgbevf: fix getting link speed data for E610 devices [ Upstream commit 53f0eb62b4d23d40686f2dd51776b8220f2887bb ] E610 adapters no longer use the VFLINKS register to read PF's link speed and linkup state. As a result VF driver cannot get actual link state and it incorrectly reports 10G which is the default option. It leads to a situation where even 1G adapters print 10G as actual link speed. The same happens when PF driver set speed different than 10G. Add new mailbox operation to let the VF driver request a PF driver to provide actual link data. Update the mailbox api to v1.6. Incorporate both ways of getting link status within the legacy ixgbe_check_mac_link_vf() function. Fixes: 4c44b450c69b ("ixgbevf: Add support for Intel(R) E610 device") Co-developed-by: Andrzej Wilczynski Signed-off-by: Andrzej Wilczynski Reviewed-by: Przemek Kitszel Reviewed-by: Aleksandr Loktionov Cc: stable@vger.kernel.org Signed-off-by: Jedrzej Jagielski Tested-by: Rafal Romanowski Signed-off-by: Jacob Keller Link: https://patch.msgid.link/20251009-jk-iwl-net-2025-10-01-v3-2-ef32a425b92a@intel.com Signed-off-by: Jakub Kicinski Stable-dep-of: a7075f501bd3 ("ixgbevf: fix mailbox API compatibility by negotiating supported features") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/ixgbevf/defines.h | 1 + .../net/ethernet/intel/ixgbevf/ixgbevf_main.c | 6 +- drivers/net/ethernet/intel/ixgbevf/mbx.h | 4 + drivers/net/ethernet/intel/ixgbevf/vf.c | 137 ++++++++++++++---- 4 files changed, 116 insertions(+), 32 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbevf/defines.h b/drivers/net/ethernet/intel/ixgbevf/defines.h index a9bc96f6399dc0..e177d1d58696aa 100644 --- a/drivers/net/ethernet/intel/ixgbevf/defines.h +++ b/drivers/net/ethernet/intel/ixgbevf/defines.h @@ -28,6 +28,7 @@ /* Link speed */ typedef u32 ixgbe_link_speed; +#define IXGBE_LINK_SPEED_UNKNOWN 0 #define IXGBE_LINK_SPEED_1GB_FULL 0x0020 #define IXGBE_LINK_SPEED_10GB_FULL 0x0080 #define IXGBE_LINK_SPEED_100_FULL 0x0008 diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index 2829bac9af9493..72ca026618d67b 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -2278,6 +2278,7 @@ static void ixgbevf_negotiate_api(struct ixgbevf_adapter *adapter) { struct ixgbe_hw *hw = &adapter->hw; static const int api[] = { + ixgbe_mbox_api_16, ixgbe_mbox_api_15, ixgbe_mbox_api_14, ixgbe_mbox_api_13, @@ -2297,7 +2298,8 @@ static void ixgbevf_negotiate_api(struct ixgbevf_adapter *adapter) idx++; } - if (hw->api_version >= ixgbe_mbox_api_15) { + /* Following is not supported by API 1.6, it is specific for 1.5 */ + if (hw->api_version == ixgbe_mbox_api_15) { hw->mbx.ops.init_params(hw); memcpy(&hw->mbx.ops, &ixgbevf_mbx_ops, sizeof(struct ixgbe_mbx_operations)); @@ -2654,6 +2656,7 @@ static void ixgbevf_set_num_queues(struct ixgbevf_adapter *adapter) case ixgbe_mbox_api_13: case ixgbe_mbox_api_14: case ixgbe_mbox_api_15: + case ixgbe_mbox_api_16: if (adapter->xdp_prog && hw->mac.max_tx_queues == rss) rss = rss > 3 ? 2 : 1; @@ -4648,6 +4651,7 @@ static int ixgbevf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) case ixgbe_mbox_api_13: case ixgbe_mbox_api_14: case ixgbe_mbox_api_15: + case ixgbe_mbox_api_16: netdev->max_mtu = IXGBE_MAX_JUMBO_FRAME_SIZE - (ETH_HLEN + ETH_FCS_LEN); break; diff --git a/drivers/net/ethernet/intel/ixgbevf/mbx.h b/drivers/net/ethernet/intel/ixgbevf/mbx.h index 835bbcc5cc8e63..c1494fd1f67b47 100644 --- a/drivers/net/ethernet/intel/ixgbevf/mbx.h +++ b/drivers/net/ethernet/intel/ixgbevf/mbx.h @@ -66,6 +66,7 @@ enum ixgbe_pfvf_api_rev { ixgbe_mbox_api_13, /* API version 1.3, linux/freebsd VF driver */ ixgbe_mbox_api_14, /* API version 1.4, linux/freebsd VF driver */ ixgbe_mbox_api_15, /* API version 1.5, linux/freebsd VF driver */ + ixgbe_mbox_api_16, /* API version 1.6, linux/freebsd VF driver */ /* This value should always be last */ ixgbe_mbox_api_unknown, /* indicates that API version is not known */ }; @@ -102,6 +103,9 @@ enum ixgbe_pfvf_api_rev { #define IXGBE_VF_GET_LINK_STATE 0x10 /* get vf link state */ +/* mailbox API, version 1.6 VF requests */ +#define IXGBE_VF_GET_PF_LINK_STATE 0x11 /* request PF to send link info */ + /* length of permanent address message returned from PF */ #define IXGBE_VF_PERMADDR_MSG_LEN 4 /* word in permanent address message with the current multicast type */ diff --git a/drivers/net/ethernet/intel/ixgbevf/vf.c b/drivers/net/ethernet/intel/ixgbevf/vf.c index da7a72ecce7a2d..55ac79f87438f2 100644 --- a/drivers/net/ethernet/intel/ixgbevf/vf.c +++ b/drivers/net/ethernet/intel/ixgbevf/vf.c @@ -313,6 +313,7 @@ int ixgbevf_get_reta_locked(struct ixgbe_hw *hw, u32 *reta, int num_rx_queues) * is not supported for this device type. */ switch (hw->api_version) { + case ixgbe_mbox_api_16: case ixgbe_mbox_api_15: case ixgbe_mbox_api_14: case ixgbe_mbox_api_13: @@ -382,6 +383,7 @@ int ixgbevf_get_rss_key_locked(struct ixgbe_hw *hw, u8 *rss_key) * or if the operation is not supported for this device type. */ switch (hw->api_version) { + case ixgbe_mbox_api_16: case ixgbe_mbox_api_15: case ixgbe_mbox_api_14: case ixgbe_mbox_api_13: @@ -552,6 +554,7 @@ static s32 ixgbevf_update_xcast_mode(struct ixgbe_hw *hw, int xcast_mode) case ixgbe_mbox_api_13: case ixgbe_mbox_api_14: case ixgbe_mbox_api_15: + case ixgbe_mbox_api_16: break; default: return -EOPNOTSUPP; @@ -624,6 +627,48 @@ static s32 ixgbevf_hv_get_link_state_vf(struct ixgbe_hw *hw, bool *link_state) return -EOPNOTSUPP; } +/** + * ixgbevf_get_pf_link_state - Get PF's link status + * @hw: pointer to the HW structure + * @speed: link speed + * @link_up: indicate if link is up/down + * + * Ask PF to provide link_up state and speed of the link. + * + * Return: IXGBE_ERR_MBX in the case of mailbox error, + * -EOPNOTSUPP if the op is not supported or 0 on success. + */ +static int ixgbevf_get_pf_link_state(struct ixgbe_hw *hw, ixgbe_link_speed *speed, + bool *link_up) +{ + u32 msgbuf[3] = {}; + int err; + + switch (hw->api_version) { + case ixgbe_mbox_api_16: + break; + default: + return -EOPNOTSUPP; + } + + msgbuf[0] = IXGBE_VF_GET_PF_LINK_STATE; + + err = ixgbevf_write_msg_read_ack(hw, msgbuf, msgbuf, + ARRAY_SIZE(msgbuf)); + if (err || (msgbuf[0] & IXGBE_VT_MSGTYPE_FAILURE)) { + err = IXGBE_ERR_MBX; + *speed = IXGBE_LINK_SPEED_UNKNOWN; + /* No need to set @link_up to false as it will be done by + * ixgbe_check_mac_link_vf(). + */ + } else { + *speed = msgbuf[1]; + *link_up = msgbuf[2]; + } + + return err; +} + /** * ixgbevf_set_vfta_vf - Set/Unset VLAN filter table address * @hw: pointer to the HW structure @@ -658,6 +703,58 @@ static s32 ixgbevf_set_vfta_vf(struct ixgbe_hw *hw, u32 vlan, u32 vind, return err; } +/** + * ixgbe_read_vflinks - Read VFLINKS register + * @hw: pointer to the HW structure + * @speed: link speed + * @link_up: indicate if link is up/down + * + * Get linkup status and link speed from the VFLINKS register. + */ +static void ixgbe_read_vflinks(struct ixgbe_hw *hw, ixgbe_link_speed *speed, + bool *link_up) +{ + u32 vflinks = IXGBE_READ_REG(hw, IXGBE_VFLINKS); + + /* if link status is down no point in checking to see if PF is up */ + if (!(vflinks & IXGBE_LINKS_UP)) { + *link_up = false; + return; + } + + /* for SFP+ modules and DA cables on 82599 it can take up to 500usecs + * before the link status is correct + */ + if (hw->mac.type == ixgbe_mac_82599_vf) { + for (int i = 0; i < 5; i++) { + udelay(100); + vflinks = IXGBE_READ_REG(hw, IXGBE_VFLINKS); + + if (!(vflinks & IXGBE_LINKS_UP)) { + *link_up = false; + return; + } + } + } + + /* We reached this point so there's link */ + *link_up = true; + + switch (vflinks & IXGBE_LINKS_SPEED_82599) { + case IXGBE_LINKS_SPEED_10G_82599: + *speed = IXGBE_LINK_SPEED_10GB_FULL; + break; + case IXGBE_LINKS_SPEED_1G_82599: + *speed = IXGBE_LINK_SPEED_1GB_FULL; + break; + case IXGBE_LINKS_SPEED_100_82599: + *speed = IXGBE_LINK_SPEED_100_FULL; + break; + default: + *speed = IXGBE_LINK_SPEED_UNKNOWN; + } +} + /** * ixgbevf_hv_set_vfta_vf - * Hyper-V variant - just a stub. * @hw: unused @@ -705,7 +802,6 @@ static s32 ixgbevf_check_mac_link_vf(struct ixgbe_hw *hw, struct ixgbe_mbx_info *mbx = &hw->mbx; struct ixgbe_mac_info *mac = &hw->mac; s32 ret_val = 0; - u32 links_reg; u32 in_msg = 0; /* If we were hit with a reset drop the link */ @@ -715,36 +811,14 @@ static s32 ixgbevf_check_mac_link_vf(struct ixgbe_hw *hw, if (!mac->get_link_status) goto out; - /* if link status is down no point in checking to see if pf is up */ - links_reg = IXGBE_READ_REG(hw, IXGBE_VFLINKS); - if (!(links_reg & IXGBE_LINKS_UP)) - goto out; - - /* for SFP+ modules and DA cables on 82599 it can take up to 500usecs - * before the link status is correct - */ - if (mac->type == ixgbe_mac_82599_vf) { - int i; - - for (i = 0; i < 5; i++) { - udelay(100); - links_reg = IXGBE_READ_REG(hw, IXGBE_VFLINKS); - - if (!(links_reg & IXGBE_LINKS_UP)) - goto out; - } - } - - switch (links_reg & IXGBE_LINKS_SPEED_82599) { - case IXGBE_LINKS_SPEED_10G_82599: - *speed = IXGBE_LINK_SPEED_10GB_FULL; - break; - case IXGBE_LINKS_SPEED_1G_82599: - *speed = IXGBE_LINK_SPEED_1GB_FULL; - break; - case IXGBE_LINKS_SPEED_100_82599: - *speed = IXGBE_LINK_SPEED_100_FULL; - break; + if (hw->mac.type == ixgbe_mac_e610_vf) { + ret_val = ixgbevf_get_pf_link_state(hw, speed, link_up); + if (ret_val) + goto out; + } else { + ixgbe_read_vflinks(hw, speed, link_up); + if (*link_up == false) + goto out; } /* if the read failed it could just be a mailbox collision, best wait @@ -951,6 +1025,7 @@ int ixgbevf_get_queues(struct ixgbe_hw *hw, unsigned int *num_tcs, case ixgbe_mbox_api_13: case ixgbe_mbox_api_14: case ixgbe_mbox_api_15: + case ixgbe_mbox_api_16: break; default: return 0; From bf580112ed61736c2645a893413a04732505d4b1 Mon Sep 17 00:00:00 2001 From: Jedrzej Jagielski Date: Mon, 20 Oct 2025 13:28:41 -0400 Subject: [PATCH 125/143] ixgbevf: fix mailbox API compatibility by negotiating supported features [ Upstream commit a7075f501bd33c93570af759b6f4302ef0175168 ] There was backward compatibility in the terms of mailbox API. Various drivers from various OSes supporting 10G adapters from Intel portfolio could easily negotiate mailbox API. This convention has been broken since introducing API 1.4. Commit 0062e7cc955e ("ixgbevf: add VF IPsec offload code") added support for IPSec which is specific only for the kernel ixgbe driver. None of the rest of the Intel 10G PF/VF drivers supports it. And actually lack of support was not included in the IPSec implementation - there were no such code paths. No possibility to negotiate support for the feature was introduced along with introduction of the feature itself. Commit 339f28964147 ("ixgbevf: Add support for new mailbox communication between PF and VF") increasing API version to 1.5 did the same - it introduced code supported specifically by the PF ESX driver. It altered API version for the VF driver in the same time not touching the version defined for the PF ixgbe driver. It led to additional discrepancies, as the code provided within API 1.6 cannot be supported for Linux ixgbe driver as it causes crashes. The issue was noticed some time ago and mitigated by Jake within the commit d0725312adf5 ("ixgbevf: stop attempting IPSEC offload on Mailbox API 1.5"). As a result we have regression for IPsec support and after increasing API to version 1.6 ixgbevf driver stopped to support ESX MBX. To fix this mess add new mailbox op asking PF driver about supported features. Basing on a response determine whether to set support for IPSec and ESX-specific enhanced mailbox. New mailbox op, for compatibility purposes, must be added within new API revision, as API version of OOT PF & VF drivers is already increased to 1.6 and doesn't incorporate features negotiate op. Features negotiation mechanism gives possibility to be extended with new features when needed in the future. Reported-by: Jacob Keller Closes: https://lore.kernel.org/intel-wired-lan/20241101-jk-ixgbevf-mailbox-v1-5-fixes-v1-0-f556dc9a66ed@intel.com/ Fixes: 0062e7cc955e ("ixgbevf: add VF IPsec offload code") Fixes: 339f28964147 ("ixgbevf: Add support for new mailbox communication between PF and VF") Reviewed-by: Jacob Keller Reviewed-by: Przemek Kitszel Reviewed-by: Aleksandr Loktionov Cc: stable@vger.kernel.org Signed-off-by: Jedrzej Jagielski Tested-by: Rafal Romanowski Signed-off-by: Jacob Keller Link: https://patch.msgid.link/20251009-jk-iwl-net-2025-10-01-v3-4-ef32a425b92a@intel.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/ixgbevf/ipsec.c | 10 +++++ drivers/net/ethernet/intel/ixgbevf/ixgbevf.h | 7 +++ .../net/ethernet/intel/ixgbevf/ixgbevf_main.c | 32 ++++++++++++- drivers/net/ethernet/intel/ixgbevf/mbx.h | 4 ++ drivers/net/ethernet/intel/ixgbevf/vf.c | 45 ++++++++++++++++++- drivers/net/ethernet/intel/ixgbevf/vf.h | 1 + 6 files changed, 96 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbevf/ipsec.c b/drivers/net/ethernet/intel/ixgbevf/ipsec.c index f804b35d79c726..83b3243f172c72 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ipsec.c +++ b/drivers/net/ethernet/intel/ixgbevf/ipsec.c @@ -271,6 +271,9 @@ static int ixgbevf_ipsec_add_sa(struct xfrm_state *xs, adapter = netdev_priv(dev); ipsec = adapter->ipsec; + if (!(adapter->pf_features & IXGBEVF_PF_SUP_IPSEC)) + return -EOPNOTSUPP; + if (xs->id.proto != IPPROTO_ESP && xs->id.proto != IPPROTO_AH) { NL_SET_ERR_MSG_MOD(extack, "Unsupported protocol for IPsec offload"); return -EINVAL; @@ -400,6 +403,9 @@ static void ixgbevf_ipsec_del_sa(struct xfrm_state *xs) adapter = netdev_priv(dev); ipsec = adapter->ipsec; + if (!(adapter->pf_features & IXGBEVF_PF_SUP_IPSEC)) + return; + if (xs->xso.dir == XFRM_DEV_OFFLOAD_IN) { sa_idx = xs->xso.offload_handle - IXGBE_IPSEC_BASE_RX_INDEX; @@ -628,6 +634,10 @@ void ixgbevf_init_ipsec_offload(struct ixgbevf_adapter *adapter) size_t size; switch (adapter->hw.api_version) { + case ixgbe_mbox_api_17: + if (!(adapter->pf_features & IXGBEVF_PF_SUP_IPSEC)) + return; + break; case ixgbe_mbox_api_14: break; default: diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h b/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h index 9b37f354d78ce0..f31068e24e867f 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h @@ -366,6 +366,13 @@ struct ixgbevf_adapter { /* Interrupt Throttle Rate */ u32 eitr_param; + u32 pf_features; +#define IXGBEVF_PF_SUP_IPSEC BIT(0) +#define IXGBEVF_PF_SUP_ESX_MBX BIT(1) + +#define IXGBEVF_SUPPORTED_FEATURES (IXGBEVF_PF_SUP_IPSEC | \ + IXGBEVF_PF_SUP_ESX_MBX) + struct ixgbevf_hw_stats stats; unsigned long state; diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index 72ca026618d67b..a2a7cb6d8ea181 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -2274,10 +2274,35 @@ static void ixgbevf_init_last_counter_stats(struct ixgbevf_adapter *adapter) adapter->stats.base_vfmprc = adapter->stats.last_vfmprc; } +/** + * ixgbevf_set_features - Set features supported by PF + * @adapter: pointer to the adapter struct + * + * Negotiate with PF supported features and then set pf_features accordingly. + */ +static void ixgbevf_set_features(struct ixgbevf_adapter *adapter) +{ + u32 *pf_features = &adapter->pf_features; + struct ixgbe_hw *hw = &adapter->hw; + int err; + + err = hw->mac.ops.negotiate_features(hw, pf_features); + if (err && err != -EOPNOTSUPP) + netdev_dbg(adapter->netdev, + "PF feature negotiation failed.\n"); + + /* Address also pre API 1.7 cases */ + if (hw->api_version == ixgbe_mbox_api_14) + *pf_features |= IXGBEVF_PF_SUP_IPSEC; + else if (hw->api_version == ixgbe_mbox_api_15) + *pf_features |= IXGBEVF_PF_SUP_ESX_MBX; +} + static void ixgbevf_negotiate_api(struct ixgbevf_adapter *adapter) { struct ixgbe_hw *hw = &adapter->hw; static const int api[] = { + ixgbe_mbox_api_17, ixgbe_mbox_api_16, ixgbe_mbox_api_15, ixgbe_mbox_api_14, @@ -2298,8 +2323,9 @@ static void ixgbevf_negotiate_api(struct ixgbevf_adapter *adapter) idx++; } - /* Following is not supported by API 1.6, it is specific for 1.5 */ - if (hw->api_version == ixgbe_mbox_api_15) { + ixgbevf_set_features(adapter); + + if (adapter->pf_features & IXGBEVF_PF_SUP_ESX_MBX) { hw->mbx.ops.init_params(hw); memcpy(&hw->mbx.ops, &ixgbevf_mbx_ops, sizeof(struct ixgbe_mbx_operations)); @@ -2657,6 +2683,7 @@ static void ixgbevf_set_num_queues(struct ixgbevf_adapter *adapter) case ixgbe_mbox_api_14: case ixgbe_mbox_api_15: case ixgbe_mbox_api_16: + case ixgbe_mbox_api_17: if (adapter->xdp_prog && hw->mac.max_tx_queues == rss) rss = rss > 3 ? 2 : 1; @@ -4652,6 +4679,7 @@ static int ixgbevf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) case ixgbe_mbox_api_14: case ixgbe_mbox_api_15: case ixgbe_mbox_api_16: + case ixgbe_mbox_api_17: netdev->max_mtu = IXGBE_MAX_JUMBO_FRAME_SIZE - (ETH_HLEN + ETH_FCS_LEN); break; diff --git a/drivers/net/ethernet/intel/ixgbevf/mbx.h b/drivers/net/ethernet/intel/ixgbevf/mbx.h index c1494fd1f67b47..a8ed23ee66aa84 100644 --- a/drivers/net/ethernet/intel/ixgbevf/mbx.h +++ b/drivers/net/ethernet/intel/ixgbevf/mbx.h @@ -67,6 +67,7 @@ enum ixgbe_pfvf_api_rev { ixgbe_mbox_api_14, /* API version 1.4, linux/freebsd VF driver */ ixgbe_mbox_api_15, /* API version 1.5, linux/freebsd VF driver */ ixgbe_mbox_api_16, /* API version 1.6, linux/freebsd VF driver */ + ixgbe_mbox_api_17, /* API version 1.7, linux/freebsd VF driver */ /* This value should always be last */ ixgbe_mbox_api_unknown, /* indicates that API version is not known */ }; @@ -106,6 +107,9 @@ enum ixgbe_pfvf_api_rev { /* mailbox API, version 1.6 VF requests */ #define IXGBE_VF_GET_PF_LINK_STATE 0x11 /* request PF to send link info */ +/* mailbox API, version 1.7 VF requests */ +#define IXGBE_VF_FEATURES_NEGOTIATE 0x12 /* get features supported by PF*/ + /* length of permanent address message returned from PF */ #define IXGBE_VF_PERMADDR_MSG_LEN 4 /* word in permanent address message with the current multicast type */ diff --git a/drivers/net/ethernet/intel/ixgbevf/vf.c b/drivers/net/ethernet/intel/ixgbevf/vf.c index 55ac79f87438f2..65257107dfc8a4 100644 --- a/drivers/net/ethernet/intel/ixgbevf/vf.c +++ b/drivers/net/ethernet/intel/ixgbevf/vf.c @@ -313,6 +313,7 @@ int ixgbevf_get_reta_locked(struct ixgbe_hw *hw, u32 *reta, int num_rx_queues) * is not supported for this device type. */ switch (hw->api_version) { + case ixgbe_mbox_api_17: case ixgbe_mbox_api_16: case ixgbe_mbox_api_15: case ixgbe_mbox_api_14: @@ -383,6 +384,7 @@ int ixgbevf_get_rss_key_locked(struct ixgbe_hw *hw, u8 *rss_key) * or if the operation is not supported for this device type. */ switch (hw->api_version) { + case ixgbe_mbox_api_17: case ixgbe_mbox_api_16: case ixgbe_mbox_api_15: case ixgbe_mbox_api_14: @@ -555,6 +557,7 @@ static s32 ixgbevf_update_xcast_mode(struct ixgbe_hw *hw, int xcast_mode) case ixgbe_mbox_api_14: case ixgbe_mbox_api_15: case ixgbe_mbox_api_16: + case ixgbe_mbox_api_17: break; default: return -EOPNOTSUPP; @@ -646,6 +649,7 @@ static int ixgbevf_get_pf_link_state(struct ixgbe_hw *hw, ixgbe_link_speed *spee switch (hw->api_version) { case ixgbe_mbox_api_16: + case ixgbe_mbox_api_17: break; default: return -EOPNOTSUPP; @@ -669,6 +673,42 @@ static int ixgbevf_get_pf_link_state(struct ixgbe_hw *hw, ixgbe_link_speed *spee return err; } +/** + * ixgbevf_negotiate_features_vf - negotiate supported features with PF driver + * @hw: pointer to the HW structure + * @pf_features: bitmask of features supported by PF + * + * Return: IXGBE_ERR_MBX in the case of mailbox error, + * -EOPNOTSUPP if the op is not supported or 0 on success. + */ +static int ixgbevf_negotiate_features_vf(struct ixgbe_hw *hw, u32 *pf_features) +{ + u32 msgbuf[2] = {}; + int err; + + switch (hw->api_version) { + case ixgbe_mbox_api_17: + break; + default: + return -EOPNOTSUPP; + } + + msgbuf[0] = IXGBE_VF_FEATURES_NEGOTIATE; + msgbuf[1] = IXGBEVF_SUPPORTED_FEATURES; + + err = ixgbevf_write_msg_read_ack(hw, msgbuf, msgbuf, + ARRAY_SIZE(msgbuf)); + + if (err || (msgbuf[0] & IXGBE_VT_MSGTYPE_FAILURE)) { + err = IXGBE_ERR_MBX; + *pf_features = 0x0; + } else { + *pf_features = msgbuf[1]; + } + + return err; +} + /** * ixgbevf_set_vfta_vf - Set/Unset VLAN filter table address * @hw: pointer to the HW structure @@ -799,6 +839,7 @@ static s32 ixgbevf_check_mac_link_vf(struct ixgbe_hw *hw, bool *link_up, bool autoneg_wait_to_complete) { + struct ixgbevf_adapter *adapter = hw->back; struct ixgbe_mbx_info *mbx = &hw->mbx; struct ixgbe_mac_info *mac = &hw->mac; s32 ret_val = 0; @@ -825,7 +866,7 @@ static s32 ixgbevf_check_mac_link_vf(struct ixgbe_hw *hw, * until we are called again and don't report an error */ if (mbx->ops.read(hw, &in_msg, 1)) { - if (hw->api_version >= ixgbe_mbox_api_15) + if (adapter->pf_features & IXGBEVF_PF_SUP_ESX_MBX) mac->get_link_status = false; goto out; } @@ -1026,6 +1067,7 @@ int ixgbevf_get_queues(struct ixgbe_hw *hw, unsigned int *num_tcs, case ixgbe_mbox_api_14: case ixgbe_mbox_api_15: case ixgbe_mbox_api_16: + case ixgbe_mbox_api_17: break; default: return 0; @@ -1080,6 +1122,7 @@ static const struct ixgbe_mac_operations ixgbevf_mac_ops = { .setup_link = ixgbevf_setup_mac_link_vf, .check_link = ixgbevf_check_mac_link_vf, .negotiate_api_version = ixgbevf_negotiate_api_version_vf, + .negotiate_features = ixgbevf_negotiate_features_vf, .set_rar = ixgbevf_set_rar_vf, .update_mc_addr_list = ixgbevf_update_mc_addr_list_vf, .update_xcast_mode = ixgbevf_update_xcast_mode, diff --git a/drivers/net/ethernet/intel/ixgbevf/vf.h b/drivers/net/ethernet/intel/ixgbevf/vf.h index 2d791bc26ae4e7..4f19b8900c29a3 100644 --- a/drivers/net/ethernet/intel/ixgbevf/vf.h +++ b/drivers/net/ethernet/intel/ixgbevf/vf.h @@ -26,6 +26,7 @@ struct ixgbe_mac_operations { s32 (*stop_adapter)(struct ixgbe_hw *); s32 (*get_bus_info)(struct ixgbe_hw *); s32 (*negotiate_api_version)(struct ixgbe_hw *hw, int api); + int (*negotiate_features)(struct ixgbe_hw *hw, u32 *pf_features); /* Link */ s32 (*setup_link)(struct ixgbe_hw *, ixgbe_link_speed, bool, bool); From f620d9ba4a099d6bd8909586270e04d64a5affb0 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 20 Oct 2025 11:44:02 -0400 Subject: [PATCH 126/143] tcp: convert to dev_net_rcu() [ Upstream commit e7b9ecce562ca6a1de32c56c597fa45e08c44ec0 ] TCP uses of dev_net() are under RCU protection, change them to dev_net_rcu() to get LOCKDEP support. Signed-off-by: Eric Dumazet Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20250301201424.2046477-4-edumazet@google.com Signed-off-by: Jakub Kicinski Stable-dep-of: 833d4313bc1e ("mptcp: reset blackhole on success with non-loopback ifaces") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- include/net/inet6_hashtables.h | 2 +- include/net/inet_hashtables.h | 2 +- net/ipv4/tcp_ipv4.c | 12 ++++++------ net/ipv4/tcp_metrics.c | 6 +++--- net/ipv6/tcp_ipv6.c | 22 +++++++++++----------- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/include/net/inet6_hashtables.h b/include/net/inet6_hashtables.h index 74dd90ff5f129f..c32878c69179da 100644 --- a/include/net/inet6_hashtables.h +++ b/include/net/inet6_hashtables.h @@ -150,7 +150,7 @@ static inline struct sock *__inet6_lookup_skb(struct inet_hashinfo *hashinfo, int iif, int sdif, bool *refcounted) { - struct net *net = dev_net(skb_dst(skb)->dev); + struct net *net = dev_net_rcu(skb_dst(skb)->dev); const struct ipv6hdr *ip6h = ipv6_hdr(skb); struct sock *sk; diff --git a/include/net/inet_hashtables.h b/include/net/inet_hashtables.h index 5eea47f135a421..da818fb0205fed 100644 --- a/include/net/inet_hashtables.h +++ b/include/net/inet_hashtables.h @@ -492,7 +492,7 @@ static inline struct sock *__inet_lookup_skb(struct inet_hashinfo *hashinfo, const int sdif, bool *refcounted) { - struct net *net = dev_net(skb_dst(skb)->dev); + struct net *net = dev_net_rcu(skb_dst(skb)->dev); const struct iphdr *iph = ip_hdr(skb); struct sock *sk; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 824048679e1b8f..d8976753d4e47e 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -494,14 +494,14 @@ int tcp_v4_err(struct sk_buff *skb, u32 info) { const struct iphdr *iph = (const struct iphdr *)skb->data; struct tcphdr *th = (struct tcphdr *)(skb->data + (iph->ihl << 2)); - struct tcp_sock *tp; + struct net *net = dev_net_rcu(skb->dev); const int type = icmp_hdr(skb)->type; const int code = icmp_hdr(skb)->code; - struct sock *sk; struct request_sock *fastopen; + struct tcp_sock *tp; u32 seq, snd_una; + struct sock *sk; int err; - struct net *net = dev_net(skb->dev); sk = __inet_lookup_established(net, net->ipv4.tcp_death_row.hashinfo, iph->daddr, th->dest, iph->saddr, @@ -786,7 +786,7 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb, arg.iov[0].iov_base = (unsigned char *)&rep; arg.iov[0].iov_len = sizeof(rep.th); - net = sk ? sock_net(sk) : dev_net(skb_dst(skb)->dev); + net = sk ? sock_net(sk) : dev_net_rcu(skb_dst(skb)->dev); /* Invalid TCP option size or twice included auth */ if (tcp_parse_auth_options(tcp_hdr(skb), &md5_hash_location, &aoh)) @@ -1965,7 +1965,7 @@ EXPORT_SYMBOL(tcp_v4_do_rcv); int tcp_v4_early_demux(struct sk_buff *skb) { - struct net *net = dev_net(skb->dev); + struct net *net = dev_net_rcu(skb->dev); const struct iphdr *iph; const struct tcphdr *th; struct sock *sk; @@ -2176,7 +2176,7 @@ static void tcp_v4_fill_cb(struct sk_buff *skb, const struct iphdr *iph, int tcp_v4_rcv(struct sk_buff *skb) { - struct net *net = dev_net(skb->dev); + struct net *net = dev_net_rcu(skb->dev); enum skb_drop_reason drop_reason; int sdif = inet_sdif(skb); int dif = inet_iif(skb); diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c index 95669935494ef8..4251670e328c83 100644 --- a/net/ipv4/tcp_metrics.c +++ b/net/ipv4/tcp_metrics.c @@ -170,7 +170,7 @@ static struct tcp_metrics_block *tcpm_new(struct dst_entry *dst, bool reclaim = false; spin_lock_bh(&tcp_metrics_lock); - net = dev_net(dst->dev); + net = dev_net_rcu(dst->dev); /* While waiting for the spin-lock the cache might have been populated * with this entry and so we have to check again. @@ -273,7 +273,7 @@ static struct tcp_metrics_block *__tcp_get_metrics_req(struct request_sock *req, return NULL; } - net = dev_net(dst->dev); + net = dev_net_rcu(dst->dev); hash ^= net_hash_mix(net); hash = hash_32(hash, tcp_metrics_hash_log); @@ -318,7 +318,7 @@ static struct tcp_metrics_block *tcp_get_metrics(struct sock *sk, else return NULL; - net = dev_net(dst->dev); + net = dev_net_rcu(dst->dev); hash ^= net_hash_mix(net); hash = hash_32(hash, tcp_metrics_hash_log); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 882ce5444572ea..06edfe0b2db904 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -376,7 +376,7 @@ static int tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, { const struct ipv6hdr *hdr = (const struct ipv6hdr *)skb->data; const struct tcphdr *th = (struct tcphdr *)(skb->data+offset); - struct net *net = dev_net(skb->dev); + struct net *net = dev_net_rcu(skb->dev); struct request_sock *fastopen; struct ipv6_pinfo *np; struct tcp_sock *tp; @@ -864,16 +864,16 @@ static void tcp_v6_send_response(const struct sock *sk, struct sk_buff *skb, u32 int oif, int rst, u8 tclass, __be32 label, u32 priority, u32 txhash, struct tcp_key *key) { - const struct tcphdr *th = tcp_hdr(skb); - struct tcphdr *t1; - struct sk_buff *buff; - struct flowi6 fl6; - struct net *net = sk ? sock_net(sk) : dev_net(skb_dst(skb)->dev); - struct sock *ctl_sk = net->ipv6.tcp_sk; + struct net *net = sk ? sock_net(sk) : dev_net_rcu(skb_dst(skb)->dev); unsigned int tot_len = sizeof(struct tcphdr); + struct sock *ctl_sk = net->ipv6.tcp_sk; + const struct tcphdr *th = tcp_hdr(skb); __be32 mrst = 0, *topt; struct dst_entry *dst; - __u32 mark = 0; + struct sk_buff *buff; + struct tcphdr *t1; + struct flowi6 fl6; + u32 mark = 0; if (tsecr) tot_len += TCPOLEN_TSTAMP_ALIGNED; @@ -1036,7 +1036,7 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb, if (!sk && !ipv6_unicast_destination(skb)) return; - net = sk ? sock_net(sk) : dev_net(skb_dst(skb)->dev); + net = sk ? sock_net(sk) : dev_net_rcu(skb_dst(skb)->dev); /* Invalid TCP option size or twice included auth */ if (tcp_parse_auth_options(th, &md5_hash_location, &aoh)) return; @@ -1739,6 +1739,7 @@ static void tcp_v6_fill_cb(struct sk_buff *skb, const struct ipv6hdr *hdr, INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb) { + struct net *net = dev_net_rcu(skb->dev); enum skb_drop_reason drop_reason; int sdif = inet6_sdif(skb); int dif = inet6_iif(skb); @@ -1748,7 +1749,6 @@ INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb) bool refcounted; int ret; u32 isn; - struct net *net = dev_net(skb->dev); drop_reason = SKB_DROP_REASON_NOT_SPECIFIED; if (skb->pkt_type != PACKET_HOST) @@ -1999,7 +1999,7 @@ INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb) void tcp_v6_early_demux(struct sk_buff *skb) { - struct net *net = dev_net(skb->dev); + struct net *net = dev_net_rcu(skb->dev); const struct ipv6hdr *hdr; const struct tcphdr *th; struct sock *sk; From bcdbf4d7d9138d57ac33fe80242e308f587a1b36 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 20 Oct 2025 11:44:03 -0400 Subject: [PATCH 127/143] tcp: cache RTAX_QUICKACK metric in a hot cache line [ Upstream commit 15492700ac41459b54a6683490adcee350ab11e3 ] tcp_in_quickack_mode() is called from input path for small packets. It calls __sk_dst_get() which reads sk->sk_dst_cache which has been put in sock_read_tx group (for good reasons). Then dst_metric(dst, RTAX_QUICKACK) also needs extra cache line misses. Cache RTAX_QUICKACK in icsk->icsk_ack.dst_quick_ack to no longer pull these cache lines for the cases a delayed ACK is scheduled. After this patch TCP receive path does not longer access sock_read_tx group. Signed-off-by: Eric Dumazet Reviewed-by: Jason Xing Reviewed-by: Neal Cardwell Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20250312083907.1931644-1-edumazet@google.com Signed-off-by: Paolo Abeni Stable-dep-of: 833d4313bc1e ("mptcp: reset blackhole on success with non-loopback ifaces") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- include/net/inet_connection_sock.h | 3 ++- net/core/sock.c | 6 +++++- net/ipv4/tcp_input.c | 3 +-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/include/net/inet_connection_sock.h b/include/net/inet_connection_sock.h index 4bd93571e6c1b5..bcc138ff087bd6 100644 --- a/include/net/inet_connection_sock.h +++ b/include/net/inet_connection_sock.h @@ -116,7 +116,8 @@ struct inet_connection_sock { #define ATO_BITS 8 __u32 ato:ATO_BITS, /* Predicted tick of soft clock */ lrcv_flowlabel:20, /* last received ipv6 flowlabel */ - unused:4; + dst_quick_ack:1, /* cache dst RTAX_QUICKACK */ + unused:3; unsigned long timeout; /* Currently scheduled timeout */ __u32 lrcvtime; /* timestamp of last received data packet */ __u16 last_seg_size; /* Size of last incoming segment */ diff --git a/net/core/sock.c b/net/core/sock.c index d392cb37a864f7..b5723adab4ebf7 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -2547,8 +2547,12 @@ void sk_setup_caps(struct sock *sk, struct dst_entry *dst) u32 max_segs = 1; sk->sk_route_caps = dst->dev->features; - if (sk_is_tcp(sk)) + if (sk_is_tcp(sk)) { + struct inet_connection_sock *icsk = inet_csk(sk); + sk->sk_route_caps |= NETIF_F_GSO; + icsk->icsk_ack.dst_quick_ack = dst_metric(dst, RTAX_QUICKACK); + } if (sk->sk_route_caps & NETIF_F_GSO) sk->sk_route_caps |= NETIF_F_GSO_SOFTWARE; if (unlikely(sk->sk_gso_disabled)) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 4c8d84fc27ca35..1d9e93a04930b8 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -331,9 +331,8 @@ static void tcp_enter_quickack_mode(struct sock *sk, unsigned int max_quickacks) static bool tcp_in_quickack_mode(struct sock *sk) { const struct inet_connection_sock *icsk = inet_csk(sk); - const struct dst_entry *dst = __sk_dst_get(sk); - return (dst && dst_metric(dst, RTAX_QUICKACK)) || + return icsk->icsk_ack.dst_quick_ack || (icsk->icsk_ack.quick && !inet_csk_in_pingpong_mode(sk)); } From 8f001670cbb2bf8f5e84a97eb0bf9dc51ee885d6 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 20 Oct 2025 11:44:04 -0400 Subject: [PATCH 128/143] net: dst: add four helpers to annotate data-races around dst->dev [ Upstream commit 88fe14253e181878c2ddb51a298ae8c468a63010 ] dst->dev is read locklessly in many contexts, and written in dst_dev_put(). Fixing all the races is going to need many changes. We probably will have to add full RCU protection. Add three helpers to ease this painful process. static inline struct net_device *dst_dev(const struct dst_entry *dst) { return READ_ONCE(dst->dev); } static inline struct net_device *skb_dst_dev(const struct sk_buff *skb) { return dst_dev(skb_dst(skb)); } static inline struct net *skb_dst_dev_net(const struct sk_buff *skb) { return dev_net(skb_dst_dev(skb)); } static inline struct net *skb_dst_dev_net_rcu(const struct sk_buff *skb) { return dev_net_rcu(skb_dst_dev(skb)); } Fixes: 4a6ce2b6f2ec ("net: introduce a new function dst_dev_put()") Signed-off-by: Eric Dumazet Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20250630121934.3399505-7-edumazet@google.com Signed-off-by: Jakub Kicinski Stable-dep-of: 833d4313bc1e ("mptcp: reset blackhole on success with non-loopback ifaces") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- include/net/dst.h | 20 ++++++++++++++++++++ net/core/dst.c | 4 ++-- net/core/sock.c | 8 ++++---- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/include/net/dst.h b/include/net/dst.h index e18826cd055952..23ee8139a75630 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -561,6 +561,26 @@ static inline void skb_dst_update_pmtu_no_confirm(struct sk_buff *skb, u32 mtu) dst->ops->update_pmtu(dst, NULL, skb, mtu, false); } +static inline struct net_device *dst_dev(const struct dst_entry *dst) +{ + return READ_ONCE(dst->dev); +} + +static inline struct net_device *skb_dst_dev(const struct sk_buff *skb) +{ + return dst_dev(skb_dst(skb)); +} + +static inline struct net *skb_dst_dev_net(const struct sk_buff *skb) +{ + return dev_net(skb_dst_dev(skb)); +} + +static inline struct net *skb_dst_dev_net_rcu(const struct sk_buff *skb) +{ + return dev_net_rcu(skb_dst_dev(skb)); +} + struct dst_entry *dst_blackhole_check(struct dst_entry *dst, u32 cookie); void dst_blackhole_update_pmtu(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb, u32 mtu, bool confirm_neigh); diff --git a/net/core/dst.c b/net/core/dst.c index cc990706b64515..9a0ddef8bee430 100644 --- a/net/core/dst.c +++ b/net/core/dst.c @@ -150,7 +150,7 @@ void dst_dev_put(struct dst_entry *dst) dst->ops->ifdown(dst, dev); WRITE_ONCE(dst->input, dst_discard); WRITE_ONCE(dst->output, dst_discard_out); - dst->dev = blackhole_netdev; + WRITE_ONCE(dst->dev, blackhole_netdev); netdev_ref_replace(dev, blackhole_netdev, &dst->dev_tracker, GFP_ATOMIC); } @@ -263,7 +263,7 @@ unsigned int dst_blackhole_mtu(const struct dst_entry *dst) { unsigned int mtu = dst_metric_raw(dst, RTAX_MTU); - return mtu ? : dst->dev->mtu; + return mtu ? : dst_dev(dst)->mtu; } EXPORT_SYMBOL_GPL(dst_blackhole_mtu); diff --git a/net/core/sock.c b/net/core/sock.c index b5723adab4ebf7..a5f248a9140426 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -2534,8 +2534,8 @@ static u32 sk_dst_gso_max_size(struct sock *sk, struct dst_entry *dst) !ipv6_addr_v4mapped(&sk->sk_v6_rcv_saddr)); #endif /* pairs with the WRITE_ONCE() in netif_set_gso(_ipv4)_max_size() */ - max_size = is_ipv6 ? READ_ONCE(dst->dev->gso_max_size) : - READ_ONCE(dst->dev->gso_ipv4_max_size); + max_size = is_ipv6 ? READ_ONCE(dst_dev(dst)->gso_max_size) : + READ_ONCE(dst_dev(dst)->gso_ipv4_max_size); if (max_size > GSO_LEGACY_MAX_SIZE && !sk_is_tcp(sk)) max_size = GSO_LEGACY_MAX_SIZE; @@ -2546,7 +2546,7 @@ void sk_setup_caps(struct sock *sk, struct dst_entry *dst) { u32 max_segs = 1; - sk->sk_route_caps = dst->dev->features; + sk->sk_route_caps = dst_dev(dst)->features; if (sk_is_tcp(sk)) { struct inet_connection_sock *icsk = inet_csk(sk); @@ -2564,7 +2564,7 @@ void sk_setup_caps(struct sock *sk, struct dst_entry *dst) sk->sk_route_caps |= NETIF_F_SG | NETIF_F_HW_CSUM; sk->sk_gso_max_size = sk_dst_gso_max_size(sk, dst); /* pairs with the WRITE_ONCE() in netif_set_gso_max_segs() */ - max_segs = max_t(u32, READ_ONCE(dst->dev->gso_max_segs), 1); + max_segs = max_t(u32, READ_ONCE(dst_dev(dst)->gso_max_segs), 1); } } sk->sk_gso_max_segs = max_segs; From 95d4308875d1d8a9066df4f8aa1a8c622ab1e5b1 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 20 Oct 2025 11:44:05 -0400 Subject: [PATCH 129/143] ipv4: adopt dst_dev, skb_dst_dev and skb_dst_dev_net[_rcu] [ Upstream commit a74fc62eec155ca5a6da8ff3856f3dc87fe24558 ] Use the new helpers as a first step to deal with potential dst->dev races. Signed-off-by: Eric Dumazet Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20250630121934.3399505-8-edumazet@google.com Signed-off-by: Jakub Kicinski Stable-dep-of: 833d4313bc1e ("mptcp: reset blackhole on success with non-loopback ifaces") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- include/net/inet_hashtables.h | 2 +- include/net/ip.h | 11 ++++++----- include/net/route.h | 2 +- net/ipv4/icmp.c | 24 +++++++++++++----------- net/ipv4/igmp.c | 2 +- net/ipv4/ip_fragment.c | 2 +- net/ipv4/ip_output.c | 6 +++--- net/ipv4/ip_vti.c | 4 ++-- net/ipv4/netfilter.c | 4 ++-- net/ipv4/route.c | 8 ++++---- net/ipv4/tcp_fastopen.c | 4 +++- net/ipv4/tcp_ipv4.c | 2 +- net/ipv4/tcp_metrics.c | 8 ++++---- net/ipv4/xfrm4_output.c | 2 +- 14 files changed, 43 insertions(+), 38 deletions(-) diff --git a/include/net/inet_hashtables.h b/include/net/inet_hashtables.h index da818fb0205fed..3c4118c63cfe1b 100644 --- a/include/net/inet_hashtables.h +++ b/include/net/inet_hashtables.h @@ -492,7 +492,7 @@ static inline struct sock *__inet_lookup_skb(struct inet_hashinfo *hashinfo, const int sdif, bool *refcounted) { - struct net *net = dev_net_rcu(skb_dst(skb)->dev); + struct net *net = skb_dst_dev_net_rcu(skb); const struct iphdr *iph = ip_hdr(skb); struct sock *sk; diff --git a/include/net/ip.h b/include/net/ip.h index bd201278c55a58..5f0f1215d2f923 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -475,7 +475,7 @@ static inline unsigned int ip_dst_mtu_maybe_forward(const struct dst_entry *dst, rcu_read_lock(); - net = dev_net_rcu(dst->dev); + net = dev_net_rcu(dst_dev(dst)); if (READ_ONCE(net->ipv4.sysctl_ip_fwd_use_pmtu) || ip_mtu_locked(dst) || !forwarding) { @@ -489,7 +489,7 @@ static inline unsigned int ip_dst_mtu_maybe_forward(const struct dst_entry *dst, if (mtu) goto out; - mtu = READ_ONCE(dst->dev->mtu); + mtu = READ_ONCE(dst_dev(dst)->mtu); if (unlikely(ip_mtu_locked(dst))) { if (rt->rt_uses_gateway && mtu > 576) @@ -509,16 +509,17 @@ static inline unsigned int ip_dst_mtu_maybe_forward(const struct dst_entry *dst, static inline unsigned int ip_skb_dst_mtu(struct sock *sk, const struct sk_buff *skb) { + const struct dst_entry *dst = skb_dst(skb); unsigned int mtu; if (!sk || !sk_fullsock(sk) || ip_sk_use_pmtu(sk)) { bool forwarding = IPCB(skb)->flags & IPSKB_FORWARDED; - return ip_dst_mtu_maybe_forward(skb_dst(skb), forwarding); + return ip_dst_mtu_maybe_forward(dst, forwarding); } - mtu = min(READ_ONCE(skb_dst(skb)->dev->mtu), IP_MAX_MTU); - return mtu - lwtunnel_headroom(skb_dst(skb)->lwtstate, mtu); + mtu = min(READ_ONCE(dst_dev(dst)->mtu), IP_MAX_MTU); + return mtu - lwtunnel_headroom(dst->lwtstate, mtu); } struct dst_metrics *ip_fib_metrics_init(struct nlattr *fc_mx, int fc_mx_len, diff --git a/include/net/route.h b/include/net/route.h index 8a11d19f897bb2..232b7bf55ba221 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -369,7 +369,7 @@ static inline int ip4_dst_hoplimit(const struct dst_entry *dst) const struct net *net; rcu_read_lock(); - net = dev_net_rcu(dst->dev); + net = dev_net_rcu(dst_dev(dst)); hoplimit = READ_ONCE(net->ipv4.sysctl_ip_default_ttl); rcu_read_unlock(); } diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 8f11870b77377d..508b23204edc5b 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -311,18 +311,20 @@ static bool icmpv4_xrlim_allow(struct net *net, struct rtable *rt, { struct dst_entry *dst = &rt->dst; struct inet_peer *peer; + struct net_device *dev; bool rc = true; if (!apply_ratelimit) return true; /* No rate limit on loopback */ - if (dst->dev && (dst->dev->flags&IFF_LOOPBACK)) + dev = dst_dev(dst); + if (dev && (dev->flags & IFF_LOOPBACK)) goto out; rcu_read_lock(); peer = inet_getpeer_v4(net->ipv4.peers, fl4->daddr, - l3mdev_master_ifindex_rcu(dst->dev)); + l3mdev_master_ifindex_rcu(dev)); rc = inet_peer_xrlim_allow(peer, READ_ONCE(net->ipv4.sysctl_icmp_ratelimit)); rcu_read_unlock(); @@ -468,13 +470,13 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb) */ static struct net_device *icmp_get_route_lookup_dev(struct sk_buff *skb) { - struct net_device *route_lookup_dev = NULL; + struct net_device *dev = skb->dev; + const struct dst_entry *dst; - if (skb->dev) - route_lookup_dev = skb->dev; - else if (skb_dst(skb)) - route_lookup_dev = skb_dst(skb)->dev; - return route_lookup_dev; + if (dev) + return dev; + dst = skb_dst(skb); + return dst ? dst_dev(dst) : NULL; } static struct rtable *icmp_route_lookup(struct net *net, struct flowi4 *fl4, @@ -873,7 +875,7 @@ static enum skb_drop_reason icmp_unreach(struct sk_buff *skb) struct net *net; u32 info = 0; - net = dev_net_rcu(skb_dst(skb)->dev); + net = skb_dst_dev_net_rcu(skb); /* * Incomplete header ? @@ -1016,7 +1018,7 @@ static enum skb_drop_reason icmp_echo(struct sk_buff *skb) struct icmp_bxm icmp_param; struct net *net; - net = dev_net_rcu(skb_dst(skb)->dev); + net = skb_dst_dev_net_rcu(skb); /* should there be an ICMP stat for ignored echos? */ if (READ_ONCE(net->ipv4.sysctl_icmp_echo_ignore_all)) return SKB_NOT_DROPPED_YET; @@ -1186,7 +1188,7 @@ static enum skb_drop_reason icmp_timestamp(struct sk_buff *skb) return SKB_NOT_DROPPED_YET; out_err: - __ICMP_INC_STATS(dev_net_rcu(skb_dst(skb)->dev), ICMP_MIB_INERRORS); + __ICMP_INC_STATS(skb_dst_dev_net_rcu(skb), ICMP_MIB_INERRORS); return SKB_DROP_REASON_PKT_TOO_SMALL; } diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 9bf09de6a2e77c..f4a87b90351e94 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -424,7 +424,7 @@ static int igmpv3_sendpack(struct sk_buff *skb) pig->csum = ip_compute_csum(igmp_hdr(skb), igmplen); - return ip_local_out(dev_net(skb_dst(skb)->dev), skb->sk, skb); + return ip_local_out(skb_dst_dev_net(skb), skb->sk, skb); } static int grec_size(struct ip_mc_list *pmc, int type, int gdel, int sdel) diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index 9ca0a183a55ffa..183856b0b74094 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -488,7 +488,7 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *skb, /* Process an incoming IP datagram fragment. */ int ip_defrag(struct net *net, struct sk_buff *skb, u32 user) { - struct net_device *dev = skb->dev ? : skb_dst(skb)->dev; + struct net_device *dev = skb->dev ? : skb_dst_dev(skb); int vif = l3mdev_master_ifindex_rcu(dev); struct ipq *qp; diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 49811c9281d424..4d432e314bcb28 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -117,7 +117,7 @@ int __ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb) skb->protocol = htons(ETH_P_IP); return nf_hook(NFPROTO_IPV4, NF_INET_LOCAL_OUT, - net, sk, skb, NULL, skb_dst(skb)->dev, + net, sk, skb, NULL, skb_dst_dev(skb), dst_output); } @@ -200,7 +200,7 @@ static int ip_finish_output2(struct net *net, struct sock *sk, struct sk_buff *s { struct dst_entry *dst = skb_dst(skb); struct rtable *rt = dst_rtable(dst); - struct net_device *dev = dst->dev; + struct net_device *dev = dst_dev(dst); unsigned int hh_len = LL_RESERVED_SPACE(dev); struct neighbour *neigh; bool is_v6gw = false; @@ -426,7 +426,7 @@ int ip_mc_output(struct net *net, struct sock *sk, struct sk_buff *skb) int ip_output(struct net *net, struct sock *sk, struct sk_buff *skb) { - struct net_device *dev = skb_dst(skb)->dev, *indev = skb->dev; + struct net_device *dev = skb_dst_dev(skb), *indev = skb->dev; skb->dev = dev; skb->protocol = htons(ETH_P_IP); diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c index f0b4419cef3493..fc951616630711 100644 --- a/net/ipv4/ip_vti.c +++ b/net/ipv4/ip_vti.c @@ -229,7 +229,7 @@ static netdev_tx_t vti_xmit(struct sk_buff *skb, struct net_device *dev, goto tx_error_icmp; } - tdev = dst->dev; + tdev = dst_dev(dst); if (tdev == dev) { dst_release(dst); @@ -259,7 +259,7 @@ static netdev_tx_t vti_xmit(struct sk_buff *skb, struct net_device *dev, xmit: skb_scrub_packet(skb, !net_eq(tunnel->net, dev_net(dev))); skb_dst_set(skb, dst); - skb->dev = skb_dst(skb)->dev; + skb->dev = skb_dst_dev(skb); err = dst_output(tunnel->net, skb->sk, skb); if (net_xmit_eval(err) == 0) diff --git a/net/ipv4/netfilter.c b/net/ipv4/netfilter.c index e0aab66cd92511..dff06b9eb6607f 100644 --- a/net/ipv4/netfilter.c +++ b/net/ipv4/netfilter.c @@ -20,12 +20,12 @@ /* route_me_harder function, used by iptable_nat, iptable_mangle + ip_queue */ int ip_route_me_harder(struct net *net, struct sock *sk, struct sk_buff *skb, unsigned int addr_type) { + struct net_device *dev = skb_dst_dev(skb); const struct iphdr *iph = ip_hdr(skb); struct rtable *rt; struct flowi4 fl4 = {}; __be32 saddr = iph->saddr; __u8 flags; - struct net_device *dev = skb_dst(skb)->dev; struct flow_keys flkeys; unsigned int hh_len; @@ -74,7 +74,7 @@ int ip_route_me_harder(struct net *net, struct sock *sk, struct sk_buff *skb, un #endif /* Change in oif may mean change in hh_len. */ - hh_len = skb_dst(skb)->dev->hard_header_len; + hh_len = skb_dst_dev(skb)->hard_header_len; if (skb_headroom(skb) < hh_len && pskb_expand_head(skb, HH_DATA_ALIGN(hh_len - skb_headroom(skb)), 0, GFP_ATOMIC)) diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 261ddb6542a40f..7d04df4fc66084 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -413,7 +413,7 @@ static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst, const void *daddr) { const struct rtable *rt = container_of(dst, struct rtable, dst); - struct net_device *dev = dst->dev; + struct net_device *dev = dst_dev(dst); struct neighbour *n; rcu_read_lock(); @@ -440,7 +440,7 @@ static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst, static void ipv4_confirm_neigh(const struct dst_entry *dst, const void *daddr) { const struct rtable *rt = container_of(dst, struct rtable, dst); - struct net_device *dev = dst->dev; + struct net_device *dev = dst_dev(dst); const __be32 *pkey = daddr; if (rt->rt_gw_family == AF_INET) { @@ -1025,7 +1025,7 @@ static void __ip_rt_update_pmtu(struct rtable *rt, struct flowi4 *fl4, u32 mtu) return; rcu_read_lock(); - net = dev_net_rcu(dst->dev); + net = dev_net_rcu(dst_dev(dst)); if (mtu < net->ipv4.ip_rt_min_pmtu) { lock = true; mtu = min(old_mtu, net->ipv4.ip_rt_min_pmtu); @@ -1323,7 +1323,7 @@ static unsigned int ipv4_default_advmss(const struct dst_entry *dst) struct net *net; rcu_read_lock(); - net = dev_net_rcu(dst->dev); + net = dev_net_rcu(dst_dev(dst)); advmss = max_t(unsigned int, ipv4_mtu(dst) - header_size, net->ipv4.ip_rt_min_advmss); rcu_read_unlock(); diff --git a/net/ipv4/tcp_fastopen.c b/net/ipv4/tcp_fastopen.c index 408985eb74eefa..86c995dc1c5e5b 100644 --- a/net/ipv4/tcp_fastopen.c +++ b/net/ipv4/tcp_fastopen.c @@ -558,6 +558,7 @@ bool tcp_fastopen_active_should_disable(struct sock *sk) void tcp_fastopen_active_disable_ofo_check(struct sock *sk) { struct tcp_sock *tp = tcp_sk(sk); + struct net_device *dev; struct dst_entry *dst; struct sk_buff *skb; @@ -575,7 +576,8 @@ void tcp_fastopen_active_disable_ofo_check(struct sock *sk) } else if (tp->syn_fastopen_ch && atomic_read(&sock_net(sk)->ipv4.tfo_active_disable_times)) { dst = sk_dst_get(sk); - if (!(dst && dst->dev && (dst->dev->flags & IFF_LOOPBACK))) + dev = dst ? dst_dev(dst) : NULL; + if (!(dev && (dev->flags & IFF_LOOPBACK))) atomic_set(&sock_net(sk)->ipv4.tfo_active_disable_times, 0); dst_release(dst); } diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index d8976753d4e47e..1572562b0498c9 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -786,7 +786,7 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb, arg.iov[0].iov_base = (unsigned char *)&rep; arg.iov[0].iov_len = sizeof(rep.th); - net = sk ? sock_net(sk) : dev_net_rcu(skb_dst(skb)->dev); + net = sk ? sock_net(sk) : skb_dst_dev_net_rcu(skb); /* Invalid TCP option size or twice included auth */ if (tcp_parse_auth_options(tcp_hdr(skb), &md5_hash_location, &aoh)) diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c index 4251670e328c83..03c068ea27b6ad 100644 --- a/net/ipv4/tcp_metrics.c +++ b/net/ipv4/tcp_metrics.c @@ -166,11 +166,11 @@ static struct tcp_metrics_block *tcpm_new(struct dst_entry *dst, unsigned int hash) { struct tcp_metrics_block *tm; - struct net *net; bool reclaim = false; + struct net *net; spin_lock_bh(&tcp_metrics_lock); - net = dev_net_rcu(dst->dev); + net = dev_net_rcu(dst_dev(dst)); /* While waiting for the spin-lock the cache might have been populated * with this entry and so we have to check again. @@ -273,7 +273,7 @@ static struct tcp_metrics_block *__tcp_get_metrics_req(struct request_sock *req, return NULL; } - net = dev_net_rcu(dst->dev); + net = dev_net_rcu(dst_dev(dst)); hash ^= net_hash_mix(net); hash = hash_32(hash, tcp_metrics_hash_log); @@ -318,7 +318,7 @@ static struct tcp_metrics_block *tcp_get_metrics(struct sock *sk, else return NULL; - net = dev_net_rcu(dst->dev); + net = dev_net_rcu(dst_dev(dst)); hash ^= net_hash_mix(net); hash = hash_32(hash, tcp_metrics_hash_log); diff --git a/net/ipv4/xfrm4_output.c b/net/ipv4/xfrm4_output.c index 3cff51ba72bb01..0ae67d537499a2 100644 --- a/net/ipv4/xfrm4_output.c +++ b/net/ipv4/xfrm4_output.c @@ -31,7 +31,7 @@ static int __xfrm4_output(struct net *net, struct sock *sk, struct sk_buff *skb) int xfrm4_output(struct net *net, struct sock *sk, struct sk_buff *skb) { return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, - net, sk, skb, skb->dev, skb_dst(skb)->dev, + net, sk, skb, skb->dev, skb_dst_dev(skb), __xfrm4_output, !(IPCB(skb)->flags & IPSKB_REROUTED)); } From 4388b7f1e42cdb1bb291f7c85ffdf185ae1b20fe Mon Sep 17 00:00:00 2001 From: Sharath Chandra Vurukala Date: Mon, 20 Oct 2025 11:44:06 -0400 Subject: [PATCH 130/143] net: Add locking to protect skb->dev access in ip_output [ Upstream commit 1dbf1d590d10a6d1978e8184f8dfe20af22d680a ] In ip_output() skb->dev is updated from the skb_dst(skb)->dev this can become invalid when the interface is unregistered and freed, Introduced new skb_dst_dev_rcu() function to be used instead of skb_dst_dev() within rcu_locks in ip_output.This will ensure that all the skb's associated with the dev being deregistered will be transnmitted out first, before freeing the dev. Given that ip_output() is called within an rcu_read_lock() critical section or from a bottom-half context, it is safe to introduce an RCU read-side critical section within it. Multiple panic call stacks were observed when UL traffic was run in concurrency with device deregistration from different functions, pasting one sample for reference. [496733.627565][T13385] Call trace: [496733.627570][T13385] bpf_prog_ce7c9180c3b128ea_cgroupskb_egres+0x24c/0x7f0 [496733.627581][T13385] __cgroup_bpf_run_filter_skb+0x128/0x498 [496733.627595][T13385] ip_finish_output+0xa4/0xf4 [496733.627605][T13385] ip_output+0x100/0x1a0 [496733.627613][T13385] ip_send_skb+0x68/0x100 [496733.627618][T13385] udp_send_skb+0x1c4/0x384 [496733.627625][T13385] udp_sendmsg+0x7b0/0x898 [496733.627631][T13385] inet_sendmsg+0x5c/0x7c [496733.627639][T13385] __sys_sendto+0x174/0x1e4 [496733.627647][T13385] __arm64_sys_sendto+0x28/0x3c [496733.627653][T13385] invoke_syscall+0x58/0x11c [496733.627662][T13385] el0_svc_common+0x88/0xf4 [496733.627669][T13385] do_el0_svc+0x2c/0xb0 [496733.627676][T13385] el0_svc+0x2c/0xa4 [496733.627683][T13385] el0t_64_sync_handler+0x68/0xb4 [496733.627689][T13385] el0t_64_sync+0x1a4/0x1a8 Changes in v3: - Replaced WARN_ON() with WARN_ON_ONCE(), as suggested by Willem de Bruijn. - Dropped legacy lines mistakenly pulled in from an outdated branch. Changes in v2: - Addressed review comments from Eric Dumazet - Used READ_ONCE() to prevent potential load/store tearing - Added skb_dst_dev_rcu() and used along with rcu_read_lock() in ip_output Signed-off-by: Sharath Chandra Vurukala Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/20250730105118.GA26100@hu-sharathv-hyd.qualcomm.com Signed-off-by: Jakub Kicinski Stable-dep-of: 833d4313bc1e ("mptcp: reset blackhole on success with non-loopback ifaces") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- include/net/dst.h | 12 ++++++++++++ net/ipv4/ip_output.c | 15 ++++++++++----- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/include/net/dst.h b/include/net/dst.h index 23ee8139a75630..e5c9ea18838381 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -566,11 +566,23 @@ static inline struct net_device *dst_dev(const struct dst_entry *dst) return READ_ONCE(dst->dev); } +static inline struct net_device *dst_dev_rcu(const struct dst_entry *dst) +{ + /* In the future, use rcu_dereference(dst->dev) */ + WARN_ON_ONCE(!rcu_read_lock_held()); + return READ_ONCE(dst->dev); +} + static inline struct net_device *skb_dst_dev(const struct sk_buff *skb) { return dst_dev(skb_dst(skb)); } +static inline struct net_device *skb_dst_dev_rcu(const struct sk_buff *skb) +{ + return dst_dev_rcu(skb_dst(skb)); +} + static inline struct net *skb_dst_dev_net(const struct sk_buff *skb) { return dev_net(skb_dst_dev(skb)); diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 4d432e314bcb28..0bda561aa8a89f 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -426,15 +426,20 @@ int ip_mc_output(struct net *net, struct sock *sk, struct sk_buff *skb) int ip_output(struct net *net, struct sock *sk, struct sk_buff *skb) { - struct net_device *dev = skb_dst_dev(skb), *indev = skb->dev; + struct net_device *dev, *indev = skb->dev; + int ret_val; + rcu_read_lock(); + dev = skb_dst_dev_rcu(skb); skb->dev = dev; skb->protocol = htons(ETH_P_IP); - return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, - net, sk, skb, indev, dev, - ip_finish_output, - !(IPCB(skb)->flags & IPSKB_REROUTED)); + ret_val = NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, + net, sk, skb, indev, dev, + ip_finish_output, + !(IPCB(skb)->flags & IPSKB_REROUTED)); + rcu_read_unlock(); + return ret_val; } EXPORT_SYMBOL(ip_output); From c159590e32346994d6f88eb55f5c4869e486da98 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Mon, 20 Oct 2025 11:44:07 -0400 Subject: [PATCH 131/143] mptcp: Call dst_release() in mptcp_active_enable(). [ Upstream commit 108a86c71c93ff28087994e6107bc99ebe336629 ] mptcp_active_enable() calls sk_dst_get(), which returns dst with its refcount bumped, but forgot dst_release(). Let's add missing dst_release(). Cc: stable@vger.kernel.org Fixes: 27069e7cb3d1 ("mptcp: disable active MPTCP in case of blackhole") Signed-off-by: Kuniyuki Iwashima Reviewed-by: Matthieu Baerts (NGI0) Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/20250916214758.650211-7-kuniyu@google.com Signed-off-by: Jakub Kicinski Stable-dep-of: 833d4313bc1e ("mptcp: reset blackhole on success with non-loopback ifaces") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/mptcp/ctrl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/mptcp/ctrl.c b/net/mptcp/ctrl.c index dd595d9b5e50c7..6a97d11bda7e2d 100644 --- a/net/mptcp/ctrl.c +++ b/net/mptcp/ctrl.c @@ -385,6 +385,8 @@ void mptcp_active_enable(struct sock *sk) if (dst && dst->dev && (dst->dev->flags & IFF_LOOPBACK)) atomic_set(&pernet->active_disable_times, 0); + + dst_release(dst); } } From ad16235c9d3ef7ec17c109ff39b7504f49d17072 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Mon, 20 Oct 2025 11:44:08 -0400 Subject: [PATCH 132/143] mptcp: Use __sk_dst_get() and dst_dev_rcu() in mptcp_active_enable(). [ Upstream commit 893c49a78d9f85e4b8081b908fb7c407d018106a ] mptcp_active_enable() is called from subflow_finish_connect(), which is icsk->icsk_af_ops->sk_rx_dst_set() and it's not always under RCU. Using sk_dst_get(sk)->dev could trigger UAF. Let's use __sk_dst_get() and dst_dev_rcu(). Fixes: 27069e7cb3d1 ("mptcp: disable active MPTCP in case of blackhole") Signed-off-by: Kuniyuki Iwashima Reviewed-by: Matthieu Baerts (NGI0) Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/20250916214758.650211-8-kuniyu@google.com Signed-off-by: Jakub Kicinski Stable-dep-of: 833d4313bc1e ("mptcp: reset blackhole on success with non-loopback ifaces") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/mptcp/ctrl.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/net/mptcp/ctrl.c b/net/mptcp/ctrl.c index 6a97d11bda7e2d..9e1f61b02a66e6 100644 --- a/net/mptcp/ctrl.c +++ b/net/mptcp/ctrl.c @@ -381,12 +381,15 @@ void mptcp_active_enable(struct sock *sk) struct mptcp_pernet *pernet = mptcp_get_pernet(sock_net(sk)); if (atomic_read(&pernet->active_disable_times)) { - struct dst_entry *dst = sk_dst_get(sk); + struct net_device *dev; + struct dst_entry *dst; - if (dst && dst->dev && (dst->dev->flags & IFF_LOOPBACK)) + rcu_read_lock(); + dst = __sk_dst_get(sk); + dev = dst ? dst_dev_rcu(dst) : NULL; + if (dev && (dev->flags & IFF_LOOPBACK)) atomic_set(&pernet->active_disable_times, 0); - - dst_release(dst); + rcu_read_unlock(); } } From 963f2239bdbc3df8d53659049830494f36acf514 Mon Sep 17 00:00:00 2001 From: "Matthieu Baerts (NGI0)" Date: Mon, 20 Oct 2025 11:44:09 -0400 Subject: [PATCH 133/143] mptcp: reset blackhole on success with non-loopback ifaces [ Upstream commit 833d4313bc1e9e194814917d23e8874d6b651649 ] When a first MPTCP connection gets successfully established after a blackhole period, 'active_disable_times' was supposed to be reset when this connection was done via any non-loopback interfaces. Unfortunately, the opposite condition was checked: only reset when the connection was established via a loopback interface. Fixing this by simply looking at the opposite. This is similar to what is done with TCP FastOpen, see tcp_fastopen_active_disable_ofo_check(). This patch is a follow-up of a previous discussion linked to commit 893c49a78d9f ("mptcp: Use __sk_dst_get() and dst_dev_rcu() in mptcp_active_enable()."), see [1]. Fixes: 27069e7cb3d1 ("mptcp: disable active MPTCP in case of blackhole") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/4209a283-8822-47bd-95b7-87e96d9b7ea3@kernel.org [1] Signed-off-by: Matthieu Baerts (NGI0) Reviewed-by: Simon Horman Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20250918-net-next-mptcp-blackhole-reset-loopback-v1-1-bf5818326639@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/mptcp/ctrl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mptcp/ctrl.c b/net/mptcp/ctrl.c index 9e1f61b02a66e6..0d60556cfefabc 100644 --- a/net/mptcp/ctrl.c +++ b/net/mptcp/ctrl.c @@ -387,7 +387,7 @@ void mptcp_active_enable(struct sock *sk) rcu_read_lock(); dst = __sk_dst_get(sk); dev = dst ? dst_dev_rcu(dst) : NULL; - if (dev && (dev->flags & IFF_LOOPBACK)) + if (!(dev && (dev->flags & IFF_LOOPBACK))) atomic_set(&pernet->active_disable_times, 0); rcu_read_unlock(); } From e4d2a1d31fc9254812882c3caab6e9b04b92b27f Mon Sep 17 00:00:00 2001 From: Devarsh Thakkar Date: Tue, 21 Oct 2025 12:43:33 -0400 Subject: [PATCH 134/143] phy: cadence: cdns-dphy: Update calibration wait time for startup state machine [ Upstream commit 2c27aaee934a1b5229152fe33a14f1fdf50da143 ] Do read-modify-write so that we re-use the characterized reset value as specified in TRM [1] to program calibration wait time which defines number of cycles to wait for after startup state machine is in bandgap enable state. This fixes PLL lock timeout error faced while using RPi DSI Panel on TI's AM62L and J721E SoC since earlier calibration wait time was getting overwritten to zero value thus failing the PLL to lockup and causing timeout. [1] AM62P TRM (Section 14.8.6.3.2.1.1 DPHY_TX_DPHYTX_CMN0_CMN_DIG_TBIT2): Link: https://www.ti.com/lit/pdf/spruj83 Cc: stable@vger.kernel.org Fixes: 7a343c8bf4b5 ("phy: Add Cadence D-PHY support") Signed-off-by: Devarsh Thakkar Tested-by: Harikrishna Shenoy Reviewed-by: Tomi Valkeinen Link: https://lore.kernel.org/r/20250704125915.1224738-3-devarsht@ti.com Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/phy/cadence/cdns-dphy.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/phy/cadence/cdns-dphy.c b/drivers/phy/cadence/cdns-dphy.c index a5d6d870034232..8d93a830ab8bff 100644 --- a/drivers/phy/cadence/cdns-dphy.c +++ b/drivers/phy/cadence/cdns-dphy.c @@ -30,6 +30,7 @@ #define DPHY_CMN_SSM DPHY_PMA_CMN(0x20) #define DPHY_CMN_SSM_EN BIT(0) +#define DPHY_CMN_SSM_CAL_WAIT_TIME GENMASK(8, 1) #define DPHY_CMN_TX_MODE_EN BIT(9) #define DPHY_CMN_PWM DPHY_PMA_CMN(0x40) @@ -421,7 +422,8 @@ static int cdns_dphy_power_on(struct phy *phy) writel(reg, dphy->regs + DPHY_BAND_CFG); /* Start TX state machine. */ - writel(DPHY_CMN_SSM_EN | DPHY_CMN_TX_MODE_EN, + reg = readl(dphy->regs + DPHY_CMN_SSM); + writel((reg & DPHY_CMN_SSM_CAL_WAIT_TIME) | DPHY_CMN_SSM_EN | DPHY_CMN_TX_MODE_EN, dphy->regs + DPHY_CMN_SSM); ret = cdns_dphy_wait_for_pll_lock(dphy); From a156af6a4dc38c2aa7c98e89520a70fb3b3e7df4 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 20 Oct 2025 16:33:43 -0400 Subject: [PATCH 135/143] NFSD: Define a proc_layoutcommit for the FlexFiles layout type [ Upstream commit 4b47a8601b71ad98833b447d465592d847b4dc77 ] Avoid a crash if a pNFS client should happen to send a LAYOUTCOMMIT operation on a FlexFiles layout. Reported-by: Robert Morris Closes: https://lore.kernel.org/linux-nfs/152f99b2-ba35-4dec-93a9-4690e625dccd@oracle.com/T/#t Cc: Thomas Haynes Cc: stable@vger.kernel.org Fixes: 9b9960a0ca47 ("nfsd: Add a super simple flex file server") Signed-off-by: Chuck Lever Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/flexfilelayout.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fs/nfsd/flexfilelayout.c b/fs/nfsd/flexfilelayout.c index 3ca5304440ff0a..3c4419da5e24c2 100644 --- a/fs/nfsd/flexfilelayout.c +++ b/fs/nfsd/flexfilelayout.c @@ -125,6 +125,13 @@ nfsd4_ff_proc_getdeviceinfo(struct super_block *sb, struct svc_rqst *rqstp, return 0; } +static __be32 +nfsd4_ff_proc_layoutcommit(struct inode *inode, struct svc_rqst *rqstp, + struct nfsd4_layoutcommit *lcp) +{ + return nfs_ok; +} + const struct nfsd4_layout_ops ff_layout_ops = { .notify_types = NOTIFY_DEVICEID4_DELETE | NOTIFY_DEVICEID4_CHANGE, @@ -133,4 +140,5 @@ const struct nfsd4_layout_ops ff_layout_ops = { .encode_getdeviceinfo = nfsd4_ff_encode_getdeviceinfo, .proc_layoutget = nfsd4_ff_proc_layoutget, .encode_layoutget = nfsd4_ff_encode_layoutget, + .proc_layoutcommit = nfsd4_ff_proc_layoutcommit, }; From ac50c6e0a8f91a02b681af81abb2362fbb67cc18 Mon Sep 17 00:00:00 2001 From: Jakub Acs Date: Wed, 1 Oct 2025 09:03:52 +0000 Subject: [PATCH 136/143] mm/ksm: fix flag-dropping behavior in ksm_madvise commit f04aad36a07cc17b7a5d5b9a2d386ce6fae63e93 upstream. syzkaller discovered the following crash: (kernel BUG) [ 44.607039] ------------[ cut here ]------------ [ 44.607422] kernel BUG at mm/userfaultfd.c:2067! [ 44.608148] Oops: invalid opcode: 0000 [#1] SMP DEBUG_PAGEALLOC KASAN NOPTI [ 44.608814] CPU: 1 UID: 0 PID: 2475 Comm: reproducer Not tainted 6.16.0-rc6 #1 PREEMPT(none) [ 44.609635] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.3-0-ga6ed6b701f0a-prebuilt.qemu.org 04/01/2014 [ 44.610695] RIP: 0010:userfaultfd_release_all+0x3a8/0x460 [ 44.617726] Call Trace: [ 44.617926] [ 44.619284] userfaultfd_release+0xef/0x1b0 [ 44.620976] __fput+0x3f9/0xb60 [ 44.621240] fput_close_sync+0x110/0x210 [ 44.622222] __x64_sys_close+0x8f/0x120 [ 44.622530] do_syscall_64+0x5b/0x2f0 [ 44.622840] entry_SYSCALL_64_after_hwframe+0x76/0x7e [ 44.623244] RIP: 0033:0x7f365bb3f227 Kernel panics because it detects UFFD inconsistency during userfaultfd_release_all(). Specifically, a VMA which has a valid pointer to vma->vm_userfaultfd_ctx, but no UFFD flags in vma->vm_flags. The inconsistency is caused in ksm_madvise(): when user calls madvise() with MADV_UNMEARGEABLE on a VMA that is registered for UFFD in MINOR mode, it accidentally clears all flags stored in the upper 32 bits of vma->vm_flags. Assuming x86_64 kernel build, unsigned long is 64-bit and unsigned int and int are 32-bit wide. This setup causes the following mishap during the &= ~VM_MERGEABLE assignment. VM_MERGEABLE is a 32-bit constant of type unsigned int, 0x8000'0000. After ~ is applied, it becomes 0x7fff'ffff unsigned int, which is then promoted to unsigned long before the & operation. This promotion fills upper 32 bits with leading 0s, as we're doing unsigned conversion (and even for a signed conversion, this wouldn't help as the leading bit is 0). & operation thus ends up AND-ing vm_flags with 0x0000'0000'7fff'ffff instead of intended 0xffff'ffff'7fff'ffff and hence accidentally clears the upper 32-bits of its value. Fix it by changing `VM_MERGEABLE` constant to unsigned long, using the BIT() macro. Note: other VM_* flags are not affected: This only happens to the VM_MERGEABLE flag, as the other VM_* flags are all constants of type int and after ~ operation, they end up with leading 1 and are thus converted to unsigned long with leading 1s. Note 2: After commit 31defc3b01d9 ("userfaultfd: remove (VM_)BUG_ON()s"), this is no longer a kernel BUG, but a WARNING at the same place: [ 45.595973] WARNING: CPU: 1 PID: 2474 at mm/userfaultfd.c:2067 but the root-cause (flag-drop) remains the same. [akpm@linux-foundation.org: rust bindgen wasn't able to handle BIT(), from Miguel] Link: https://lore.kernel.org/oe-kbuild-all/202510030449.VfSaAjvd-lkp@intel.com/ Link: https://lkml.kernel.org/r/20251001090353.57523-2-acsjakub@amazon.de Fixes: 7677f7fd8be7 ("userfaultfd: add minor fault registration mode") Signed-off-by: Jakub Acs Signed-off-by: Miguel Ojeda Acked-by: David Hildenbrand Acked-by: SeongJae Park Tested-by: Alice Ryhl Tested-by: Miguel Ojeda Cc: Xu Xin Cc: Chengming Zhou Cc: Peter Xu Cc: Axel Rasmussen Cc: Signed-off-by: Andrew Morton [acsjakub@amazon.de: adjust context in bindgings_helper.h] Signed-off-by: Jakub Acs Signed-off-by: Greg Kroah-Hartman --- include/linux/mm.h | 2 +- rust/bindings/bindings_helper.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/include/linux/mm.h b/include/linux/mm.h index f0fa8404957dbf..13b4bd7355c14c 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -320,7 +320,7 @@ extern unsigned int kobjsize(const void *objp); #define VM_MIXEDMAP 0x10000000 /* Can contain "struct page" and pure PFN pages */ #define VM_HUGEPAGE 0x20000000 /* MADV_HUGEPAGE marked this vma */ #define VM_NOHUGEPAGE 0x40000000 /* MADV_NOHUGEPAGE marked this vma */ -#define VM_MERGEABLE 0x80000000 /* KSM may merge identical pages */ +#define VM_MERGEABLE BIT(31) /* KSM may merge identical pages */ #ifdef CONFIG_ARCH_USES_HIGH_VMA_FLAGS #define VM_HIGH_ARCH_BIT_0 32 /* bit only usable on 64-bit architectures */ diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index a80783fcbe042a..8b97919a86e2d7 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -33,3 +33,4 @@ const gfp_t RUST_CONST_HELPER___GFP_ZERO = __GFP_ZERO; const gfp_t RUST_CONST_HELPER___GFP_HIGHMEM = ___GFP_HIGHMEM; const gfp_t RUST_CONST_HELPER___GFP_NOWARN = ___GFP_NOWARN; const blk_features_t RUST_CONST_HELPER_BLK_FEAT_ROTATIONAL = BLK_FEAT_ROTATIONAL; +const vm_flags_t RUST_CONST_HELPER_VM_MERGEABLE = VM_MERGEABLE; From 6de6d315f34c539ae5fbdb1e5c5ed8570adba35e Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Fri, 19 Sep 2025 15:58:28 +0100 Subject: [PATCH 137/143] arm64: cputype: Add Neoverse-V3AE definitions commit 3bbf004c4808e2c3241e5c1ad6cc102f38a03c39 upstream. Add cputype definitions for Neoverse-V3AE. These will be used for errata detection in subsequent patches. These values can be found in the Neoverse-V3AE TRM: https://developer.arm.com/documentation/SDEN-2615521/9-0/ ... in section A.6.1 ("MIDR_EL1, Main ID Register"). Signed-off-by: Mark Rutland Cc: James Morse Cc: Will Deacon Cc: Catalin Marinas Signed-off-by: Ryan Roberts Signed-off-by: Will Deacon Signed-off-by: Ryan Roberts Signed-off-by: Greg Kroah-Hartman --- arch/arm64/include/asm/cputype.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm64/include/asm/cputype.h b/arch/arm64/include/asm/cputype.h index d92a0203e5a93d..c279a0a9b3660e 100644 --- a/arch/arm64/include/asm/cputype.h +++ b/arch/arm64/include/asm/cputype.h @@ -93,6 +93,7 @@ #define ARM_CPU_PART_NEOVERSE_V2 0xD4F #define ARM_CPU_PART_CORTEX_A720 0xD81 #define ARM_CPU_PART_CORTEX_X4 0xD82 +#define ARM_CPU_PART_NEOVERSE_V3AE 0xD83 #define ARM_CPU_PART_NEOVERSE_V3 0xD84 #define ARM_CPU_PART_CORTEX_X925 0xD85 #define ARM_CPU_PART_CORTEX_A725 0xD87 @@ -180,6 +181,7 @@ #define MIDR_NEOVERSE_V2 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_V2) #define MIDR_CORTEX_A720 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A720) #define MIDR_CORTEX_X4 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_X4) +#define MIDR_NEOVERSE_V3AE MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_V3AE) #define MIDR_NEOVERSE_V3 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_V3) #define MIDR_CORTEX_X925 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_X925) #define MIDR_CORTEX_A725 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A725) From f3ccb491865418d7536648ba7a7dcbbb5e4b4f99 Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Fri, 19 Sep 2025 15:58:29 +0100 Subject: [PATCH 138/143] arm64: errata: Apply workarounds for Neoverse-V3AE commit 0c33aa1804d101c11ba1992504f17a42233f0e11 upstream. Neoverse-V3AE is also affected by erratum #3312417, as described in its Software Developer Errata Notice (SDEN) document: Neoverse V3AE (MP172) SDEN v9.0, erratum 3312417 https://developer.arm.com/documentation/SDEN-2615521/9-0/ Enable the workaround for Neoverse-V3AE, and document this. Signed-off-by: Mark Rutland Cc: James Morse Cc: Will Deacon Cc: Catalin Marinas Signed-off-by: Ryan Roberts Signed-off-by: Will Deacon Signed-off-by: Greg Kroah-Hartman --- Documentation/arch/arm64/silicon-errata.rst | 2 ++ arch/arm64/Kconfig | 1 + arch/arm64/kernel/cpu_errata.c | 1 + 3 files changed, 4 insertions(+) diff --git a/Documentation/arch/arm64/silicon-errata.rst b/Documentation/arch/arm64/silicon-errata.rst index b42fea07c5cec8..b6dacd012539a4 100644 --- a/Documentation/arch/arm64/silicon-errata.rst +++ b/Documentation/arch/arm64/silicon-errata.rst @@ -198,6 +198,8 @@ stable kernels. +----------------+-----------------+-----------------+-----------------------------+ | ARM | Neoverse-V3 | #3312417 | ARM64_ERRATUM_3194386 | +----------------+-----------------+-----------------+-----------------------------+ +| ARM | Neoverse-V3AE | #3312417 | ARM64_ERRATUM_3194386 | ++----------------+-----------------+-----------------+-----------------------------+ | ARM | MMU-500 | #841119,826419 | N/A | +----------------+-----------------+-----------------+-----------------------------+ | ARM | MMU-600 | #1076982,1209401| N/A | diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 7887d18cce3e45..40ae4dd961b152 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -1111,6 +1111,7 @@ config ARM64_ERRATUM_3194386 * ARM Neoverse-V1 erratum 3324341 * ARM Neoverse V2 erratum 3324336 * ARM Neoverse-V3 erratum 3312417 + * ARM Neoverse-V3AE erratum 3312417 On affected cores "MSR SSBS, #0" instructions may not affect subsequent speculative instructions, which may permit unexepected diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c index a78f247029aec3..3f675ae57d09ac 100644 --- a/arch/arm64/kernel/cpu_errata.c +++ b/arch/arm64/kernel/cpu_errata.c @@ -455,6 +455,7 @@ static const struct midr_range erratum_spec_ssbs_list[] = { MIDR_ALL_VERSIONS(MIDR_NEOVERSE_V1), MIDR_ALL_VERSIONS(MIDR_NEOVERSE_V2), MIDR_ALL_VERSIONS(MIDR_NEOVERSE_V3), + MIDR_ALL_VERSIONS(MIDR_NEOVERSE_V3AE), {} }; #endif From d28c1b1566a1ca2ce8b200ff61598d6a0360dfbf Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 20 Oct 2025 10:04:22 -0700 Subject: [PATCH 139/143] dmaengine: Add missing cleanup on module unload Upstream commit b7cb9a034305 ("dmaengine: idxd: Fix refcount underflow on module unload") fixes a refcount underflow by replacing the call to idxd_cleanup() in the remove function with direct cleanup calls. That works fine upstream. However, upstream removed support for IOMMU_DEV_FEAT_IOPF, which is still supported in v6.12.y. The backport of commit b7cb9a034305 into v6.12.y misses the call to disable it. This results in a warning backtrace when unloading and reloading the module. WARNING: CPU: 0 PID: 665849 at drivers/pci/ats.c:337 pci_reset_pri+0x4c/0x60 ... RIP: 0010:pci_reset_pri+0xa7/0x130 Add the missing cleanup call to fix the problem. Fixes: ce81905bec91 ("dmaengine: idxd: Fix refcount underflow on module unload") Cc: Yi Sun Cc: Shuai Xue Cc: Dave Jiang Cc: Vinicius Costa Gomes Cc: Vinod Koul Signed-off-by: Guenter Roeck Acked-by: Vinicius Costa Gomes Signed-off-by: Greg Kroah-Hartman --- drivers/dma/idxd/init.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/dma/idxd/init.c b/drivers/dma/idxd/init.c index 74a83203181d6f..e55136bb525e2c 100644 --- a/drivers/dma/idxd/init.c +++ b/drivers/dma/idxd/init.c @@ -923,6 +923,8 @@ static void idxd_remove(struct pci_dev *pdev) idxd_cleanup_interrupts(idxd); if (device_pasid_enabled(idxd)) idxd_disable_system_pasid(idxd); + if (device_user_pasid_enabled(idxd)) + idxd_disable_sva(idxd->pdev); pci_iounmap(pdev, idxd->reg_base); put_device(idxd_confdev(idxd)); pci_disable_device(pdev); From 4fc43debf5047d2469bdef3b25c02121afa7ef3d Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 23 Oct 2025 16:20:48 +0200 Subject: [PATCH 140/143] Linux 6.12.55 Link: https://lore.kernel.org/r/20251021195035.953989698@linuxfoundation.org Tested-by: Salvatore Bonaccorso Tested-by: Florian Fainelli Tested-by: Hardik Garg Tested-by: Shuah Khan Link: https://lore.kernel.org/r/20251022060141.370358070@linuxfoundation.org Tested-by: Brett A C Sheffield Tested-by: Peter Schneider Tested-by: Ron Economos Tested-by: Pavel Machek (CIP) Tested-by: Mark Brown Tested-by: Jon Hunter Tested-by: Florian Fainelli Tested-by: Dileep Malepu Tested-by: Hardik Garg Tested-by: Linux Kernel Functional Testing Signed-off-by: Greg Kroah-Hartman --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 0c6deb33c23935..d4c679b2d4bcfc 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 VERSION = 6 PATCHLEVEL = 12 -SUBLEVEL = 54 +SUBLEVEL = 55 EXTRAVERSION = NAME = Baby Opossum Posse From 11355efeb9d2c531ef0b1012ff6246e49527e9f7 Mon Sep 17 00:00:00 2001 From: Naushir Patuck Date: Fri, 24 Oct 2025 14:16:53 +0100 Subject: [PATCH 141/143] defconfig: Remove hailo driver from defconfigs Remove CONFIG_MEDIA_PCI_HAILO from all the arm64 defconfig files as this driver is no longer built in the kernel tree. Signed-off-by: Naushir Patuck --- arch/arm64/configs/bcm2711_defconfig | 1 - arch/arm64/configs/bcm2711_rt_defconfig | 1 - arch/arm64/configs/bcm2712_defconfig | 1 - 3 files changed, 3 deletions(-) diff --git a/arch/arm64/configs/bcm2711_defconfig b/arch/arm64/configs/bcm2711_defconfig index cab13cedcdf009..9e9ca97bd5d1c1 100644 --- a/arch/arm64/configs/bcm2711_defconfig +++ b/arch/arm64/configs/bcm2711_defconfig @@ -1004,7 +1004,6 @@ CONFIG_VIDEO_EM28XX_V4L2=m CONFIG_VIDEO_EM28XX_ALSA=m CONFIG_VIDEO_EM28XX_DVB=m CONFIG_MEDIA_PCI_SUPPORT=y -CONFIG_MEDIA_PCI_HAILO=m CONFIG_RADIO_SAA7706H=m CONFIG_RADIO_SHARK=m CONFIG_RADIO_SHARK2=m diff --git a/arch/arm64/configs/bcm2711_rt_defconfig b/arch/arm64/configs/bcm2711_rt_defconfig index 239b9ae0088228..aca3ae33e6819e 100644 --- a/arch/arm64/configs/bcm2711_rt_defconfig +++ b/arch/arm64/configs/bcm2711_rt_defconfig @@ -1004,7 +1004,6 @@ CONFIG_VIDEO_EM28XX_V4L2=m CONFIG_VIDEO_EM28XX_ALSA=m CONFIG_VIDEO_EM28XX_DVB=m CONFIG_MEDIA_PCI_SUPPORT=y -CONFIG_MEDIA_PCI_HAILO=m CONFIG_RADIO_SAA7706H=m CONFIG_RADIO_SHARK=m CONFIG_RADIO_SHARK2=m diff --git a/arch/arm64/configs/bcm2712_defconfig b/arch/arm64/configs/bcm2712_defconfig index e6f0904704447c..1afb5485b41972 100644 --- a/arch/arm64/configs/bcm2712_defconfig +++ b/arch/arm64/configs/bcm2712_defconfig @@ -1006,7 +1006,6 @@ CONFIG_VIDEO_EM28XX_V4L2=m CONFIG_VIDEO_EM28XX_ALSA=m CONFIG_VIDEO_EM28XX_DVB=m CONFIG_MEDIA_PCI_SUPPORT=y -CONFIG_MEDIA_PCI_HAILO=m CONFIG_RADIO_SAA7706H=m CONFIG_RADIO_SHARK=m CONFIG_RADIO_SHARK2=m From 11a1e7417c127bf42f5617c1e561b00a28d82fad Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Fri, 24 Oct 2025 09:54:05 +0100 Subject: [PATCH 142/143] configs: Add the AD799X driver module Signed-off-by: Phil Elwell --- arch/arm/configs/bcm2709_defconfig | 1 + arch/arm/configs/bcm2711_defconfig | 1 + arch/arm/configs/bcmrpi_defconfig | 1 + arch/arm64/configs/bcm2711_defconfig | 1 + arch/arm64/configs/bcm2711_rt_defconfig | 1 + arch/arm64/configs/bcm2712_defconfig | 1 + 6 files changed, 6 insertions(+) diff --git a/arch/arm/configs/bcm2709_defconfig b/arch/arm/configs/bcm2709_defconfig index 308a25dd51eafb..47e07e0b20ec3b 100644 --- a/arch/arm/configs/bcm2709_defconfig +++ b/arch/arm/configs/bcm2709_defconfig @@ -1432,6 +1432,7 @@ CONFIG_IIO=m CONFIG_IIO_BUFFER_CB=m CONFIG_IIO_SW_TRIGGER=m CONFIG_ADXL355_I2C=m +CONFIG_AD799X=m CONFIG_MCP320X=m CONFIG_MCP3422=m CONFIG_TI_ADS1015=m diff --git a/arch/arm/configs/bcm2711_defconfig b/arch/arm/configs/bcm2711_defconfig index 003e89e0bd0e1f..74b20392c5e64c 100644 --- a/arch/arm/configs/bcm2711_defconfig +++ b/arch/arm/configs/bcm2711_defconfig @@ -1476,6 +1476,7 @@ CONFIG_IIO=m CONFIG_IIO_BUFFER_CB=m CONFIG_IIO_SW_TRIGGER=m CONFIG_ADXL355_I2C=m +CONFIG_AD799X=m CONFIG_MCP320X=m CONFIG_MCP3422=m CONFIG_TI_ADS1015=m diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig index 09523c520d1b33..fc36f87c389a23 100644 --- a/arch/arm/configs/bcmrpi_defconfig +++ b/arch/arm/configs/bcmrpi_defconfig @@ -1424,6 +1424,7 @@ CONFIG_IIO=m CONFIG_IIO_BUFFER_CB=m CONFIG_IIO_SW_TRIGGER=m CONFIG_ADXL355_I2C=m +CONFIG_AD799X=m CONFIG_MCP320X=m CONFIG_MCP3422=m CONFIG_TI_ADS1015=m diff --git a/arch/arm64/configs/bcm2711_defconfig b/arch/arm64/configs/bcm2711_defconfig index 9e9ca97bd5d1c1..4a02f5204fa4c9 100644 --- a/arch/arm64/configs/bcm2711_defconfig +++ b/arch/arm64/configs/bcm2711_defconfig @@ -1544,6 +1544,7 @@ CONFIG_IIO=m CONFIG_IIO_BUFFER_CB=m CONFIG_IIO_SW_TRIGGER=m CONFIG_ADXL355_I2C=m +CONFIG_AD799X=m CONFIG_MCP320X=m CONFIG_MCP3422=m CONFIG_TI_ADS1015=m diff --git a/arch/arm64/configs/bcm2711_rt_defconfig b/arch/arm64/configs/bcm2711_rt_defconfig index aca3ae33e6819e..a3d38c3725ed6f 100644 --- a/arch/arm64/configs/bcm2711_rt_defconfig +++ b/arch/arm64/configs/bcm2711_rt_defconfig @@ -1543,6 +1543,7 @@ CONFIG_IIO=m CONFIG_IIO_BUFFER_CB=m CONFIG_IIO_SW_TRIGGER=m CONFIG_ADXL355_I2C=m +CONFIG_AD799X=m CONFIG_MCP320X=m CONFIG_MCP3422=m CONFIG_TI_ADS1015=m diff --git a/arch/arm64/configs/bcm2712_defconfig b/arch/arm64/configs/bcm2712_defconfig index 1afb5485b41972..f6d2221d1bdb0d 100644 --- a/arch/arm64/configs/bcm2712_defconfig +++ b/arch/arm64/configs/bcm2712_defconfig @@ -1546,6 +1546,7 @@ CONFIG_IIO=m CONFIG_IIO_BUFFER_CB=m CONFIG_IIO_SW_TRIGGER=m CONFIG_ADXL355_I2C=m +CONFIG_AD799X=m CONFIG_MCP320X=m CONFIG_MCP3422=m CONFIG_TI_ADS1015=m From 2e376fd6b1f2fe1ab785fa607f36db6669d0bb8b Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Fri, 24 Oct 2025 10:15:52 +0100 Subject: [PATCH 143/143] overlays: i2c-sensor: Add the ad799x ADC family Add support for the AD799[1,2,3,4,5,7,8,9] ADCs to the i2c-sensor overlay. Signed-off-by: Phil Elwell --- arch/arm/boot/dts/overlays/README | 32 ++++++++++++++++--- .../boot/dts/overlays/i2c-sensor-common.dtsi | 25 ++++++++++++++- 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/arch/arm/boot/dts/overlays/README b/arch/arm/boot/dts/overlays/README index c7df8e3b1a4928..2640a5678432a3 100644 --- a/arch/arm/boot/dts/overlays/README +++ b/arch/arm/boot/dts/overlays/README @@ -2421,16 +2421,40 @@ Name: i2c-sensor Info: Adds support for a number of I2C barometric pressure, temperature, light level and chemical sensors on i2c_arm Load: dtoverlay=i2c-sensor,= -Params: addr Set the address for the ADS7828, ADS7830, - ADT7410, AS73211, AS7331, BH1750, BME280, - BME680, BMP280, BMP380, BMP58x, CCS811, DS1621, - HDC100X, HDC3020, JC42, LM75, MCP980x, +Params: addr Set the address for the AD799X, ADS7828, + ADS7830, ADT7410, AS73211, AS7331, BH1750, + BME280, BME680, BMP280, BMP380, BMP58x, CCS811, + DS1621, HDC100X, HDC3020, JC42, LM75, MCP980x, MPU6050, MPU9250, MS5637, MS5803, MS5805, MS5837, MS8607, SHT3x, TMP102 or VEML6030. i2c-bus Supports all the standard I2C bus selection parameters - see "dtoverlay -h i2c-bus" + ad7991 Select the Analog Devices AD7991 ADC + Valid addresses 0x28-0x29, default 0x28 + + ad7992 Select the Analog Devices AD7992 ADC + Valid addresses 0x20-0x24, default 0x20 + + ad7993 Select the Analog Devices AD7993 ADC + Valid addresses 0x20-0x24, default 0x20 + + ad7994 Select the Analog Devices AD7994 ADC + Valid addresses 0x20-0x24, default 0x20 + + ad7995 Select the Analog Devices AD7995 ADC + Valid addresses 0x28-0x29, default 0x28 + + ad7997 Select the Analog Devices AD7997 ADC + Valid addresses 0x20-0x24, default 0x20 + + ad7998 Select the Analog Devices AD7998 ADC + Valid addresses 0x20-0x24, default 0x20 + + ad7999 Select the Analog Devices AD7999 ADC + Valid address 0x29 (the default) + ads7828 Select the TI ADS7828 50kHz 8-channel 12bit ADC Valid addresses 0x48-0x4b, default 0x48 diff --git a/arch/arm/boot/dts/overlays/i2c-sensor-common.dtsi b/arch/arm/boot/dts/overlays/i2c-sensor-common.dtsi index 5213b38e675539..5d52b16c8d47b3 100755 --- a/arch/arm/boot/dts/overlays/i2c-sensor-common.dtsi +++ b/arch/arm/boot/dts/overlays/i2c-sensor-common.dtsi @@ -818,6 +818,20 @@ }; }; + fragment@56 { + target = <&i2cbus>; + __dormant__ { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + ad799x: ad799x@20 { + compatible = "adi,ad799x"; // To be overridden + reg = <0x20>; + }; + }; + }; + fragment@98 { target-path = "/"; __dormant__ { @@ -896,6 +910,14 @@ veml6030 = <0>,"+50+99"; tmp117 = <0>,"+51+98"; scd4x = <0>,"+52"; + ad7991 = <0>,"+56", <&ad799x>,"compatible=adi,ad7991", <&ad799x>,"reg:0=0x28"; + ad7992 = <0>,"+56", <&ad799x>,"compatible=adi,ad7992"; + ad7993 = <0>,"+56", <&ad799x>,"compatible=adi,ad7993"; + ad7994 = <0>,"+56", <&ad799x>,"compatible=adi,ad7994"; + ad7995 = <0>,"+56", <&ad799x>,"compatible=adi,ad7995", <&ad799x>,"reg:0=0x28"; + ad7997 = <0>,"+56", <&ad799x>,"compatible=adi,ad7997"; + ad7998 = <0>,"+56", <&ad799x>,"compatible=adi,ad7998"; + ad7999 = <0>,"+56", <&ad799x>,"compatible=adi,ad7999", <&ad799x>,"reg:0=0x29"; addr = <&bme280>,"reg:0", <&bmp280>,"reg:0", <&tmp102>,"reg:0", <&lm75>,"reg:0", <&hdc100x>,"reg:0", <&sht3x>,"reg:0", @@ -910,7 +932,8 @@ <&as7331>,"reg:0", <&adxl345>,"reg:0", <&ads7828>,"reg:0", <&ads7830>,"reg:0", <&adxl355>,"reg:0", <&bmp58x>,"reg:0", - <&veml6030>,"reg:0", <&tmp117>,"reg:0"; + <&veml6030>,"reg:0", <&tmp117>,"reg:0", + <&ad799x>,"reg:0"; int_pin = <&int_pins>, "brcm,pins:0", <&int_pins>, "reg:0", <&max30102>, "interrupts:0",