From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-pj1-f74.google.com (mail-pj1-f74.google.com [209.85.216.74]) (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 24167303A3D for ; Tue, 2 Dec 2025 23:03:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.74 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764716611; cv=none; b=kNs5QgjX9RFEokLGflLg/hdfzswj4wWfpHQwGOHOg8ovaGMYXefGSo81nluIP8obrdCAuifR4vkrCEZITTT/EA7UtzBJw5m3zG0CqmtO7KOG8EvYNnleg7XwYu96yxuoGu1hwUVanMvzZX+8yhMjUPYWj0+pQ43csb+Af2iTRVA= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764716611; c=relaxed/simple; bh=mCNNwuyvQrPxXIJnD3o+3qG9HX5gin7Atc9BmlenXE8=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=idZb9YojgB8+kaNqY6cL1PP/ORPyiXUyEAB0JDCPacxbnwW4ZsxrAWbn9dIvhzwhJ4RxmFqb+naex/9m7/X9TT8solrw4Fo/6tWV0JbEws7RPsKvOd/SliEzB5WJsRQOtNEqlfD/VD69GSF4aLPfIFM6h+SykfibhdEy9Xoz1mk= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--skhawaja.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=O9nP14ra; arc=none smtp.client-ip=209.85.216.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--skhawaja.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="O9nP14ra" Received: by mail-pj1-f74.google.com with SMTP id 98e67ed59e1d1-342701608e2so6137397a91.1 for ; Tue, 02 Dec 2025 15:03:29 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1764716609; x=1765321409; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=nzO62LzLWhogZFr6q9clLaPPERMHuTUpUvC1p+wqPE0=; b=O9nP14raVDB/lbPYrouThAORCWaK8/kCLv5/w4cvHnNVWNJEGUJc4Z522fN8llEAYp c2fe3KP3+kCDCvK4GINZX/HZ5T/zfSTp+4VNuK860afEqyNzwGtrBkV3wOaYE6ttMD2D guEJH3R0U1zvLcZUAEimbmHLFvw8TbAHdlrQ30Fz9jiY3RwPPpAKRkb9AdXlMdhUY4wG cRO3o79hZ/cgISYlUFofKlCG+HLNoAToHOXDsRray3OnEsvW5CZgY9V4Zg4T+OSFlKqS K0U/QUmoteYZYl8wdjoYKvZWGJXAn/kBlXKjQVYfugodMrp0vRt/ZnISqiGsbxIKnMjv qKGg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1764716609; x=1765321409; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=nzO62LzLWhogZFr6q9clLaPPERMHuTUpUvC1p+wqPE0=; b=nNkkSzMqtcP4fRm5z3yxHC+/fJqAgO1PSfU/CNU8wlWBXFmPQBDuuopv3QsUh7e6wz WOjzzPvzGmK0EiVsbLbnyLo8ZjVaDgwPGjB6A0xUoXb4sQ9vF8liXfVHr3LLMZ1zWJKJ 4idKP0MIUwvSITrc93MeX25Or1b7z6FicgROMdI6M/gIzqP23FqH1O5r+TsIdfhmjDzl RX1qRKN+9udO2WzyJgxdaznIlKWjfcgRKLxvldQE4o0wUUtKz7wuGhBkethucLDSKpE0 NTvOFORV6efgyn61ivP15gttmp+yuCDzRmXEcqduYmdvNyubDqAXFEKGnkIf22HuYUcY hIBA== X-Forwarded-Encrypted: i=1; AJvYcCVah0cfTigNNstbDqB8dNNMYjPmXyjjayj5XhbMCiDPcNT65C2OPxgjSMIV56pGH6LKEafjeXnTgWnugio=@vger.kernel.org X-Gm-Message-State: AOJu0YxzbQDyuvrZmzRJXLas1LJYn/PpAo9G6DV1WNgKWzX05CB0wR5j nQYJq9UnPRuQmwDsZ4j89k17i7tXlcygRPzfeXxWrY4rD9jfhXwImSLLzqw8c9Sy+8ogUXuWACa mQk2Mel88Q8dTcA== X-Google-Smtp-Source: AGHT+IGZ44HWJjsQSNE+u7/5limV9AQ6F/F6G4pJhNrvWTghmZsysAgAjveAeb9VEkC56XFvr1Ap3z84/zVBdQ== X-Received: from pjbsc13.prod.google.com ([2002:a17:90b:510d:b0:341:bc50:b0c5]) (user=skhawaja job=prod-delivery.src-stubby-dispatcher) by 2002:a17:90b:3147:b0:340:b152:efe7 with SMTP id 98e67ed59e1d1-349126b815amr341784a91.11.1764716609302; Tue, 02 Dec 2025 15:03:29 -0800 (PST) Date: Tue, 2 Dec 2025 23:02:46 +0000 In-Reply-To: <20251202230303.1017519-1-skhawaja@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20251202230303.1017519-1-skhawaja@google.com> X-Mailer: git-send-email 2.52.0.158.g65b55ccf14-goog Message-ID: <20251202230303.1017519-17-skhawaja@google.com> Subject: [RFC PATCH v2 16/32] iommu: Add API to preserve/unpreserve a device From: Samiullah Khawaja To: David Woodhouse , Lu Baolu , Joerg Roedel , Will Deacon , Pasha Tatashin , Jason Gunthorpe , iommu@lists.linux.dev Cc: Samiullah Khawaja , Robin Murphy , Pratyush Yadav , Kevin Tian , Alex Williamson , linux-kernel@vger.kernel.org, Saeed Mahameed , Adithya Jayachandran , Parav Pandit , Leon Romanovsky , William Tu , Vipin Sharma , dmatlack@google.com, YiFei Zhu , Chris Li , praan@google.com Content-Type: text/plain; charset="UTF-8" iommu_preserve_device/iommu_unpreserve_device can be used to preserve/unpreserve a device for liveupdate. During device preservation the state of the associated IOMMU is also preserved. The device can only be preseved if the attached iommu domain is preserved and the assocated iommu supports preservation. Signed-off-by: Samiullah Khawaja --- drivers/iommu/iommu.c | 3 + drivers/iommu/liveupdate.c | 115 +++++++++++++++++++++++++++++++++++++ include/linux/iommu-lu.h | 2 + include/linux/iommu.h | 18 ++++++ 4 files changed, 138 insertions(+) diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index a70898d11959..3feb440de40a 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -382,6 +382,9 @@ static struct dev_iommu *dev_iommu_get(struct device *dev) mutex_init(¶m->lock); dev->iommu = param; +#ifdef CONFIG_LIVEUPDATE + dev->iommu->device_ser = NULL; +#endif return param; } diff --git a/drivers/iommu/liveupdate.c b/drivers/iommu/liveupdate.c index 25a943e5e1e3..5780761a7024 100644 --- a/drivers/iommu/liveupdate.c +++ b/drivers/iommu/liveupdate.c @@ -11,6 +11,7 @@ #include #include #include +#include #include static void iommu_liveupdate_free_objs(u64 next, bool incoming) @@ -209,3 +210,117 @@ int iommu_domain_unpreserve(struct iommu_domain *domain) return 0; } EXPORT_SYMBOL_GPL(iommu_domain_unpreserve); + +static int iommu_preserve_locked(struct iommu_device *iommu) +{ + struct iommu_lu_flb_obj *flb_obj; + struct iommu_ser *iommu_ser; + int idx, ret; + + if (!iommu->ops->preserve) + return -EOPNOTSUPP; + + if (iommu->outgoing_preserved_state) { + iommu->outgoing_preserved_state->obj.ref_count++; + return 0; + } + + ret = liveupdate_flb_get_outgoing(&iommu_flb, (void **)&flb_obj); + if (ret) + return ret; + + idx = reserve_obj_ser((struct iommu_objs_ser **)&flb_obj->iommus, MAX_IOMMU_SERS); + if (idx < 0) + return idx; + + iommu_ser = &flb_obj->iommus->iommus[idx]; + idx = flb_obj->ser->nr_iommus++; + iommu_ser->obj.idx = idx; + iommu_ser->obj.ref_count = 1; + + ret = iommu->ops->preserve(iommu, iommu_ser); + if (ret) + iommu_ser->obj.deleted = true; + + iommu->outgoing_preserved_state = iommu_ser; + return ret; +} + +static void iommu_unpreserve_locked(struct iommu_device *iommu) +{ + struct iommu_ser *iommu_ser = iommu->outgoing_preserved_state; + + iommu_ser->obj.ref_count--; + if (iommu_ser->obj.ref_count) + return; + + iommu->outgoing_preserved_state = NULL; + iommu->ops->unpreserve(iommu, iommu_ser); + iommu_ser->obj.deleted = true; +} + +int iommu_preserve_device(struct iommu_domain *domain, struct device *dev) +{ + struct iommu_lu_flb_obj *flb_obj; + struct device_ser *device_ser; + struct dev_iommu *iommu; + struct pci_dev *pdev; + int ret, idx; + + if (!dev_is_pci(dev)) + return -EOPNOTSUPP; + + if (!domain->preserved_state) + return -EINVAL; + + pdev = to_pci_dev(dev); + iommu = dev->iommu; + if (!iommu->iommu_dev->ops->preserve_device || + !iommu->iommu_dev->ops->preserve) + return -EOPNOTSUPP; + + if (!iommu->iommu_dev->ops->preserve) + return -EOPNOTSUPP; + + ret = liveupdate_flb_get_outgoing(&iommu_flb, (void **)&flb_obj); + if (ret) + return ret; + + guard(mutex)(&flb_obj->lock); + idx = reserve_obj_ser((struct iommu_objs_ser **)&flb_obj->devices, MAX_IOMMU_SERS); + if (idx < 0) + return idx; + + device_ser = &flb_obj->devices->devices[idx]; + idx = flb_obj->ser->nr_devices++; + device_ser->obj.idx = idx; + device_ser->obj.ref_count = 1; + + ret = iommu_preserve_locked(iommu->iommu_dev); + if (ret) { + device_ser->obj.deleted = true; + return ret; + } + + device_ser->domain_iommu_ser.domain_phys = __pa(domain->preserved_state); + device_ser->domain_iommu_ser.iommu_phys = __pa(iommu->iommu_dev->outgoing_preserved_state); + device_ser->devid = pci_dev_id(pdev); + device_ser->pci_domain = pci_domain_nr(pdev->bus); + device_ser->token = device_ser->obj.idx + 1; + + ret = iommu->iommu_dev->ops->preserve_device(dev, device_ser); + if (ret) { + device_ser->obj.deleted = true; + iommu_unpreserve_locked(iommu->iommu_dev); + return ret; + } + + dev->iommu->device_ser = device_ser; + domain->preserved_state->attach_count++; + return device_ser->token; +} + +int iommu_unpreserve_device(struct iommu_domain *domain, struct device *dev) +{ + return -EOPNOTSUPP; +} diff --git a/include/linux/iommu-lu.h b/include/linux/iommu-lu.h index 2c8e5ac746ad..95375530b7be 100644 --- a/include/linux/iommu-lu.h +++ b/include/linux/iommu-lu.h @@ -13,6 +13,8 @@ int iommu_domain_preserve(struct iommu_domain *domain, struct iommu_domain_ser **ser); int iommu_domain_unpreserve(struct iommu_domain *domain); +int iommu_preserve_device(struct iommu_domain *domain, struct device *dev); +int iommu_unpreserve_device(struct iommu_domain *domain, struct device *dev); int iommu_liveupdate_register_flb(struct liveupdate_file_handler *handler); int iommu_liveupdate_unregister_flb(struct liveupdate_file_handler *handler); diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 387cbe20c83b..45da5b88f35d 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -654,6 +654,10 @@ __iommu_copy_struct_to_user(const struct iommu_user_data *dst_data, * resources shared/passed to user space IOMMU instance. Associate * it with a nesting @parent_domain. It is required for driver to * set @viommu->ops pointing to its own viommu_ops + * @preserve_device: Preserve state of a device for liveupdate. + * @unpreserve_device: Unpreserve state that was preserved earlier. + * @preserve: Preserve state of iommu translation hardware for liveupdate. + * @unpreserve: Unpreserve state of iommu that was preserved earlier. * @owner: Driver module providing these ops * @identity_domain: An always available, always attachable identity * translation. @@ -710,6 +714,11 @@ struct iommu_ops { struct iommu_domain *parent_domain, const struct iommu_user_data *user_data); + int (*preserve_device)(struct device *dev, struct device_ser *device_ser); + void (*unpreserve_device)(struct device *dev, struct device_ser *device_ser); + int (*preserve)(struct iommu_device *iommu, struct iommu_ser *iommu_ser); + void (*unpreserve)(struct iommu_device *iommu, struct iommu_ser *iommu_ser); + const struct iommu_domain_ops *default_domain_ops; struct module *owner; struct iommu_domain *identity_domain; @@ -805,6 +814,8 @@ struct iommu_domain_ops { * @singleton_group: Used internally for drivers that have only one group * @max_pasids: number of supported PASIDs * @ready: set once iommu_device_register() has completed successfully + * @outgoing_preserved_state: preserved iommu state of outgoing kernel for + * liveupdate. */ struct iommu_device { struct list_head list; @@ -814,6 +825,10 @@ struct iommu_device { struct iommu_group *singleton_group; u32 max_pasids; bool ready; + +#ifdef CONFIG_LIVEUPDATE + struct iommu_ser *outgoing_preserved_state; +#endif }; /** @@ -868,6 +883,9 @@ struct dev_iommu { u32 pci_32bit_workaround:1; u32 require_direct:1; u32 shadow_on_flush:1; +#ifdef CONFIG_LIVEUPDATE + struct device_ser *device_ser; +#endif }; int iommu_device_register(struct iommu_device *iommu, -- 2.52.0.158.g65b55ccf14-goog