Line data Source code
1 : /**
2 : * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3 : * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4 : * CANN Open Software License Agreement Version 2.0 (the "License").
5 : * Please refer to the License for details. You may not use this file except in compliance with the License.
6 : * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7 : * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8 : * See LICENSE in the root of the software repository for the full text of the License.
9 : */
10 : #include <securec.h>
11 : #include "adapter_rts_common.h"
12 : #include "peterson_lock.h"
13 :
14 : namespace hccl {
15 525 : PetersonLock::PetersonLock(u64 timeoutSec)
16 525 : : size_(MIN_SHM_LEN),
17 525 : type_(Type::HOST),
18 1050 : typeName_("Host"),
19 525 : timeout_(timeoutSec),
20 525 : myTurn_(TURN_FOR_HOST)
21 525 : {}
22 :
23 13 : PetersonLock::PetersonLock(void* devPtr, u64 timeoutSec)
24 13 : : size_(MIN_SHM_LEN),
25 13 : type_(Type::DEVICE),
26 26 : typeName_("Device"),
27 13 : timeout_(timeoutSec),
28 13 : devMem_(devPtr, size_, false),
29 13 : myTurn_(TURN_FOR_DEVICE)
30 13 : {}
31 :
32 536 : PetersonLock::~PetersonLock() { DeInit(); }
33 :
34 538 : HcclResult PetersonLock::Init()
35 : {
36 538 : if (type_ == Type::HOST) {
37 525 : if (AllocDeviceMem() != HCCL_SUCCESS) {
38 0 : return HCCL_E_INTERNAL;
39 : }
40 : }
41 :
42 538 : auto buffer = reinterpret_cast<u8*>(devMem_.ptr());
43 538 : size_t offset = 0;
44 538 : turn_ = reinterpret_cast<volatile u32*>(buffer + offset);
45 538 : offset += sizeof(u32);
46 :
47 538 : hostFlag_ = reinterpret_cast<volatile u32*>(buffer + offset);
48 538 : offset += sizeof(u32);
49 :
50 538 : deviceFlag_ = reinterpret_cast<volatile u32*>(buffer + offset);
51 538 : offset += sizeof(u32);
52 :
53 538 : HCCL_INFO(
54 : "[PetersonLock][Init] type [%s] init success, memSize [%lu Byte] timeout[%lu s]", typeName_.c_str(),
55 : devMem_.size(), timeout_);
56 538 : return HCCL_SUCCESS;
57 : }
58 :
59 525 : HcclResult PetersonLock::AllocDeviceMem()
60 : {
61 525 : if (devMem_.ptr() != nullptr || devMem_.size() != 0) {
62 0 : HCCL_ERROR("[PetersonLock][AllocDeviceMem] init failed, maybe it's already inited");
63 0 : return HCCL_E_INTERNAL;
64 : }
65 :
66 525 : CHK_RET(DeviceMem::alloc(devMem_, size_));
67 :
68 525 : if (hrtMemSet(devMem_.ptr(), devMem_.size(), devMem_.size()) != HCCL_SUCCESS) {
69 0 : HCCL_ERROR("[PetersonLock][AllocDeviceMem] memset device memory failed");
70 0 : return HCCL_E_INTERNAL;
71 : }
72 :
73 525 : HCCL_INFO("[PetersonLock][AllocDeviceMem] Type[%s] alloc memSize[%lu Byte]", typeName_.c_str(), size_);
74 525 : return HCCL_SUCCESS;
75 : }
76 :
77 535 : HcclResult PetersonLock::DeInit()
78 : {
79 535 : size_ = 0;
80 :
81 535 : turn_ = nullptr;
82 535 : hostFlag_ = nullptr;
83 535 : deviceFlag_ = nullptr;
84 535 : return HCCL_SUCCESS;
85 : }
86 :
87 2 : u64 PetersonLock::GetDevMemAddr() const { return reinterpret_cast<u64>(devMem_.ptr()); }
88 :
89 400 : HcclResult PetersonLock::Lock()
90 : {
91 400 : if (turn_ == nullptr || hostFlag_ == nullptr || deviceFlag_ == nullptr) {
92 0 : HCCL_ERROR("[PetersonLock][lock] ptr is nullptr, maybe not call Init()");
93 0 : return HCCL_E_INTERNAL;
94 : }
95 :
96 400 : HCCL_DEBUG("[PetersonLock][Lock] type [%s] before require the lock", typeName_.c_str());
97 400 : auto startTime = std::chrono::steady_clock::now();
98 400 : auto timeout = std::chrono::seconds(timeout_);
99 :
100 : /* 更新flag表明自己准备获取锁 */
101 400 : CHK_RET(WriteSelfFlag(FLAG_LOCK));
102 :
103 : /* 更新TURN值 */
104 400 : CHK_RET(WriteTurn());
105 :
106 : /*
107 : * 这里判断为真只有一种场景会进入等待,需满足以下两个条件
108 : * - 条件1:peer也想要获取锁
109 : * - 条件2:self更新的turn等于自己设置的值
110 : * 通常条件2意味着peer执行的更快,使得自己是覆盖写turn,那么自己就需要等待
111 : *
112 : * (PS:当然这里存在并发场景,即self刚写完turn就又读turn,peer也是刚写完turn就又读turn,
113 : * 此时两端都满足上述为真,就都进入等待,但是因为turn最终只会有一个值,
114 : * 所以再循环一遍后最后turn的值就只能是一个了,此时谁最后更新谁就去等待)
115 : *
116 : * Q:为什么读取peer为FLAG_UNLOCK时本端一定可以安全获取锁?
117 : * A:当自己读到peer是释放锁状态时,peer侧有三种场景:
118 : * - 场景1:peer不准备获取锁,此时self可以安全获取锁
119 : * - 场景2:peer已经释放了锁,此时self可以安全获取锁
120 : * - 场景3:peer侧也想获取锁,但是由于并发(self读flag peer写flag)导致peer更新的flag未被self读到
121 : * 对于场景3,由于严格内存序那么写self与读peer操作一定是串行,此时self虽未及时读到peer的flag,
122 : * 但是因为自己的flag已经被更新,不会与peer的读有并发,因此一定能被peer读到,那么peer就会获取到
123 : * self要获取锁,就会进入前面描述的状态,即等待self释放锁
124 : * 时序图描述大致如下:
125 : * self peer
126 : * write flag (LOCK)
127 : * write turn (HOST)
128 : * read peer flag (UNLOCK) write flag (LOCK) # 这里并发导致未及时读到最新值
129 : * 成功获取锁 write turn (DEVICE) # 这里自己一定是最后更新的turn
130 : * read peer flag (LOCK) # 读到的一定是获取锁,因为没有并发问题
131 : * peer==LOCK && turn==DEVICE 为真,所以等待
132 : */
133 : u32 peerFlag;
134 : u32 turn;
135 : while (true) {
136 1649 : if ((std::chrono::steady_clock::now() - startTime) > timeout) {
137 0 : HCCL_ERROR("[PetersonLock][Lock] type [%s] get lock timeout [%lu s]", typeName_.c_str(), timeout_);
138 :
139 : /* 重置flag */
140 0 : CHK_RET(WriteSelfFlag(FLAG_UNLOCK));
141 0 : return HCCL_E_TIMEOUT;
142 : }
143 :
144 1643 : CHK_RET(ReadPeerFlag(peerFlag));
145 1648 : CHK_RET(ReadTurn(turn));
146 :
147 1647 : if (peerFlag == FLAG_LOCK && turn == myTurn_) {
148 1249 : Wait();
149 : } else {
150 398 : HCCL_DEBUG("[PetersonLock][Lock] type [%s] got the lock", typeName_.c_str());
151 400 : break;
152 : }
153 1249 : }
154 :
155 400 : HCCL_DEBUG("[PetersonLock][Lock] type [%s] after require the lock", typeName_.c_str());
156 400 : return HCCL_SUCCESS;
157 : }
158 :
159 400 : HcclResult PetersonLock::Unlock()
160 : {
161 400 : if (deviceFlag_ == nullptr) {
162 0 : HCCL_ERROR("[PetersonLock][Unlock] ptr is nullptr, maybe not call Init()");
163 0 : return HCCL_E_INTERNAL;
164 : }
165 :
166 : /* 释放锁 */
167 400 : WriteSelfFlag(FLAG_UNLOCK);
168 :
169 400 : HCCL_DEBUG("[PetersonLock][Unlock] type [%s] release the lock", typeName_.c_str());
170 400 : return HCCL_SUCCESS;
171 : }
172 :
173 800 : HcclResult PetersonLock::WriteSelfFlag(u32 selfFlag)
174 : {
175 800 : if (type_ == Type::DEVICE) {
176 400 : *deviceFlag_ = selfFlag;
177 : } else {
178 400 : u32 hostFlag = selfFlag;
179 800 : if (hrtMemSyncCopy(
180 400 : const_cast<u32*>(hostFlag_), sizeof(u32), &hostFlag, sizeof(u32),
181 : HcclRtMemcpyKind::HCCL_RT_MEMCPY_KIND_HOST_TO_DEVICE)
182 400 : != HCCL_SUCCESS) {
183 0 : HCCL_INFO("[PetersonLock][WriteSelfFlag] H2D write hostFlag not correct");
184 0 : return HCCL_E_INTERNAL;
185 : }
186 : }
187 :
188 800 : MemFence();
189 800 : return HCCL_SUCCESS;
190 : }
191 :
192 400 : HcclResult PetersonLock::WriteTurn()
193 : {
194 400 : if (type_ == Type::DEVICE) {
195 200 : *turn_ = TURN_FOR_DEVICE;
196 : } else {
197 200 : u32 turn = TURN_FOR_HOST;
198 400 : if (hrtMemSyncCopy(
199 200 : const_cast<u32*>(turn_), sizeof(u32), &turn, sizeof(u32),
200 : HcclRtMemcpyKind::HCCL_RT_MEMCPY_KIND_HOST_TO_DEVICE)
201 200 : != HCCL_SUCCESS) {
202 0 : HCCL_INFO("[PetersonLock][WriteSelfFlag] H2D write turn not correct");
203 0 : return HCCL_E_INTERNAL;
204 : }
205 : }
206 :
207 400 : MemFence();
208 400 : return HCCL_SUCCESS;
209 : }
210 :
211 1643 : HcclResult PetersonLock::ReadPeerFlag(u32& peerFlag)
212 : {
213 1643 : if (type_ == Type::DEVICE) {
214 1449 : peerFlag = *hostFlag_;
215 : } else {
216 394 : if (hrtMemSyncCopy(
217 194 : &peerFlag, sizeof(u32), const_cast<u32*>(deviceFlag_), sizeof(u32),
218 : HcclRtMemcpyKind::HCCL_RT_MEMCPY_KIND_DEVICE_TO_HOST)
219 200 : != HCCL_SUCCESS) {
220 1 : HCCL_INFO("[PetersonLock][ReadPeerFlag] D2H read device flag not correct");
221 0 : return HCCL_E_INTERNAL;
222 : }
223 : }
224 1648 : return HCCL_SUCCESS;
225 : }
226 :
227 1648 : HcclResult PetersonLock::ReadTurn(u32& turn)
228 : {
229 1648 : if (type_ == Type::DEVICE) {
230 1449 : turn = *turn_;
231 : } else {
232 399 : if (hrtMemSyncCopy(
233 199 : &turn, sizeof(u32), const_cast<u32*>(turn_), sizeof(u32),
234 : HcclRtMemcpyKind::HCCL_RT_MEMCPY_KIND_DEVICE_TO_HOST)
235 200 : != HCCL_SUCCESS) {
236 2 : HCCL_INFO("[PetersonLock][ReadPeerFlag] D2H read turn not correct");
237 0 : return HCCL_E_INTERNAL;
238 : }
239 : }
240 1647 : return HCCL_SUCCESS;
241 : }
242 :
243 200 : PetersonLockGuard::PetersonLockGuard(PetersonLock* lock) : lock_(lock), lockFailed_(false)
244 : {
245 200 : if (lock_ == nullptr) {
246 0 : HCCL_ERROR("[PetersonLockGuard] invalid lock");
247 0 : lockFailed_ = true;
248 0 : return;
249 : }
250 :
251 200 : if (lock_->Lock() != HCCL_SUCCESS) {
252 0 : HCCL_ERROR("[PetersonLockGuard] lock failed");
253 0 : lockFailed_ = true;
254 0 : lock_ = nullptr;
255 : }
256 : }
257 :
258 200 : PetersonLockGuard::~PetersonLockGuard()
259 : {
260 200 : if (lock_) {
261 200 : lock_->Unlock();
262 : }
263 200 : }
264 : } // namespace hccl
|