Acquire the swap device reference at the point the swap device is looked up and release it at each exit path, rather than grabbing and dropping it on every slot allocation. This also fixes a race where only the swap type value was retrieved at lookup time without holding a reference. If swapoff raced after the type was acquired, subsequent operations would reference a stale swap device. put_swap_device_by_type() is now placed at all relevant cleanup paths in both swap.c and user.c to ensure the reference is properly released when the hibernation swap operation completes or fails. Signed-off-by: Youngjun Park --- kernel/power/swap.c | 12 +++++++++--- kernel/power/user.c | 9 ++++++++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/kernel/power/swap.c b/kernel/power/swap.c index 2e64869bb5a0..c230b0fa5a5f 100644 --- a/kernel/power/swap.c +++ b/kernel/power/swap.c @@ -350,9 +350,10 @@ static int swsusp_swap_check(void) hib_resume_bdev_file = bdev_file_open_by_dev(swsusp_resume_device, BLK_OPEN_WRITE, NULL, NULL); - if (IS_ERR(hib_resume_bdev_file)) + if (IS_ERR(hib_resume_bdev_file)) { + put_swap_device_by_type(root_swap); return PTR_ERR(hib_resume_bdev_file); - + } return 0; } @@ -418,6 +419,7 @@ static int get_swap_writer(struct swap_map_handle *handle) err_rel: release_swap_writer(handle); err_close: + put_swap_device_by_type(root_swap); swsusp_close(); return ret; } @@ -480,8 +482,11 @@ static int swap_writer_finish(struct swap_map_handle *handle, flush_swap_writer(handle); } - if (error) + if (error) { free_all_swap_pages(root_swap); + put_swap_device_by_type(root_swap); + } + release_swap_writer(handle); swsusp_close(); @@ -1647,6 +1652,7 @@ int swsusp_unmark(void) * We just returned from suspend, we don't need the image any more. */ free_all_swap_pages(root_swap); + put_swap_device_by_type(root_swap); return error; } diff --git a/kernel/power/user.c b/kernel/power/user.c index 4401cfe26e5c..9cb6c24d49ea 100644 --- a/kernel/power/user.c +++ b/kernel/power/user.c @@ -90,8 +90,11 @@ static int snapshot_open(struct inode *inode, struct file *filp) data->free_bitmaps = !error; } } - if (error) + if (error) { hibernate_release(); + if (data->swap >= 0) + put_swap_device_by_type(data->swap); + } data->frozen = false; data->ready = false; @@ -115,6 +118,8 @@ static int snapshot_release(struct inode *inode, struct file *filp) data = filp->private_data; data->dev = 0; free_all_swap_pages(data->swap); + if (data->swap >= 0) + put_swap_device_by_type(data->swap); if (data->frozen) { pm_restore_gfp_mask(); free_basic_memory_bitmaps(); @@ -235,6 +240,8 @@ static int snapshot_set_swap_area(struct snapshot_data *data, offset = swap_area.offset; } + if (data->swap >= 0) + put_swap_device_by_type(data->swap); /* * User space encodes device types as two-byte values, * so we need to recode them -- 2.34.1