PCIe RAS學習專題(3):AER內核處理流程梳理

目錄

一、AER內核處理整體流程梳理

二、AER代碼重要部分梳理

1、AER初始化階段

2、中斷上半部 aer_irq

3、中斷下半部 aer_isr

3.1、aer_isr_one_error

3.2、find_source_device

3.3、aer_process_err_devices

3.4、handle_error_source

3.5、pcie_do_recovery 整體邏輯

3.5.1、pcie_do_recovery 整體總結--AER處理的核心部分

3.5.2、附加: aer_root_reset的函數分析--輔助理解pcie_do_recovery

三、內核處理流程整體總結



一、AER內核處理整體流程梳理

可能有理解不到位寫得不對的地方

二、AER代碼重要部分梳理

AER驅動與pciehp、pcie-dpc類似,都是作為PCIe port的可選服務,這些服務模塊掛載在PCIe port驅動上,由portdrv_core統一管理。服務的注冊通過pcie_port_service_register函數完成:

1、AER初始化階段

static struct pcie_port_service_driver aerdriver = {.name       = "aer",.port_type  = PCIE_ANY_PORT,.service    = PCIE_PORT_SERVICE_AER,
?.probe      = aer_probe,.remove     = aer_remove,
};
?
int __init pcie_aer_init(void)
{if (!pci_aer_available())return -ENXIO;return pcie_port_service_register(&aerdriver);
}

PCIe AER驅動屬于PCIe port driver, 其綁定的是PCIe root port

同時,port_type = PCIE_ANY_PORT這個澄清內核增加了PCIE RCEC支持,針對RCEC設備也可以AER處理

static int aer_probe(struct pcie_device *dev)
{... .../* * AER 僅支持根端口(Root Port)或根復合體事件收集器(RCEC)* 檢查PCIe設備類型,如果不是這兩種類型則直接返回*/if ((pci_pcie_type(port) != PCI_EXP_TYPE_RC_EC) &&(pci_pcie_type(port) != PCI_EXP_TYPE_ROOT_PORT))return -ENODEV;  // 設備不支持
?/* 為根端口控制結構(aer_rpc)分配內核內存 */rpc = devm_kzalloc(device, sizeof(struct aer_rpc), GFP_KERNEL);if (!rpc)return -ENOMEM;  /* 內存分配失敗 */
?/* 初始化aer_rpc結構體 */rpc->rpd = port;  /* 保存根端口設備 */INIT_KFIFO(rpc->aer_fifo);  /* 初始化用于AER事件的FIFO隊列 */set_service_data(dev, rpc);  /* 將rpc與pcie_device關聯 */
?status = devm_request_threaded_irq(device, dev->irq, aer_irq, aer_isr,IRQF_SHARED, "aerdrv", dev);... .../* 在根端口上啟用AER功能 */aer_enable_rootport(rpc);... ...
}

主要做了下面兩件事情:

(1)注冊AER事件的線程化中斷處理程序:aer_irq: 上半部(快速處理),aer_isr: 下半部(實際處理)

(2)在根端口上啟用AER功能

2、中斷上半部 aer_irq

?
static irqreturn_t aer_irq(int irq, void *context)
{... ...// 讀取根錯誤狀態寄存器pci_read_config_dword(rp, aer + PCI_ERR_ROOT_STATUS, &e_src.status);// 檢查是否真的有錯誤發生(可糾正或不可糾正錯誤)if (!(e_src.status & (PCI_ERR_ROOT_UNCOR_RCV|PCI_ERR_ROOT_COR_RCV)))return IRQ_NONE;  // 如果沒有錯誤,返回IRQ_NONE表示不是我們的中斷
?// 讀取錯誤源ID寄存器,獲取詳細錯誤信息pci_read_config_dword(rp, aer + PCI_ERR_ROOT_ERR_SRC, &e_src.id);// 清除根錯誤狀態寄存器(寫1清除)pci_write_config_dword(rp, aer + PCI_ERR_ROOT_STATUS, e_src.status);
?// 嘗試將錯誤信息放入FIFO隊列if (!kfifo_put(&rpc->aer_fifo, e_src))return IRQ_HANDLED;  // 如果隊列已滿,直接返回IRQ_HANDLED
?// 成功放入隊列,返回IRQ_WAKE_THREAD喚醒下半部處理線程return IRQ_WAKE_THREAD;
}

這是中斷處理的上半部,主要負責快速讀取錯誤狀態并暫存數據,實際處理會在下半部中進行,主要做了下面幾件事:

(1)通過PCI_ERR_ROOT_STATUS寄存器檢測錯誤類型

(2)讀取PCI_ERR_ROOT_ERR_SRC獲取錯誤源詳細信息

(3)清除錯誤狀態,喚醒中斷下半部

3、中斷下半部 aer_isr

3.1、aer_isr_one_error

上半部 aer_irq 將錯誤存入FIFO,下半部 aer_isr 是消費者,從FIFO取出錯誤處理

static irqreturn_t aer_isr(int irq, void *context)
{... ...// 循環處理FIFO中的所有錯誤信息while (kfifo_get(&rpc->aer_fifo, &e_src)) {// 對每個錯誤調用處理函數aer_isr_one_error(rpc, &e_src);}return IRQ_HANDLED;
}
?
static void aer_isr_one_error(struct aer_rpc *rpc, struct aer_err_source *e_src)
{... ...if (e_src->status & PCI_ERR_ROOT_COR_RCV) {// 設置可糾正錯誤信息e_info.id = ERR_COR_ID(e_src->id); ? ? ? // 提取可糾正錯誤IDe_info.severity = AER_CORRECTABLE; ? ? ? // 設置錯誤嚴重性為可糾正
?// 檢查是否多個可糾正錯誤if (e_src->status & PCI_ERR_ROOT_MULTI_COR_RCV)e_info.multi_error_valid = 1; ? ? ?  // 標記為多個錯誤elsee_info.multi_error_valid = 0;
?// 打印端口錯誤信息aer_print_port_info(pdev, &e_info);
?// 查找錯誤源設備并處理錯誤if (find_source_device(pdev, &e_info))aer_process_err_devices(&e_info);}
?// 處理不可糾正錯誤if (e_src->status & PCI_ERR_ROOT_UNCOR_RCV) {// 設置不可糾正錯誤信息e_info.id = ERR_UNCOR_ID(e_src->id); ? ? // 提取不可糾正錯誤ID
?// 判斷是否為致命錯誤if (e_src->status & PCI_ERR_ROOT_FATAL_RCV)e_info.severity = AER_FATAL; ? ? ? ? // 致命錯誤elsee_info.severity = AER_NONFATAL; ? ?  // 非致命錯誤
?// 檢查是否多個不可糾正錯誤if (e_src->status & PCI_ERR_ROOT_MULTI_UNCOR_RCV)e_info.multi_error_valid = 1; ? ? ?  // 標記為多個錯誤elsee_info.multi_error_valid = 0;
?// 打印端口錯誤信息aer_print_port_info(pdev, &e_info);
?// 查找錯誤源設備并處理錯誤if (find_source_device(pdev, &e_info))aer_process_err_devices(&e_info);}
}

錯誤處理流程

(1)先更新錯誤統計(pci_rootport_aer_stats_incr)

(2)打印錯誤信息(aer_print_port_info)

(3)定位錯誤源設備(find_source_device)

(4)處理錯誤設備(aer_process_err_devices)

3.2、find_source_device
static bool find_source_device(struct pci_dev *parent,struct aer_err_info *e_info)
{... .../* 檢查根端口本身是否是發送錯誤消息的代理 */result = find_device_iter(dev, e_info);if (result)return true;
?/* 根據父設備類型采用不同的搜索方式 */if (pci_pcie_type(parent) == PCI_EXP_TYPE_RC_EC)/* 如果是根復合體事件收集器(RCEC),則遍歷RCEC */pcie_walk_rcec(parent, find_device_iter, e_info);else/* 否則遍歷根端口的下屬總線 */pci_walk_bus(parent->subordinate, find_device_iter, e_info);... ...
}

在PCIe設備樹中定位觸發AER(高級錯誤報告)的具體設備,支持從根端口(Root Port)或根復合體事件收集器(RCEC)開始搜索。

首先檢查根端口自身是否是錯誤源,如果不是,則向下遍歷設備樹:

  • 對于RCEC類型設備使用pcie_walk_rcec()

  • 對于普通根端口使用pci_walk_bus()

3.3、aer_process_err_devices
static inline void aer_process_err_devices(struct aer_err_info *e_info)
{int i;
?/* 第一階段:報告所有錯誤信息(在處理前先記錄,避免因復位等操作丟失記錄) */for (i = 0; i < e_info->error_dev_num && e_info->dev[i]; i++) {if (aer_get_device_error_info(e_info->dev[i], e_info))aer_print_error(e_info->dev[i], e_info);}
?/* 第二階段:處理所有錯誤源 */for (i = 0; i < e_info->error_dev_num && e_info->dev[i]; i++) {if (aer_get_device_error_info(e_info->dev[i], e_info))handle_error_source(e_info->dev[i], e_info);}
}

該函數主要負責兩個階段處理錯誤設備:

(1)錯誤信息報告階段:先收集并打印所有設備的錯誤信息

(2)錯誤處理階段:然后對所有設備執行實際的錯誤處理

3.4、handle_error_source
static void handle_error_source(struct pci_dev *dev, struct aer_err_info *info)
{/* 獲取設備的AER能力寄存器偏移量 */int aer = dev->aer_cap;
?/* 處理可糾正錯誤(AER_CORRECTABLE)*/if (info->severity == AER_CORRECTABLE) {// 可糾正錯誤不需要軟件干預,無需走完整的錯誤恢復流程。if (aer)/* 清除可糾正錯誤狀態寄存器(寫1清除) */pci_write_config_dword(dev, aer + PCI_ERR_COR_STATUS,info->status);/* 如果設備支持原生AER處理,清除設備狀態 */if (pcie_aer_is_native(dev))pcie_clear_device_status(dev);}/* 處理非致命錯誤(AER_NONFATAL)*/else if (info->severity == AER_NONFATAL)/* 執行標準恢復流程(I/O通道狀態正常) */pcie_do_recovery(dev, pci_channel_io_normal, aer_root_reset);/* 處理致命錯誤(AER_FATAL)*/else if (info->severity == AER_FATAL)/* 執行強制恢復流程(I/O通道已凍結) */pcie_do_recovery(dev, pci_channel_io_frozen, aer_root_reset);/* 減少設備的引用計數(配對之前可能的pci_dev_get) */pci_dev_put(dev);
}

函數根據錯誤嚴重級別采取不同的處理措施:

可糾正錯誤:僅清除錯誤狀態寄存器,額外調用pcie_clear_device_status確保狀態清除

非致命和致命錯誤都調用pcie_do_recovery,但傳入不同的I/O通道狀態:

pci_channel_io_normal:鏈路仍可用

pci_channel_io_frozen:鏈路已凍結

非致命錯誤:觸發普通恢復流程

致命錯誤:觸發強制恢復流程

3.5、pcie_do_recovery 整體邏輯

下面這塊邏輯比較隱晦:

pci_ers_result_t pcie_do_recovery(struct pci_dev *dev,pci_channel_state_t state,pci_ers_result_t (*reset_subordinates)(struct pci_dev *pdev))
{/** - 如果是根端口/下游端口/RCEC/RCiEP,恢復該設備及其下級設備* - 其他設備類型,恢復該設備及同端口下的所有設備*/if (type == PCI_EXP_TYPE_ROOT_PORT ||type == PCI_EXP_TYPE_DOWNSTREAM ||type == PCI_EXP_TYPE_RC_EC ||type == PCI_EXP_TYPE_RC_END)bridge = dev; ?// 端口類設備自身作為恢復起點elsebridge = pci_upstream_bridge(dev); ?// 其他設備向上找到最近的端口... .../* 階段1:錯誤檢測處理 */if (state == pci_channel_io_frozen) {/* 凍結狀態處理 */pci_walk_bridge(bridge, report_frozen_detected, &status);if (reset_subordinates(bridge) != PCI_ERS_RESULT_RECOVERED) {pci_warn(bridge, "下級設備重置失敗\n");goto failed;}} else {/* 正常狀態處理 */pci_walk_bridge(bridge, report_normal_detected, &status);}
?/* 階段2:MMIO重新啟用 */if (status == PCI_ERS_RESULT_CAN_RECOVER) {status = PCI_ERS_RESULT_RECOVERED;pci_walk_bridge(bridge, report_mmio_enabled, &status);}
?/* 階段3:插槽重置處理 */if (status == PCI_ERS_RESULT_NEED_RESET) {// 插槽重置函數,然后再調用, 驅動的slot_reset回調status = PCI_ERS_RESULT_RECOVERED;pci_walk_bridge(bridge, report_slot_reset, &status);}
?.../* 階段4:恢復完成處理 */pci_walk_bridge(bridge, report_resume, &status);
?// 如果OS原生控制AER,清除設備錯誤狀態; 如果平臺控制AER,由平臺負責清除if (host->native_aer || pcie_ports_native) {pcie_clear_device_status(dev);pci_aer_clear_nonfatal_status(dev);}... ...
}

首先是 pci_walk_bridge(bridge, report_frozen_detected, &status) 這塊比較繞,展開后可以發現:

static void pci_walk_bridge(struct pci_dev *bridge,int (*cb)(struct pci_dev *, void *),void *userdata)
{if (bridge->subordinate)pci_walk_bus(bridge->subordinate, cb, userdata);elsecb(bridge, userdata);
}
?
void pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *),void *userdata)
{struct pci_dev *dev;struct pci_bus *bus;struct list_head *next;int retval;
?bus = top;down_read(&pci_bus_sem);next = top->devices.next;for (;;) {if (next == &bus->devices) {/* end of this bus, go up or finish */if (bus == top)break;next = bus->self->bus_list.next;bus = bus->self->bus;continue;}dev = list_entry(next, struct pci_dev, bus_list);if (dev->subordinate) {/* this is a pci-pci bridge, do its devices next */next = dev->subordinate->devices.next;bus = dev->subordinate;} elsenext = dev->bus_list.next;retval = cb(dev, userdata);if (retval)break;}up_read(&pci_bus_sem);
?
}

首先分析 pci_walk_bus,該函數主要做了以下兩件事:

(1)優先向下遍歷橋接設備的子總線,確保處理完整個子樹后再返回上級,如:

Bus 0 (top)
├─ Device A(橋接器)→ Bus 1
│ ? ├─ Device C
│ ? └─ Device D
└─ Device B

遍歷順序:Bus 0 → Device A → Bus 1 → Device C → Device D → Device B

(2)回調函數cb (即report_frozen_detected)返回非零值會立即終止遍歷(例如在錯誤恢復中已找到目標設備時)。

下面再看 report_frozen_detected這塊調用流程的邏輯:

static int report_frozen_detected(struct pci_dev *dev, void *data)
{return report_error_detected(dev, pci_channel_io_frozen, data);
}
?
static int report_error_detected(struct pci_dev *dev,pci_channel_state_t state,enum pci_ERS_result *result)
{const struct pci_error_handlers *err_handler;... ...if (!pci_dev_set_io_state(dev, state) ||!dev->driver ||!dev->driver->err_handler ||!dev->driver->err_handler->error_detected) {/* 如果整個設備subtree沒有error_detected回調,PCI_ERS_RESULT_NO_AER_DRIVER將阻止后續任何設備的錯誤回調 */if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) {/* 非橋設備沒有回調則標記為無法恢復 */vote = PCI_ERS_RESULT_NO_AER_DRIVER;} else {vote = PCI_ERS_RESULT_NONE;}} else {/* 獲取錯誤處理程序并調用error_detected回調 */err_handler = dev->driver->err_handler;vote = err_handler->error_detected(dev, state);}... ...
}
?
static pci_ers_result_t merge_result(enum pci_ers_result orig,enum pci_ers_result new)
{if (new == PCI_ERS_RESULT_NO_AER_DRIVER)return PCI_ERS_RESULT_NO_AER_DRIVER;
?if (new == PCI_ERS_RESULT_NONE)return orig;
?switch (orig) {case PCI_ERS_RESULT_CAN_RECOVER:case PCI_ERS_RESULT_RECOVERED:orig = new;break;case PCI_ERS_RESULT_DISCONNECT:if (new == PCI_ERS_RESULT_NEED_RESET)orig = PCI_ERS_RESULT_NEED_RESET;break;default:break;}
?return orig;
}

可以發現,這個函數的主要作用是遍歷PCI總線上的多個設備時(例如通過 pci_walk_bus),綜合所有設備的錯誤恢復狀態,決定最終的恢復策略(如是否需要復位、是否斷開設備等),根據代碼看,整體的設備是否可恢復狀態合并邏輯是這樣的:

原始狀態 (orig)新狀態 (new)合并結果
CAN_RECOVERDISCONNECTDISCONNECT
RECOVEREDNEED_RESETNEED_RESET
DISCONNECTNEED_RESETNEED_RESET
DISCONNECTCAN_RECOVERDISCONNECT(不降級)
NO_AER_DRIVER任意NO_AER_DRIVER(最高優先級)

因此,pci_walk_bridge 這個函數的作用就是,如果設備是橋,根據橋和下面的子設備error_detected 回調函數,綜合判斷設備要不要恢復,是走DISCONNECTNEED_RESET還是CAN_RECOVER。如果設備是RCEP,直接判斷因該置位自己設備為哪種預備狀態

3.5.1、pcie_do_recovery 整體總結--AER處理的核心部分

接下來重新回到 pcie_do_recovery 函數來看,這個函數的邏輯就比較清晰了,即:

(1)如果錯誤為 FATAL 錯誤,即設備A已經被標記成 pci_channel_io_frozen 狀態了,這個時候先用深度優先算法,遍歷該設備A和其下子設備,去檢查是否滿足reset條件,然后綜合該設備和設備下掛子設備能否reset,給A這條線路置一個 PCI_ERS_RESULT_NEED_RESET 還是 PCI_ERS_RESULT_CAN_RECOVER 等的標識

(2)同時,如果錯誤為 FATAL 錯誤,會調用 reset_subordinates(bridge) != PCI_ERS_RESULT_RECOVERED,進而調用 aer_root_reset去重置該 bridge,并期待返回 PCI_ERS_RESULT_RECOVERED標志,否則報異常

(3)如果錯誤為NON - FATAL錯誤,僅僅標記該端口的PCIe端口status狀態為 PCI_ERS_RESULT_NONE 或者是 PCI_ERS_RESULT_NO_AER_DRIVER,不進行端口或者總線的重置操作

(4)然后,根據端口在上面被標記的status狀態,遍歷調用 report_mmio_enabled 去恢復特定端口的MMIO功能。同時,更新端口A的status位

(5)然后,對執行MMIO恢復后的,status被標記為PCI_ERS_RESULT_NEED_RESET的端口,遍歷調用 report_slot_reset ,進行槽位級別的復位

(6)最后,根據設置的是固件優先還是OS優先,去清除Device Status寄存器和Uncorrectable Error Status 寄存器的響應錯誤bit位

3.5.2、附加: aer_root_reset的函數分析--輔助理解pcie_do_recovery
static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
{// * - RCiEP需要找到關聯的RCEC,其他設備直接找到根端口if (type == PCI_EXP_TYPE_RC_END)root = dev->rcec; ? ?// RCiEP使用關聯的RCECelseroot = pcie_find_root_port(dev); ?// 其他設備查找根端口
?// 如果平臺保留AER控制權,RCiEP可能沒有可見的RCEC,此時root可能為NULL,寄存器操作由固件負責aer = root ? root->aer_cap : 0; ?// 獲取AER能力位置
?/* 階段1: 禁用根端口錯誤中斷 */if ((host->native_aer || pcie_ports_native) && aer) {pci_read_config_dword(root, aer + PCI_ERR_ROOT_COMMAND, &reg32);reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK; ?// 清除中斷使能位pci_write_config_dword(root, aer + PCI_ERR_ROOT_COMMAND, reg32);}
?/* 階段2: 執行設備重置 */if (type == PCI_EXP_TYPE_RC_EC || type == PCI_EXP_TYPE_RC_END) {/* RCEC/RCiEP使用功能級重置(FLR) */rc = pcie_reset_flr(dev, PCI_RESET_DO_RESET);} else {/* 根端口/下游端口使用總線錯誤重置 */rc = pci_bus_error_reset(dev);pci_info(dev, "%s端口鏈路已重置(%d)\n",pci_is_root_bus(dev->bus) ? "根" : "下游", rc);}
?/* 階段3: 清理并恢復中斷 */if ((host->native_aer || pcie_ports_native) && aer) {/* 清除根錯誤狀態寄存器 */pci_read_config_dword(root, aer + PCI_ERR_ROOT_STATUS, &reg32);pci_write_config_dword(root, aer + PCI_ERR_ROOT_STATUS, reg32);
?/* 重新啟用根端口錯誤中斷 */pci_read_config_dword(root, aer + PCI_ERR_ROOT_COMMAND, &reg32);reg32 |= ROOT_PORT_INTR_ON_MESG_MASK; ?// 設置中斷使能位pci_write_config_dword(root, aer + PCI_ERR_ROOT_COMMAND, reg32);}
?/* 返回結果: 成功恢復或需要斷開 */return rc ? PCI_ERS_RESULT_DISCONNECT : PCI_ERS_RESULT_RECOVERED;
}

主要做下面幾件事情:

(1)禁用根端口錯誤中斷

#define ROOT_PORT_INTR_ON_MESG_MASK (PCI_ERR_ROOT_CMD_COR_EN|   \PCI_ERR_ROOT_CMD_NONFATAL_EN|   \PCI_ERR_ROOT_CMD_FATAL_EN)

(2)執行設備重置(FLR或鏈路重置),如果是RCEC/RCiEP,通過pcie_reset_flr()執行功能級重置;如果是根端口或者下游端口,通過pci_bus_error_reset()執行總線級重置

(3)清除根錯誤狀態

(4)重新啟用根端口錯誤中斷

接著向下看調用 pci_bus_error_reset()

int pci_bus_error_reset(struct pci_dev *bridge)
{/* 情況1:總線無槽位(如嵌入式設備),直接跳轉總線復位 */if (list_empty(&bus->slots))goto bus_reset;
?/* 階段1:檢查所有槽位是否支持熱復位 */list_for_each_entry(slot, &bus->slots, list)if (pci_probe_reset_slot(slot)) ?// 探測槽位復位能力goto bus_reset; ?// 任一槽位不支持則改用總線復位
?/* 階段2:執行實際槽位復位 */list_for_each_entry(slot, &bus->slots, list)if (pci_slot_reset(slot, PCI_RESET_DO_RESET)) ?// 實際復位操作goto bus_reset; ?// 任一槽位復位失敗則改用總線復位... ...
/* 降級處理路徑:總線級復位 */
bus_reset:mutex_unlock(&pci_slot_mutex);return pci_bus_reset(bridge->subordinate, PCI_RESET_DO_RESET);
}

采用漸進式復位策略:先嘗試最小影響的槽位復位(pci_slot_reset),失敗時自動降級為總線復位(pci_bus_reset)

pci_slot_reset--> pci_reset_hotplug_slot(slot->hotplug, probe);--> hotplug->ops->reset_slot(hotplug, probe);--> pciehp_reset_slot--> pci_bridge_secondary_bus_reset(ctrl->pcie->port);

(1)置位橋控制寄存器的BUS_RESET位,保持復位狀態至少2ms(符合PCI規范v3.0 7.6.4.2)

(2)清除BUS_RESET位,等待1秒確保下游設備完成初始化

pci_bus_reset(struct pci_bus *bus, bool probe)--> pci_bridge_secondary_bus_reset(bus->self);

三、內核處理流程整體總結

(1)EP設備發生AER錯誤,通過error msg上報到root port, root port上報中斷給CPU處理

(2)Correctable Errors處理流程:獲取出錯的設備和清狀態,讀取設備詳細錯誤信息

(3)NON-FATAL Errors處理流程:獲取出錯的設備和清狀態,讀取設備詳細錯誤信息,錯誤恢復處理

(4)FATAL Errors處理流程:大體類似non-fatal,只是錯誤恢復的時候有差異,FATAL Errors影響pcie link鏈路,因此會做鏈路的恢復

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/89666.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/89666.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/89666.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

?HAProxy負載均衡集群概述

前言&#xff1a; 在現代分布式系統中&#xff0c;負載均衡和高可用性是保障服務穩定性和性能的關鍵技術。HAProxy 作為一款高性能的 TCP/HTTP 負載均衡器&#xff0c;憑借其輕量級、高并發處理能力和靈活的配置機制&#xff0c;成為構建高可用架構的核心組件之一。通過智能的流…

ELN:生物醫藥科研的數字化引擎——衍因科技引領高效創新

在生物醫藥研究領域&#xff0c;實驗數據的準確記錄與管理是科研成敗的關鍵。想象一個場景&#xff1a;某頂尖醫學院實驗室&#xff0c;研究員小張正為一項抗癌藥物實驗焦頭爛額。紙質記錄本中&#xff0c;數據混亂、協作困難&#xff0c;導致實驗重復率高達20%。引入衍因科技的…

暑假---作業2

學習目標&#xff1a;xss-1abs 1-8關python美現自動化布爾自注的2、代碼進行優化(二分查找)學習內容&#xff1a;1.xss-1abs 1-8關1<h2 align"center">歡迎用戶test</h2>2 <script> alert (1)</script&gt<center> <form action&…

【Tensor數據轉換】——深度學習.Torch框架

目錄 1 Tensor與Numpy 1.1 張量轉Numpy 1.2 Numpy轉張量 1 Tensor與Numpy 1.1 張量轉Numpy 調用numpy()方法可以把Tensor轉換為Numpy&#xff0c;此時內存是共享的。 使用copy()方法可以避免內存共享 import torch import numpy as np# tensor轉numpy:numpy() def test0…

基于Tranformer的NLP實戰(5):BERT實戰-基于Pytorch Lightning的文本分類模型

文本分類作為自然語言處理中的基礎任務&#xff0c;能夠幫助我們將海量醫學摘要自動歸類到具體疾病領域中。本文將基于NVIDIA NeMo框架&#xff0c;構建一個用于醫學疾病摘要分類的深度學習應用&#xff0c;支持將摘要劃分為三類&#xff1a;癌癥類疾病、神經系統疾病及障礙、以…

14-鏈路聚合

鏈路聚合技術 一 鏈路聚合概述鏈路聚合定義鏈路聚合是把多條物理鏈路聚合在一起&#xff0c;形成一條邏輯鏈路。應用在交換機、路由器、服務器間鏈路。分為三層鏈路聚合和二層鏈路聚合。二 鏈路聚合的作用 1. 鏈路聚合模式靜態聚合模式 端口不與對端設備交互信息。選擇參考端口…

學習C++、QT---28(QT庫中使用QShortcut類對快捷鍵創建和使用的講解)

每日一言 所有的努力&#xff0c;都是為了讓未來的自己感謝現在的你。 QShortcut 我們的記事本肯定要有通過快捷鍵對字體的放大和縮小進行控制的功能啊&#xff0c;那么我們這邊就這個問題我們需要先學習一下QShortCut 我們這個類就是專門做快捷鍵的 老樣子我們剛開始學習這個…

Web Worker:讓前端飛起來的隱形引擎

目錄 Web Worker&#xff1a;讓前端飛起來的隱形引擎 一、什么是 Web Worker&#xff1f; 1、為什么需要 web worker 2、什么是 web worker 二、基本使用方法 1、創建一個 Worker 文件&#xff08;worker.js&#xff09; 2、主線程引入并使用 三、實戰案例&#xff1a;…

關于在VScode中使用git的一些步驟常用命令及其常見問題:

輸入 gitee用戶 gitee綁定郵箱git config --global user.name "automated-piggy-senior" git config --global user.email "1323280131qq.com"克隆遠程庫到本地 git clone https://gitee.com/automated-piggy-senior/20250717-test.git常見問題1&#xff1…

LeafletJS 性能優化:處理大數據量地圖

引言 LeafletJS 作為一個輕量、靈活的 JavaScript 地圖庫&#xff0c;以其高效的渲染能力和模塊化設計深受開發者喜愛。然而&#xff0c;當處理大數據量&#xff08;如數千個標記、復雜的 GeoJSON 數據或高分辨率瓦片&#xff09;時&#xff0c;LeafletJS 的性能可能面臨挑戰&…

LLM(Large Language Model)大規模語言模型淺析

參考: https://zhuanlan.zhihu.com/p/7046080918 LLM(Large Language Model)大規模語言模型,是指具有大規模參數和復雜計算結構的機器學習模型。大模型里常說的多少B, B 是 Billion 的縮寫&#xff0c;表示 十億,如DeepSeek滿血版 671B(6710億參數); 大模型本質上是一個使用海量…

【后端】配置SqlSugar ORM框架并添加倉儲

目錄 1.添加包 2.配置連接字符串 3.配置SqlSugar 3.1.添加基礎類 3.2.添加方法 3.2.1.讀取配置方法 3.2.2.枚舉擴展方法 3.3.添加管理類&#xff08;重要&#xff09; 4.配置倉儲 4.1.倉儲接口添加 5.注冊 6.使用 該文檔是配置SqlSugar多租戶和加倉儲教程。使用 S…

全國高等院校計算機基礎教育研究會2025學術年會在西寧成功舉辦 ——高原論道啟新程,數智融合育英才

7 月16日至18日&#xff0c;全國高等院校計算機基礎教育研究會2025學術年會在青海西寧隆重召開。大會以“數智融合&#xff0c;創新計算機教育”為主題&#xff0c;匯聚人工智能領域頂尖專家學者、高校校長、產業翹楚及一線教師300 多人&#xff0c;共商人工智能時代計算機基礎…

AppTrace:重新定義免填邀請碼,解鎖用戶裂變新高度

??在移動互聯網時代&#xff0c;?用戶裂變是App增長的核心引擎&#xff0c;而邀請機制則是裂變的關鍵驅動力。然而&#xff0c;傳統的邀請碼機制——依賴用戶手動輸入、記憶復雜字符——已經成為用戶體驗的絆腳石&#xff0c;導致轉化率下降、運營成本上升。?AppTrace? 作…

神經網絡常見激活函數 13-Softplus函數

文章目錄Softplus函數導函數函數和導函數圖像優缺點PyTorch 中的 Softplus 函數TensorFlow 中的 Softplus 函數Softplus 函數導函數 Softplus函數 Softplus?(x)ln?(1ex)\begin{aligned} \operatorname{Softplus}(x) & \ln \bigl(1 e^{\,x}\bigr) \end{aligned} Softplu…

深度理解 KVM:Linux 內核系統學習的重要角度

&#x1f4d6; 推薦閱讀&#xff1a;《Yocto項目實戰教程:高效定制嵌入式Linux系統》 &#x1f3a5; 更多學習視頻請關注 B 站&#xff1a;嵌入式Jerry 深度理解 KVM&#xff1a;Linux 內核系統學習的重要角度 作者&#xff1a;嵌入式 Jerry 一、為什么開發者需要學習 KVM&…

閉包的定義和應用場景

一、閉包是什么&#xff1f; 閉包是指函數可以“記住”并訪問它定義時的詞法作用域&#xff0c;即使這個函數在其作用域鏈之外執行。 簡單說&#xff1a;函數 A 在函數 B 中被定義&#xff0c;并在函數 B 外部被調用&#xff0c;它依然能訪問函數 B 中的變量&#xff0c;這就是…

北京-4年功能測試2年空窗-報培訓班學測開-第五十四天

今天交付的成果是&#xff0c;初版簡歷雖然只寫了項目部分&#xff0c;但用了一整天&#xff0c;期間聯系了前司組長&#xff0c;拿到了性能測試報告。然后再看壓測腳本&#xff0c;突然能看懂了&#xff0c;對服務端日志也能看懂些了&#xff0c;還找到了客戶端日志怎么說呢&a…

算法訓練營day24 回溯算法③ 93.復原IP地址 、78.子集、 90.子集II

今天繼續回溯算法的專題&#xff0c;第三篇博客&#xff01; 93.復原IP地址 輸入&#xff1a;s "25525511135" 輸出&#xff1a;["255.255.11.135","255.255.111.35"] 切割字符串為4段&#xff0c;當進行到第四段的時候對第四段字符串進行判斷…

jeccg-boot框架實現xls模板導出功能

文章目錄一、后端部分二、前端部分三、模板制作一、后端部分 //1、在application-dev.yml文件增加模板路徑path :#模板路徑saxls: /data/opt/saxls/ //2、控制層寫法 public class sabassalController extends JeecgController<sabassalVo, IsabassalService> {Autowired…