From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.6 required=3.0 tests=DKIM_INVALID,DKIM_SIGNED, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY, SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9053CC38BEE for ; Mon, 24 Feb 2020 18:24:49 +0000 (UTC) Received: from kanga.kvack.org (kanga.kvack.org [205.233.56.17]) by mail.kernel.org (Postfix) with ESMTP id 46B4520838 for ; Mon, 24 Feb 2020 18:24:49 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b="wurWsTSw" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 46B4520838 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=linaro.org Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=owner-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix) id ECE2B6B0037; Mon, 24 Feb 2020 13:24:39 -0500 (EST) Received: by kanga.kvack.org (Postfix, from userid 40) id E7E076B006C; Mon, 24 Feb 2020 13:24:39 -0500 (EST) X-Delivered-To: int-list-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix, from userid 63042) id CF8BC6B006E; Mon, 24 Feb 2020 13:24:39 -0500 (EST) X-Delivered-To: linux-mm@kvack.org Received: from forelay.hostedemail.com (smtprelay0014.hostedemail.com [216.40.44.14]) by kanga.kvack.org (Postfix) with ESMTP id B26D16B0037 for ; Mon, 24 Feb 2020 13:24:39 -0500 (EST) Received: from smtpin16.hostedemail.com (10.5.19.251.rfc1918.com [10.5.19.251]) by forelay02.hostedemail.com (Postfix) with ESMTP id 3614A2C04 for ; Mon, 24 Feb 2020 18:24:39 +0000 (UTC) X-FDA: 76525846278.16.eye10_62bb0975b421c X-HE-Tag: eye10_62bb0975b421c X-Filterd-Recvd-Size: 12159 Received: from mail-wr1-f68.google.com (mail-wr1-f68.google.com [209.85.221.68]) by imf18.hostedemail.com (Postfix) with ESMTP for ; Mon, 24 Feb 2020 18:24:38 +0000 (UTC) Received: by mail-wr1-f68.google.com with SMTP id g3so11536537wrs.12 for ; Mon, 24 Feb 2020 10:24:38 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=KpThO/73Hf8fKZWwG7eIK7pB6a161VXU//ezplFtiNc=; b=wurWsTSwZWeEfUF1UTnYzLLaQe0lN+Dv6qSbHjX5O5rqnLdhTJOHXmk9UkcMBOg7w4 6TVFg+ZKq5x75bexXPCFt5CxGQguPkCp4XxNSb76hEQ51bwIabnFhtKhYniJA1Fzax9v gpOWpzpmNrvGSAUC9hemFVdyEzaFDx1qUNQDA9icjWjQgkLMMiYTDdVeISFYx7fPv9hI FAh9lzvLRZXvc8JHu2aEuUxHzVUn6op4/iQlDFAJ1HlVn9Z8BUU5yVYWMEaJAgpjOXeq X3Ft1x7U09LJdUVNN7hiSdDaZOgDO9VWVnVaCmW87gkYO9wAsgt4iwnS4LLCbz/LOipA KtPA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=KpThO/73Hf8fKZWwG7eIK7pB6a161VXU//ezplFtiNc=; b=BLwvHLJNW9BBiqvYBGiZBWwfpna+IsxIsWRH5xydwHzDPZaFhVqFw5tB9n7O2Gh52K mqZ1m39OLNEtGQ6zIYNhdt3BJd9roAeavJr7PJ2kIUAbqhqb1SEu+/f/a51w3mE7+NVv ac7euicLIwUOg7TEFS9dH/qMFI9UqRuVfFGT1wolAbf0ytDEc3HPghEOlha1Um7s7nxo THcVmnasz9HoL0GVIhzCZbRImzcNxVfg24y6aookRNt4GEjG7E7zfUW8RQCDMg6lllQI /yu2qt1G0WcyI/hphge91Tum0X6dHgeXDscn4m34nN4kcersD+ggOd2K0phLVj4uuVUO 2qXQ== X-Gm-Message-State: APjAAAVRaZ/Uc85yNkkePWLDxTKkCkDaV/UN/xhc+s3FbzgyYmhZ/Pq+ fAZmF0Kd/KpTOrAZRr6FEtdJfQ== X-Google-Smtp-Source: APXvYqyOwGuE49FsphDPhVMErAww2yo0bMR9pVKChxLNjHcxrwuaei0Bub3jjd1F5uS+m8Nj72D/Ew== X-Received: by 2002:a5d:4ac8:: with SMTP id y8mr67394296wrs.272.1582568677336; Mon, 24 Feb 2020 10:24:37 -0800 (PST) Received: from localhost.localdomain ([2001:171b:c9a8:fbc0:116c:c27a:3e7f:5eaf]) by smtp.gmail.com with ESMTPSA id n3sm304255wmc.27.2020.02.24.10.24.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 24 Feb 2020 10:24:36 -0800 (PST) From: Jean-Philippe Brucker To: iommu@lists.linux-foundation.org, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-pci@vger.kernel.org, linux-mm@kvack.org Cc: joro@8bytes.org, robh+dt@kernel.org, mark.rutland@arm.com, catalin.marinas@arm.com, will@kernel.org, robin.murphy@arm.com, kevin.tian@intel.com, baolu.lu@linux.intel.com, Jonathan.Cameron@huawei.com, jacob.jun.pan@linux.intel.com, christian.koenig@amd.com, yi.l.liu@intel.com, zhangfei.gao@linaro.org, Jean-Philippe Brucker Subject: [PATCH v4 07/26] arm64: mm: Pin down ASIDs for sharing mm with devices Date: Mon, 24 Feb 2020 19:23:42 +0100 Message-Id: <20200224182401.353359-8-jean-philippe@linaro.org> X-Mailer: git-send-email 2.25.0 In-Reply-To: <20200224182401.353359-1-jean-philippe@linaro.org> References: <20200224182401.353359-1-jean-philippe@linaro.org> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Bogosity: Ham, tests=bogofilter, spamicity=0.000000, version=1.2.4 Sender: owner-linux-mm@kvack.org Precedence: bulk X-Loop: owner-majordomo@kvack.org List-ID: From: Jean-Philippe Brucker To enable address space sharing with the IOMMU, introduce mm_context_get(= ) and mm_context_put(), that pin down a context and ensure that it will kee= p its ASID after a rollover. Export the symbols to let the modular SMMUv3 driver use them. Pinning is necessary because a device constantly needs a valid ASID, unlike tasks that only require one when running. Without pinning, we woul= d need to notify the IOMMU when we're about to use a new ASID for a task, and it would get complicated when a new task is assigned a shared ASID. Consider the following scenario with no ASID pinned: 1. Task t1 is running on CPUx with shared ASID (gen=3D1, asid=3D1) 2. Task t2 is scheduled on CPUx, gets ASID (1, 2) 3. Task tn is scheduled on CPUy, a rollover occurs, tn gets ASID (2, 1) We would now have to immediately generate a new ASID for t1, notify the IOMMU, and finally enable task tn. We are holding the lock during all that time, since we can't afford having another CPU trigger a rollover. The IOMMU issues invalidation commands that can take tens of milliseconds. It gets needlessly complicated. All we wanted to do was schedule task tn, that has no business with the IOMMU. By letting the IOMMU pin tasks when needed, we avoid stalling the slow path, and let the pinning fail when we're out of shareable ASIDs. After a rollover, the allocator expects at least one ASID to be available in addition to the reserved ones (one per CPU). So (NR_ASIDS - NR_CPUS - 1) is the maximum number of ASIDs that can be shared with the IOMMU. Signed-off-by: Jean-Philippe Brucker --- v2->v4: handle KPTI --- arch/arm64/include/asm/mmu.h | 1 + arch/arm64/include/asm/mmu_context.h | 11 ++- arch/arm64/mm/context.c | 103 +++++++++++++++++++++++++-- 3 files changed, 109 insertions(+), 6 deletions(-) diff --git a/arch/arm64/include/asm/mmu.h b/arch/arm64/include/asm/mmu.h index e4d862420bb4..70ac3d4cbd3e 100644 --- a/arch/arm64/include/asm/mmu.h +++ b/arch/arm64/include/asm/mmu.h @@ -18,6 +18,7 @@ =20 typedef struct { atomic64_t id; + unsigned long pinned; void *vdso; unsigned long flags; } mm_context_t; diff --git a/arch/arm64/include/asm/mmu_context.h b/arch/arm64/include/as= m/mmu_context.h index 3827ff4040a3..70715c10c02a 100644 --- a/arch/arm64/include/asm/mmu_context.h +++ b/arch/arm64/include/asm/mmu_context.h @@ -175,7 +175,13 @@ static inline void cpu_replace_ttbr1(pgd_t *pgdp) #define destroy_context(mm) do { } while(0) void check_and_switch_context(struct mm_struct *mm, unsigned int cpu); =20 -#define init_new_context(tsk,mm) ({ atomic64_set(&(mm)->context.id, 0); = 0; }) +static inline int +init_new_context(struct task_struct *tsk, struct mm_struct *mm) +{ + atomic64_set(&mm->context.id, 0); + mm->context.pinned =3D 0; + return 0; +} =20 #ifdef CONFIG_ARM64_SW_TTBR0_PAN static inline void update_saved_ttbr0(struct task_struct *tsk, @@ -248,6 +254,9 @@ switch_mm(struct mm_struct *prev, struct mm_struct *n= ext, void verify_cpu_asid_bits(void); void post_ttbr_update_workaround(void); =20 +unsigned long mm_context_get(struct mm_struct *mm); +void mm_context_put(struct mm_struct *mm); + #endif /* !__ASSEMBLY__ */ =20 #endif /* !__ASM_MMU_CONTEXT_H */ diff --git a/arch/arm64/mm/context.c b/arch/arm64/mm/context.c index 121aba5b1941..5558de88b67d 100644 --- a/arch/arm64/mm/context.c +++ b/arch/arm64/mm/context.c @@ -26,6 +26,10 @@ static DEFINE_PER_CPU(atomic64_t, active_asids); static DEFINE_PER_CPU(u64, reserved_asids); static cpumask_t tlb_flush_pending; =20 +static unsigned long max_pinned_asids; +static unsigned long nr_pinned_asids; +static unsigned long *pinned_asid_map; + #define ASID_MASK (~GENMASK(asid_bits - 1, 0)) #define ASID_FIRST_VERSION (1UL << asid_bits) =20 @@ -73,6 +77,9 @@ void verify_cpu_asid_bits(void) =20 static void set_kpti_asid_bits(void) { + unsigned int k; + u8 *dst =3D (u8 *)asid_map; + u8 *src =3D (u8 *)pinned_asid_map; unsigned int len =3D BITS_TO_LONGS(NUM_USER_ASIDS) * sizeof(unsigned lo= ng); /* * In case of KPTI kernel/user ASIDs are allocated in @@ -80,7 +87,8 @@ static void set_kpti_asid_bits(void) * is set, then the ASID will map only userspace. Thus * mark even as reserved for kernel. */ - memset(asid_map, 0xaa, len); + for (k =3D 0; k < len; k++) + dst[k] =3D src[k] | 0xaa; } =20 static void set_reserved_asid_bits(void) @@ -88,9 +96,12 @@ static void set_reserved_asid_bits(void) if (arm64_kernel_unmapped_at_el0()) set_kpti_asid_bits(); else - bitmap_clear(asid_map, 0, NUM_USER_ASIDS); + bitmap_copy(asid_map, pinned_asid_map, NUM_USER_ASIDS); } =20 +#define asid_gen_match(asid) \ + (!(((asid) ^ atomic64_read(&asid_generation)) >> asid_bits)) + static void flush_context(void) { int i; @@ -161,6 +172,14 @@ static u64 new_context(struct mm_struct *mm) if (check_update_reserved_asid(asid, newasid)) return newasid; =20 + /* + * If it is pinned, we can keep using it. Note that reserved + * takes priority, because even if it is also pinned, we need to + * update the generation into the reserved_asids. + */ + if (mm->context.pinned) + return newasid; + /* * We had a valid ASID in a previous life, so try to re-use * it if possible. @@ -219,8 +238,7 @@ void check_and_switch_context(struct mm_struct *mm, u= nsigned int cpu) * because atomic RmWs are totally ordered for a given location. */ old_active_asid =3D atomic64_read(&per_cpu(active_asids, cpu)); - if (old_active_asid && - !((asid ^ atomic64_read(&asid_generation)) >> asid_bits) && + if (old_active_asid && asid_gen_match(asid) && atomic64_cmpxchg_relaxed(&per_cpu(active_asids, cpu), old_active_asid, asid)) goto switch_mm_fastpath; @@ -228,7 +246,7 @@ void check_and_switch_context(struct mm_struct *mm, u= nsigned int cpu) raw_spin_lock_irqsave(&cpu_asid_lock, flags); /* Check that our ASID belongs to the current generation. */ asid =3D atomic64_read(&mm->context.id); - if ((asid ^ atomic64_read(&asid_generation)) >> asid_bits) { + if (!asid_gen_match(asid)) { asid =3D new_context(mm); atomic64_set(&mm->context.id, asid); } @@ -251,6 +269,68 @@ void check_and_switch_context(struct mm_struct *mm, = unsigned int cpu) cpu_switch_mm(mm->pgd, mm); } =20 +unsigned long mm_context_get(struct mm_struct *mm) +{ + unsigned long flags; + u64 asid; + + raw_spin_lock_irqsave(&cpu_asid_lock, flags); + + asid =3D atomic64_read(&mm->context.id); + + if (mm->context.pinned) { + mm->context.pinned++; + asid &=3D ~ASID_MASK; + goto out_unlock; + } + + if (nr_pinned_asids >=3D max_pinned_asids) { + asid =3D 0; + goto out_unlock; + } + + if (!asid_gen_match(asid)) { + /* + * We went through one or more rollover since that ASID was + * used. Ensure that it is still valid, or generate a new one. + */ + asid =3D new_context(mm); + atomic64_set(&mm->context.id, asid); + } + + asid &=3D ~ASID_MASK; + + nr_pinned_asids++; + __set_bit(asid2idx(asid), pinned_asid_map); + mm->context.pinned++; + +out_unlock: + raw_spin_unlock_irqrestore(&cpu_asid_lock, flags); + + /* Set the equivalent of USER_ASID_BIT */ + if (asid && IS_ENABLED(CONFIG_UNMAP_KERNEL_AT_EL0)) + asid |=3D 1; + + return asid; +} +EXPORT_SYMBOL_GPL(mm_context_get); + +void mm_context_put(struct mm_struct *mm) +{ + unsigned long flags; + u64 asid =3D atomic64_read(&mm->context.id) & ~ASID_MASK; + + raw_spin_lock_irqsave(&cpu_asid_lock, flags); + + if (--mm->context.pinned =3D=3D 0) { + __clear_bit(asid2idx(asid), pinned_asid_map); + nr_pinned_asids--; + } + + raw_spin_unlock_irqrestore(&cpu_asid_lock, flags); +} +EXPORT_SYMBOL_GPL(mm_context_put); + /* Errata workaround post TTBRx_EL1 update. */ asmlinkage void post_ttbr_update_workaround(void) { @@ -279,6 +359,19 @@ static int asids_init(void) panic("Failed to allocate bitmap for %lu ASIDs\n", NUM_USER_ASIDS); =20 + pinned_asid_map =3D kcalloc(BITS_TO_LONGS(NUM_USER_ASIDS), + sizeof(*pinned_asid_map), GFP_KERNEL); + if (!pinned_asid_map) + panic("Failed to allocate pinned bitmap\n"); + + /* + * We assume that an ASID is always available after a rollover. This + * means that even if all CPUs have a reserved ASID, there still is at + * least one slot available in the asid map. + */ + max_pinned_asids =3D num_available_asids - num_possible_cpus() - 1; + nr_pinned_asids =3D 0; + /* * We cannot call set_reserved_asid_bits() here because CPU * caps are not finalized yet, so it is safer to assume KPTI --=20 2.25.0