From nobody Sun Oct 5 09:20:46 2025 Received: from mail-qt1-f182.google.com (mail-qt1-f182.google.com [209.85.160.182]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D63B3185E4A for ; Mon, 29 Sep 2025 01:04:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.160.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759107873; cv=none; b=ekC4fSnVzUJL9CJitAlG0atgKZkqT5T24dT5Y4MHRx+KPGY3yT9+ApdhwkuvZhs2vSzIqxr8Vnw9g+rH/7dAlsIU7ZoL4o8TyDiB5Rc49wh80fbW1DGlU8sNwEi2DN8v2j4wcy0OOlEJaIpMnm+g2jHUNjro30kawl6lDLIB77g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759107873; c=relaxed/simple; bh=Y7nqGbM4CxQL86kixsX259srVrd5urtxz+FvNAryO8g=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=pBaePG8CkyXBQDXpMaDq+yGnMZDa20IsPImO7H3JyY+jpbShNArWUzdihhLN9D0j2l6C8A38Tg98H0GFc+SagtrBXt5UU8sOlHtbGkL6V/imhHxC8yuxy1CzJEA80h4h0UD8kq7908N3+DdasQ6JLle+6/KPPVAR4kPEOFi3sIw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=soleen.com; spf=pass smtp.mailfrom=soleen.com; dkim=pass (2048-bit key) header.d=soleen.com header.i=@soleen.com header.b=FUvUjTul; arc=none smtp.client-ip=209.85.160.182 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=soleen.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=soleen.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=soleen.com header.i=@soleen.com header.b="FUvUjTul" Received: by mail-qt1-f182.google.com with SMTP id d75a77b69052e-4df4d23fb59so16773991cf.1 for ; Sun, 28 Sep 2025 18:04:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=soleen.com; s=google; t=1759107870; x=1759712670; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:from:to:cc:subject:date:message-id :reply-to; bh=zcSgDH9VLG2SUi6g3+OVDiDVEtFrrRUlaRrL6JR5uy0=; b=FUvUjTulAcdL0Hy8nICHHKSiz4BRUYwCLvUG/OMnwizHU3hoOuUCwkFFK5kO5eFjtH WN61fs5sMjCB53dKMEEmdviCxsa1QIqH20uoxvDJgRv6R12h+RyjmYBYxRg9M/7L8TMh HkP3nK7MZdlZqoW27b5GfnzBLpcMDypmFiByGIbJriynIkEKNZ6kpO7nir/2DcaUuC+m iD9Eivck40OG/ruaBuaUNyym9Aebljh9NoDsQCq4v2Ks3/ADahe65xDmLno6h8AODh2v o1/PZh4G3/n/0wSi9XWVq3wkP+XWrYs2IxvzviHlkp0aOjs/FB2hjAxRYtcXaTWSWfxm FUlQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1759107870; x=1759712670; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=zcSgDH9VLG2SUi6g3+OVDiDVEtFrrRUlaRrL6JR5uy0=; b=rePOnMLn4qG9WNmChqNxQZ/q1Bk61U/PkFbJdOi4g0lV+DzuilZuJiLtfcF7TGiuom YeWX+CXh+LZWlYIRwQg15F9CBAA91kfKLZaquwInn++DdznaYd3WXYG5cx8O1QD7k1Iu p+hAE8gtJHWWAUiA39MJGT/9+mvj2oiqQ17LlY4fRtODujs74W995BOmAnIVJibNXN0i TLAgqP5Bzz2Cpa8MGZ5FwG9p7WEba+kYHYOP7QzLn0ZTjm+d3+HI9hYP0S5Jj5r5Js5T PSY4Kl69U/B5lB5KnIkD+B8DQ9adJw7ltx7Afuna6bR81Pt2A19x65bIxrej8rUvWFzn iBRw== X-Forwarded-Encrypted: i=1; AJvYcCXGQEMtdSodrRpm0/GvcTxzEjLC8M3x9zpUhO2I71MaoPlVhNF7/StODncA21x20+3zcslxAfAkk7yV6mQ=@vger.kernel.org X-Gm-Message-State: AOJu0YyZm8dwl7trXsYWkVRqW/UZi0dBwXgchdtDR2H19PfVp/nGHHpW qjMrMunIF5CatEfwzc2tv4CV2Tchmv63suQGF3kBC9Fdo3W1Y5nPu3X2JT9/+MpYRgk= X-Gm-Gg: ASbGncst/cNIYsTx9TBmjPJ8jNlBEQlmG9m/y7GIVxjoQ+9VYw8ikLT9+84UF2u5nM0 sU8H4U/nEmcgEgGyyK5VohzqHeMsbGfTb3qLL68WULU7aCL4n0TOPZM44KwYO7tGHMFCYVjFTK8 o4sSsmWQfcJ9P3OKVpSJvF5XBng0jw8dteK/R8ajWGo/M9qaTqK1/jcRICy7/pxnfCtphT/YZGv C3e3Ubat/wVNDRck3DuZxSPKdu5j+NuXRst3ztMttx2okvURW6iEB55GIm7Hr97KcuxLTOoZk+g GPMEY01J620/tJrrsL7QPBWjjRQ0jJdUaLHnoYdBO6GuNnQ5AkpRRrjx022n9y7oqg1NoggVfcR FGbuOaM3pLK7XZeQ99xoA5yTqejv+rH52tPZKYue/3V5Up6MutLcOa0WET6luJrgmgjpU0ehFt4 /o77Oq5dWJvgvWHUbsKA== X-Google-Smtp-Source: AGHT+IGF0S9IclOBJkoE08yEa53PAxQoR+uRPkimKbM/oD4UzxexMGmwGzEkKHwSpei9vqN/FCsqQA== X-Received: by 2002:ac8:5f53:0:b0:4b9:d7c2:756a with SMTP id d75a77b69052e-4da4cd49c0cmr194938831cf.77.1759107869557; Sun, 28 Sep 2025 18:04:29 -0700 (PDT) Received: from soleen.c.googlers.com.com (53.47.86.34.bc.googleusercontent.com. [34.86.47.53]) by smtp.gmail.com with ESMTPSA id d75a77b69052e-4db0c0fbe63sm64561521cf.23.2025.09.28.18.04.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 28 Sep 2025 18:04:28 -0700 (PDT) From: Pasha Tatashin To: pratyush@kernel.org, jasonmiu@google.com, graf@amazon.com, changyuanl@google.com, pasha.tatashin@soleen.com, rppt@kernel.org, dmatlack@google.com, rientjes@google.com, corbet@lwn.net, rdunlap@infradead.org, ilpo.jarvinen@linux.intel.com, kanie@linux.alibaba.com, ojeda@kernel.org, aliceryhl@google.com, masahiroy@kernel.org, akpm@linux-foundation.org, tj@kernel.org, yoann.congal@smile.fr, mmaurer@google.com, roman.gushchin@linux.dev, chenridong@huawei.com, axboe@kernel.dk, mark.rutland@arm.com, jannh@google.com, vincent.guittot@linaro.org, hannes@cmpxchg.org, dan.j.williams@intel.com, david@redhat.com, joel.granados@kernel.org, rostedt@goodmis.org, anna.schumaker@oracle.com, song@kernel.org, zhangguopeng@kylinos.cn, linux@weissschuh.net, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-mm@kvack.org, gregkh@linuxfoundation.org, tglx@linutronix.de, mingo@redhat.com, bp@alien8.de, dave.hansen@linux.intel.com, x86@kernel.org, hpa@zytor.com, rafael@kernel.org, dakr@kernel.org, bartosz.golaszewski@linaro.org, cw00.choi@samsung.com, myungjoo.ham@samsung.com, yesanishhere@gmail.com, Jonathan.Cameron@huawei.com, quic_zijuhu@quicinc.com, aleksander.lobakin@intel.com, ira.weiny@intel.com, andriy.shevchenko@linux.intel.com, leon@kernel.org, lukas@wunner.de, bhelgaas@google.com, wagi@kernel.org, djeffery@redhat.com, stuart.w.hayes@gmail.com, ptyadav@amazon.de, lennart@poettering.net, brauner@kernel.org, linux-api@vger.kernel.org, linux-fsdevel@vger.kernel.org, saeedm@nvidia.com, ajayachandra@nvidia.com, jgg@nvidia.com, parav@nvidia.com, leonro@nvidia.com, witu@nvidia.com, hughd@google.com, skhawaja@google.com, chrisl@kernel.org, steven.sistare@oracle.com Subject: [PATCH v4 12/30] liveupdate: luo_ioctl: add user interface Date: Mon, 29 Sep 2025 01:03:03 +0000 Message-ID: <20250929010321.3462457-13-pasha.tatashin@soleen.com> X-Mailer: git-send-email 2.51.0.536.g15c5d4f767-goog In-Reply-To: <20250929010321.3462457-1-pasha.tatashin@soleen.com> References: <20250929010321.3462457-1-pasha.tatashin@soleen.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Introduce the user-space interface for the Live Update Orchestrator via ioctl commands, enabling external control over the live update process and management of preserved resources. The idea is that there is going to be a single userspace agent driving the live update, therefore, only a single process can ever hold this device opened at a time. The following ioctl commands are introduced: LIVEUPDATE_IOCTL_GET_STATE Allows userspace to query the current state of the LUO state machine (e.g., NORMAL, PREPARED, UPDATED). LIVEUPDATE_IOCTL_SET_EVENT Enables userspace to drive the LUO state machine by sending global events. This includes: LIVEUPDATE_PREPARE To begin the state-saving process. LIVEUPDATE_FINISH To signal completion of restoration in the new kernel. LIVEUPDATE_CANCEL To abort a prepared update. LIVEUPDATE_IOCTL_CREATE_SESSION Provides a way for userspace to create a named session for grouping file descriptors that need to be preserved. It returns a new file descriptor representing the session. LIVEUPDATE_IOCTL_RETRIEVE_SESSION Allows the userspace agent in the new kernel to reclaim a preserved session by its name, receiving a new file descriptor to manage the restored resources. Signed-off-by: Pasha Tatashin --- include/uapi/linux/liveupdate.h | 199 ++++++++++++++++++++++++++++++ kernel/liveupdate/luo_internal.h | 20 +++ kernel/liveupdate/luo_ioctl.c | 201 +++++++++++++++++++++++++++++++ 3 files changed, 420 insertions(+) diff --git a/include/uapi/linux/liveupdate.h b/include/uapi/linux/liveupdat= e.h index e8c0c210a790..2e38ef3094aa 100644 --- a/include/uapi/linux/liveupdate.h +++ b/include/uapi/linux/liveupdate.h @@ -14,6 +14,32 @@ #include #include =20 +/** + * DOC: General ioctl format + * + * The ioctl interface follows a general format to allow for extensibility= . Each + * ioctl is passed in a structure pointer as the argument providing the si= ze of + * the structure in the first u32. The kernel checks that any structure sp= ace + * beyond what it understands is 0. This allows userspace to use the backw= ard + * compatible portion while consistently using the newer, larger, structur= es. + * + * ioctls use a standard meaning for common errnos: + * + * - ENOTTY: The IOCTL number itself is not supported at all + * - E2BIG: The IOCTL number is supported, but the provided structure has + * non-zero in a part the kernel does not understand. + * - EOPNOTSUPP: The IOCTL number is supported, and the structure is + * understood, however a known field has a value the kernel does not + * understand or support. + * - EINVAL: Everything about the IOCTL was understood, but a field is not + * correct. + * - ENOENT: A provided token does not exist. + * - ENOMEM: Out of memory. + * - EOVERFLOW: Mathematics overflowed. + * + * As well as additional errnos, within specific ioctls. + */ + /** * enum liveupdate_state - Defines the possible states of the live update * orchestrator. @@ -94,4 +120,177 @@ enum liveupdate_event { /* The maximum length of session name including null termination */ #define LIVEUPDATE_SESSION_NAME_LENGTH 56 =20 +/* The ioctl type, documented in ioctl-number.rst */ +#define LIVEUPDATE_IOCTL_TYPE 0xBA + +/* The /dev/liveupdate ioctl commands */ +enum { + LIVEUPDATE_CMD_BASE =3D 0x00, + LIVEUPDATE_CMD_GET_STATE =3D LIVEUPDATE_CMD_BASE, + LIVEUPDATE_CMD_SET_EVENT =3D 0x01, + LIVEUPDATE_CMD_CREATE_SESSION =3D 0x02, + LIVEUPDATE_CMD_RETRIEVE_SESSION =3D 0x03, +}; + +/** + * struct liveupdate_ioctl_get_state - ioctl(LIVEUPDATE_IOCTL_GET_STATE) + * @size: Input; sizeof(struct liveupdate_ioctl_get_state) + * @state: Output; The current live update state. + * + * Query the current state of the live update orchestrator. + * + * The kernel fills the @state with the current + * state of the live update subsystem. Possible states are: + * + * - %LIVEUPDATE_STATE_NORMAL: Default state; no live update operation is + * currently in progress. + * - %LIVEUPDATE_STATE_PREPARED: The preparation phase (triggered by + * %LIVEUPDATE_PREPARE) has completed + * successfully. The system is ready for the + * reboot transition. Note that some + * device operations (e.g., unbinding, new D= MA + * mappings) might be restricted in this sta= te. + * - %LIVEUPDATE_STATE_UPDATED: The system has successfully rebooted into= the + * new kernel via live update. It is now run= ning + * the new kernel code and is awaiting the + * completion signal from user space via + * %LIVEUPDATE_FINISH after restoration task= s are + * done. + * + * See the definition of &enum liveupdate_state for more details on each s= tate. + * + * Return: 0 on success, negative error code on failure. + */ +struct liveupdate_ioctl_get_state { + __u32 size; + __u32 state; +}; + +#define LIVEUPDATE_IOCTL_GET_STATE \ + _IO(LIVEUPDATE_IOCTL_TYPE, LIVEUPDATE_CMD_GET_STATE) + +/** + * struct liveupdate_ioctl_set_event - ioctl(LIVEUPDATE_IOCTL_SET_EVENT) + * @size: Input; sizeof(struct liveupdate_ioctl_set_event) + * @event: Input; The live update event. + * + * Notify live update orchestrator about global event, that causes a state + * transition. + * + * Event, can be one of the following: + * + * - %LIVEUPDATE_PREPARE: Initiates the live update preparation phase. This + * typically triggers the saving process for items = marked + * via the PRESERVE ioctls. This typically occurs + * *before* the "blackout window", while user + * applications (e.g., VMs) may still be running. K= ernel + * subsystems receiving the %LIVEUPDATE_PREPARE eve= nt + * should serialize necessary state. This command d= oes + * not transfer data. + * - %LIVEUPDATE_FINISH: Signal restoration completion and triggercleanup. + * + * Signals that user space has completed all necess= ary + * restoration actions in the new kernel (after a l= ive + * update reboot). Calling this ioctl triggers the + * cleanup phase: any resources that were successfu= lly + * preserved but were *not* subsequently restored + * (reclaimed) via the RESTORE ioctls will have the= ir + * preserved state discarded and associated kernel + * resources released. Involved devices may be rese= t. All + * desired restorations *must* be completed *before* + * this. Kernel callbacks for the %LIVEUPDATE_FINISH + * event must not fail. Successfully completing this + * phase transitions the system state from + * %LIVEUPDATE_STATE_UPDATED back to + * %LIVEUPDATE_STATE_NORMAL. This command does + * not transfer data. + * - %LIVEUPDATE_CANCEL: Cancel the live update preparation phase. + * + * Notifies the live update subsystem to abort the + * preparation sequence potentially initiated by + * %LIVEUPDATE_PREPARE event. + * + * When triggered, subsystems receiving the + * %LIVEUPDATE_CANCEL event should revert any state + * changes or actions taken specifically for the ab= orted + * prepare phase (e.g., discard partially serialized + * state). The kernel releases resources allocated + * specifically for this *aborted preparation attem= pt*. + * + * This operation cancels the current *attempt* to + * prepare for a live update but does **not** remove + * previously validated items from the internal list + * of potentially preservable resources. + * + * This command does not transfer data. Kernel call= backs + * for the %LIVEUPDATE_CANCEL event must not fail. + * + * See the definition of &enum liveupdate_event for more details on each s= tate. + * + * Return: 0 on success, negative error code on failure. + */ +struct liveupdate_ioctl_set_event { + __u32 size; + __u32 event; +}; + +#define LIVEUPDATE_IOCTL_SET_EVENT \ + _IO(LIVEUPDATE_IOCTL_TYPE, LIVEUPDATE_CMD_SET_EVENT) + +/** + * struct liveupdate_ioctl_create_session - ioctl(LIVEUPDATE_IOCTL_CREATE_= SESSION) + * @size: Input; sizeof(struct liveupdate_ioctl_create_session) + * @fd: Output; The new file descriptor for the created session. + * @name: Input; A null-terminated string for the session name, max + * length %LIVEUPDATE_SESSION_NAME_LENGTH including termination + * char. + * + * Creates a new live update session for managing preserved resources. + * This ioctl can only be called on the main /dev/liveupdate device. + * + * Return: 0 on success, negative error code on failure. + */ +struct liveupdate_ioctl_create_session { + __u32 size; + __s32 fd; + __u8 name[LIVEUPDATE_SESSION_NAME_LENGTH]; +}; + +#define LIVEUPDATE_IOCTL_CREATE_SESSION \ + _IO(LIVEUPDATE_IOCTL_TYPE, LIVEUPDATE_CMD_CREATE_SESSION) + +/** + * struct liveupdate_ioctl_retrieve_session - ioctl(LIVEUPDATE_IOCTL_RETRI= EVE_SESSION) + * @size: Input; sizeof(struct liveupdate_ioctl_retrieve_session) + * @fd: Output; The new file descriptor for the retrieved session. + * @name: Input; A null-terminated string identifying the session to re= trieve. + * The name must exactly match the name used when the session was + * created in the previous kernel. + * + * Retrieves a handle (a new file descriptor) for a preserved session by i= ts + * name. This is the primary mechanism for a userspace agent to regain con= trol + * of its preserved resources after a live update. + * + * The userspace application provides the null-terminated `name` of a sess= ion + * it created before the live update. If a preserved session with a matchi= ng + * name is found, the kernel instantiates it and returns a new file descri= ptor + * in the `fd` field. This new session FD can then be used for all file-sp= ecific + * operations, such as restoring individual file descriptors with + * LIVEUPDATE_SESSION_RESTORE_FD. + * + * It is the responsibility of the userspace application to know the names= of + * the sessions it needs to retrieve. If no session with the given name is + * found, the ioctl will fail with -ENOENT. + * + * This ioctl can only be called on the main /dev/liveupdate device when t= he + * system is in the LIVEUPDATE_STATE_UPDATED state. + */ +struct liveupdate_ioctl_retrieve_session { + __u32 size; + __s32 fd; + __u8 name[64]; +}; + +#define LIVEUPDATE_IOCTL_RETRIEVE_SESSION \ + _IO(LIVEUPDATE_IOCTL_TYPE, LIVEUPDATE_CMD_RETRIEVE_SESSION) #endif /* _UAPI_LIVEUPDATE_H */ diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_inter= nal.h index 9223f71844ca..a14e0b685ccb 100644 --- a/kernel/liveupdate/luo_internal.h +++ b/kernel/liveupdate/luo_internal.h @@ -17,6 +17,26 @@ */ #define luo_restore_fail(__fmt, ...) panic(__fmt, ##__VA_ARGS__) =20 +struct luo_ucmd { + void __user *ubuffer; + u32 user_size; + void *cmd; +}; + +static inline int luo_ucmd_respond(struct luo_ucmd *ucmd, + size_t kernel_cmd_size) +{ + /* + * Copy the minimum of what the user provided and what we actually + * have. + */ + if (copy_to_user(ucmd->ubuffer, ucmd->cmd, + min_t(size_t, ucmd->user_size, kernel_cmd_size))) { + return -EFAULT; + } + return 0; +} + int luo_cancel(void); int luo_prepare(void); int luo_freeze(void); diff --git a/kernel/liveupdate/luo_ioctl.c b/kernel/liveupdate/luo_ioctl.c index fc2afb450ad5..01ccb8a6d3f4 100644 --- a/kernel/liveupdate/luo_ioctl.c +++ b/kernel/liveupdate/luo_ioctl.c @@ -5,6 +5,25 @@ * Pasha Tatashin */ =20 +/** + * DOC: LUO ioctl Interface + * + * The IOCTL user-space control interface for the LUO subsystem. + * It registers a character device, typically found at ``/dev/liveupdate``, + * which allows a userspace agent to manage the LUO state machine and its + * associated resources, such as preservable file descriptors. + * + * To ensure that the state machine is controlled by a single entity, acce= ss + * to this device is exclusive: only one process is permitted to have + * ``/dev/liveupdate`` open at any given time. Subsequent open attempts wi= ll + * fail with -EBUSY until the first process closes its file descriptor. + * This singleton model simplifies state management by preventing conflict= ing + * commands from multiple userspace agents. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include #include #include #include @@ -19,10 +38,191 @@ =20 struct luo_device_state { struct miscdevice miscdev; + atomic_t in_use; +}; + +static int luo_ioctl_get_state(struct luo_ucmd *ucmd) +{ + struct liveupdate_ioctl_get_state *argp =3D ucmd->cmd; + + argp->state =3D liveupdate_get_state(); + + return luo_ucmd_respond(ucmd, sizeof(*argp)); +} + +static int luo_ioctl_set_event(struct luo_ucmd *ucmd) +{ + struct liveupdate_ioctl_set_event *argp =3D ucmd->cmd; + int ret; + + switch (argp->event) { + case LIVEUPDATE_PREPARE: + ret =3D luo_prepare(); + break; + case LIVEUPDATE_FINISH: + ret =3D luo_finish(); + break; + case LIVEUPDATE_CANCEL: + ret =3D luo_cancel(); + break; + default: + ret =3D -EOPNOTSUPP; + } + + return ret; +} + +static int luo_ioctl_create_session(struct luo_ucmd *ucmd) +{ + struct liveupdate_ioctl_create_session *argp =3D ucmd->cmd; + struct file *file; + int ret; + + argp->fd =3D get_unused_fd_flags(O_CLOEXEC); + if (argp->fd < 0) + return argp->fd; + + ret =3D luo_session_create(argp->name, &file); + if (ret) + return ret; + + ret =3D luo_ucmd_respond(ucmd, sizeof(*argp)); + if (ret) { + fput(file); + put_unused_fd(argp->fd); + return ret; + } + + fd_install(argp->fd, file); + + return 0; +} + +static int luo_ioctl_retrieve_session(struct luo_ucmd *ucmd) +{ + struct liveupdate_ioctl_retrieve_session *argp =3D ucmd->cmd; + struct file *file; + int ret; + + argp->fd =3D get_unused_fd_flags(O_CLOEXEC); + if (argp->fd < 0) + return argp->fd; + + ret =3D luo_session_retrieve(argp->name, &file); + if (ret < 0) { + put_unused_fd(argp->fd); + + return ret; + } + + ret =3D luo_ucmd_respond(ucmd, sizeof(*argp)); + if (ret) { + fput(file); + put_unused_fd(argp->fd); + return ret; + } + + fd_install(argp->fd, file); + + return 0; +} + +static int luo_open(struct inode *inodep, struct file *filep) +{ + struct luo_device_state *ldev =3D container_of(filep->private_data, + struct luo_device_state, + miscdev); + + if (atomic_cmpxchg(&ldev->in_use, 0, 1)) + return -EBUSY; + + return 0; +} + +static int luo_release(struct inode *inodep, struct file *filep) +{ + struct luo_device_state *ldev =3D container_of(filep->private_data, + struct luo_device_state, + miscdev); + atomic_set(&ldev->in_use, 0); + + return 0; +} + +union ucmd_buffer { + struct liveupdate_ioctl_create_session create; + struct liveupdate_ioctl_get_state state; + struct liveupdate_ioctl_retrieve_session retrieve; + struct liveupdate_ioctl_set_event event; }; =20 +struct luo_ioctl_op { + unsigned int size; + unsigned int min_size; + unsigned int ioctl_num; + int (*execute)(struct luo_ucmd *ucmd); +}; + +#define IOCTL_OP(_ioctl, _fn, _struct, _last) = \ + [_IOC_NR(_ioctl) - LIVEUPDATE_CMD_BASE] =3D { \ + .size =3D sizeof(_struct) + \ + BUILD_BUG_ON_ZERO(sizeof(union ucmd_buffer) < \ + sizeof(_struct)), \ + .min_size =3D offsetofend(_struct, _last), \ + .ioctl_num =3D _ioctl, \ + .execute =3D _fn, \ + } + +static const struct luo_ioctl_op luo_ioctl_ops[] =3D { + IOCTL_OP(LIVEUPDATE_IOCTL_CREATE_SESSION, luo_ioctl_create_session, + struct liveupdate_ioctl_create_session, name), + IOCTL_OP(LIVEUPDATE_IOCTL_GET_STATE, luo_ioctl_get_state, + struct liveupdate_ioctl_get_state, state), + IOCTL_OP(LIVEUPDATE_IOCTL_RETRIEVE_SESSION, luo_ioctl_retrieve_session, + struct liveupdate_ioctl_retrieve_session, name), + IOCTL_OP(LIVEUPDATE_IOCTL_SET_EVENT, luo_ioctl_set_event, + struct liveupdate_ioctl_set_event, event), +}; + +static long luo_ioctl(struct file *filep, unsigned int cmd, unsigned long = arg) +{ + const struct luo_ioctl_op *op; + struct luo_ucmd ucmd =3D {}; + union ucmd_buffer buf; + unsigned int nr; + int ret; + + nr =3D _IOC_NR(cmd); + if (nr < LIVEUPDATE_CMD_BASE || + (nr - LIVEUPDATE_CMD_BASE) >=3D ARRAY_SIZE(luo_ioctl_ops)) { + return -EINVAL; + } + + ucmd.ubuffer =3D (void __user *)arg; + ret =3D get_user(ucmd.user_size, (u32 __user *)ucmd.ubuffer); + if (ret) + return ret; + + op =3D &luo_ioctl_ops[nr - LIVEUPDATE_CMD_BASE]; + if (op->ioctl_num !=3D cmd) + return -ENOIOCTLCMD; + if (ucmd.user_size < op->min_size) + return -EINVAL; + + ucmd.cmd =3D &buf; + ret =3D copy_struct_from_user(ucmd.cmd, op->size, ucmd.ubuffer, + ucmd.user_size); + if (ret) + return ret; + + return op->execute(&ucmd); +} + static const struct file_operations luo_fops =3D { .owner =3D THIS_MODULE, + .open =3D luo_open, + .release =3D luo_release, + .unlocked_ioctl =3D luo_ioctl, }; =20 static struct luo_device_state luo_dev =3D { @@ -31,6 +231,7 @@ static struct luo_device_state luo_dev =3D { .name =3D "liveupdate", .fops =3D &luo_fops, }, + .in_use =3D ATOMIC_INIT(0), }; =20 static int __init liveupdate_init(void) --=20 2.51.0.536.g15c5d4f767-goog