18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (c) 2007 Cisco Systems, Inc. All rights reserved. 38c2ecf20Sopenharmony_ci * Copyright (c) 2007, 2008 Mellanox Technologies. All rights reserved. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * This software is available to you under a choice of one of two 68c2ecf20Sopenharmony_ci * licenses. You may choose to be licensed under the terms of the GNU 78c2ecf20Sopenharmony_ci * General Public License (GPL) Version 2, available from the file 88c2ecf20Sopenharmony_ci * COPYING in the main directory of this source tree, or the 98c2ecf20Sopenharmony_ci * OpenIB.org BSD license below: 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or 128c2ecf20Sopenharmony_ci * without modification, are permitted provided that the following 138c2ecf20Sopenharmony_ci * conditions are met: 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * - Redistributions of source code must retain the above 168c2ecf20Sopenharmony_ci * copyright notice, this list of conditions and the following 178c2ecf20Sopenharmony_ci * disclaimer. 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * - Redistributions in binary form must reproduce the above 208c2ecf20Sopenharmony_ci * copyright notice, this list of conditions and the following 218c2ecf20Sopenharmony_ci * disclaimer in the documentation and/or other materials 228c2ecf20Sopenharmony_ci * provided with the distribution. 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 258c2ecf20Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 268c2ecf20Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 278c2ecf20Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 288c2ecf20Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 298c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 308c2ecf20Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 318c2ecf20Sopenharmony_ci * SOFTWARE. 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 358c2ecf20Sopenharmony_ci#include <linux/module.h> 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#include "mlx4.h" 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cienum { 408c2ecf20Sopenharmony_ci MLX4_CATAS_POLL_INTERVAL = 5 * HZ, 418c2ecf20Sopenharmony_ci}; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ciint mlx4_internal_err_reset = 1; 468c2ecf20Sopenharmony_cimodule_param_named(internal_err_reset, mlx4_internal_err_reset, int, 0644); 478c2ecf20Sopenharmony_ciMODULE_PARM_DESC(internal_err_reset, 488c2ecf20Sopenharmony_ci "Reset device on internal errors if non-zero (default 1)"); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic int read_vendor_id(struct mlx4_dev *dev) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci u16 vendor_id = 0; 538c2ecf20Sopenharmony_ci int ret; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci ret = pci_read_config_word(dev->persist->pdev, 0, &vendor_id); 568c2ecf20Sopenharmony_ci if (ret) { 578c2ecf20Sopenharmony_ci mlx4_err(dev, "Failed to read vendor ID, ret=%d\n", ret); 588c2ecf20Sopenharmony_ci return ret; 598c2ecf20Sopenharmony_ci } 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci if (vendor_id == 0xffff) { 628c2ecf20Sopenharmony_ci mlx4_err(dev, "PCI can't be accessed to read vendor id\n"); 638c2ecf20Sopenharmony_ci return -EINVAL; 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci return 0; 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic int mlx4_reset_master(struct mlx4_dev *dev) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci int err = 0; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci if (mlx4_is_master(dev)) 748c2ecf20Sopenharmony_ci mlx4_report_internal_err_comm_event(dev); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci if (!pci_channel_offline(dev->persist->pdev)) { 778c2ecf20Sopenharmony_ci err = read_vendor_id(dev); 788c2ecf20Sopenharmony_ci /* If PCI can't be accessed to read vendor ID we assume that its 798c2ecf20Sopenharmony_ci * link was disabled and chip was already reset. 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_ci if (err) 828c2ecf20Sopenharmony_ci return 0; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci err = mlx4_reset(dev); 858c2ecf20Sopenharmony_ci if (err) 868c2ecf20Sopenharmony_ci mlx4_err(dev, "Fail to reset HCA\n"); 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci return err; 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic int mlx4_reset_slave(struct mlx4_dev *dev) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci#define COM_CHAN_RST_REQ_OFFSET 0x10 958c2ecf20Sopenharmony_ci#define COM_CHAN_RST_ACK_OFFSET 0x08 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci u32 comm_flags; 988c2ecf20Sopenharmony_ci u32 rst_req; 998c2ecf20Sopenharmony_ci u32 rst_ack; 1008c2ecf20Sopenharmony_ci unsigned long end; 1018c2ecf20Sopenharmony_ci struct mlx4_priv *priv = mlx4_priv(dev); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci if (pci_channel_offline(dev->persist->pdev)) 1048c2ecf20Sopenharmony_ci return 0; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci comm_flags = swab32(readl((__iomem char *)priv->mfunc.comm + 1078c2ecf20Sopenharmony_ci MLX4_COMM_CHAN_FLAGS)); 1088c2ecf20Sopenharmony_ci if (comm_flags == 0xffffffff) { 1098c2ecf20Sopenharmony_ci mlx4_err(dev, "VF reset is not needed\n"); 1108c2ecf20Sopenharmony_ci return 0; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (!(dev->caps.vf_caps & MLX4_VF_CAP_FLAG_RESET)) { 1148c2ecf20Sopenharmony_ci mlx4_err(dev, "VF reset is not supported\n"); 1158c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci rst_req = (comm_flags & (u32)(1 << COM_CHAN_RST_REQ_OFFSET)) >> 1198c2ecf20Sopenharmony_ci COM_CHAN_RST_REQ_OFFSET; 1208c2ecf20Sopenharmony_ci rst_ack = (comm_flags & (u32)(1 << COM_CHAN_RST_ACK_OFFSET)) >> 1218c2ecf20Sopenharmony_ci COM_CHAN_RST_ACK_OFFSET; 1228c2ecf20Sopenharmony_ci if (rst_req != rst_ack) { 1238c2ecf20Sopenharmony_ci mlx4_err(dev, "Communication channel isn't sync, fail to send reset\n"); 1248c2ecf20Sopenharmony_ci return -EIO; 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci rst_req ^= 1; 1288c2ecf20Sopenharmony_ci mlx4_warn(dev, "VF is sending reset request to Firmware\n"); 1298c2ecf20Sopenharmony_ci comm_flags = rst_req << COM_CHAN_RST_REQ_OFFSET; 1308c2ecf20Sopenharmony_ci __raw_writel((__force u32)cpu_to_be32(comm_flags), 1318c2ecf20Sopenharmony_ci (__iomem char *)priv->mfunc.comm + MLX4_COMM_CHAN_FLAGS); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci end = msecs_to_jiffies(MLX4_COMM_TIME) + jiffies; 1348c2ecf20Sopenharmony_ci while (time_before(jiffies, end)) { 1358c2ecf20Sopenharmony_ci comm_flags = swab32(readl((__iomem char *)priv->mfunc.comm + 1368c2ecf20Sopenharmony_ci MLX4_COMM_CHAN_FLAGS)); 1378c2ecf20Sopenharmony_ci rst_ack = (comm_flags & (u32)(1 << COM_CHAN_RST_ACK_OFFSET)) >> 1388c2ecf20Sopenharmony_ci COM_CHAN_RST_ACK_OFFSET; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci /* Reading rst_req again since the communication channel can 1418c2ecf20Sopenharmony_ci * be reset at any time by the PF and all its bits will be 1428c2ecf20Sopenharmony_ci * set to zero. 1438c2ecf20Sopenharmony_ci */ 1448c2ecf20Sopenharmony_ci rst_req = (comm_flags & (u32)(1 << COM_CHAN_RST_REQ_OFFSET)) >> 1458c2ecf20Sopenharmony_ci COM_CHAN_RST_REQ_OFFSET; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (rst_ack == rst_req) { 1488c2ecf20Sopenharmony_ci mlx4_warn(dev, "VF Reset succeed\n"); 1498c2ecf20Sopenharmony_ci return 0; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci cond_resched(); 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci mlx4_err(dev, "Fail to send reset over the communication channel\n"); 1548c2ecf20Sopenharmony_ci return -ETIMEDOUT; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ciint mlx4_comm_internal_err(u32 slave_read) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci return (u32)COMM_CHAN_EVENT_INTERNAL_ERR == 1608c2ecf20Sopenharmony_ci (slave_read & (u32)COMM_CHAN_EVENT_INTERNAL_ERR) ? 1 : 0; 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_civoid mlx4_enter_error_state(struct mlx4_dev_persistent *persist) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci int err; 1668c2ecf20Sopenharmony_ci struct mlx4_dev *dev; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if (!mlx4_internal_err_reset) 1698c2ecf20Sopenharmony_ci return; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci mutex_lock(&persist->device_state_mutex); 1728c2ecf20Sopenharmony_ci if (persist->state & MLX4_DEVICE_STATE_INTERNAL_ERROR) 1738c2ecf20Sopenharmony_ci goto out; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci dev = persist->dev; 1768c2ecf20Sopenharmony_ci mlx4_err(dev, "device is going to be reset\n"); 1778c2ecf20Sopenharmony_ci if (mlx4_is_slave(dev)) { 1788c2ecf20Sopenharmony_ci err = mlx4_reset_slave(dev); 1798c2ecf20Sopenharmony_ci } else { 1808c2ecf20Sopenharmony_ci mlx4_crdump_collect(dev); 1818c2ecf20Sopenharmony_ci err = mlx4_reset_master(dev); 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci if (!err) { 1858c2ecf20Sopenharmony_ci mlx4_err(dev, "device was reset successfully\n"); 1868c2ecf20Sopenharmony_ci } else { 1878c2ecf20Sopenharmony_ci /* EEH could have disabled the PCI channel during reset. That's 1888c2ecf20Sopenharmony_ci * recoverable and the PCI error flow will handle it. 1898c2ecf20Sopenharmony_ci */ 1908c2ecf20Sopenharmony_ci if (!pci_channel_offline(dev->persist->pdev)) 1918c2ecf20Sopenharmony_ci BUG_ON(1); 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci dev->persist->state |= MLX4_DEVICE_STATE_INTERNAL_ERROR; 1948c2ecf20Sopenharmony_ci mutex_unlock(&persist->device_state_mutex); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci /* At that step HW was already reset, now notify clients */ 1978c2ecf20Sopenharmony_ci mlx4_dispatch_event(dev, MLX4_DEV_EVENT_CATASTROPHIC_ERROR, 0); 1988c2ecf20Sopenharmony_ci mlx4_cmd_wake_completions(dev); 1998c2ecf20Sopenharmony_ci return; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ciout: 2028c2ecf20Sopenharmony_ci mutex_unlock(&persist->device_state_mutex); 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic void mlx4_handle_error_state(struct mlx4_dev_persistent *persist) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci int err = 0; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci mlx4_enter_error_state(persist); 2108c2ecf20Sopenharmony_ci mutex_lock(&persist->interface_state_mutex); 2118c2ecf20Sopenharmony_ci if (persist->interface_state & MLX4_INTERFACE_STATE_UP && 2128c2ecf20Sopenharmony_ci !(persist->interface_state & MLX4_INTERFACE_STATE_DELETION)) { 2138c2ecf20Sopenharmony_ci err = mlx4_restart_one(persist->pdev); 2148c2ecf20Sopenharmony_ci mlx4_info(persist->dev, "mlx4_restart_one was ended, ret=%d\n", 2158c2ecf20Sopenharmony_ci err); 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci mutex_unlock(&persist->interface_state_mutex); 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic void dump_err_buf(struct mlx4_dev *dev) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci struct mlx4_priv *priv = mlx4_priv(dev); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci int i; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci mlx4_err(dev, "Internal error detected:\n"); 2278c2ecf20Sopenharmony_ci for (i = 0; i < priv->fw.catas_size; ++i) 2288c2ecf20Sopenharmony_ci mlx4_err(dev, " buf[%02x]: %08x\n", 2298c2ecf20Sopenharmony_ci i, swab32(readl(priv->catas_err.map + i))); 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistatic void poll_catas(struct timer_list *t) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci struct mlx4_priv *priv = from_timer(priv, t, catas_err.timer); 2358c2ecf20Sopenharmony_ci struct mlx4_dev *dev = &priv->dev; 2368c2ecf20Sopenharmony_ci u32 slave_read; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci if (mlx4_is_slave(dev)) { 2398c2ecf20Sopenharmony_ci slave_read = swab32(readl(&priv->mfunc.comm->slave_read)); 2408c2ecf20Sopenharmony_ci if (mlx4_comm_internal_err(slave_read)) { 2418c2ecf20Sopenharmony_ci mlx4_warn(dev, "Internal error detected on the communication channel\n"); 2428c2ecf20Sopenharmony_ci goto internal_err; 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci } else if (readl(priv->catas_err.map)) { 2458c2ecf20Sopenharmony_ci dump_err_buf(dev); 2468c2ecf20Sopenharmony_ci goto internal_err; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci if (dev->persist->state & MLX4_DEVICE_STATE_INTERNAL_ERROR) { 2508c2ecf20Sopenharmony_ci mlx4_warn(dev, "Internal error mark was detected on device\n"); 2518c2ecf20Sopenharmony_ci goto internal_err; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci mod_timer(&priv->catas_err.timer, 2558c2ecf20Sopenharmony_ci round_jiffies(jiffies + MLX4_CATAS_POLL_INTERVAL)); 2568c2ecf20Sopenharmony_ci return; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ciinternal_err: 2598c2ecf20Sopenharmony_ci if (mlx4_internal_err_reset) 2608c2ecf20Sopenharmony_ci queue_work(dev->persist->catas_wq, &dev->persist->catas_work); 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic void catas_reset(struct work_struct *work) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci struct mlx4_dev_persistent *persist = 2668c2ecf20Sopenharmony_ci container_of(work, struct mlx4_dev_persistent, 2678c2ecf20Sopenharmony_ci catas_work); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci mlx4_handle_error_state(persist); 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_civoid mlx4_start_catas_poll(struct mlx4_dev *dev) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci struct mlx4_priv *priv = mlx4_priv(dev); 2758c2ecf20Sopenharmony_ci phys_addr_t addr; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&priv->catas_err.list); 2788c2ecf20Sopenharmony_ci timer_setup(&priv->catas_err.timer, poll_catas, 0); 2798c2ecf20Sopenharmony_ci priv->catas_err.map = NULL; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci if (!mlx4_is_slave(dev)) { 2828c2ecf20Sopenharmony_ci addr = pci_resource_start(dev->persist->pdev, 2838c2ecf20Sopenharmony_ci priv->fw.catas_bar) + 2848c2ecf20Sopenharmony_ci priv->fw.catas_offset; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci priv->catas_err.map = ioremap(addr, priv->fw.catas_size * 4); 2878c2ecf20Sopenharmony_ci if (!priv->catas_err.map) { 2888c2ecf20Sopenharmony_ci mlx4_warn(dev, "Failed to map internal error buffer at 0x%llx\n", 2898c2ecf20Sopenharmony_ci (unsigned long long)addr); 2908c2ecf20Sopenharmony_ci return; 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci priv->catas_err.timer.expires = 2958c2ecf20Sopenharmony_ci round_jiffies(jiffies + MLX4_CATAS_POLL_INTERVAL); 2968c2ecf20Sopenharmony_ci add_timer(&priv->catas_err.timer); 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_civoid mlx4_stop_catas_poll(struct mlx4_dev *dev) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci struct mlx4_priv *priv = mlx4_priv(dev); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci del_timer_sync(&priv->catas_err.timer); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci if (priv->catas_err.map) { 3068c2ecf20Sopenharmony_ci iounmap(priv->catas_err.map); 3078c2ecf20Sopenharmony_ci priv->catas_err.map = NULL; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci if (dev->persist->interface_state & MLX4_INTERFACE_STATE_DELETION) 3118c2ecf20Sopenharmony_ci flush_workqueue(dev->persist->catas_wq); 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ciint mlx4_catas_init(struct mlx4_dev *dev) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci INIT_WORK(&dev->persist->catas_work, catas_reset); 3178c2ecf20Sopenharmony_ci dev->persist->catas_wq = create_singlethread_workqueue("mlx4_health"); 3188c2ecf20Sopenharmony_ci if (!dev->persist->catas_wq) 3198c2ecf20Sopenharmony_ci return -ENOMEM; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci return 0; 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_civoid mlx4_catas_end(struct mlx4_dev *dev) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci if (dev->persist->catas_wq) { 3278c2ecf20Sopenharmony_ci destroy_workqueue(dev->persist->catas_wq); 3288c2ecf20Sopenharmony_ci dev->persist->catas_wq = NULL; 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci} 331