Skip to content

Commit ae7cff3

Browse files
ifduCompute-Runtime-Automation
authored andcommitted
fix: waitUserFence on bind and unbind in Xe
This fixes illegal memory accesses by the job submitted to the GuC. Also some unit tests are added to harness the vmBind operation. Related-To: NEO-7996 Signed-off-by: Francois Dugast <[email protected]> Signed-off-by: Mateusz Jablonski <[email protected]>
1 parent 87905f2 commit ae7cff3

File tree

3 files changed

+179
-20
lines changed

3 files changed

+179
-20
lines changed

shared/source/os_interface/linux/xe/ioctl_helper_xe.cpp

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,9 @@
3737
#define XE_USERPTR_FAKE_MASK 0x7FFFFF
3838

3939
#define USER_FENCE_VALUE 0xc0ffee0000000000ull
40-
#define XE_ONE_SEC 1000
4140

4241
namespace NEO {
4342

44-
#define VMBIND_FENCE_TAG 0x123987
45-
struct XeFakeExtUserFence {
46-
uint32_t tag;
47-
uint64_t addr;
48-
uint64_t value;
49-
};
50-
5143
static_assert(DRM_XE_ENGINE_CLASS_RENDER == I915_ENGINE_CLASS_RENDER);
5244
static_assert(DRM_XE_ENGINE_CLASS_COPY == I915_ENGINE_CLASS_COPY);
5345
static_assert(DRM_XE_ENGINE_CLASS_VIDEO_DECODE == I915_ENGINE_CLASS_VIDEO);
@@ -633,9 +625,9 @@ void IoctlHelperXe::fillVmBindExtSetPat(VmBindExtSetPatT &vmBindExtSetPat, uint6
633625

634626
void IoctlHelperXe::fillVmBindExtUserFence(VmBindExtUserFenceT &vmBindExtUserFence, uint64_t fenceAddress, uint64_t fenceValue, uint64_t nextExtension) {
635627
xeLog(" -> IoctlHelperXe::%s 0x%lx 0x%lx\n", __FUNCTION__, fenceAddress, fenceValue);
636-
auto xeBindExtUserFence = reinterpret_cast<XeFakeExtUserFence *>(vmBindExtUserFence);
628+
auto xeBindExtUserFence = reinterpret_cast<UserFenceExtension *>(vmBindExtUserFence);
637629
UNRECOVERABLE_IF(!xeBindExtUserFence);
638-
xeBindExtUserFence->tag = VMBIND_FENCE_TAG;
630+
xeBindExtUserFence->tag = UserFenceExtension::tagValue;
639631
xeBindExtUserFence->addr = fenceAddress;
640632
xeBindExtUserFence->value = fenceValue;
641633
}
@@ -1194,9 +1186,9 @@ int IoctlHelperXe::xeVmBind(const VmBindParams &vmBindParams, bool bindOp) {
11941186
struct drm_xe_sync sync[1] = {};
11951187
sync[0].flags = DRM_XE_SYNC_USER_FENCE | DRM_XE_SYNC_SIGNAL;
11961188
extraBindFlag = XE_VM_BIND_FLAG_ASYNC;
1197-
auto xeBindExtUserFence = reinterpret_cast<XeFakeExtUserFence *>(vmBindParams.extensions);
1189+
auto xeBindExtUserFence = reinterpret_cast<UserFenceExtension *>(vmBindParams.extensions);
11981190
UNRECOVERABLE_IF(!xeBindExtUserFence);
1199-
UNRECOVERABLE_IF(xeBindExtUserFence->tag != VMBIND_FENCE_TAG);
1191+
UNRECOVERABLE_IF(xeBindExtUserFence->tag != UserFenceExtension::tagValue);
12001192
sync[0].addr = xeBindExtUserFence->addr;
12011193
sync[0].timeline_value = xeBindExtUserFence->value;
12021194

@@ -1234,14 +1226,14 @@ int IoctlHelperXe::xeVmBind(const VmBindParams &vmBindParams, bool bindOp) {
12341226
bind.bind.op,
12351227
xeGetBindOpName(bind.bind.op),
12361228
bind.num_syncs);
1237-
12381229
ret = IoctlHelper::ioctl(DrmIoctl::GemVmBind, &bind);
1239-
1240-
if (!bindOp) {
1241-
return xeWaitUserFence(DRM_XE_UFENCE_WAIT_U64, DRM_XE_UFENCE_WAIT_EQ,
1242-
sync[0].addr,
1243-
sync[0].timeline_value, NULL, XE_ONE_SEC);
1230+
if (ret != 0) {
1231+
return ret;
12441232
}
1233+
1234+
return xeWaitUserFence(DRM_XE_UFENCE_WAIT_U64, DRM_XE_UFENCE_WAIT_EQ,
1235+
sync[0].addr,
1236+
sync[0].timeline_value, NULL, XE_ONE_SEC);
12451237
}
12461238

12471239
xeLog(" -> IoctlHelperXe::%s %s found=%d vmid=0x%x h=0x%x s=0x%llx o=0x%llx l=0x%llx f=0x%llx r=%d\n",

shared/source/os_interface/linux/xe/ioctl_helper_xe.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ struct drm_xe_engine_class_instance;
2323

2424
#define PRELIM_I915_UFENCE_WAIT_SOFT (1 << 15)
2525

26+
#define XE_ONE_SEC 1000
27+
2628
namespace NEO {
2729

2830
struct BindInfo {
@@ -102,8 +104,6 @@ class IoctlHelperXe : public IoctlHelper {
102104
private:
103105
template <typename... XeLogArgs>
104106
void xeLog(XeLogArgs &&...args) const;
105-
int xeWaitUserFence(uint64_t mask, uint16_t op, uint64_t addr, uint64_t value, struct drm_xe_engine_class_instance *eci, int64_t timeout);
106-
int xeVmBind(const VmBindParams &vmBindParams, bool bindOp);
107107
uint32_t xeSyncObjCreate(uint32_t flags);
108108
bool xeSyncObjWait(uint32_t *handles, uint32_t count, uint64_t absTimeoutNsec, uint32_t flags, uint32_t *firstSignaled);
109109
void xeSyncObjDestroy(uint32_t handle);
@@ -117,6 +117,15 @@ class IoctlHelperXe : public IoctlHelper {
117117
const char *xeGetBindOpName(int bindOp);
118118
const char *xeGetengineClassName(uint32_t engineClass);
119119
std::vector<uint8_t> queryData(uint32_t queryId);
120+
int xeWaitUserFence(uint64_t mask, uint16_t op, uint64_t addr, uint64_t value, struct drm_xe_engine_class_instance *eci, int64_t timeout);
121+
int xeVmBind(const VmBindParams &vmBindParams, bool bindOp);
122+
123+
struct UserFenceExtension {
124+
static constexpr uint32_t tagValue = 0x123987;
125+
uint32_t tag;
126+
uint64_t addr;
127+
uint64_t value;
128+
};
120129

121130
protected:
122131
int chipsetId = 0;

shared/test/unit_test/os_interface/linux/xe/ioctl_helper_xe_tests.cpp

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ using NEO::PrelimI915::drm_syncobj_destroy;
2525
using NEO::PrelimI915::drm_syncobj_wait;
2626

2727
struct MockIoctlHelperXe : IoctlHelperXe {
28+
using IoctlHelperXe::bindInfo;
2829
using IoctlHelperXe::IoctlHelperXe;
2930
using IoctlHelperXe::xeDecanonize;
3031
using IoctlHelperXe::xeGetBindOpName;
@@ -593,15 +594,34 @@ class DrmMockXe : public DrmMockCustom {
593594
};
594595
ret = 0;
595596
} break;
597+
case DrmIoctl::GemVmBind: {
598+
ret = gemVmBindReturn;
599+
auto vmBindInput = static_cast<drm_xe_vm_bind *>(arg);
600+
vmBindInputs.push_back(*vmBindInput);
601+
602+
EXPECT_EQ(1u, vmBindInput->num_syncs);
603+
604+
auto &syncInput = reinterpret_cast<drm_xe_sync *>(vmBindInput->syncs)[0];
605+
syncInputs.push_back(syncInput);
606+
} break;
607+
608+
case DrmIoctl::GemWaitUserFence: {
609+
ret = waitUserFenceReturn;
610+
auto waitUserFenceInput = static_cast<drm_xe_wait_user_fence *>(arg);
611+
waitUserFenceInputs.push_back(*waitUserFenceInput);
612+
} break;
613+
596614
case DrmIoctl::GemContextSetparam:
597615
case DrmIoctl::GemContextGetparam:
616+
598617
default:
599618
break;
600619
}
601620
return ret;
602621
}
603622
int forceIoctlAnswer = 0;
604623
int setIoctlAnswer = 0;
624+
int gemVmBindReturn = 0;
605625
const drm_xe_engine_class_instance queryEngines[9] = {
606626
{DRM_XE_ENGINE_CLASS_RENDER, 0, 0},
607627
{DRM_XE_ENGINE_CLASS_COPY, 1, 0},
@@ -615,6 +635,11 @@ class DrmMockXe : public DrmMockCustom {
615635

616636
uint64_t queryMemUsage[37]{}; // 1 qword for num regions and 12 qwords per region
617637
uint64_t queryGts[27]{}; // 1 qword for num gts and 13 qwords per gt
638+
639+
StackVec<drm_xe_wait_user_fence, 1> waitUserFenceInputs;
640+
StackVec<drm_xe_vm_bind, 1> vmBindInputs;
641+
StackVec<drm_xe_sync, 1> syncInputs;
642+
int waitUserFenceReturn = 0;
618643
};
619644

620645
TEST(IoctlHelperXeTest, whenCallingIoctlThenProperValueIsReturned) {
@@ -977,3 +1002,136 @@ TEST(IoctlHelperXeTest, givenDisabledFtrMultiTileArchWhenCreatingEngineInfoThenM
9771002
EXPECT_EQ(0u, hwInfo->gtSystemInfo.MultiTileArchInfo.TileMask);
9781003
}
9791004
}
1005+
1006+
TEST(IoctlHelperXeTest, whenCallingVmBindThenWaitUserFenceIsCalled) {
1007+
DebugManagerStateRestore restorer;
1008+
auto executionEnvironment = std::make_unique<MockExecutionEnvironment>();
1009+
DrmMockXe drm{*executionEnvironment->rootDeviceEnvironments[0]};
1010+
auto xeIoctlHelper = std::make_unique<MockIoctlHelperXe>(drm);
1011+
1012+
uint64_t fenceAddress = 0x4321;
1013+
uint64_t fenceValue = 0x789;
1014+
1015+
BindInfo mockBindInfo{};
1016+
mockBindInfo.handle = 0x1234;
1017+
xeIoctlHelper->bindInfo.push_back(mockBindInfo);
1018+
1019+
VmBindExtUserFenceT vmBindExtUserFence{};
1020+
1021+
xeIoctlHelper->fillVmBindExtUserFence(vmBindExtUserFence, fenceAddress, fenceValue, 0u);
1022+
1023+
VmBindParams vmBindParams{};
1024+
vmBindParams.handle = mockBindInfo.handle;
1025+
vmBindParams.extensions = castToUint64(&vmBindExtUserFence);
1026+
1027+
drm.vmBindInputs.clear();
1028+
drm.syncInputs.clear();
1029+
drm.waitUserFenceInputs.clear();
1030+
EXPECT_EQ(0, xeIoctlHelper->vmBind(vmBindParams));
1031+
EXPECT_EQ(1u, drm.vmBindInputs.size());
1032+
EXPECT_EQ(1u, drm.syncInputs.size());
1033+
EXPECT_EQ(1u, drm.waitUserFenceInputs.size());
1034+
{
1035+
auto &sync = drm.syncInputs[0];
1036+
1037+
EXPECT_EQ(fenceAddress, sync.addr);
1038+
EXPECT_EQ(fenceValue, sync.timeline_value);
1039+
1040+
auto &waitUserFence = drm.waitUserFenceInputs[0];
1041+
1042+
EXPECT_EQ(fenceAddress, waitUserFence.addr);
1043+
EXPECT_EQ(static_cast<uint16_t>(DRM_XE_UFENCE_WAIT_EQ), waitUserFence.op);
1044+
EXPECT_EQ(static_cast<uint16_t>(DRM_XE_UFENCE_WAIT_SOFT_OP), waitUserFence.flags);
1045+
EXPECT_EQ(fenceValue, waitUserFence.value);
1046+
EXPECT_EQ(static_cast<uint64_t>(DRM_XE_UFENCE_WAIT_U64), waitUserFence.mask);
1047+
EXPECT_EQ(static_cast<uint16_t>(XE_ONE_SEC), waitUserFence.timeout);
1048+
EXPECT_EQ(0u, waitUserFence.num_engines);
1049+
EXPECT_EQ(0u, waitUserFence.instances);
1050+
}
1051+
drm.vmBindInputs.clear();
1052+
drm.syncInputs.clear();
1053+
drm.waitUserFenceInputs.clear();
1054+
EXPECT_EQ(0, xeIoctlHelper->vmUnbind(vmBindParams));
1055+
EXPECT_EQ(1u, drm.vmBindInputs.size());
1056+
EXPECT_EQ(1u, drm.syncInputs.size());
1057+
EXPECT_EQ(1u, drm.waitUserFenceInputs.size());
1058+
{
1059+
auto &sync = drm.syncInputs[0];
1060+
1061+
EXPECT_EQ(fenceAddress, sync.addr);
1062+
EXPECT_EQ(fenceValue, sync.timeline_value);
1063+
1064+
auto &waitUserFence = drm.waitUserFenceInputs[0];
1065+
1066+
EXPECT_EQ(fenceAddress, waitUserFence.addr);
1067+
EXPECT_EQ(static_cast<uint16_t>(DRM_XE_UFENCE_WAIT_EQ), waitUserFence.op);
1068+
EXPECT_EQ(static_cast<uint16_t>(DRM_XE_UFENCE_WAIT_SOFT_OP), waitUserFence.flags);
1069+
EXPECT_EQ(fenceValue, waitUserFence.value);
1070+
EXPECT_EQ(static_cast<uint64_t>(DRM_XE_UFENCE_WAIT_U64), waitUserFence.mask);
1071+
EXPECT_EQ(static_cast<uint16_t>(XE_ONE_SEC), waitUserFence.timeout);
1072+
EXPECT_EQ(0u, waitUserFence.num_engines);
1073+
EXPECT_EQ(0u, waitUserFence.instances);
1074+
}
1075+
}
1076+
1077+
TEST(IoctlHelperXeTest, whenGemVmBindFailsThenErrorIsPropagated) {
1078+
DebugManagerStateRestore restorer;
1079+
auto executionEnvironment = std::make_unique<MockExecutionEnvironment>();
1080+
DrmMockXe drm{*executionEnvironment->rootDeviceEnvironments[0]};
1081+
auto xeIoctlHelper = std::make_unique<MockIoctlHelperXe>(drm);
1082+
1083+
uint64_t fenceAddress = 0x4321;
1084+
uint64_t fenceValue = 0x789;
1085+
1086+
BindInfo mockBindInfo{};
1087+
mockBindInfo.handle = 0x1234;
1088+
xeIoctlHelper->bindInfo.push_back(mockBindInfo);
1089+
1090+
VmBindExtUserFenceT vmBindExtUserFence{};
1091+
1092+
xeIoctlHelper->fillVmBindExtUserFence(vmBindExtUserFence, fenceAddress, fenceValue, 0u);
1093+
1094+
VmBindParams vmBindParams{};
1095+
vmBindParams.handle = mockBindInfo.handle;
1096+
vmBindParams.extensions = castToUint64(&vmBindExtUserFence);
1097+
1098+
drm.waitUserFenceInputs.clear();
1099+
1100+
int errorValue = -1;
1101+
drm.gemVmBindReturn = errorValue;
1102+
EXPECT_EQ(errorValue, xeIoctlHelper->vmBind(vmBindParams));
1103+
EXPECT_EQ(0u, drm.waitUserFenceInputs.size());
1104+
1105+
EXPECT_EQ(errorValue, xeIoctlHelper->vmUnbind(vmBindParams));
1106+
EXPECT_EQ(0u, drm.waitUserFenceInputs.size());
1107+
}
1108+
1109+
TEST(IoctlHelperXeTest, whenUserFenceFailsThenErrorIsPropagated) {
1110+
DebugManagerStateRestore restorer;
1111+
auto executionEnvironment = std::make_unique<MockExecutionEnvironment>();
1112+
DrmMockXe drm{*executionEnvironment->rootDeviceEnvironments[0]};
1113+
auto xeIoctlHelper = std::make_unique<MockIoctlHelperXe>(drm);
1114+
1115+
uint64_t fenceAddress = 0x4321;
1116+
uint64_t fenceValue = 0x789;
1117+
1118+
BindInfo mockBindInfo{};
1119+
mockBindInfo.handle = 0x1234;
1120+
xeIoctlHelper->bindInfo.push_back(mockBindInfo);
1121+
1122+
VmBindExtUserFenceT vmBindExtUserFence{};
1123+
1124+
xeIoctlHelper->fillVmBindExtUserFence(vmBindExtUserFence, fenceAddress, fenceValue, 0u);
1125+
1126+
VmBindParams vmBindParams{};
1127+
vmBindParams.handle = mockBindInfo.handle;
1128+
vmBindParams.extensions = castToUint64(&vmBindExtUserFence);
1129+
1130+
drm.waitUserFenceInputs.clear();
1131+
1132+
int errorValue = -1;
1133+
drm.waitUserFenceReturn = errorValue;
1134+
1135+
EXPECT_EQ(errorValue, xeIoctlHelper->vmBind(vmBindParams));
1136+
EXPECT_EQ(errorValue, xeIoctlHelper->vmUnbind(vmBindParams));
1137+
}

0 commit comments

Comments
 (0)