/*
 * Copyright (C) 2023-2026 Intel Corporation
 *
 * SPDX-License-Identifier: MIT
 *
 */

#include "level_zero/sysman/source/api/events/linux/sysman_os_events_imp.h"

#include "shared/source/debug_settings/debug_settings_manager.h"

#include "level_zero/sysman/source/api/events/linux/sysman_os_events_netlink.h"
#include "level_zero/sysman/source/api/global_operations/sysman_global_operations.h"
#include "level_zero/sysman/source/api/ras/linux/ras_util/sysman_ras_util.h"
#include "level_zero/sysman/source/driver/sysman_driver_handle_imp.h"
#include "level_zero/sysman/source/shared/linux/sysman_fs_access_interface.h"
#include "level_zero/sysman/source/shared/linux/zes_os_sysman_driver_imp.h"
#include "level_zero/sysman/source/shared/linux/zes_os_sysman_imp.h"

namespace L0 {
namespace Sysman {

const std::string LinuxEventsUtil::add("add");
const std::string LinuxEventsUtil::remove("remove");
const std::string LinuxEventsUtil::change("change");
const std::string LinuxEventsUtil::unbind("unbind");
const std::string LinuxEventsUtil::bind("bind");

bool LinuxEventsImp::eventListen(zes_event_type_flags_t &pEvent, uint64_t timeout) {
    // This is dummy implementation, Actual implementation is handled at driver level
    // for all devices.
    return false;
}

ze_result_t LinuxEventsImp::eventRegister(zes_event_type_flags_t events) {
    if (0xFFFF < events) {
        return ZE_RESULT_ERROR_INVALID_ENUMERATION;
    }

    auto pLinuxSysmanDriverImp = static_cast<LinuxSysmanDriverImp *>(globalSysmanDriver->pOsSysmanDriver);
    if (pLinuxSysmanDriverImp == nullptr) {
        PRINT_STRING(NEO::debugManager.flags.PrintDebugMessages.get(), stderr,
                     "%s", "Os Sysman driver not initialized\n");
        return ZE_RESULT_ERROR_UNINITIALIZED;
    }
    pLinuxSysmanDriverImp->eventRegister(events, pLinuxSysmanImp->getSysmanDeviceImp());
    return ZE_RESULT_SUCCESS;
}

LinuxEventsImp::LinuxEventsImp(OsSysman *pOsSysman) {
    pLinuxSysmanImp = static_cast<LinuxSysmanImp *>(pOsSysman);
}

OsEvents *OsEvents::create(OsSysman *pOsSysman) {
    LinuxEventsImp *pLinuxEventsImp = new LinuxEventsImp(pOsSysman);
    return static_cast<OsEvents *>(pLinuxEventsImp);
}

LinuxEventsUtil::LinuxEventsUtil(LinuxSysmanDriverImp *pOsSysmanDriverImp) : pLinuxSysmanDriverImp(pOsSysmanDriverImp) {
}

bool LinuxEventsUtil::checkRasEvent(zes_event_type_flags_t &pEvent, SysmanDeviceImp *pSysmanDeviceImp, zes_event_type_flags_t registeredEvents) {
    for (auto rasHandle : pSysmanDeviceImp->pRasHandleContext->handleList) {
        zes_ras_properties_t properties = {};
        rasHandle->rasGetProperties(&properties);
        if ((registeredEvents & ZES_EVENT_TYPE_FLAG_RAS_CORRECTABLE_ERRORS) && (properties.type == ZES_RAS_ERROR_TYPE_CORRECTABLE)) {
            if (LinuxEventsUtil::checkRasEventOccured(rasHandle) == true) {
                pEvent |= ZES_EVENT_TYPE_FLAG_RAS_CORRECTABLE_ERRORS;
                return true;
            }
        }
        if ((registeredEvents & ZES_EVENT_TYPE_FLAG_RAS_UNCORRECTABLE_ERRORS) && (properties.type == ZES_RAS_ERROR_TYPE_UNCORRECTABLE)) {
            if (LinuxEventsUtil::checkRasEventOccured(rasHandle) == true) {
                pEvent |= ZES_EVENT_TYPE_FLAG_RAS_UNCORRECTABLE_ERRORS;
                return true;
            }
        }
    }
    return false;
}

bool LinuxEventsUtil::checkRasEventOccured(Ras *rasHandle) {
    zes_ras_config_t config = {};
    zes_ras_state_t state = {};
    rasHandle->rasGetConfig(&config);
    if (ZE_RESULT_SUCCESS == rasHandle->rasGetState(&state, 0)) {
        uint64_t totalCategoryThreshold = 0;
        for (int i = 0; i < ZES_MAX_RAS_ERROR_CATEGORY_COUNT; i++) {
            totalCategoryThreshold += state.category[i];
            if ((config.detailedThresholds.category[i] > 0) && (state.category[i] > config.detailedThresholds.category[i])) {
                return true;
            }
        }
        if ((config.totalThreshold > 0) && (totalCategoryThreshold > config.totalThreshold)) {
            return true;
        }
    }
    return false;
}

void LinuxEventsUtil::eventRegister(zes_event_type_flags_t events, SysmanDeviceImp *pSysmanDevice) {
    std::call_once(initEventsOnce, [this]() {
        this->init();
    });

    zes_event_type_flags_t prevRegisteredEvents = 0;
    if (deviceEventsMap.find(pSysmanDevice) != deviceEventsMap.end()) {
        prevRegisteredEvents = deviceEventsMap[pSysmanDevice];
    }

    {
        std::unique_lock<std::mutex> lock(eventsMutex);
        if (!events) {
            // If user is trying to register events with empty events argument, then clear all the registered events
            if (deviceEventsMap.find(pSysmanDevice) != deviceEventsMap.end()) {
                deviceEventsMap[pSysmanDevice] = events;
            } else {
                deviceEventsMap.emplace(pSysmanDevice, events);
            }
        } else {
            zes_event_type_flags_t registeredEvents = 0;
            // supportedEventMask --> this mask checks for events that supported currently
            zes_event_type_flags_t supportedEventMask = ZES_EVENT_TYPE_FLAG_FABRIC_PORT_HEALTH | ZES_EVENT_TYPE_FLAG_DEVICE_DETACH |
                                                        ZES_EVENT_TYPE_FLAG_DEVICE_ATTACH | ZES_EVENT_TYPE_FLAG_DEVICE_RESET_REQUIRED |
                                                        ZES_EVENT_TYPE_FLAG_MEM_HEALTH | ZES_EVENT_TYPE_FLAG_RAS_CORRECTABLE_ERRORS |
                                                        ZES_EVENT_TYPE_FLAG_RAS_UNCORRECTABLE_ERRORS |
                                                        ZES_EVENT_TYPE_FLAG_SURVIVABILITY_MODE_DETECTED;
            if (deviceEventsMap.find(pSysmanDevice) != deviceEventsMap.end()) {
                registeredEvents = deviceEventsMap[pSysmanDevice];
            }
            registeredEvents |= (events & supportedEventMask);
            deviceEventsMap[pSysmanDevice] = registeredEvents;
        }

        // Write to Pipe only if eventregister() is called during listen and previously registered events are modified.
        if ((pipeFd[1] != -1) && (prevRegisteredEvents != deviceEventsMap[pSysmanDevice])) {
            uint8_t value = 0x00;
            if (NEO::SysCalls::write(pipeFd[1], &value, 1) < 0) {
                PRINT_STRING(NEO::debugManager.flags.PrintDebugMessages.get(), stderr,
                             "%s", "Write to Pipe failed\n");
            }
        }
    }
}

void LinuxEventsUtil::init() {
    pUdevLib = pLinuxSysmanDriverImp->getUdevLibHandle();
}

void LinuxEventsUtil::initNetlink() {
    // Iterate all known sysman devices to find one that supports netlink events. This handles heterogeneous multi-device systems
    // where only some devices support netlink, regardless of which device triggered init.
    for (auto *sysmanDevice : globalSysmanDriver->sysmanDevices) {
        auto *dev = static_cast<SysmanDeviceImp *>(sysmanDevice);
        if (netlinkInitialize(pLinuxSysmanDriverImp, pDrmNl, dev) == ZE_RESULT_SUCCESS) {
            return;
        }
    }
    pDrmNl = nullptr;
}

ze_result_t LinuxEventsUtil::eventsListen(uint64_t timeout, uint32_t count, zes_device_handle_t *phDevices, uint32_t *pNumDeviceEvents, zes_event_type_flags_t *pEvents) {
    memset(pEvents, 0, count * sizeof(zes_event_type_flags_t));
    *pNumDeviceEvents = 0;
    std::vector<zes_event_type_flags_t> registeredEvents(count);
    for (uint32_t devIndex = 0; devIndex < count; devIndex++) {
        auto device = static_cast<SysmanDeviceImp *>(L0::Sysman::SysmanDevice::fromHandle(phDevices[devIndex]));
        if (device == nullptr) {
            return ZE_RESULT_ERROR_INVALID_ARGUMENT;
        }
        {
            std::unique_lock<std::mutex> lock(eventsMutex);
            if (deviceEventsMap.find(device) != deviceEventsMap.end()) {
                registeredEvents[devIndex] = deviceEventsMap[device];
            }
        }
        if (registeredEvents[devIndex]) {
            if ((registeredEvents[devIndex] & ZES_EVENT_TYPE_FLAG_RAS_CORRECTABLE_ERRORS) || (registeredEvents[devIndex] & ZES_EVENT_TYPE_FLAG_RAS_UNCORRECTABLE_ERRORS)) {
                if (checkRasEvent(pEvents[devIndex], device, registeredEvents[devIndex])) {
                    *pNumDeviceEvents = 1;
                    return ZE_RESULT_SUCCESS;
                }
            }

            if (registeredEvents[devIndex] & ZES_EVENT_TYPE_FLAG_DEVICE_RESET_REQUIRED) {
                zes_device_state_t deviceState = {};
                device->pGlobalOperations->deviceGetState(&deviceState);
                if (deviceState.reset & ZES_RESET_REASON_FLAG_REPAIR) {
                    pEvents[devIndex] |= ZES_EVENT_TYPE_FLAG_DEVICE_RESET_REQUIRED;
                    *pNumDeviceEvents = 1;
                    return ZE_RESULT_SUCCESS;
                }
            }

            if (registeredEvents[devIndex] & ZES_EVENT_TYPE_FLAG_SURVIVABILITY_MODE_DETECTED) {
                if (device->isDeviceInSurvivabilityMode) {
                    pEvents[devIndex] |= ZES_EVENT_TYPE_FLAG_SURVIVABILITY_MODE_DETECTED;
                    *pNumDeviceEvents = 1;
                    return ZE_RESULT_SUCCESS;
                }
            }
        }
    }

    if (listenSystemEvents(pEvents, count, registeredEvents, phDevices, timeout)) {
        *pNumDeviceEvents = 1;
    }

    return ZE_RESULT_SUCCESS;
}

bool LinuxEventsUtil::isResetRequired(void *dev, zes_event_type_flags_t &pEvent) {
    if (action.compare(change) != 0) {
        return false;
    }

    std::vector<std::string> properties{"RESET_FAILED", "RESET_REQUIRED", "DEVICE_STATUS"};
    for (auto &property : properties) {
        const char *propVal = nullptr;
        propVal = pUdevLib->getEventPropertyValue(dev, property.c_str());
        if (propVal) {
            std::string expectedStr = "NEEDS_RESET";
            if (property != "DEVICE_STATUS") {
                if (atoi(propVal) == 1) {
                    pEvent |= ZES_EVENT_TYPE_FLAG_DEVICE_RESET_REQUIRED;
                    return true;
                }
            } else if (propVal == expectedStr) {
                pEvent |= ZES_EVENT_TYPE_FLAG_DEVICE_RESET_REQUIRED;
                return true;
            }
        }
    }

    return false;
}

bool LinuxEventsUtil::isSurvivabilityModeAsExpected(FsAccessInterface *pFsAccess, const std::string &devPath, const std::string &mode) {
    if (pFsAccess == nullptr) {
        return false;
    }

    // /sys/bus/pci/devices/<device>/survivability_mode will contain either "Boot" or "Runtime".
    const std::string survivabilityModeFilePath = "/sys" + devPath + "/survivability_mode";
    std::string survivabilityModeVal;
    if (pFsAccess->read(survivabilityModeFilePath, survivabilityModeVal) != ZE_RESULT_SUCCESS) {
        return false;
    }

    return survivabilityModeVal == mode;
}

bool LinuxEventsUtil::checkDeviceDetachEvent(zes_event_type_flags_t &pEvent) {
    if (action.compare(remove) == 0) {
        pEvent |= ZES_EVENT_TYPE_FLAG_DEVICE_DETACH;
        return true;
    }
    return false;
}

bool LinuxEventsUtil::checkDeviceAttachEvent(zes_event_type_flags_t &pEvent) {
    if (action.compare(add) == 0) {
        pEvent |= ZES_EVENT_TYPE_FLAG_DEVICE_ATTACH;
        return true;
    }
    return false;
}

bool LinuxEventsUtil::checkIfMemHealthChanged(void *dev, zes_event_type_flags_t &pEvent) {
    if (action.compare(change) != 0) {
        return false;
    }

    std::vector<std::string> properties{"MEM_HEALTH_ALARM", "RESET_REQUIRED", "EC_PENDING", "DEGRADED", "EC_FAILED", "SPARING_STATUS_UNKNOWN"};
    for (auto &property : properties) {
        const char *propVal = nullptr;
        propVal = pUdevLib->getEventPropertyValue(dev, property.c_str());
        if (propVal && atoi(propVal) == 1) {
            pEvent |= ZES_EVENT_TYPE_FLAG_MEM_HEALTH;
            return true;
        }
    }
    return false;
}

bool LinuxEventsUtil::checkIfFabricPortStatusChanged(void *dev, zes_event_type_flags_t &pEvent) {
    if (action.compare(change) != 0) {
        return false;
    }

    const char *str = pUdevLib->getEventPropertyValue(dev, "TYPE");
    if (str == nullptr) {
        return false;
    }

    std::string expectedStr = "PORT_CHANGE";
    if (expectedStr == str) {
        pEvent |= ZES_EVENT_TYPE_FLAG_FABRIC_PORT_HEALTH;
        return true;
    }
    return false;
}

bool LinuxEventsUtil::checkDeviceWedgedEvent(void *dev, zes_event_type_flags_t &pEvent) {
    // Only proceed when udev event for the device is "change"
    if (action.compare(change) != 0) {
        return false;
    }

    const char *propVal = pUdevLib->getEventPropertyValue(dev, "WEDGED");
    if ((propVal != nullptr) && std::string(propVal) == "cold-reset") {
        pEvent |= ZES_EVENT_TYPE_FLAG_DEVICE_RESET_REQUIRED;
        return true;
    }

    if ((propVal != nullptr) && std::string(propVal) == "vendor-specific") {
        pEvent |= ZES_EVENT_TYPE_FLAG_SURVIVABILITY_MODE_DETECTED;
        return true;
    }

    return false;
}

bool LinuxEventsUtil::handleNetlinkEvents(zes_event_type_flags_t *pEvents, uint32_t count, zes_device_handle_t *phDevices, const std::vector<zes_event_type_flags_t> &registeredEvents) {
    return netlinkHandleEvents(pDrmNl, pEvents, count, phDevices, registeredEvents);
}

bool LinuxEventsUtil::processNetlinkRasEvent(const DrmRasEvent &netlinkEvent, zes_event_type_flags_t *pEvents, uint32_t count, zes_device_handle_t *phDevices, const std::vector<zes_event_type_flags_t> &registeredEvents) {
    return netlinkProcessRasEvent(netlinkEvent, pEvents, count, phDevices, registeredEvents);
}

void LinuxEventsUtil::getDevIndexToDevPathMap(std::vector<zes_event_type_flags_t> &registeredEvents, uint32_t count, zes_device_handle_t *phDevices, std::map<uint32_t, std::string> &mapOfDevIndexToDevPath, FsAccessInterface *&pFsAccess) {
    for (uint32_t devIndex = 0; devIndex < count; devIndex++) {
        auto device = static_cast<SysmanDeviceImp *>(L0::Sysman::SysmanDevice::fromHandle(phDevices[devIndex]));
        if (deviceEventsMap.find(device) != deviceEventsMap.end()) {
            registeredEvents[devIndex] = deviceEventsMap[device];
        }
        if (!registeredEvents[devIndex]) {
            continue;
        }

        auto *osInterface = static_cast<L0::Sysman::LinuxSysmanImp *>(device->deviceGetOsInterface());
        if (!osInterface) {
            PRINT_STRING(NEO::debugManager.flags.PrintDebugMessages.get(), stderr,
                         "%s", "Failed to get OS Interface\n");
            UNRECOVERABLE_IF(true);
        }
        std::string bdf;
        auto pSysfsAccess = &osInterface->getSysfsAccess();
        if (pSysfsAccess->getRealPath("device", bdf) == ZE_RESULT_SUCCESS) {
            // /sys needs to be removed from real path inorder to equate with
            // DEVPATH property of uevent.
            // Example of real path: /sys/devices/pci0000:97/0000:97:02.0/0000:98:00.0/0000:99:01.0/0000:9a:00.0
            // Example of DEVPATH: /devices/pci0000:97/0000:97:02.0/0000:98:00.0/0000:99:01.0/0000:9a:00.0/i915.iaf.0
            const auto loc = bdf.find("/devices");
            if (loc == std::string::npos) {
                PRINT_STRING(NEO::debugManager.flags.PrintDebugMessages.get(), stderr,
                             "%s", "Invalid device path\n");
                continue;
            }

            bdf = bdf.substr(loc);
            mapOfDevIndexToDevPath.emplace(devIndex, bdf);
            pFsAccess = &osInterface->getFsAccess();
        } else {
            PRINT_STRING(NEO::debugManager.flags.PrintDebugMessages.get(), stderr,
                         "%s", "Failed to get real path of device\n");
        }
    }
}

bool LinuxEventsUtil::checkDeviceEvents(std::vector<zes_event_type_flags_t> &registeredEvents, const std::map<uint32_t, std::string> &mapOfDevIndexToDevPath, FsAccessInterface *pFsAccess, zes_event_type_flags_t *pEvents, void *dev, zes_device_handle_t *phDevices) {
    const char *devicePath = pUdevLib->getEventPropertyValue(dev, "DEVPATH");
    bool retVal = false;
    if (devicePath != nullptr) {
        std::string devPath(devicePath);
        for (auto it = mapOfDevIndexToDevPath.begin(); it != mapOfDevIndexToDevPath.end(); it++) {
            if (devPath.find(it->second.c_str()) != std::string::npos) {
                if (registeredEvents[it->first] & ZES_EVENT_TYPE_FLAG_DEVICE_DETACH) {
                    if (checkDeviceDetachEvent(pEvents[it->first])) {
                        retVal = true;
                    }
                }
                if (registeredEvents[it->first] & ZES_EVENT_TYPE_FLAG_DEVICE_ATTACH) {
                    if (checkDeviceAttachEvent(pEvents[it->first])) {
                        retVal = true;
                    }
                }
                if (registeredEvents[it->first] & ZES_EVENT_TYPE_FLAG_SURVIVABILITY_MODE_DETECTED) {
                    if (isSurvivabilityModeAsExpected(pFsAccess, it->second, "Runtime") &&
                        checkDeviceWedgedEvent(dev, pEvents[it->first])) {
                        retVal = true;
                    }
                }
                if (registeredEvents[it->first] & ZES_EVENT_TYPE_FLAG_DEVICE_RESET_REQUIRED) {
                    if (isResetRequired(dev, pEvents[it->first])) {
                        retVal = true;
                    }
                    // Check for wedged event with cold-reset requirement
                    if (checkDeviceWedgedEvent(dev, pEvents[it->first])) {
                        retVal = true;
                        // Cache the wedged state directly
                        auto pSysmanDevice = static_cast<SysmanDeviceImp *>(L0::Sysman::SysmanDevice::fromHandle(phDevices[it->first]));
                        auto *pLinuxSysmanImp = static_cast<LinuxSysmanImp *>(pSysmanDevice->deviceGetOsInterface());
                        pLinuxSysmanImp->isDeviceInWedgedState = true;
                        PRINT_STRING(NEO::debugManager.flags.PrintDebugMessages.get(), stderr,
                                     "Device %d got wedged cold-reset event\n", it->first);
                    }
                }
                if (registeredEvents[it->first] & ZES_EVENT_TYPE_FLAG_MEM_HEALTH) {
                    if (checkIfMemHealthChanged(dev, pEvents[it->first])) {
                        retVal = true;
                    }
                }
                if (registeredEvents[it->first] & ZES_EVENT_TYPE_FLAG_FABRIC_PORT_HEALTH) {
                    if (checkIfFabricPortStatusChanged(dev, pEvents[it->first])) {
                        retVal = true;
                    }
                }
                break;
            }
        }
    }
    return retVal;
}

bool LinuxEventsUtil::listenSystemEvents(zes_event_type_flags_t *pEvents, uint32_t count, std::vector<zes_event_type_flags_t> &registeredEvents, zes_device_handle_t *phDevices, uint64_t timeout) {
    std::call_once(initEventsOnce, [this]() {
        this->init();
    });

    std::call_once(initNetlinkOnce, [this]() {
        this->initNetlink();
    });

    bool retval = false;
    std::vector<std::string> subsystemList;
    std::map<uint32_t, std::string> mapOfDevIndexToDevPath = {};
    FsAccessInterface *pFsAccess = nullptr;

    if (pUdevLib == nullptr) {
        PRINT_STRING(NEO::debugManager.flags.PrintDebugMessages.get(), stderr,
                     "%s", "libudev library instantiation failed\n");
        return retval;
    }

    subsystemList.push_back("drm");
    subsystemList.push_back("auxiliary");

    std::vector<PollDescriptor> pollSources;
    pollSources.push_back({{pUdevLib->registerEventsFromSubsystemAndGetFd(subsystemList), POLLIN, 0}, PollSourceType::udev});

    auto start = L0::Sysman::SteadyClock::now();
    std::chrono::duration<double, std::milli> timeElapsed;
    {
        std::unique_lock<std::mutex> lock(eventsMutex);
        if (NEO::SysCalls::pipe(pipeFd) < 0) {
            PRINT_STRING(NEO::debugManager.flags.PrintDebugMessages.get(), stderr,
                         "%s", "Creation of pipe failed\n");
        }
        pollSources.push_back({{pipeFd[0], POLLIN, 0}, PollSourceType::pipe});

        if (isNetlinkSubscribed(pDrmNl)) {
            pollSources.push_back({{netlinkGetSocketFd(pDrmNl), POLLIN, 0}, PollSourceType::netlink});
        }

        getDevIndexToDevPathMap(registeredEvents, count, phDevices, mapOfDevIndexToDevPath, pFsAccess);
    }

    std::vector<struct pollfd> pfds(pollSources.size());
    auto syncPfds = [&]() {
        for (size_t i = 0; i < pollSources.size(); i++) {
            pfds[i] = pollSources[i].pfd;
        }
    };
    auto syncRevents = [&]() {
        for (size_t i = 0; i < pollSources.size(); i++) {
            pollSources[i].pfd.revents = pfds[i].revents;
        }
    };

    syncPfds();
    while (NEO::SysCalls::poll(pfds.data(), static_cast<nfds_t>(pfds.size()), static_cast<int>(timeout)) > 0) {
        syncRevents();

        bool pipeTriggered = false;
        bool netlinkReady = false;
        bool udevReady = false;

        for (auto &src : pollSources) {
            if (src.pfd.revents & POLLIN) {
                if (src.type == PollSourceType::udev) {
                    udevReady = true;
                } else if (src.type == PollSourceType::pipe) {
                    pipeTriggered = true;
                } else {
                    netlinkReady = true;
                }
            }
        }

        if (pipeTriggered) {
            std::unique_lock<std::mutex> lock(eventsMutex);
            uint8_t dummy;
            ssize_t pipeReadRet = NEO::SysCalls::read(pipeFd[0], &dummy, 1);
            PRINT_STRING(NEO::debugManager.flags.PrintDebugMessages.get() && (pipeReadRet < 0),
                         stderr, "read() on event pipe fd=%d failed errno=%d\n", pipeFd[0], errno);
            mapOfDevIndexToDevPath.clear();
            pFsAccess = nullptr;
            getDevIndexToDevPathMap(registeredEvents, count, phDevices, mapOfDevIndexToDevPath, pFsAccess);
        }

        if (mapOfDevIndexToDevPath.empty()) {
            break;
        }

        if (udevReady) {
            void *dev = nullptr;
            dev = pUdevLib->allocateDeviceToReceiveData();
            if (dev != nullptr) {
                auto eventTypePtr = pUdevLib->getEventType(dev);
                if (eventTypePtr != nullptr) {
                    action = std::string(eventTypePtr);
                    if (checkDeviceEvents(registeredEvents, mapOfDevIndexToDevPath, pFsAccess, pEvents, dev, phDevices)) {
                        retval = true;
                    }
                }
                pUdevLib->dropDeviceReference(dev);
            }
        }

        if (netlinkReady) {
            if (handleNetlinkEvents(pEvents, count, phDevices, registeredEvents)) {
                retval = true;
            }
        }

        if (retval) {
            break;
        }

        timeElapsed = L0::Sysman::SteadyClock::now() - start;
        if (timeout > timeElapsed.count()) {
            timeout = timeout - timeElapsed.count();
            continue;
        } else {
            break;
        }
    }

    {
        std::unique_lock<std::mutex> lock(eventsMutex);
        for (uint8_t i = 0; i < 2; i++) {
            if (pipeFd[i] != -1) {
                NEO::SysCalls::close(pipeFd[i]);
                pipeFd[i] = -1;
            }
        }
    }
    // Note: We do NOT unsubscribe from netlink here. It is unsubscribed in the destructor of LinuxEventsUtil, which is called when Sysman driver is closed.
    // This is to ensure that we can receive netlink events for the entire duration when Sysman driver is open.
    return retval;
}

} // namespace Sysman
} // namespace L0
