Skip to content

Commit f85b1c6

Browse files
prati0100akpm00
authored andcommitted
liveupdate: luo_file: remember retrieve() status
LUO keeps track of successful retrieve attempts on a LUO file. It does so to avoid multiple retrievals of the same file. Multiple retrievals cause problems because once the file is retrieved, the serialized data structures are likely freed and the file is likely in a very different state from what the code expects. The retrieve boolean in struct luo_file keeps track of this, and is passed to the finish callback so it knows what work was already done and what it has left to do. All this works well when retrieve succeeds. When it fails, luo_retrieve_file() returns the error immediately, without ever storing anywhere that a retrieve was attempted or what its error code was. This results in an errored LIVEUPDATE_SESSION_RETRIEVE_FD ioctl to userspace, but nothing prevents it from trying this again. The retry is problematic for much of the same reasons listed above. The file is likely in a very different state than what the retrieve logic normally expects, and it might even have freed some serialization data structures. Attempting to access them or free them again is going to break things. For example, if memfd managed to restore 8 of its 10 folios, but fails on the 9th, a subsequent retrieve attempt will try to call kho_restore_folio() on the first folio again, and that will fail with a warning since it is an invalid operation. Apart from the retry, finish() also breaks. Since on failure the retrieved bool in luo_file is never touched, the finish() call on session close will tell the file handler that retrieve was never attempted, and it will try to access or free the data structures that might not exist, much in the same way as the retry attempt. There is no sane way of attempting the retrieve again. Remember the error retrieve returned and directly return it on a retry. Also pass this status code to finish() so it can make the right decision on the work it needs to do. This is done by changing the bool to an integer. A value of 0 means retrieve was never attempted, a positive value means it succeeded, and a negative value means it failed and the error code is the value. Link: https://lkml.kernel.org/r/20260216132221.987987-1-pratyush@kernel.org Fixes: 7c722a7 ("liveupdate: luo_file: implement file systems callbacks") Signed-off-by: Pratyush Yadav (Google) <pratyush@kernel.org> Reviewed-by: Mike Rapoport (Microsoft) <rppt@kernel.org> Cc: Pasha Tatashin <pasha.tatashin@soleen.com> Cc: <stable@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
1 parent dd085fe commit f85b1c6

3 files changed

Lines changed: 37 additions & 20 deletions

File tree

include/linux/liveupdate.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,11 @@ struct file;
2323
/**
2424
* struct liveupdate_file_op_args - Arguments for file operation callbacks.
2525
* @handler: The file handler being called.
26-
* @retrieved: The retrieve status for the 'can_finish / finish'
27-
* operation.
26+
* @retrieve_status: The retrieve status for the 'can_finish / finish'
27+
* operation. A value of 0 means the retrieve has not been
28+
* attempted, a positive value means the retrieve was
29+
* successful, and a negative value means the retrieve failed,
30+
* and the value is the error code of the call.
2831
* @file: The file object. For retrieve: [OUT] The callback sets
2932
* this to the new file. For other ops: [IN] The caller sets
3033
* this to the file being operated on.
@@ -40,7 +43,7 @@ struct file;
4043
*/
4144
struct liveupdate_file_op_args {
4245
struct liveupdate_file_handler *handler;
43-
bool retrieved;
46+
int retrieve_status;
4447
struct file *file;
4548
u64 serialized_data;
4649
void *private_data;

kernel/liveupdate/luo_file.c

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -134,9 +134,12 @@ static LIST_HEAD(luo_file_handler_list);
134134
* state that is not preserved. Set by the handler's .preserve()
135135
* callback, and must be freed in the handler's .unpreserve()
136136
* callback.
137-
* @retrieved: A flag indicating whether a user/kernel in the new kernel has
137+
* @retrieve_status: Status code indicating whether a user/kernel in the new kernel has
138138
* successfully called retrieve() on this file. This prevents
139-
* multiple retrieval attempts.
139+
* multiple retrieval attempts. A value of 0 means a retrieve()
140+
* has not been attempted, a positive value means the retrieve()
141+
* was successful, and a negative value means the retrieve()
142+
* failed, and the value is the error code of the call.
140143
* @mutex: A mutex that protects the fields of this specific instance
141144
* (e.g., @retrieved, @file), ensuring that operations like
142145
* retrieving or finishing a file are atomic.
@@ -161,7 +164,7 @@ struct luo_file {
161164
struct file *file;
162165
u64 serialized_data;
163166
void *private_data;
164-
bool retrieved;
167+
int retrieve_status;
165168
struct mutex mutex;
166169
struct list_head list;
167170
u64 token;
@@ -298,7 +301,6 @@ int luo_preserve_file(struct luo_file_set *file_set, u64 token, int fd)
298301
luo_file->file = file;
299302
luo_file->fh = fh;
300303
luo_file->token = token;
301-
luo_file->retrieved = false;
302304
mutex_init(&luo_file->mutex);
303305

304306
args.handler = fh;
@@ -577,7 +579,12 @@ int luo_retrieve_file(struct luo_file_set *file_set, u64 token,
577579
return -ENOENT;
578580

579581
guard(mutex)(&luo_file->mutex);
580-
if (luo_file->retrieved) {
582+
if (luo_file->retrieve_status < 0) {
583+
/* Retrieve was attempted and it failed. Return the error code. */
584+
return luo_file->retrieve_status;
585+
}
586+
587+
if (luo_file->retrieve_status > 0) {
581588
/*
582589
* Someone is asking for this file again, so get a reference
583590
* for them.
@@ -590,16 +597,19 @@ int luo_retrieve_file(struct luo_file_set *file_set, u64 token,
590597
args.handler = luo_file->fh;
591598
args.serialized_data = luo_file->serialized_data;
592599
err = luo_file->fh->ops->retrieve(&args);
593-
if (!err) {
594-
luo_file->file = args.file;
595-
596-
/* Get reference so we can keep this file in LUO until finish */
597-
get_file(luo_file->file);
598-
*filep = luo_file->file;
599-
luo_file->retrieved = true;
600+
if (err) {
601+
/* Keep the error code for later use. */
602+
luo_file->retrieve_status = err;
603+
return err;
600604
}
601605

602-
return err;
606+
luo_file->file = args.file;
607+
/* Get reference so we can keep this file in LUO until finish */
608+
get_file(luo_file->file);
609+
*filep = luo_file->file;
610+
luo_file->retrieve_status = 1;
611+
612+
return 0;
603613
}
604614

605615
static int luo_file_can_finish_one(struct luo_file_set *file_set,
@@ -615,7 +625,7 @@ static int luo_file_can_finish_one(struct luo_file_set *file_set,
615625
args.handler = luo_file->fh;
616626
args.file = luo_file->file;
617627
args.serialized_data = luo_file->serialized_data;
618-
args.retrieved = luo_file->retrieved;
628+
args.retrieve_status = luo_file->retrieve_status;
619629
can_finish = luo_file->fh->ops->can_finish(&args);
620630
}
621631

@@ -632,7 +642,7 @@ static void luo_file_finish_one(struct luo_file_set *file_set,
632642
args.handler = luo_file->fh;
633643
args.file = luo_file->file;
634644
args.serialized_data = luo_file->serialized_data;
635-
args.retrieved = luo_file->retrieved;
645+
args.retrieve_status = luo_file->retrieve_status;
636646

637647
luo_file->fh->ops->finish(&args);
638648
luo_flb_file_finish(luo_file->fh);
@@ -788,7 +798,6 @@ int luo_file_deserialize(struct luo_file_set *file_set,
788798
luo_file->file = NULL;
789799
luo_file->serialized_data = file_ser[i].data;
790800
luo_file->token = file_ser[i].token;
791-
luo_file->retrieved = false;
792801
mutex_init(&luo_file->mutex);
793802
list_add_tail(&luo_file->list, &file_set->files_list);
794803
}

mm/memfd_luo.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,12 @@ static void memfd_luo_finish(struct liveupdate_file_op_args *args)
326326
struct memfd_luo_folio_ser *folios_ser;
327327
struct memfd_luo_ser *ser;
328328

329-
if (args->retrieved)
329+
/*
330+
* If retrieve was successful, nothing to do. If it failed, retrieve()
331+
* already cleaned up everything it could. So nothing to do there
332+
* either. Only need to clean up when retrieve was not called.
333+
*/
334+
if (args->retrieve_status)
330335
return;
331336

332337
ser = phys_to_virt(args->serialized_data);

0 commit comments

Comments
 (0)