Skip to content

Commit 3d40e7a

Browse files
committed
plugin/amdgpu: Support for checkpoint of dmabuf fds
amdgpu libraries that use dmabuf fd to share GPU memory between processes close the dmabuf fds immediately after using them. However, it is possible that checkpoint of a process catches one of the dmabuf fds open. In that case, the amdgpu plugin needs to handle it. The checkpoint of the dmabuf fd does require the device file it was exported from to have already been dumped To identify which device this dmabuf fd was exprted from, attempt to import it on each device, then record the dmabuf handle it imports as. This handle can be used to restore it. Signed-off-by: David Francis <David.Francis@amd.com>
1 parent 773df95 commit 3d40e7a

File tree

8 files changed

+250
-22
lines changed

8 files changed

+250
-22
lines changed

plugins/amdgpu/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ endif
2727
criu-amdgpu.pb-c.c: criu-amdgpu.proto
2828
protoc --proto_path=. --c_out=. criu-amdgpu.proto
2929

30-
amdgpu_plugin.so: amdgpu_plugin.c amdgpu_plugin_drm.c amdgpu_plugin_topology.c amdgpu_plugin_util.c criu-amdgpu.pb-c.c amdgpu_socket_utils.c
30+
amdgpu_plugin.so: amdgpu_plugin.c amdgpu_plugin_drm.c amdgpu_plugin_dmabuf.c amdgpu_plugin_topology.c amdgpu_plugin_util.c criu-amdgpu.pb-c.c amdgpu_socket_utils.c
3131
$(CC) $(PLUGIN_CFLAGS) $(shell $(COMPEL) includes) $^ -o $@ $(PLUGIN_INCLUDE) $(PLUGIN_LDFLAGS) $(LIBDRM_INC)
3232

3333
amdgpu_plugin_clean:

plugins/amdgpu/amdgpu_plugin.c

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include "rst-malloc.h"
3939

4040
#include "common/list.h"
41+
#include "amdgpu_plugin_dmabuf.h"
4142
#include "amdgpu_plugin_drm.h"
4243
#include "amdgpu_plugin_util.h"
4344
#include "amdgpu_plugin_topology.h"
@@ -46,7 +47,7 @@
4647
#include "img-streamer.h"
4748
#include "image.h"
4849
#include "cr_options.h"
49-
50+
#include "util.h"
5051
struct vma_metadata {
5152
struct list_head list;
5253
uint64_t old_pgoff;
@@ -1403,7 +1404,17 @@ int amdgpu_plugin_dump_file(int fd, int id)
14031404
return -1;
14041405
}
14051406

1406-
/* Check whether this plugin was called for kfd or render nodes */
1407+
/* Check whether this plugin was called for kfd, dmabuf or render nodes */
1408+
ret = get_dmabuf_info(fd, &st);
1409+
if (ret < 0) {
1410+
pr_perror("Failed to get dmabuf info");
1411+
return -1;
1412+
} else if (ret == 0) {
1413+
pr_info("Dumping dmabuf fd = %d\n", fd);
1414+
ret = amdgpu_plugin_dmabuf_dump(fd, id, &st);
1415+
return ret;
1416+
}
1417+
14071418
if (major(st.st_rdev) != major(st_kfd.st_rdev) || minor(st.st_rdev) != 0) {
14081419

14091420
/* This is RenderD dumper plugin, for now just save renderD
@@ -1415,9 +1426,6 @@ int amdgpu_plugin_dump_file(int fd, int id)
14151426
return ret;
14161427

14171428
ret = record_dumped_fd(fd, true);
1418-
if (ret)
1419-
return ret;
1420-
14211429
/* Need to return success here so that criu can call plugins for renderD nodes */
14221430
return ret;
14231431
}
@@ -1541,7 +1549,6 @@ static int restore_devices(struct kfd_ioctl_criu_args *args, CriuKfd *e)
15411549
int ret = 0, bucket_index = 0;
15421550

15431551
pr_debug("Restoring %d devices\n", e->num_of_gpus);
1544-
15451552
args->num_devices = e->num_of_gpus;
15461553
device_buckets = xzalloc(sizeof(*device_buckets) * args->num_devices);
15471554
if (!device_buckets)
@@ -1826,12 +1833,17 @@ int amdgpu_plugin_restore_file(int id, bool *retry_needed)
18261833
* first as we assume restore_maps is already filled. Need to fix this later.
18271834
*/
18281835
snprintf(img_path, sizeof(img_path), IMG_DRM_FILE, id);
1829-
pr_info("Restoring RenderD %s\n", img_path);
18301836

18311837
img_fp = open_img_file(img_path, false, &img_size);
1832-
if (!img_fp)
1833-
return -EINVAL;
1834-
1838+
if (!img_fp) {
1839+
ret = amdgpu_plugin_dmabuf_restore(id);
1840+
if (ret == 1) {
1841+
*retry_needed = true;
1842+
return 0;
1843+
}
1844+
return ret;
1845+
}
1846+
pr_info("Restoring RenderD %s\n", img_path);
18351847
pr_debug("RenderD Image file size:%ld\n", img_size);
18361848
buf = xmalloc(img_size);
18371849
if (!buf) {
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
#include <errno.h>
2+
#include <fcntl.h>
3+
#include <stdlib.h>
4+
#include <stdint.h>
5+
#include <stdbool.h>
6+
#include <string.h>
7+
#include <stdio.h>
8+
#include <unistd.h>
9+
10+
#include <sys/stat.h>
11+
#include <sys/mman.h>
12+
#include <sys/types.h>
13+
#include <linux/limits.h>
14+
15+
#include "common/list.h"
16+
#include "criu-amdgpu.pb-c.h"
17+
18+
#include "xmalloc.h"
19+
#include "criu-log.h"
20+
#include "amdgpu_plugin_drm.h"
21+
#include "amdgpu_plugin_util.h"
22+
#include "amdgpu_plugin_dmabuf.h"
23+
#include "fdstore.h"
24+
25+
#include "util.h"
26+
#include "common/scm.h"
27+
28+
/* Return < 0 for error, > 0 for "not a dmabuf" and 0 "is a dmabuf" */
29+
int get_dmabuf_info(int fd, struct stat *st)
30+
{
31+
char path[PATH_MAX];
32+
33+
if (read_fd_link(fd, path, sizeof(path)) < 0)
34+
return -1;
35+
36+
if (strncmp(path, DMABUF_LINK, strlen(DMABUF_LINK)) != 0)
37+
return 1;
38+
39+
return 0;
40+
}
41+
42+
int amdgpu_plugin_dmabuf_dump(int dmabuf_fd, int id, struct stat *dmabuf_stat) {
43+
int ret = 0;
44+
char path[PATH_MAX];
45+
size_t len = 0;
46+
unsigned char *buf = NULL;
47+
int gem_handle;
48+
49+
pr_info("TWI: Dumping dmabuf fd = %d\n", dmabuf_fd);
50+
51+
gem_handle = handle_for_shared_bo_fd(dmabuf_fd);
52+
if (gem_handle < 0) {
53+
pr_err("Failed to get handle for dmabuf_fd = %d\n", dmabuf_fd);
54+
return -EAGAIN; /* Retry needed */
55+
}
56+
57+
CriuDmabufNode *node = xmalloc(sizeof(*node));
58+
if (!node) {
59+
pr_err("Failed to allocate memory for dmabuf node\n");
60+
return -ENOMEM;
61+
}
62+
criu_dmabuf_node__init(node);
63+
64+
node->gem_handle = gem_handle;
65+
66+
if (node->gem_handle < 0) {
67+
pr_err("Failed to get handle for dmabuf_fd\n");
68+
xfree(node);
69+
return -EINVAL;
70+
}
71+
72+
/* Serialize metadata to a file */
73+
snprintf(path, sizeof(path), IMG_DMABUF_FILE, id);
74+
len = criu_dmabuf_node__get_packed_size(node);
75+
buf = xmalloc(len);
76+
if (!buf) {
77+
pr_err("Failed to allocate buffer for dmabuf metadata\n");
78+
xfree(node);
79+
return -ENOMEM;
80+
}
81+
criu_dmabuf_node__pack(node, buf);
82+
ret = write_img_file(path, buf, len);
83+
84+
xfree(buf);
85+
xfree(node);
86+
return ret;
87+
}
88+
89+
int amdgpu_plugin_dmabuf_restore(int id) {
90+
char path[PATH_MAX];
91+
size_t img_size;
92+
FILE *img_fp = NULL;
93+
int ret = 0;
94+
CriuDmabufNode *rd = NULL;
95+
unsigned char *buf = NULL;
96+
int fd_id;
97+
98+
snprintf(path, sizeof(path), IMG_DMABUF_FILE, id);
99+
100+
pr_info("TWI: Restoring dmabuf fd, id = %d\n", id);
101+
102+
/* Read serialized metadata */
103+
img_fp = open_img_file(path, false, &img_size);
104+
if (!img_fp) {
105+
pr_err("Failed to open dmabuf metadata file: %s\n", path);
106+
return -EINVAL;
107+
}
108+
109+
pr_debug("dmabuf Image file size:%ld\n", img_size);
110+
buf = xmalloc(img_size);
111+
if (!buf) {
112+
pr_perror("Failed to allocate memory");
113+
return -ENOMEM;
114+
}
115+
116+
ret = read_fp(img_fp, buf, img_size);
117+
if (ret) {
118+
pr_perror("Unable to read from %s", path);
119+
xfree(buf);
120+
return ret;
121+
}
122+
123+
rd = criu_dmabuf_node__unpack(NULL, img_size, buf);
124+
if (rd == NULL) {
125+
pr_perror("Unable to parse the dmabuf message %d", id);
126+
xfree(buf);
127+
fclose(img_fp);
128+
return -1;
129+
}
130+
fclose(img_fp);
131+
132+
pr_info("TWI: dmabuf node gem_handle = %d\n", rd->gem_handle);
133+
134+
/* Match GEM handle with shared_dmabuf list */
135+
fd_id = amdgpu_id_for_handle(rd->gem_handle);
136+
if (fd_id == -1) {
137+
pr_err("Failed to find dmabuf_fd for GEM handle = %d\n",
138+
rd->gem_handle);
139+
return 1;
140+
}
141+
int dmabuf_fd = fdstore_get(fd_id);
142+
pr_info("TWI: dmabuf node fd_id = %d, dmabuf_fd = %d\n", fd_id, dmabuf_fd);
143+
if (dmabuf_fd == -1) {
144+
pr_err("Failed to find dmabuf_fd for GEM handle = %d\n",
145+
rd->gem_handle);
146+
return 1; /* Retry needed */
147+
} else {
148+
pr_info("Restored dmabuf_fd = %d for GEM handle = %d\n",
149+
dmabuf_fd, rd->gem_handle);
150+
}
151+
ret = dmabuf_fd;
152+
153+
pr_info("Successfully restored dmabuf_fd %d\n",
154+
dmabuf_fd);
155+
criu_dmabuf_node__free_unpacked(rd, NULL);
156+
xfree(buf);
157+
return ret;
158+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
2+
#ifndef __AMDGPU_PLUGIN_DMABUF_H__
3+
#define __AMDGPU_PLUGIN_DMABUF_H__
4+
5+
#include "amdgpu_plugin_util.h"
6+
#include "criu-amdgpu.pb-c.h"
7+
8+
int amdgpu_plugin_dmabuf_dump(int fd, int id, struct stat *dmabuf_stat);
9+
int amdgpu_plugin_dmabuf_restore(int id);
10+
11+
int get_dmabuf_info(int fd, struct stat *st);
12+
13+
#endif /* __AMDGPU_PLUGIN_DMABUF_H__ */

plugins/amdgpu/amdgpu_plugin_drm.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ int get_gem_handle(amdgpu_device_handle h_dev, int dmabuf_fd)
4848
return -1;
4949
}
5050

51-
drmPrimeFDToHandle(fd, dmabuf_fd, &handle);
51+
if (drmPrimeFDToHandle(fd, dmabuf_fd, &handle))
52+
return -1;
5253

5354
return handle;
5455
}
@@ -470,18 +471,21 @@ int amdgpu_plugin_drm_restore_file(int fd, CriuRenderNode *rd)
470471
if (work_already_completed(boinfo->handle, rd->drm_render_minor)) {
471472
continue;
472473
} else if (boinfo->handle != -1) {
474+
pr_info("TWI: restore bo %d\n", boinfo->handle);
473475
if (boinfo->is_import) {
474476
fd_id = amdgpu_id_for_handle(boinfo->handle);
475477
if (fd_id == -1) {
476478
retry_needed = true;
477479
continue;
478480
}
479481
dmabuf_fd = fdstore_get(fd_id);
482+
pr_info("TWI: restore bo %d: fd_id %d, dmabuf_fd %d\n", boinfo->handle, fd_id, dmabuf_fd);
480483
}
481484
}
482485

483486
if (boinfo->is_import) {
484487
drmPrimeFDToHandle(device_fd, dmabuf_fd, &handle);
488+
pr_info("TWI: restore bo imported to handle %d\n", handle);
485489
} else {
486490
union drm_amdgpu_gem_create create_args = {0};
487491

@@ -498,6 +502,7 @@ int amdgpu_plugin_drm_restore_file(int fd, CriuRenderNode *rd)
498502
handle = create_args.out.handle;
499503

500504
drmPrimeHandleToFD(device_fd, handle, 0, &dmabuf_fd);
505+
pr_info("TWI: restore bo created at handle %d and exported to fd %d\n", handle, dmabuf_fd);
501506
}
502507

503508
change_args.handle = handle;

plugins/amdgpu/amdgpu_plugin_util.c

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include "amdgpu_drm.h"
3838
#include "amdgpu_plugin_util.h"
3939
#include "amdgpu_plugin_topology.h"
40+
#include "amdgpu_plugin_drm.h"
4041

4142
static LIST_HEAD(dumped_fds);
4243
static LIST_HEAD(shared_bos);
@@ -105,6 +106,46 @@ int record_shared_bo(int handle, bool is_imported) {
105106
return 0;
106107
}
107108

109+
int handle_for_shared_bo_fd(int fd) {
110+
struct dumped_fd *df;
111+
int trial_handle;
112+
amdgpu_device_handle h_dev;
113+
uint32_t major, minor;
114+
struct shared_bo *bo;
115+
116+
list_for_each_entry(df, &dumped_fds, l) {
117+
118+
/* see if the gem handle for fd using the hdev for df->fd is the
119+
same as bo->handle. */
120+
121+
if (!df->is_drm) {
122+
continue;
123+
}
124+
125+
if (amdgpu_device_initialize(df->fd, &major, &minor, &h_dev)) {
126+
pr_err("Failed to initialize amdgpu device\n");
127+
continue;
128+
}
129+
130+
trial_handle = get_gem_handle(h_dev, fd);
131+
if (trial_handle < 0)
132+
continue;
133+
134+
pr_info("TWI: Check device %d, got handle %d\n", df->fd, trial_handle);
135+
136+
list_for_each_entry(bo, &shared_bos, l) {
137+
if (bo->handle == trial_handle) {
138+
pr_info("TWI: And that handle exists\n");
139+
return trial_handle;
140+
}
141+
}
142+
143+
amdgpu_device_deinitialize(h_dev);
144+
}
145+
146+
return -1;
147+
}
148+
108149
int record_completed_work(int handle, int id) {
109150
struct restore_completed_work *work;
110151

@@ -131,13 +172,6 @@ bool work_already_completed(int handle, int id) {
131172
}
132173

133174
void clear_restore_state() {
134-
while (!list_empty(&shared_dmabuf_fds)) {
135-
struct shared_dmabuf *st = list_first_entry(&shared_dmabuf_fds, struct shared_dmabuf, l);
136-
list_del(&st->l);
137-
close(st->dmabuf_fd);
138-
free(st);
139-
}
140-
141175
while (!list_empty(&completed_work)) {
142176
struct restore_completed_work *st = list_first_entry(&completed_work, struct restore_completed_work, l);
143177
list_del(&st->l);

plugins/amdgpu/amdgpu_plugin_util.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@
5353
/* Name of file having serialized data of DRM device */
5454
#define IMG_DRM_FILE "amdgpu-renderD-%d.img"
5555

56+
/* Name of file having serialized data of dmabuf meta */
57+
#define IMG_DMABUF_FILE "amdgpu-dmabuf_%d.img"
58+
5659
/* Name of file having serialized data of DRM device buffer objects (BOs) */
5760
#define IMG_DRM_PAGES_FILE "amdgpu-drm-pages-%d-%d-%04x.img"
5861

@@ -61,6 +64,7 @@
6164
#define HSAKMT_SHM "/hsakmt_shared_mem"
6265
#define HSAKMT_SEM_PATH "/dev/shm/sem.hsakmt_semaphore"
6366
#define HSAKMT_SEM "hsakmt_semaphore"
67+
#define DMABUF_LINK "/dmabuf"
6468

6569
/* Help macros to build sDMA command packets */
6670
#define SDMA_PACKET(op, sub_op, e) ((((e)&0xFFFF) << 16) | (((sub_op)&0xFF) << 8) | (((op)&0xFF) << 0))
@@ -123,9 +127,7 @@ void clear_dumped_fds();
123127

124128
bool shared_bo_has_exporter(int handle);
125129
int record_shared_bo(int handle, bool is_imported);
126-
127-
int record_shared_dmabuf_fd(int handle, int dmabuf_fd);
128-
int dmabuf_fd_for_handle(int handle);
130+
int handle_for_shared_bo_fd(int dmabuf_fd);
129131

130132
int record_completed_work(int handle, int id);
131133
bool work_already_completed(int handle, int id);

0 commit comments

Comments
 (0)