Re: [RFC 18/26] plugins/amdgpu: Convert away from libc buffered file IO

Tvrtko Ursulin <[email protected]> Mon, 9 Mar 2026 15:30:30 +0000
Newsgroups dev.linux.lists.criu
Message-ID <[email protected]>
Gentle ping on this one, and patch 17 as well. If those two are reviewed 
I can implement the small tweaks as agreed and re-spin the series up to 
patch 20. That would be purely cleanup and then I keep churning at the 
real stuff.

Tvrtko

On 20/02/2026 12:05, Tvrtko Ursulin wrote:
> Currently the code contains multiple places where callers expect the errno
> to be valid (implied by calling pr_perror to log file IO errors) after
> calling open_img_file(), read_fp() and write_fp() helpers. Problem there
> is that those helpers can destroy the errno courtesy of themselves
> emitting log messages via fprintf.
> 
> Furthermore, the callers then sometimes invent the errors to return back
> to the caller, or even outside of the plugin.
> 
> On top of that there is no benefit to buffered file IO given the plugin
> reads and writes in buffer object size chunks, so it it preferrable to
> just go direct and avoid any possibility of extra copying to and from libc
> temporary buffers.
> 
> So lets just convert it all in one swoop to POSIX IO and make sure correct
> errnos are always propagated to the caller (including to CRIU core).
> 
> Hopefully this removes all instances of incorrect pr_perror log messages.
> 
> Signed-off-by: Tvrtko Ursulin <[email protected]>
> ---
>   plugins/amdgpu/amdgpu_plugin.c        | 126 ++++++++++++--------------
>   plugins/amdgpu/amdgpu_plugin_dmabuf.c |  14 ++-
>   plugins/amdgpu/amdgpu_plugin_drm.c    |  32 +++----
>   plugins/amdgpu/amdgpu_plugin_util.c   | 106 ++++++++++++----------
>   plugins/amdgpu/amdgpu_plugin_util.h   |   8 +-
>   5 files changed, 140 insertions(+), 146 deletions(-)
> 
> diff --git a/plugins/amdgpu/amdgpu_plugin.c b/plugins/amdgpu/amdgpu_plugin.c
> index a653ba1aa2d9..dc808fe32373 100644
> --- a/plugins/amdgpu/amdgpu_plugin.c
> +++ b/plugins/amdgpu/amdgpu_plugin.c
> @@ -557,7 +557,7 @@ void free_and_unmap(uint64_t size, amdgpu_bo_handle h_bo, amdgpu_va_handle h_va,
>   	amdgpu_bo_free(h_bo);
>   }
>   
> -int sdma_copy_bo(int shared_fd, uint64_t size, FILE *storage_fp,
> +int sdma_copy_bo(int shared_fd, uint64_t size, int storage_fd,
>   		 void *buffer, size_t buffer_size, amdgpu_device_handle h_dev,
>   		 uint64_t max_copy_size, enum sdma_op_type type, bool do_not_free)
>   {
> @@ -689,7 +689,8 @@ int sdma_copy_bo(int shared_fd, uint64_t size, FILE *storage_fp,
>   		memset(ib, 0, packets_per_buffer * 28);
>   
>   		if (type == SDMA_OP_VRAM_WRITE) {
> -			err = read_fp(storage_fp, buffer, min(bytes_remain, buffer_bo_size));
> +			err = img_read(storage_fd, buffer,
> +				       min(bytes_remain, buffer_bo_size));
>   			if (err) {
>   				pr_perror("failed to read from storage");
>   				goto err_bo_list;
> @@ -764,7 +765,8 @@ int sdma_copy_bo(int shared_fd, uint64_t size, FILE *storage_fp,
>   		}
>   
>   		if (type == SDMA_OP_VRAM_READ) {
> -			err = write_fp(storage_fp, buffer, buffer_bo_size - buffer_space_remain);
> +			err = img_write(storage_fd, buffer,
> +					buffer_bo_size - buffer_space_remain);
>   			if (err) {
>   				pr_perror("failed to write out to storage");
>   				goto err_cs_submit_ib;
> @@ -817,11 +819,11 @@ void *dump_bo_contents(void *_thread_data)
>   	struct amdgpu_gpu_info gpu_info = { 0 };
>   	amdgpu_device_handle h_dev;
>   	size_t max_bo_size = 0, image_size = 0, buffer_size;
> +	int bo_contents_fd = -1;
>   	uint64_t max_copy_size;
>   	uint32_t major, minor;
>   	int num_bos = 0;
>   	int i, ret = 0;
> -	FILE *bo_contents_fp = NULL;
>   	void *buffer = NULL;
>   	char img_path[40];
>   
> @@ -862,10 +864,9 @@ void *dump_bo_contents(void *_thread_data)
>   	}
>   
>   	snprintf(img_path, sizeof(img_path), IMG_KFD_PAGES_FILE, thread_data->id, thread_data->gpu_id);
> -	bo_contents_fp = open_img_file(img_path, true, &image_size);
> -	if (!bo_contents_fp) {
> -		pr_perror("Cannot fopen %s", img_path);
> -		ret = -EIO;
> +	bo_contents_fd = open_img_file(img_path, true, &image_size);
> +	if (bo_contents_fd < 0) {
> +		ret = bo_contents_fd;
>   		goto exit;
>   	}
>   
> @@ -879,8 +880,9 @@ void *dump_bo_contents(void *_thread_data)
>   		num_bos++;
>   
>   		/* perform sDMA based vram copy */
> -		ret = sdma_copy_bo(bo_buckets[i].dmabuf_fd, bo_buckets[i].size, bo_contents_fp, buffer, buffer_size, h_dev, max_copy_size,
> -				   SDMA_OP_VRAM_READ, false);
> +		ret = sdma_copy_bo(bo_buckets[i].dmabuf_fd, bo_buckets[i].size,
> +				   bo_contents_fd, buffer, buffer_size, h_dev,
> +				   max_copy_size, SDMA_OP_VRAM_READ, false);
>   
>   		if (ret) {
>   			pr_err("Failed to drain the BO using sDMA: bo_buckets[%d]\n", i);
> @@ -891,8 +893,8 @@ void *dump_bo_contents(void *_thread_data)
>   exit:
>   	pr_info("Thread[0x%x] done num_bos:%d ret:%d\n", thread_data->gpu_id, num_bos, ret);
>   
> -	if (bo_contents_fp)
> -		fclose(bo_contents_fp);
> +	if (bo_contents_fd >= 0)
> +		close(bo_contents_fd);
>   
>   	xfree(buffer);
>   
> @@ -909,9 +911,9 @@ void *restore_bo_contents(void *_thread_data)
>   	size_t image_size = 0, total_bo_size = 0, max_bo_size = 0, buffer_size;
>   	struct amdgpu_gpu_info gpu_info = { 0 };
>   	amdgpu_device_handle h_dev;
> +	int bo_contents_fd = -1;
>   	uint64_t max_copy_size;
>   	uint32_t major, minor;
> -	FILE *bo_contents_fp = NULL;
>   	void *buffer = NULL;
>   	char img_path[40];
>   	int num_bos = 0;
> @@ -936,10 +938,9 @@ void *restore_bo_contents(void *_thread_data)
>   								   SDMA_LINEAR_COPY_MAX_SIZE - 1;
>   
>   	snprintf(img_path, sizeof(img_path), IMG_KFD_PAGES_FILE, thread_data->id, thread_data->gpu_id);
> -	bo_contents_fp = open_img_file(img_path, false, &image_size);
> -	if (!bo_contents_fp) {
> -		pr_perror("Cannot fopen %s", img_path);
> -		ret = -errno;
> +	bo_contents_fd = open_img_file(img_path, false, &image_size);
> +	if (bo_contents_fd < 0) {
> +		ret = bo_contents_fd;
>   		goto exit;
>   	}
>   
> @@ -978,8 +979,9 @@ void *restore_bo_contents(void *_thread_data)
>   
>   		num_bos++;
>   
> -		ret = sdma_copy_bo(bo_buckets[i].dmabuf_fd, bo_buckets[i].size, bo_contents_fp, buffer, buffer_size, h_dev, max_copy_size,
> -				   SDMA_OP_VRAM_WRITE, false);
> +		ret = sdma_copy_bo(bo_buckets[i].dmabuf_fd, bo_buckets[i].size,
> +				   bo_contents_fd, buffer, buffer_size, h_dev,
> +				   max_copy_size, SDMA_OP_VRAM_WRITE, false);
>   		if (ret) {
>   			pr_err("Failed to fill the BO using sDMA: bo_buckets[%d]\n", i);
>   			break;
> @@ -991,8 +993,8 @@ void *restore_bo_contents(void *_thread_data)
>   exit:
>   	pr_info("Thread[0x%x] done num_bos:%d ret:%d\n", thread_data->gpu_id, num_bos, ret);
>   
> -	if (bo_contents_fp)
> -		fclose(bo_contents_fp);
> +	if (bo_contents_fd >= 0)
> +		close(bo_contents_fd);
>   
>   	xfree(buffer);
>   
> @@ -1016,9 +1018,7 @@ int check_hsakmt_shared_mem(uint64_t *shared_mem_size, uint32_t *shared_mem_magi
>   
>   	/* First 4 bytes of shared file is the magic */
>   	ret = read_file(HSAKMT_SHM_PATH, shared_mem_magic, sizeof(*shared_mem_magic));
> -	if (ret)
> -		pr_perror("Failed to read shared mem magic");
> -	else
> +	if (!ret)
>   		pr_debug("Shared mem magic:0x%x\n", *shared_mem_magic);
>   
>   	return 0;
> @@ -1137,15 +1137,12 @@ int amdgpu_id_for_handle(int handle)
>   static int load_img(char *filename, unsigned char **out_buf, size_t *out_len)
>   {
>   	unsigned char *buf;
> -	FILE *img_fp;
> +	int fd, ret;
>   	size_t len;
> -	int ret;
>   
> -	img_fp = open_img_file(filename, false, &len);
> -	if (!img_fp) {
> -		ret = -ENOENT;
> -		goto out;
> -	}
> +	fd = open_img_file(filename, false, &len);
> +	if (fd < 0)
> +		return fd;
>   
>   	buf = xmalloc(len);
>   	if (!buf) {
> @@ -1153,7 +1150,7 @@ static int load_img(char *filename, unsigned char **out_buf, size_t *out_len)
>   		goto out_close;
>   	}
>   
> -	ret = read_fp(img_fp, buf, len);
> +	ret = img_read(fd, buf, len);
>   	if (ret) {
>   		xfree(buf);
>   	} else {
> @@ -1162,11 +1159,7 @@ static int load_img(char *filename, unsigned char **out_buf, size_t *out_len)
>   	}
>   
>   out_close:
> -	fclose(img_fp);
> -out:
> -	if (ret < 0)
> -		pr_err("Unable to read from %s", filename);
> -
> +	close(fd);
>   	return ret;
>   }
>   
> @@ -1956,7 +1949,7 @@ int amdgpu_plugin_restore_file(int id, bool *retry_needed)
>   	CriuKfd *e = NULL;
>   	struct kfd_ioctl_criu_args args = { 0 };
>   	size_t img_size;
> -	FILE *img_fp = NULL;
> +	int img_fd;
>   
>   	*retry_needed = false;
>   
> @@ -1967,8 +1960,8 @@ int amdgpu_plugin_restore_file(int id, bool *retry_needed)
>   
>   	snprintf(img_path, sizeof(img_path), IMG_KFD_FILE, id);
>   
> -	img_fp = open_img_file(img_path, false, &img_size);
> -	if (!img_fp)
> +	img_fd = open_img_file(img_path, false, &img_size);
> +	if (img_fd < 0)
>   		return amdgpu_plugin_restore_drm_file(id, retry_needed);
>   
>   	fd = open(AMDGPU_KFD_DEVICE, O_RDWR | O_CLOEXEC);
> @@ -1985,19 +1978,18 @@ int amdgpu_plugin_restore_file(int id, bool *retry_needed)
>   	pr_info("KFD Image file size:%ld\n", img_size);
>   	buf = xmalloc(img_size);
>   	if (!buf) {
> -		fclose(img_fp);
> +		close(img_fd);
>   		return -ENOMEM;
>   	}
>   
> -	ret = read_fp(img_fp, buf, img_size);
> +	ret = img_read(img_fd, buf, img_size);
> +	close(img_fd);
>   	if (ret) {
>   		pr_perror("Unable to read from %s", img_path);
> -		fclose(img_fp);
>   		xfree(buf);
>   		return ret;
>   	}
>   
> -	fclose(img_fp);
>   	e = criu_kfd__unpack(NULL, img_size, buf);
>   	if (e == NULL) {
>   		pr_err("Unable to parse the KFD message %#x\n", id);
> @@ -2254,25 +2246,23 @@ err:
>   	return ret;
>   }
>   
> -FILE *get_bo_contents_fp(int id, int gpu_id, size_t tot_size)
> +static int get_bo_contents_fd(int id, int gpu_id, size_t tot_size)
>   {
>   	char img_path[PATH_MAX];
>   	size_t image_size = 0;
> -	FILE *bo_contents_fp = NULL;
> +	int fd;
>   
>   	snprintf(img_path, sizeof(img_path), IMG_KFD_PAGES_FILE, id, gpu_id);
> -	bo_contents_fp = open_img_file(img_path, false, &image_size);
> -	if (!bo_contents_fp) {
> -		pr_perror("Cannot fopen %s", img_path);
> -		return NULL;
> -	}
> +	fd = open_img_file(img_path, false, &image_size);
> +	if (fd < 0)
> +		return fd;
>   
>   	if (tot_size != image_size) {
>   		pr_err("%s size mismatch (current:%ld:expected:%ld)\n", img_path, image_size, tot_size);
> -		fclose(bo_contents_fp);
> -		return NULL;
> +		close(fd);
> +		return -EINVAL;
>   	}
> -	return bo_contents_fp;
> +	return fd;
>   }
>   
>   struct parallel_thread_data {
> @@ -2289,7 +2279,7 @@ void *parallel_restore_bo_contents(void *_thread_data)
>   	amdgpu_device_handle h_dev;
>   	uint64_t max_copy_size;
>   	size_t total_bo_size = 0, max_bo_size = 0, buffer_size = 0;
> -	FILE *bo_contents_fp = NULL;
> +	int bo_contents_fd = -1;
>   	parallel_restore_entry *entry;
>   	parallel_restore_cmd *restore_cmd = thread_data->restore_cmd;
>   	int ret = 0;
> @@ -2310,12 +2300,12 @@ void *parallel_restore_bo_contents(void *_thread_data)
>   
>   	buffer_size = kfd_max_buffer_size > 0 ? min(kfd_max_buffer_size, max_bo_size) : max_bo_size;
>   
> -	bo_contents_fp = get_bo_contents_fp(restore_cmd->cmd_head.id, thread_data->gpu_id, total_bo_size);
> -	if (bo_contents_fp == NULL) {
> -		ret = -1;
> +	bo_contents_fd = get_bo_contents_fd(restore_cmd->cmd_head.id, thread_data->gpu_id, total_bo_size);
> +	if (bo_contents_fd < 0) {
> +		ret = bo_contents_fd;
>   		goto err_sdma;
>   	}
> -	offset = ftell(bo_contents_fp);
> +	offset = lseek(bo_contents_fd, 0, SEEK_CUR);
>   	if (offset < 0) {
>   		ret = -errno;
>   		pr_perror("Failed to alloc aligned memory. Consider setting KFD_MAX_BUFFER_SIZE.");
> @@ -2334,17 +2324,17 @@ void *parallel_restore_bo_contents(void *_thread_data)
>   			continue;
>   
>   		entry = &restore_cmd->entries[i];
> -		ret = fseeko(bo_contents_fp, entry->read_offset + offset,
> -			     SEEK_SET);
> -		if (ret < 0) {
> +		ret = lseek(bo_contents_fd, entry->read_offset + offset,
> +			    SEEK_SET);
> +		if (ret) {
>   			ret = -errno;
>   			pr_err("Failed to seek for BO using sDMA: bo_buckets[%d]\n", i);
>   			goto err_sdma;
>   		}
> -		ret = sdma_copy_bo(restore_cmd->fds_write[entry->write_id], entry->size, bo_contents_fp,
> -				   buffer, buffer_size, h_dev,
> -				   max_copy_size, SDMA_OP_VRAM_WRITE, false);
> -
> +		ret = sdma_copy_bo(restore_cmd->fds_write[entry->write_id],
> +				   entry->size, bo_contents_fd, buffer,
> +				   buffer_size, h_dev, max_copy_size,
> +				   SDMA_OP_VRAM_WRITE, false);
>   		if (ret) {
>   			pr_err("Failed to fill the BO using sDMA: bo_buckets[%d]\n", i);
>   			goto err_sdma;
> @@ -2352,8 +2342,8 @@ void *parallel_restore_bo_contents(void *_thread_data)
>   	}
>   
>   err_sdma:
> -	if (bo_contents_fp)
> -		fclose(bo_contents_fp);
> +	if (bo_contents_fd >= 0)
> +		close(bo_contents_fd);
>   	if (buffer)
>   		xfree(buffer);
>   	amdgpu_device_deinitialize(h_dev);
> diff --git a/plugins/amdgpu/amdgpu_plugin_dmabuf.c b/plugins/amdgpu/amdgpu_plugin_dmabuf.c
> index 859ff42bb76a..fb894de16d75 100644
> --- a/plugins/amdgpu/amdgpu_plugin_dmabuf.c
> +++ b/plugins/amdgpu/amdgpu_plugin_dmabuf.c
> @@ -96,20 +96,19 @@ int __amdgpu_plugin_dmabuf_dump(int dmabuf_fd, int id)
>   int amdgpu_plugin_dmabuf_restore(int id)
>   {
>   	char path[PATH_MAX];
> +	int fd_id, img_fd;
>   	size_t img_size;
> -	FILE *img_fp = NULL;
>   	int ret = 0;
>   	CriuDmabufNode *rd = NULL;
>   	unsigned char *buf = NULL;
> -	int fd_id;
>   
>   	snprintf(path, sizeof(path), IMG_DMABUF_FILE, id);
>   
>   	/* Read serialized metadata */
> -	img_fp = open_img_file(path, false, &img_size);
> -	if (!img_fp) {
> +	img_fd = open_img_file(path, false, &img_size);
> +	if (img_fd < 0) {
>   		pr_err("Failed to open dmabuf metadata file: %s\n", path);
> -		return -EINVAL;
> +		return img_fd;
>   	}
>   
>   	pr_debug("dmabuf Image file size:%ld\n", img_size);
> @@ -119,10 +118,9 @@ int amdgpu_plugin_dmabuf_restore(int id)
>   		return -ENOMEM;
>   	}
>   
> -	ret = read_fp(img_fp, buf, img_size);
> -	fclose(img_fp);
> +	ret = img_read(img_fd, buf, img_size);
> +	close(img_fd);
>   	if (ret) {
> -		pr_perror("Unable to read from %s", path);
>   		xfree(buf);
>   		return ret;
>   	}
> diff --git a/plugins/amdgpu/amdgpu_plugin_drm.c b/plugins/amdgpu/amdgpu_plugin_drm.c
> index d0f84b46997c..070b5b19ec47 100644
> --- a/plugins/amdgpu/amdgpu_plugin_drm.c
> +++ b/plugins/amdgpu/amdgpu_plugin_drm.c
> @@ -163,8 +163,8 @@ static int restore_bo_contents_drm(int drm_render_minor, CriuRenderNode *rd, int
>   	amdgpu_device_handle h_dev;
>   	uint64_t max_copy_size;
>   	uint32_t major, minor;
> -	FILE *bo_contents_fp = NULL;
>   	void *buffer = NULL;
> +	int bo_contents_fd;
>   	char img_path[40];
>   	int i, ret = 0;
>   
> @@ -209,24 +209,23 @@ static int restore_bo_contents_drm(int drm_render_minor, CriuRenderNode *rd, int
>   
>   		snprintf(img_path, sizeof(img_path), IMG_DRM_PAGES_FILE, rd->id, drm_render_minor, i);
>   
> -		bo_contents_fp = open_img_file(img_path, false, &image_size);
> -		if (!bo_contents_fp) {
> -			ret = -EIO;
> +		bo_contents_fd = open_img_file(img_path, false, &image_size);
> +		if (bo_contents_fd < 0) {
> +			ret = bo_contents_fd;
>   			pr_err("Failed to open BO image file %s\n", img_path);
>   			break;
>   		}
>   
> -		ret = sdma_copy_bo(dmabufs[i], rd->bo_entries[i]->size, bo_contents_fp, buffer, buffer_size, h_dev, max_copy_size,
> -				   SDMA_OP_VRAM_WRITE, true);
> +		ret = sdma_copy_bo(dmabufs[i], rd->bo_entries[i]->size,
> +				   bo_contents_fd, buffer, buffer_size, h_dev,
> +				   max_copy_size, SDMA_OP_VRAM_WRITE, true);
> +		close(bo_contents_fd);
>   		if (ret) {
>   			pr_err("Failed to fill the BO using sDMA: bo_buckets[%d]\n", i);
>   			break;
>   		}
>   		pr_debug("** Successfully filled the BO using sDMA: bo_buckets[%d] **\n",
>   			 i);
> -
> -		if (bo_contents_fp)
> -			fclose(bo_contents_fp);
>   	}
>   
>   exit:
> @@ -307,12 +306,12 @@ int amdgpu_plugin_drm_dump_file(int fd, int id, struct stat *drm)
>   		DrmBoEntry *boinfo = rd->bo_entries[i];
>   		struct drm_amdgpu_gem_list_handles_entry handle_entry = list_handles_entries[i];
>   		union drm_amdgpu_gem_mmap mmap_args = { 0 };
> +		int bo_contents_fd;
>   		int dmabuf_fd;
>   		uint32_t major, minor;
>   		amdgpu_device_handle h_dev;
>   		void *buffer = NULL;
>   		char img_path[40];
> -		FILE *bo_contents_fp = NULL;
>   		int device_fd;
>   
>   		boinfo->size = handle_entry.size;
> @@ -388,25 +387,24 @@ int amdgpu_plugin_drm_dump_file(int fd, int id, struct stat *drm)
>   
>   		snprintf(img_path, sizeof(img_path), IMG_DRM_PAGES_FILE, rd->id, rd->drm_render_minor, i);
>   		image_size = handle_entry.size;
> -		bo_contents_fp = open_img_file(img_path, true, &image_size);
> -		if (!bo_contents_fp) {
> -			ret = -EIO;
> +		bo_contents_fd = open_img_file(img_path, true, &image_size);
> +		if (bo_contents_fd < 0) {
> +			ret = bo_contents_fd;
>   			goto exit;
>   		}
>   
>   		posix_memalign(&buffer, sysconf(_SC_PAGE_SIZE), handle_entry.size);
>   
> -		ret = sdma_copy_bo(dmabuf_fd, handle_entry.size, bo_contents_fp, buffer, handle_entry.size, h_dev, 0x1000,
> +		ret = sdma_copy_bo(dmabuf_fd, handle_entry.size, bo_contents_fd,
> +				   buffer, handle_entry.size, h_dev, 0x1000,
>   				   SDMA_OP_VRAM_READ, false);
> +		close(bo_contents_fd);
>   		if (ret)
>   			goto exit;
>   
>   		if (dmabuf_fd != KFD_INVALID_FD)
>   			close(dmabuf_fd);
>   
> -		if (bo_contents_fp)
> -			fclose(bo_contents_fp);
> -
>   		ret = amdgpu_device_deinitialize(h_dev);
>   		if (ret)
>   			goto exit;
> diff --git a/plugins/amdgpu/amdgpu_plugin_util.c b/plugins/amdgpu/amdgpu_plugin_util.c
> index 5925624740e7..411348b2ce0e 100644
> --- a/plugins/amdgpu/amdgpu_plugin_util.c
> +++ b/plugins/amdgpu/amdgpu_plugin_util.c
> @@ -192,26 +192,38 @@ void clear_dumped_fds()
>   	}
>   }
>   
> -int read_fp(FILE *fp, void *buf, const size_t buf_len)
> +int img_read(int fd, void *buf, const size_t buf_len)
>   {
> -	size_t len_read;
> +	ssize_t len;
>   
> -	len_read = fread(buf, 1, buf_len, fp);
> -	if (len_read != buf_len) {
> -		pr_err("Unable to read file (read:%ld buf_len:%ld)\n", len_read, buf_len);
> -		return -EIO;
> +	len = read(fd, buf, buf_len);
> +	if (len < 0 || len != buf_len) {
> +		int ret;
> +
> +		if (len >= 0)
> +			errno = EIO;
> +		ret = -errno;
> +		pr_perror("Unable to read file (read:%ld buf_len:%ld)\n",
> +			  len, buf_len);
> +		return ret;
>   	}
>   	return 0;
>   }
>   
> -int write_fp(FILE *fp, const void *buf, const size_t buf_len)
> +int img_write(int fd, const void *buf, const size_t buf_len)
>   {
> -	size_t len_write;
> +	ssize_t len;
>   
> -	len_write = fwrite(buf, 1, buf_len, fp);
> -	if (len_write != buf_len) {
> -		pr_err("Unable to write file (wrote:%ld buf_len:%ld)\n", len_write, buf_len);
> -		return -EIO;
> +	len = write(fd, buf, buf_len);
> +	if (len < 0 || len != buf_len) {
> +		int ret;
> +
> +		if (len >= 0)
> +			errno = EIO;
> +		ret = -errno;
> +		pr_perror("Unable to write file (wrote:%ld buf_len:%ld)\n",
> +			  len, buf_len);
> +		return ret;
>   	}
>   	return 0;
>   }
> @@ -222,63 +234,60 @@ int write_fp(FILE *fp, const void *buf, const size_t buf_len)
>    * We store the size of the actual contents in the first 8-bytes of
>    * the file. This allows us to determine the file size when using
>    * criu_image_streamer when fseek and fstat are not available. The
> - * FILE * returned is already at the location of the first actual
> + * file descriptor returned is already at the location of the first actual
>    * contents.
>    *
>    * @param path The file path
>    * @param write False for read, true for write
>    * @param size Size of actual contents
> - * @return FILE *if successful, NULL if failed
> + * @return file descriptor if successful, negative integer if failed
>    */
> -FILE *open_img_file(char *path, bool write, size_t *size)
> +int open_img_file(char *path, bool write, size_t *size)
>   {
> -	FILE *fp = NULL;
>   	int fd, ret;
>   
> -	if (opts.stream)
> +	if (opts.stream) {
>   		fd = img_streamer_open(path, write ? O_DUMP : O_RSTR);
> -	else
> -		fd = openat(criu_get_image_dir(), path, write ? (O_WRONLY | O_CREAT) : O_RDONLY, 0600);
> -
> -	if (fd < 0) {
> -		pr_err("%s: Failed to open for %s\n", path, write ? "write" : "read");
> -		return NULL;
> +		if (fd < 0)
> +			errno = -fd;
> +	} else {
> +		fd = openat(criu_get_image_dir(), path,
> +			    write ? (O_WRONLY | O_CREAT) : O_RDONLY, 0600);
>   	}
>   
> -	fp = fdopen(fd, write ? "w" : "r");
> -	if (!fp) {
> -		pr_err("%s: Failed get pointer for %s\n", path, write ? "write" : "read");
> -		return NULL;
> +	if (fd < 0) {
> +		pr_perror("%s: Failed to open for %s\n",
> +			  path, write ? "write" : "read");
> +		return fd;
>   	}
>   
>   	if (write)
> -		ret = write_fp(fp, size, sizeof(*size));
> +		ret = img_write(fd, size, sizeof(*size));
>   	else
> -		ret = read_fp(fp, size, sizeof(*size));
> -
> +		ret = img_read(fd, size, sizeof(*size));
>   	if (ret) {
> -		pr_err("%s:Failed to access file size\n", path);
> -		fclose(fp);
> -		return NULL;
> +		close(fd);
> +		return ret;
>   	}
>   
>   	pr_debug("%s:Opened file for %s with size:%ld\n", path, write ? "write" : "read", *size);
> -	return fp;
> +	return fd;
>   }
>   
>   int read_file(const char *file_path, void *buf, const size_t buf_len)
>   {
> -	int ret;
> -	FILE *fp;
> +	int ret, fd;
>   
> -	fp = fopen(file_path, "r");
> -	if (!fp) {
> -		pr_err("Cannot fopen %s\n", file_path);
> -		return -errno;
> +	fd = open(file_path, O_RDONLY);
> +	if (fd < 0) {
> +		int ret = errno;
> +
> +		pr_perror("Cannot open %s\n", file_path);
> +		return -ret;
>   	}
>   
> -	ret = read_fp(fp, buf, buf_len);
> -	fclose(fp); /* this will also close fd */
> +	ret = img_read(fd, buf, buf_len);
> +	close(fd);
>   	return ret;
>   }
>   
> @@ -296,16 +305,15 @@ int read_file(const char *file_path, void *buf, const size_t buf_len)
>    */
>   int write_img_file(char *path, const void *buf, const size_t buf_len)
>   {
> -	int ret;
> -	FILE *fp;
> +	int ret, fd;
>   	size_t len = buf_len;
>   
> -	fp = open_img_file(path, true, &len);
> -	if (!fp)
> -		return -errno;
> +	fd = open_img_file(path, true, &len);
> +	if (fd < 0)
> +		return fd;
>   
> -	ret = write_fp(fp, buf, buf_len);
> -	fclose(fp); /* this will also close fd */
> +	ret = img_write(fd, buf, buf_len);
> +	close(fd);
>   	return ret;
>   }
>   
> diff --git a/plugins/amdgpu/amdgpu_plugin_util.h b/plugins/amdgpu/amdgpu_plugin_util.h
> index 014abf35ccb6..eb0c28674d18 100644
> --- a/plugins/amdgpu/amdgpu_plugin_util.h
> +++ b/plugins/amdgpu/amdgpu_plugin_util.h
> @@ -106,11 +106,11 @@ extern bool kfd_vram_size_check;
>   extern bool kfd_numa_check;
>   extern bool kfd_capability_check;
>   
> -int read_fp(FILE *fp, void *buf, const size_t buf_len);
> -int write_fp(FILE *fp, const void *buf, const size_t buf_len);
> +int img_read(int fd, void *buf, const size_t buf_len);
> +int img_write(int fd, const void *buf, const size_t buf_len);
>   int read_file(const char *file_path, void *buf, const size_t buf_len);
>   int write_img_file(char *path, const void *buf, const size_t buf_len);
> -FILE *open_img_file(char *path, bool write, size_t *size);
> +int open_img_file(char *path, bool write, size_t *size);
>   
>   int record_dumped_fd(int fd, bool is_drm);
>   struct list_head *get_dumped_fds();
> @@ -127,7 +127,7 @@ void clear_restore_state();
>   
>   void print_kfd_bo_stat(int bo_cnt, struct kfd_criu_bo_bucket *bo_list);
>   
> -int sdma_copy_bo(int shared_fd, uint64_t size, FILE *storage_fp,
> +int sdma_copy_bo(int shared_fd, uint64_t size, int storage_fd,
>   		 void *buffer, size_t buffer_size, amdgpu_device_handle h_dev,
>   		 uint64_t max_copy_size, enum sdma_op_type type, bool do_not_free);
>