Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[mellanox] Implement Watchdog API based on the new platform API #2607

Merged
merged 1 commit into from
Feb 28, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,23 @@
try:
from sonic_platform_base.chassis_base import ChassisBase
from sonic_platform_api.psu import Psu
from sonic_platform_api.watchdog import get_watchdog
except ImportError as e:
raise ImportError (str(e) + "- required module not found")

MLNX_NUM_PSU = 2

class Chassis(ChassisBase):
"""Platform-specific Chassis class"""

def __init__(self):
ChassisBase.__init__(self)

# Initialize PSU list
for index in range(MLNX_NUM_PSU):
psu = Psu(index)
self._psu_list.append(psu)

# Initialize watchdog
self._watchdog = get_watchdog()

288 changes: 288 additions & 0 deletions platform/mellanox/mlnx-platform-api/sonic_platform_api/watchdog.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,288 @@
#!/usr/bin/env python

"""
Mellanox

Module contains an implementation of SONiC Platform Base API and
provides access to hardware watchdog on Mellanox platforms
"""

import os
import fcntl
import array
import time

from sonic_platform_base.watchdog_base import WatchdogBase

""" ioctl constants """
IO_WRITE = 0x40000000
IO_READ = 0x80000000
IO_READ_WRITE = 0xC0000000
IO_SIZE_INT = 0x00040000
IO_SIZE_40 = 0x00280000
IO_TYPE_WATCHDOG = ord('W') << 8

WDR_INT = IO_READ | IO_SIZE_INT | IO_TYPE_WATCHDOG
WDR_40 = IO_READ | IO_SIZE_40 | IO_TYPE_WATCHDOG
WDWR_INT = IO_READ_WRITE | IO_SIZE_INT | IO_TYPE_WATCHDOG

""" Watchdog ioctl commands """
WDIOC_GETSUPPORT = 0 | WDR_40
WDIOC_GETSTATUS = 1 | WDR_INT
WDIOC_GETBOOTSTATUS = 2 | WDR_INT
WDIOC_GETTEMP = 3 | WDR_INT
WDIOC_SETOPTIONS = 4 | WDR_INT
WDIOC_KEEPALIVE = 5 | WDR_INT
WDIOC_SETTIMEOUT = 6 | WDWR_INT
WDIOC_GETTIMEOUT = 7 | WDR_INT
WDIOC_SETPRETIMEOUT = 8 | WDWR_INT
WDIOC_GETPRETIMEOUT = 9 | WDR_INT
WDIOC_GETTIMELEFT = 10 | WDR_INT

""" Watchdog status constants """
WDIOS_DISABLECARD = 0x0001
WDIOS_ENABLECARD = 0x0002

""" Mellanox main watchdog identity string """
WD_MLNX_MAIN_IDENTITY = "mlx-wdt-main"
""" watchdog sysfs """
WD_SYSFS_PATH = "/sys/class/watchdog/"


WD_COMMON_ERROR = -1


class WatchdogImplBase(WatchdogBase):
"""
Base class that implements common logic for interacting
with watchdog using ioctl commands
"""

def __init__(self, wd_device_path):
"""
Open a watchdog handle
@param wd_device_path Path to watchdog device
"""

self.watchdog_path = wd_device_path
self.watchdog = os.open(self.watchdog_path, os.O_WRONLY)

# Opening a watchdog descriptor starts
# watchdog timer;
# by default it should be stopped
self._disablecard()
self.armed = False

self.timeout = self._gettimeout()

def _enablecard(self):
"""
Turn on the watchdog timer
"""

req = array.array('h', [WDIOS_ENABLECARD])
fcntl.ioctl(self.watchdog, WDIOC_SETOPTIONS, req, False)

def _disablecard(self):
"""
Turn off the watchdog timer
"""

req = array.array('h', [WDIOS_DISABLECARD])
fcntl.ioctl(self.watchdog, WDIOC_SETOPTIONS, req, False)

def _keepalive(self):
"""
Keep alive watchdog timer
"""

fcntl.ioctl(self.watchdog, WDIOC_KEEPALIVE)

def _settimeout(self, seconds):
"""
Set watchdog timer timeout
@param seconds - timeout in seconds
@return is the actual set timeout
"""

req = array.array('I', [seconds])
fcntl.ioctl(self.watchdog, WDIOC_SETTIMEOUT, req, True)

return int(req[0])

def _gettimeout(self):
"""
Get watchdog timeout
@return watchdog timeout
"""

req = array.array('I', [0])
fcntl.ioctl(self.watchdog, WDIOC_GETTIMEOUT, req, True)

return int(req[0])

def _gettimeleft(self):
"""
Get time left before watchdog timer expires
@return time left in seconds
"""

req = array.array('I', [0])
fcntl.ioctl(self.watchdog, WDIOC_GETTIMELEFT, req, True)

return int(req[0])

def arm(self, seconds):
"""
Implements arm WatchdogBase API
"""

ret = WD_COMMON_ERROR
if seconds < 0:
return ret

try:
if self.timeout != seconds:
self.timeout = self._settimeout(seconds)
if self.armed:
self._keepalive()
else:
self._enablecard()
self.armed = True
ret = self.timeout
except IOError:
pass

return ret

def disarm(self):
"""
Implements disarm WatchdogBase API
"""

disarmed = False
if self.armed:
try:
self._disablecard()
self.armed = False
disarmed = True
except IOError:
pass

return disarmed

def is_armed(self):
"""
Implements is_armed WatchdogBase API
"""

return self.armed

def get_remaining_time(self):
"""
Implements get_remaining_time WatchdogBase API
"""

timeleft = WD_COMMON_ERROR

if self.armed:
try:
timeleft = self._gettimeleft()
except IOError:
pass

return timeleft

def __del__(self):
"""
Close watchdog
"""

os.close(self.watchdog)


class WatchdogType1(WatchdogImplBase):
"""
Watchdog type 1
"""

def arm(self, seconds):
"""
Call arm from WatchdgoImplBase and save the timestamp
when the watchdog was armed
"""

ret = WatchdogImplBase.arm(self, seconds)
# Save the watchdog arm timestamp
# requiered for get_remaining_time()
self.arm_timestamp = time.time()

return ret

def get_remaining_time(self):
"""
Watchdog Type 1 does not support timeleft
operation, we will calculate timeleft based
on timeout and arm timestamp
"""

timeleft = WD_COMMON_ERROR

if self.armed:
timeleft = int(self.timeout - (time.time() - self.arm_timestamp))

return timeleft

class WatchdogType2(WatchdogImplBase):
"""
Watchdog type 2
"""

pass


def is_mlnx_wd_main(dev):
"""
Checks if dev is Mellanox main watchdog
"""

try:
with open("{}/{}/identity".format(WD_SYSFS_PATH, dev)) as identity_file:
identity = identity_file.read().strip()
if identity == WD_MLNX_MAIN_IDENTITY:
return True
except IOError:
pass

return False


def is_wd_type2(dev):
"""
Checks if dev is Mellanox type 2 watchdog
"""

return os.path.exists("{}/{}/timeleft".format(WD_SYSFS_PATH, dev))


def get_watchdog():
"""
Return WatchdogType1 or WatchdogType2 based on system
"""

watchdog_main_device_name = None

for device in os.listdir("/dev/"):
if device.startswith("watchdog") and is_mlnx_wd_main(device):
watchdog_main_device_name = device

watchdog_device_path = "/dev/{}".format(watchdog_main_device_name)

watchdog = None

if is_wd_type2(watchdog_main_device_name):
watchdog = WatchdogType2(watchdog_device_path)
else:
watchdog = WatchdogType1(watchdog_device_path)

return watchdog