Skip to content

Commit ed9f50d

Browse files
Junchao-MellanoxBYGX-wcr
authored andcommitted
[Mellanox] do not use system clock to avoid issues caused by system time change (sonic-net#21446)
- Why I did it Mellanox platform API uses standard python time function time.time() in many places. time.time() gets time from system clock which could be changed by NTP or user. Adjusting system clock will affect the code logical and causes bugs. For example, in platform/mellanox/mlnx-platform-api/sonic_platform/utils.py there is a Timer class, the timer will trigger event with unexpected interval if user/NTP changes the system clock. This PR changes time.time() to time.monotonic to avoid such issue. - How I did it Use time.monotonic() instead of time.time . - How to verify it Manual test. Unit test.
1 parent f414c4c commit ed9f50d

File tree

8 files changed

+30
-27
lines changed

8 files changed

+30
-27
lines changed

platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#
22
# SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES
3-
# Copyright (c) 2019-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3+
# Copyright (c) 2019-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
44
# Apache-2.0
55
#
66
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -447,7 +447,7 @@ def get_change_event_for_module_host_management_mode(self, timeout):
447447
timeout = 1000.0 if timeout >= 1000 else float(timeout)
448448
port_dict = {}
449449
error_dict = {}
450-
begin = time.time()
450+
begin = time.monotonic()
451451
wait_ready_task = sfp.SFP.get_wait_ready_task()
452452

453453
while True:
@@ -524,7 +524,7 @@ def get_change_event_for_module_host_management_mode(self, timeout):
524524
}
525525
else:
526526
if not wait_forever:
527-
elapse = time.time() - begin
527+
elapse = time.monotonic() - begin
528528
if elapse * 1000 >= timeout:
529529
return True, {'sfp': {}}
530530

@@ -569,7 +569,7 @@ def get_change_event_legacy(self, timeout):
569569
timeout = 1000.0 if timeout >= 1000 else float(timeout)
570570
port_dict = {}
571571
error_dict = {}
572-
begin = time.time()
572+
begin = time.monotonic()
573573

574574
while True:
575575
fds_events = self.poll_obj.poll(timeout)
@@ -619,7 +619,7 @@ def get_change_event_legacy(self, timeout):
619619
}
620620
else:
621621
if not wait_forever:
622-
elapse = time.time() - begin
622+
elapse = time.monotonic() - begin
623623
if elapse * 1000 >= timeout:
624624
return True, {'sfp': {}}
625625

platform/mellanox/mlnx-platform-api/sonic_platform/dpuctlplat.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#
22
# SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES
3-
# Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3+
# Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
44
# Apache-2.0
55
#
66
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -206,8 +206,8 @@ def wait_for_pci(self):
206206
return True
207207
poll_obj = poll()
208208
poll_obj.register(dir_fd, POLLIN)
209-
start = time.time()
210-
while (time.time() - start) < WAIT_FOR_PCI_DEV:
209+
start = time.monotonic()
210+
while (time.monotonic() - start) < WAIT_FOR_PCI_DEV:
211211
events = poll_obj.poll(WAIT_FOR_PCI_DEV * 1000)
212212
if events:
213213
if os.path.exists(os.path.dirname(self.get_pci_dev_path())):
@@ -473,9 +473,9 @@ def boot_prog_context(self):
473473
@contextmanager
474474
def time_check_context(self, msg):
475475
if self.verbosity:
476-
start_time = time.time()
476+
start_time = time.monotonic()
477477
yield
478-
end_time = time.time()
478+
end_time = time.monotonic()
479479
self.log_info(f"Total time taken = {end_time - start_time} for {msg}")
480480
return
481481
yield

platform/mellanox/mlnx-platform-api/sonic_platform/sfp.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#
22
# SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES
3-
# Copyright (c) 2019-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3+
# Copyright (c) 2019-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
44
# Apache-2.0
55
#
66
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -1475,14 +1475,14 @@ def initialize_sfp_modules(cls, sfp_list):
14751475
# Resetting SFP requires a reloading of module firmware, it takes up to 3 seconds
14761476
# according to standard
14771477
max_wait_time = 3.5
1478-
begin = time.time()
1478+
begin = time.monotonic()
14791479
while True:
14801480
ready_sfp_set = wait_ready_task.get_ready_set()
14811481
for sfp_index in ready_sfp_set:
14821482
s = sfp_list[sfp_index]
14831483
logger.log_debug(f'SFP {sfp_index} is recovered from resetting state')
14841484
s.on_event(EVENT_RESET_DONE)
1485-
elapse = time.time() - begin
1485+
elapse = time.monotonic() - begin
14861486
if elapse < max_wait_time:
14871487
time.sleep(0.5)
14881488
else:

platform/mellanox/mlnx-platform-api/sonic_platform/utils.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#
22
# SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES
3-
# Copyright (c) 2020-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3+
# Copyright (c) 2020-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
44
# SPDX-License-Identifier: Apache-2.0
55
#
66
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -340,7 +340,7 @@ def schedule(self, interval, cb, repeat=True, run_now=True):
340340
self.add_timer_event(timer_event, run_now)
341341

342342
def add_timer_event(self, timer_event, run_now=True):
343-
timestamp = time.time()
343+
timestamp = time.monotonic()
344344
if not run_now:
345345
timestamp += timer_event.interval
346346

@@ -356,7 +356,7 @@ def stop(self):
356356

357357
def run(self):
358358
while not self._stop_event.is_set():
359-
now = time.time()
359+
now = time.monotonic()
360360
item = self._timestamp_queue.get()
361361
self._min_timestamp = item[0]
362362
if self._min_timestamp > now:

platform/mellanox/mlnx-platform-api/sonic_platform/wait_sfp_ready_task.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#
2-
# Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES.
2+
# SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES
3+
# Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
34
# Apache-2.0
45
#
56
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -65,7 +66,7 @@ def schedule_wait(self, sfp_index):
6566
is_empty = len(self._wait_dict) == 0
6667

6768
# The item will be expired in 3 seconds
68-
self._wait_dict[sfp_index] = time.time() + self.WAIT_TIME
69+
self._wait_dict[sfp_index] = time.monotonic() + self.WAIT_TIME
6970

7071
if is_empty:
7172
logger.log_debug('An item arrives, wake up WaitSfpReadyTask')
@@ -120,7 +121,7 @@ def run(self):
120121
self.event.wait()
121122
self.event.clear()
122123

123-
now = time.time()
124+
now = time.monotonic()
124125
with self.lock:
125126
logger.log_debug(f'Processing wait SFP dict: {self._wait_dict}, now={now}')
126127
for sfp_index, expire_time in self._wait_dict.items():

platform/mellanox/mlnx-platform-api/sonic_platform/watchdog.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#
2-
# Copyright (c) 2019-2024 NVIDIA CORPORATION & AFFILIATES.
2+
# SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES
3+
# Copyright (c) 2019-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
34
# Apache-2.0
45
#
56
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -229,7 +230,7 @@ def arm(self, seconds):
229230
# Save the watchdog arm timestamp
230231
# requiered for get_remaining_time()
231232
os.makedirs('/tmp/nvidia', exist_ok=True)
232-
utils.write_file(self.TIMESTAMP_FILE, str(time.time()))
233+
utils.write_file(self.TIMESTAMP_FILE, str(time.monotonic()))
233234

234235
return ret
235236

@@ -244,7 +245,7 @@ def get_remaining_time(self):
244245

245246
if self.is_armed():
246247
arm_timestamp = utils.read_float_from_file(self.TIMESTAMP_FILE)
247-
timeleft = int(self.timeout - (time.time() - arm_timestamp))
248+
timeleft = int(self.timeout - (time.monotonic() - arm_timestamp))
248249

249250
return timeleft
250251

platform/mellanox/mlnx-platform-api/tests/test_change_event.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#
2-
# Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES.
2+
# SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES
3+
# Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
34
# Apache-2.0
45
#
56
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -34,7 +35,7 @@
3435
class TestChangeEvent:
3536
@mock.patch('sonic_platform.sfp.SFP.get_fd_for_polling_legacy')
3637
@mock.patch('select.poll')
37-
@mock.patch('time.time')
38+
@mock.patch('time.monotonic')
3839
@mock.patch('sonic_platform.device_data.DeviceDataManager.is_module_host_management_mode', mock.MagicMock(return_value=False))
3940
@mock.patch('sonic_platform.device_data.DeviceDataManager.get_sfp_count', mock.MagicMock(return_value=1))
4041
@mock.patch('sonic_platform.chassis.extract_RJ45_ports_index', mock.MagicMock(return_value=[]))
@@ -87,7 +88,7 @@ def test_get_change_event_legacy(self, mock_status, mock_time, mock_create_poll,
8788
@mock.patch('sonic_platform.wait_sfp_ready_task.WaitSfpReadyTask.get_ready_set')
8889
@mock.patch('sonic_platform.sfp.SFP.get_fd')
8990
@mock.patch('select.poll')
90-
@mock.patch('time.time')
91+
@mock.patch('time.monotonic')
9192
@mock.patch('sonic_platform.device_data.DeviceDataManager.is_module_host_management_mode', mock.MagicMock(return_value=True))
9293
@mock.patch('sonic_platform.device_data.DeviceDataManager.get_sfp_count', mock.MagicMock(return_value=1))
9394
@mock.patch('sonic_platform.chassis.extract_RJ45_ports_index', mock.MagicMock(return_value=[]))

platform/mellanox/mlnx-platform-api/tests/test_dpuctlplat.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#
22
# SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES
3-
# Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3+
# Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
44
# Apache-2.0
55
#
66
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -461,7 +461,7 @@ def mock_time_diff():
461461
mock_time_diff.counter += 1
462462
return mock_time_diff.counter * timeout_val
463463
mock_time_diff.counter = 0
464-
with patch("time.time", wraps=mock_time_diff):
464+
with patch("time.monotonic", wraps=mock_time_diff):
465465
# PCI Device is not recognized
466466
assert not dpuctl_obj.wait_for_pci()
467467
pci_parent_path = os.path.dirname(pci_dev_path)

0 commit comments

Comments
 (0)