From mboxrd@z Thu Jan 1 00:00:00 1970 From: Jacob Pan Subject: [PATCH V4 06/18] iommu/ioasid: Add free function and states Date: Sat, 27 Feb 2021 14:01:14 -0800 Message-ID: <1614463286-97618-7-git-send-email-jacob.jun.pan@linux.intel.com> References: <1614463286-97618-1-git-send-email-jacob.jun.pan@linux.intel.com> Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Return-path: In-Reply-To: <1614463286-97618-1-git-send-email-jacob.jun.pan-VuQAYsv1563Yd54FQh9/CA@public.gmane.org> List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: iommu-bounces-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org Sender: "iommu" To: LKML , Joerg Roedel , Lu Baolu , David Woodhouse , iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org, cgroups-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Tejun Heo , Li Zefan , Johannes Weiner , Jean-Philippe Brucker Cc: "Tian, Kevin" , Dave Jiang , Raj Ashok , Jonathan Corbet , Alex Williamson , Jason Gunthorpe , Wu Hao When an actively used IOASID is freed due to exceptions, users must be notified to perform the cleanup. The IOASID shall be put in a pending state until all users completed their cleanup work. This patch adds ioasid_free() function to let the caller initiate the freeing process. Both ioasid_free() and ioasid_put() decrements reference counts. Unlike ioasid_put(), the ioasid_free() function also transition the IOASID to the free pending state where further ioasid_get() is prohibited. This paves the way for FREE event notifications that will be introduced next. Signed-off-by: Jacob Pan --- drivers/iommu/ioasid.c | 73 ++++++++++++++++++++++++++++++++++++++++++ include/linux/ioasid.h | 5 +++ 2 files changed, 78 insertions(+) diff --git a/drivers/iommu/ioasid.c b/drivers/iommu/ioasid.c index d7b476651027..a10f8154c680 100644 --- a/drivers/iommu/ioasid.c +++ b/drivers/iommu/ioasid.c @@ -15,8 +15,26 @@ static ioasid_t ioasid_capacity = PCI_PASID_MAX; static ioasid_t ioasid_capacity_avail = PCI_PASID_MAX; static DEFINE_XARRAY_ALLOC(ioasid_sets); + +enum ioasid_state { + IOASID_STATE_IDLE, + IOASID_STATE_ACTIVE, + IOASID_STATE_FREE_PENDING, +}; + +/** + * struct ioasid_data - Meta data about ioasid + * + * @id: Unique ID + * @refs: Number of active users + * @state: Track state of the IOASID + * @set: ioasid_set of the IOASID belongs to + * @private: Private data associated with the IOASID + * @rcu: For free after RCU grace period + */ struct ioasid_data { ioasid_t id; + enum ioasid_state state; struct ioasid_set *set; void *private; struct rcu_head rcu; @@ -597,6 +615,7 @@ ioasid_t ioasid_alloc(struct ioasid_set *set, ioasid_t min, ioasid_t max, goto exit_free; } data->id = id; + data->state = IOASID_STATE_IDLE; /* Store IOASID in the per set data */ if (xa_err(xa_store(&set->xa, id, data, GFP_ATOMIC))) { @@ -631,6 +650,56 @@ static void ioasid_do_free_locked(struct ioasid_data *data) ioasid_set_free_locked(data->set); } +static void ioasid_free_locked(struct ioasid_set *set, ioasid_t ioasid) +{ + struct ioasid_data *data; + + data = xa_load(&active_allocator->xa, ioasid); + if (!data) { + pr_err_ratelimited("Trying to free unknown IOASID %u\n", ioasid); + return; + } + if (data->set != set) { + pr_warn("Cannot free IOASID %u due to set ownership\n", ioasid); + return; + } + /* Check if the set exists */ + if (WARN_ON(!xa_load(&ioasid_sets, data->set->id))) + return; + + /* Free is already in progress */ + if (data->state == IOASID_STATE_FREE_PENDING) + return; + + data->state = IOASID_STATE_FREE_PENDING; + /* + * If the refcount is 1, it means there is no other users of the IOASID + * other than IOASID core itself. There is no need to notify anyone. + */ + if (!refcount_dec_and_test(&data->refs)) + return; + + ioasid_do_free_locked(data); +} + +/** + * ioasid_free - Drop reference on an IOASID. Free if refcount drops to 0, + * including free from its set and system-wide list. + * @set: The ioasid_set to check permission with. If not NULL, IOASID + * free will fail if the set does not match. + * @ioasid: The IOASID to remove + * + * TODO: return true if all references dropped, false if async work is in + * progress, IOASID is in FREE_PENDING state. wait queue to be used for blocking + * free task. + */ +void ioasid_free(struct ioasid_set *set, ioasid_t ioasid) +{ + spin_lock(&ioasid_allocator_lock); + ioasid_free_locked(set, ioasid); + spin_unlock(&ioasid_allocator_lock); +} +EXPORT_SYMBOL_GPL(ioasid_free); int ioasid_get_locked(struct ioasid_set *set, ioasid_t ioasid) { struct ioasid_data *data; @@ -640,6 +709,10 @@ int ioasid_get_locked(struct ioasid_set *set, ioasid_t ioasid) pr_err("Trying to get unknown IOASID %u\n", ioasid); return -EINVAL; } + if (data->state == IOASID_STATE_FREE_PENDING) { + pr_err("Trying to get IOASID being freed%u\n", ioasid); + return -EBUSY; + } /* Check set ownership if the set is non-null */ if (set && data->set != set) { diff --git a/include/linux/ioasid.h b/include/linux/ioasid.h index 095f4e50dc58..cabaf0b0348f 100644 --- a/include/linux/ioasid.h +++ b/include/linux/ioasid.h @@ -72,6 +72,7 @@ int ioasid_get(struct ioasid_set *set, ioasid_t ioasid); int ioasid_get_locked(struct ioasid_set *set, ioasid_t ioasid); bool ioasid_put(struct ioasid_set *set, ioasid_t ioasid); bool ioasid_put_locked(struct ioasid_set *set, ioasid_t ioasid); +void ioasid_free(struct ioasid_set *set, ioasid_t ioasid); void *ioasid_find(struct ioasid_set *set, ioasid_t ioasid, bool (*getter)(void *)); int ioasid_register_allocator(struct ioasid_allocator_ops *allocator); @@ -105,6 +106,10 @@ static inline struct ioasid_set *ioasid_set_alloc(void *token, ioasid_t quota, return ERR_PTR(-ENOTSUPP); } +static inline void ioasid_free(struct ioasid_set *set, ioasid_t ioasid) +{ +} + static inline struct ioasid_set *ioasid_find_mm_set(struct mm_struct *token) { return NULL; -- 2.25.1