LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: Kernel crosscompilers
From: Michael Ellerman @ 2021-05-10  6:07 UTC (permalink / raw)
  To: Christophe Leroy, Arnd Bergmann, linuxppc-dev@ozlabs.org,
	Segher Boessenkool
In-Reply-To: <1bf8523a-848a-7686-c179-7bedb18979ac@csgroup.eu>

Christophe Leroy <christophe.leroy@csgroup.eu> writes:
> Le 08/05/2021 à 08:46, Christophe Leroy a écrit :
>> Le 06/05/2021 à 16:17, Arnd Bergmann a écrit :
>>> On Thu, May 6, 2021 at 2:42 PM Christophe Leroy
>>> <christophe.leroy@csgroup.eu> wrote:
>>>>
>>>> Hello Arnd,
>>>>
>>>> May I ask you whether you plan to build cross compilers based on GCC 11.1 at
>>>> https://mirrors.edge.kernel.org/pub/tools/crosstool/ ?
>>>
>>> Hi Christophe,
>>>
>>> I've built a snapshot a few days before the release, that one is
>>> identical to 11.1
>>> except for the reported version number. I've tried to ask around for
>>> help testing
>>> this, but so far I have not heard from anyone.
>>>
>>> Building a new set of compilers takes around a day on my build box, so I want
>>> to make sure I don't have to do it more often than necessary. If you are able
>>> to give the binaries a spin, preferably on a ppc64le or arm64 host, please let
>>> me know how it goes and I'll rebuilt them on the release tag.
>>>
>> 
>> Hi Arnd,
>> 
>> I don't have any ppc or arm host I can build on.
>> I'm building on x86 for powerpc embedded boards.
>> 
>> I have tried your GCC 11 snapshot, I get something booting but it crashes when launching init.
>> 
>> [    7.368410] init[1]: bad frame in sys_sigreturn: 7fb2fd60 nip 001083cc lr 001083c4
>> [    7.376283] Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b
>> [    7.383680] CPU: 0 PID: 1 Comm: init Not tainted 5.12.0-s3k-dev-16316-g9e799d5df185 #5054
>> [    7.391767] Call Trace:
>> [    7.394174] [c9023db0] [c00211e8] panic+0x130/0x304 (unreliable)
>> [    7.400112] [c9023e10] [c0024e68] do_exit+0x874/0x910
>> [    7.405104] [c9023e50] [c0024f80] do_group_exit+0x40/0xc4
>> [    7.410440] [c9023e60] [c0033334] get_signal+0x1d8/0x93c
>> [    7.415689] [c9023ec0] [c0007f34] do_notify_resume+0x6c/0x314
>> [    7.421369] [c9023f20] [c000d580] syscall_exit_prepare+0x120/0x184
>> [    7.427479] [c9023f30] [c001101c] ret_from_syscall+0xc/0x28
>> 
>> Something is going wrong with asm goto output. I implemented get_user() helpers with asm goto this 
>> cycle (commit 5cd29b1fd3e8). I tested it with CLANG before submitting, it was working.
>> 
>> Seems like there is something wrong with it with GCC11. When forcing CONFIG_CC_HAS_ASM_GOTO_OUTPUT 
>> to 'n', the kernel boots ok.
>> 
>
> I found the problem, that's due to r10 register being reused by GCC in the copy loop below:
>
>    10:	7d 09 03 a6 	mtctr   r8
>    14:	80 ca 00 00 	lwz     r6,0(r10)
>    18:	80 ea 00 04 	lwz     r7,4(r10)
>    1c:	90 c9 00 08 	stw     r6,8(r9)
>    20:	90 e9 00 0c 	stw     r7,12(r9)
>    24:	39 0a 00 08 	addi    r8,r10,8
>    28:	39 29 00 08 	addi    r9,r9,8
> =>2c:	81 4a 00 08 	lwz     r10,8(r10)
>    30:	81 6a 00 0c 	lwz     r11,12(r10)
>    34:	91 49 00 08 	stw     r10,8(r9)
>    38:	91 69 00 0c 	stw     r11,12(r9)
>    3c:	39 48 00 08 	addi    r10,r8,8
>    40:	39 29 00 08 	addi    r9,r9,8
>    44:	42 00 ff d0 	bdnz    14 <__unsafe_restore_general_regs+0x14>
>
> earlyclobber modifier is missing in the CONFIG_CC_HAS_ASM_GOTO_OUTPUT version of __get_user_asm2_goto().

Thanks for tracking that down. I hit it last week when testing Arnd's
compilers but hadn't had time to find the root cause.

cheers

^ permalink raw reply

* Re: [V3 PATCH 09/16] powerpc/pseries/vas: Implement to get all capabilities
From: Nicholas Piggin @ 2021-05-10  6:13 UTC (permalink / raw)
  To: Haren Myneni, herbert, linux-crypto, linuxppc-dev, mpe
In-Reply-To: <f6cdf811a29c22056740d48fa3de010f4ea4b848.camel@linux.ibm.com>

Excerpts from Haren Myneni's message of April 18, 2021 7:08 am:
> 
> pHyp provides various VAS capabilities such as GZIP default and QoS
> capabilities which are used to determine total number of credits
> available in LPAR, maximum window credits, maximum LPAR credits,
> whether usermode copy/paste is supported, and etc.
> 
> So first retrieve overall vas capabilities using
> H_QUERY_VAS_CAPABILITIES HCALL which tells the specific features that
> are available. Then retrieve the specific capabilities by using the
> feature type in H_QUERY_VAS_CAPABILITIES HCALL.
> 
> pHyp supports only GZIP default and GZIP QoS capabilities right now.

Changelog and title could use a bit of work.

> 
> Signed-off-by: Haren Myneni <haren@linux.ibm.com>
> ---
>  arch/powerpc/platforms/pseries/vas.c | 130 +++++++++++++++++++++++++++
>  1 file changed, 130 insertions(+)
> 
> diff --git a/arch/powerpc/platforms/pseries/vas.c b/arch/powerpc/platforms/pseries/vas.c
> index 06960151477c..35946fb02995 100644
> --- a/arch/powerpc/platforms/pseries/vas.c
> +++ b/arch/powerpc/platforms/pseries/vas.c
> @@ -30,6 +30,13 @@
>  /* phyp allows one credit per window right now */
>  #define DEF_WIN_CREDS		1
>  
> +static struct vas_all_capabs capabs_all;

Does this name come from PAPR? If not, capabilities or caps are better 
for readability than capabs.

> +static int copypaste_feat;

Should be a bool? And what does it mean? copy-paste is a host core 
capability.

> +
> +struct vas_capabs vcapabs[VAS_MAX_FEAT_TYPE];
> +
> +DEFINE_MUTEX(vas_pseries_mutex);

Can these be made static if they're only used here, and export them if a 
future patch uses them (or add the header declaration now).


> +
>  static int64_t hcall_return_busy_check(int64_t rc)
>  {
>  	/* Check if we are stalled for some time */
> @@ -215,3 +222,126 @@ int plpar_vas_query_capabilities(const u64 hcall, u8 query_type,
>  		return -EIO;
>  	}
>  }
> +
> +/*
> + * Get the specific capabilities based on the feature type.
> + * Right now supports GZIP default and GZIP QoS capabilities.
> + */
> +static int get_vas_capabilities(u8 feat, enum vas_cop_feat_type type,
> +				struct vas_ct_capabs_be *capab_be)
> +{
> +	struct vas_ct_capabs *capab;
> +	struct vas_capabs *vcapab;
> +	int rc = 0;
> +
> +	vcapab = &vcapabs[type];
> +	memset(vcapab, 0, sizeof(*vcapab));
> +	INIT_LIST_HEAD(&vcapab->list);
> +
> +	capab = &vcapab->capab;
> +
> +	rc = plpar_vas_query_capabilities(H_QUERY_VAS_CAPABILITIES, feat,
> +					  (u64)virt_to_phys(capab_be));
> +	if (rc)
> +		return rc;
> +
> +	capab->user_mode = capab_be->user_mode;
> +	if (!(capab->user_mode & VAS_COPY_PASTE_USER_MODE)) {
> +		pr_err("User space COPY/PASTE is not supported\n");
> +		return -ENOTSUPP;
> +	}
> +
> +	snprintf(capab->name, VAS_DESCR_LEN + 1, "%.8s",
> +		 (char *)&capab_be->descriptor);
> +	capab->descriptor = be64_to_cpu(capab_be->descriptor);
> +	capab->win_type = capab_be->win_type;
> +	if (capab->win_type >= VAS_MAX_FEAT_TYPE) {
> +		pr_err("Unsupported window type %u\n", capab->win_type);
> +		return -EINVAL;
> +	}
> +	capab->max_lpar_creds = be16_to_cpu(capab_be->max_lpar_creds);
> +	capab->max_win_creds = be16_to_cpu(capab_be->max_win_creds);
> +	atomic_set(&capab->target_lpar_creds,
> +		   be16_to_cpu(capab_be->target_lpar_creds));
> +	if (feat == VAS_GZIP_DEF_FEAT) {
> +		capab->def_lpar_creds = be16_to_cpu(capab_be->def_lpar_creds);
> +
> +		if (capab->max_win_creds < DEF_WIN_CREDS) {
> +			pr_err("Window creds(%u) > max allowed window creds(%u)\n",
> +			       DEF_WIN_CREDS, capab->max_win_creds);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	copypaste_feat = 1;
> +
> +	return 0;
> +}
> +
> +static int __init pseries_vas_init(void)
> +{
> +	struct vas_ct_capabs_be *ct_capabs_be;
> +	struct vas_all_capabs_be *capabs_be;
> +	int rc;
> +
> +	/*
> +	 * Linux supports user space COPY/PASTE only with Radix
> +	 */
> +	if (!radix_enabled()) {
> +		pr_err("API is supported only with radix page tables\n");
> +		return -ENOTSUPP;
> +	}
> +
> +	capabs_be = kmalloc(sizeof(*capabs_be), GFP_KERNEL);
> +	if (!capabs_be)
> +		return -ENOMEM;
> +	/*
> +	 * Get VAS overall capabilities by passing 0 to feature type.
> +	 */
> +	rc = plpar_vas_query_capabilities(H_QUERY_VAS_CAPABILITIES, 0,
> +					  (u64)virt_to_phys(capabs_be));
> +	if (rc)
> +		goto out;
> +
> +	snprintf(capabs_all.name, VAS_DESCR_LEN, "%.7s",
> +		 (char *)&capabs_be->descriptor);
> +	capabs_all.descriptor = be64_to_cpu(capabs_be->descriptor);
> +	capabs_all.feat_type = be64_to_cpu(capabs_be->feat_type);
> +
> +	ct_capabs_be = kmalloc(sizeof(*ct_capabs_be), GFP_KERNEL);
> +	if (!ct_capabs_be) {
> +		rc = -ENOMEM;
> +		goto out;
> +	}
> +	/*
> +	 * QOS capabilities available
> +	 */
> +	if (capabs_all.feat_type & VAS_GZIP_QOS_FEAT_BIT) {
> +		rc = get_vas_capabilities(VAS_GZIP_QOS_FEAT,
> +					  VAS_GZIP_QOS_FEAT_TYPE, ct_capabs_be);
> +
> +		if (rc)
> +			goto out_ct;
> +	}
> +	/*
> +	 * Default capabilities available
> +	 */
> +	if (capabs_all.feat_type & VAS_GZIP_DEF_FEAT_BIT) {
> +		rc = get_vas_capabilities(VAS_GZIP_DEF_FEAT,
> +					  VAS_GZIP_DEF_FEAT_TYPE, ct_capabs_be);
> +		if (rc)
> +			goto out_ct;
> +	}

Using the same buffer for two hcalls? Do they fill in different parts of 
it?

> +
> +	if (!copypaste_feat)
> +		pr_err("GZIP feature is not supported\n");

This is dead code AFAIKS, because errors will always branch to out.

Thanks,
Nick

> +
> +	pr_info("GZIP feature is available\n");
> +
> +out_ct:
> +	kfree(ct_capabs_be);
> +out:
> +	kfree(capabs_be);
> +	return rc;
> +}
> +machine_device_initcall(pseries, pseries_vas_init);
> -- 
> 2.18.2
> 
> 
> 

^ permalink raw reply

* Re: [V3 PATCH 10/16] powerpc/pseries/vas: Integrate API with open/close windows
From: Nicholas Piggin @ 2021-05-10  6:18 UTC (permalink / raw)
  To: Haren Myneni, herbert, linux-crypto, linuxppc-dev, mpe
In-Reply-To: <4b66c4eea2c0213be658180c987d81f3bb82293d.camel@linux.ibm.com>

Excerpts from Haren Myneni's message of April 18, 2021 7:08 am:
> 
> This patch adds VAS window allocatioa/close with the corresponding
> HCALLs. Also changes to integrate with the existing user space VAS
> API and provide register/unregister functions to NX pseries driver.
> 
> The driver register function is used to create the user space
> interface (/dev/crypto/nx-gzip) and unregister to remove this entry.
> 
> The user space process opens this device node and makes an ioctl
> to allocate VAS window. The close interface is used to deallocate
> window.
> 
> Signed-off-by: Haren Myneni <haren@linux.ibm.com>
> ---
>  arch/powerpc/include/asm/vas.h          |   5 +
>  arch/powerpc/platforms/book3s/Kconfig   |   2 +-
>  arch/powerpc/platforms/pseries/Makefile |   1 +
>  arch/powerpc/platforms/pseries/vas.c    | 212 ++++++++++++++++++++++++
>  4 files changed, 219 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/powerpc/include/asm/vas.h b/arch/powerpc/include/asm/vas.h
> index d15784506a54..aa1974aba27e 100644
> --- a/arch/powerpc/include/asm/vas.h
> +++ b/arch/powerpc/include/asm/vas.h
> @@ -270,6 +270,11 @@ struct vas_all_capabs {
>  	u64     feat_type;
>  };
>  
> +int plpar_vas_query_capabilities(const u64 hcall, u8 query_type,
> +				 u64 result);
> +int vas_register_api_pseries(struct module *mod,
> +			     enum vas_cop_type cop_type, const char *name);
> +void vas_unregister_api_pseries(void);
>  #endif
>  
>  /*
> diff --git a/arch/powerpc/platforms/book3s/Kconfig b/arch/powerpc/platforms/book3s/Kconfig
> index 51e14db83a79..bed21449e8e5 100644
> --- a/arch/powerpc/platforms/book3s/Kconfig
> +++ b/arch/powerpc/platforms/book3s/Kconfig
> @@ -1,7 +1,7 @@
>  # SPDX-License-Identifier: GPL-2.0
>  config PPC_VAS
>  	bool "IBM Virtual Accelerator Switchboard (VAS)"
> -	depends on PPC_POWERNV && PPC_64K_PAGES
> +	depends on (PPC_POWERNV || PPC_PSERIES) && PPC_64K_PAGES
>  	default y
>  	help
>  	  This enables support for IBM Virtual Accelerator Switchboard (VAS).
> diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile
> index c8a2b0b05ac0..4cda0ef87be0 100644
> --- a/arch/powerpc/platforms/pseries/Makefile
> +++ b/arch/powerpc/platforms/pseries/Makefile
> @@ -30,3 +30,4 @@ obj-$(CONFIG_PPC_SVM)		+= svm.o
>  obj-$(CONFIG_FA_DUMP)		+= rtas-fadump.o
>  
>  obj-$(CONFIG_SUSPEND)		+= suspend.o
> +obj-$(CONFIG_PPC_VAS)		+= vas.o
> diff --git a/arch/powerpc/platforms/pseries/vas.c b/arch/powerpc/platforms/pseries/vas.c
> index 35946fb02995..0ade0d6d728f 100644
> --- a/arch/powerpc/platforms/pseries/vas.c
> +++ b/arch/powerpc/platforms/pseries/vas.c
> @@ -222,6 +222,218 @@ int plpar_vas_query_capabilities(const u64 hcall, u8 query_type,
>  		return -EIO;
>  	}
>  }
> +EXPORT_SYMBOL_GPL(plpar_vas_query_capabilities);
> +
> +/*
> + * Allocate window and setup IRQ mapping.
> + */
> +static int allocate_setup_window(struct vas_window *txwin,
> +				 u64 *domain, u8 wintype)
> +{
> +	int rc;
> +
> +	rc = plpar_vas_allocate_window(txwin, domain, wintype, DEF_WIN_CREDS);
> +	if (rc)
> +		return rc;
> +
> +	txwin->wcreds_max = DEF_WIN_CREDS;
> +
> +	return 0;
> +}
> +
> +static struct vas_window *vas_allocate_window(struct vas_tx_win_open_attr *uattr,
> +					      enum vas_cop_type cop_type)
> +{
> +	long domain[PLPAR_HCALL9_BUFSIZE] = {VAS_DEFAULT_DOMAIN_ID};
> +	struct vas_ct_capabs *ct_capab;
> +	struct vas_capabs *capabs;
> +	struct vas_window *txwin;
> +	int rc;
> +
> +	txwin = kzalloc(sizeof(*txwin), GFP_KERNEL);
> +	if (!txwin)
> +		return ERR_PTR(-ENOMEM);
> +
> +	/*
> +	 * A VAS window can have many credits which means that many
> +	 * requests can be issued simultaneously. But phyp restricts
> +	 * one credit per window.
> +	 * phyp introduces 2 different types of credits:
> +	 * Default credit type (Uses normal priority FIFO):
> +	 *	A limited number of credits are assigned to partitions
> +	 *	based on processor entitlement. But these credits may be
> +	 *	over-committed on a system depends on whether the CPUs
> +	 *	are in shared or dedicated modes - that is, more requests
> +	 *	may be issued across the system than NX can service at
> +	 *	once which can result in paste command failure (RMA_busy).
> +	 *	Then the process has to resend requests or fall-back to
> +	 *	SW compression.
> +	 * Quality of Service (QoS) credit type (Uses high priority FIFO):
> +	 *	To avoid NX HW contention, the system admins can assign
> +	 *	QoS credits for each LPAR so that this partition is
> +	 *	guaranteed access to NX resources. These credits are
> +	 *	assigned to partitions via the HMC.
> +	 *	Refer PAPR for more information.
> +	 *
> +	 * Allocate window with QoS credits if user requested. Otherwise
> +	 * default credits are used.
> +	 */
> +	if (uattr->flags & VAS_WIN_QOS_CREDITS)
> +		capabs = &vcapabs[VAS_GZIP_QOS_FEAT_TYPE];
> +	else
> +		capabs = &vcapabs[VAS_GZIP_DEF_FEAT_TYPE];
> +
> +	ct_capab = &capabs->capab;
> +
> +	if (atomic_inc_return(&ct_capab->used_lpar_creds) >
> +			atomic_read(&ct_capab->target_lpar_creds)) {
> +		pr_err("Credits are not available to allocate window\n");
> +		rc = -EINVAL;
> +		goto out;
> +	}
> +
> +	/*
> +	 * The user space is requesting to allocate a window on a VAS
> +	 * instance (or chip) where the process is executing.
> +	 * On powerVM, domain values are passed to pHyp to select chip /
> +	 * VAS instance. Useful if the process is affinity to NUMA node.
> +	 * pHyp selects VAS instance if VAS_DEFAULT_DOMAIN_ID (-1) is
> +	 * passed for domain values.
> +	 */
> +	if (uattr->vas_id == -1) {
> +		/*
> +		 * To allocate VAS window, pass same domain values returned
> +		 * from this HCALL.
> +		 */
> +		rc = plpar_hcall9(H_HOME_NODE_ASSOCIATIVITY, domain,
> +				  VPHN_FLAG_VCPU, smp_processor_id());
> +		if (rc != H_SUCCESS) {
> +			pr_err("HCALL(%x): failed with ret(%d)\n",
> +			       H_HOME_NODE_ASSOCIATIVITY, rc);
> +			goto out;
> +		}
> +	}
> +
> +	/*
> +	 * Allocate / Deallocate window HCALLs and setup / free IRQs
> +	 * have to be protected with mutex. Otherwise, since IRQ is freed
> +	 * after deallocate HCALL, may see the case where window ID and
> +	 * fault interrupt could be reused before free IRQ (for the old
> +	 * window) in kernel. It can result in setup IRQ fail for the
> +	 * new window.
> +	 */

It's a bit difficult to understand that comment.

The window deallocate is protected with the mutex, then the mutex
gets dropped. Some time later presumably the IRQ gets freed.

What prevents the window ID from being reused in between?

Thanks,
Nick

> +	mutex_lock(&vas_pseries_mutex);
> +	rc = allocate_setup_window(txwin, (u64 *)&domain[0],
> +				   ct_capab->win_type);
> +	mutex_unlock(&vas_pseries_mutex);
> +	if (rc)
> +		goto out;
> +
> +	/*
> +	 * Modify window and it is ready to use.
> +	 */
> +	rc = plpar_vas_modify_window(txwin);
> +	if (!rc)
> +		rc = vas_reference_task(&txwin->task);
> +	if (rc)
> +		goto out_free;
> +
> +	txwin->lpar.win_type = ct_capab->win_type;
> +	mutex_lock(&vas_pseries_mutex);
> +	list_add(&txwin->lpar.win_list, &capabs->list);
> +	mutex_unlock(&vas_pseries_mutex);
> +
> +	return txwin;
> +
> +out_free:
> +	plpar_vas_deallocate_window(txwin->winid);
> +out:
> +	atomic_dec(&ct_capab->used_lpar_creds);
> +	kfree(txwin);
> +	return ERR_PTR(rc);
> +}
> +
> +static u64 vas_paste_address(void *addr)
> +{
> +	struct vas_window *win = addr;
> +
> +	return win->lpar.win_addr;
> +}
> +
> +static int deallocate_free_window(struct vas_window *win)
> +{
> +	int rc = 0;
> +
> +	rc = plpar_vas_deallocate_window(win->winid);
> +	if (!rc)
> +		kfree(win->lpar.name);
> +
> +	return rc;
> +}
> +
> +static int vas_deallocate_window(void *addr)
> +{
> +	struct vas_window *win = (struct vas_window *)addr;
> +	struct vas_ct_capabs *capabs;
> +	int rc = 0;
> +
> +	if (!win)
> +		return -EINVAL;
> +
> +	/* Should not happen */
> +	if (win->lpar.win_type >= VAS_MAX_FEAT_TYPE) {
> +		pr_err("Window (%u): Invalid window type %u\n",
> +				win->winid, win->lpar.win_type);
> +		return -EINVAL;
> +	}
> +
> +	capabs = &vcapabs[win->lpar.win_type].capab;
> +	mutex_lock(&vas_pseries_mutex);
> +	rc = deallocate_free_window(win);
> +	if (rc) {
> +		mutex_unlock(&vas_pseries_mutex);
> +		return rc;
> +	}
> +
> +	list_del(&win->lpar.win_list);
> +	atomic_dec(&capabs->used_lpar_creds);
> +	mutex_unlock(&vas_pseries_mutex);
> +
> +	vas_drop_reference_task(&win->task);
> +
> +	kfree(win);
> +	return 0;
> +}
> +
> +static struct vas_user_win_ops vops_pseries = {
> +	.open_win	= vas_allocate_window,	/* Open and configure window */
> +	.paste_addr	= vas_paste_address,	/* To do copy/paste */
> +	.close_win	= vas_deallocate_window, /* Close window */
> +};
> +
> +/*
> + * Supporting only nx-gzip coprocessor type now, but this API code
> + * extended to other coprocessor types later.
> + */
> +int vas_register_api_pseries(struct module *mod, enum vas_cop_type cop_type,
> +			     const char *name)
> +{
> +	int rc;
> +
> +	if (!copypaste_feat)
> +		return -ENOTSUPP;
> +
> +	rc = vas_register_coproc_api(mod, cop_type, name, &vops_pseries);
> +
> +	return rc;
> +}
> +EXPORT_SYMBOL_GPL(vas_register_api_pseries);
> +
> +void vas_unregister_api_pseries(void)
> +{
> +	vas_unregister_coproc_api();
> +}
> +EXPORT_SYMBOL_GPL(vas_unregister_api_pseries);
>  
>  /*
>   * Get the specific capabilities based on the feature type.
> -- 
> 2.18.2
> 
> 
> 

^ permalink raw reply

* Re: [V3 PATCH 10/16] powerpc/pseries/vas: Integrate API with open/close windows
From: Nicholas Piggin @ 2021-05-10  6:28 UTC (permalink / raw)
  To: Haren Myneni, herbert, linux-crypto, linuxppc-dev, mpe
In-Reply-To: <4b66c4eea2c0213be658180c987d81f3bb82293d.camel@linux.ibm.com>

Excerpts from Haren Myneni's message of April 18, 2021 7:08 am:
> +static int deallocate_free_window(struct vas_window *win)
> +{
> +	int rc = 0;
> +
> +	rc = plpar_vas_deallocate_window(win->winid);
> +	if (!rc)
> +		kfree(win->lpar.name);

Oh, did this kfree sneak in here? The allocation appears in patch 11
I think.

Thanks,
Nick


^ permalink raw reply

* Re: [V3 PATCH 12/16] powerpc/pseries/vas: sysfs interface to export capabilities
From: Nicholas Piggin @ 2021-05-10  6:34 UTC (permalink / raw)
  To: Haren Myneni, herbert, linux-crypto, linuxppc-dev, mpe
In-Reply-To: <60176ad795219afbeaf51ad596af4bae710617b7.camel@linux.ibm.com>

Excerpts from Haren Myneni's message of April 18, 2021 7:10 am:
> 
> pHyp provides GZIP default and GZIP QoS capabilities which gives
> the total number of credits are available in LPAR. This patch
> creates sysfs entries and exports LPAR credits, the currently used
> and the available credits for each feature.
> 
> /sys/kernel/vas/VasCaps/VDefGzip: (default GZIP capabilities)
> 	avail_lpar_creds /* Available credits to use */
> 	target_lpar_creds /* Total credits available which can be
> 			 /* changed with DLPAR operation */
> 	used_lpar_creds  /* Used credits */

/sys/kernel/ is not an appropriate directory to put it in. Also camel 
case is not thought very highly of these days.

And s/capabs/caps/g applies here (and all other patches).

Thanks,
Nick

^ permalink raw reply

* Re: [V3 PATCH 15/16] crypto/nx: Get NX capabilities for GZIP coprocessor type
From: Nicholas Piggin @ 2021-05-10  6:38 UTC (permalink / raw)
  To: Haren Myneni, herbert, linux-crypto, linuxppc-dev, mpe
In-Reply-To: <e5fff6adbf3ce7769b0efe4846f39dbc6c795dd1.camel@linux.ibm.com>

Excerpts from Haren Myneni's message of April 18, 2021 7:12 am:
> 
> phyp provides NX capabilities which gives recommended minimum
> compression / decompression length and maximum request buffer size
> in bytes.
> 
> Changes to get NX overall capabilities which points to the specific
> features phyp supports. Then retrieve NXGZIP specific capabilities.
> 
> Signed-off-by: Haren Myneni <haren@linux.ibm.com>
> ---
>  drivers/crypto/nx/nx-common-pseries.c | 83 +++++++++++++++++++++++++++
>  1 file changed, 83 insertions(+)
> 
> diff --git a/drivers/crypto/nx/nx-common-pseries.c b/drivers/crypto/nx/nx-common-pseries.c
> index 9a40fca8a9e6..49224870d05e 100644
> --- a/drivers/crypto/nx/nx-common-pseries.c
> +++ b/drivers/crypto/nx/nx-common-pseries.c
> @@ -9,6 +9,7 @@
>   */
>  
>  #include <asm/vio.h>
> +#include <asm/hvcall.h>
>  #include <asm/vas.h>
>  
>  #include "nx-842.h"
> @@ -20,6 +21,24 @@ MODULE_DESCRIPTION("842 H/W Compression driver for IBM Power processors");
>  MODULE_ALIAS_CRYPTO("842");
>  MODULE_ALIAS_CRYPTO("842-nx");
>  
> +struct nx_ct_capabs_be {

What does "ct" mean? I've seen it in a few other places too.

> +	__be64	descriptor;
> +	__be64	req_max_processed_len;	/* Max bytes in one GZIP request */
> +	__be64	min_compress_len;	/* Min compression size in bytes */
> +	__be64	min_decompress_len;	/* Min decompression size in bytes */
> +} __packed __aligned(0x1000);
> +
> +struct nx_ct_capabs {
> +	char	name[VAS_DESCR_LEN + 1];
> +	u64	descriptor;
> +	u64	req_max_processed_len;	/* Max bytes in one GZIP request */
> +	u64	min_compress_len;	/* Min compression in bytes */
> +	u64	min_decompress_len;	/* Min decompression in bytes */
> +};
> +
> +u64 capab_feat = 0;

Why is this here and not a local variable?

> +struct nx_ct_capabs nx_ct_capab;

It's okay and generally better to use the same name as the struct name
in this situation, i.e.,

"struct nx_ct_capabs nx_ct_capabs"

(modulo static / caps / etc)

Thanks,
Nick

^ permalink raw reply

* Re: [PATCH] powerpc/pmu: Make the generic compat PMU use the architected events
From: Madhavan Srinivasan @ 2021-05-10  7:13 UTC (permalink / raw)
  To: Paul Mackerras, linuxppc-dev; +Cc: Michael Ellerman
In-Reply-To: <YJD7L9yeoxvxqeYi@thinks.paulus.ozlabs.org>


On 5/4/21 1:13 PM, Paul Mackerras wrote:
> This changes generic-compat-pmu.c so that it only uses architected
> events defined in Power ISA v3.0B, rather than event encodings which,
> while common to all the IBM Power Systems implementations, are
> nevertheless implementation-specific rather than architected.  The


Yeah as you pointed, this was aimed at IBM system implementations.
Thanks for the patch and patch looks fine to me.

Reviewed-by: Madhavan Srinivasan <maddy@linux.ibm.com>

I can send a follow up patch to return EINVAL for a non-zero value
other than pmc and pmcsel filed via check_attr_config.

> intention is that any CPU implementation designed to conform to Power
> ISA v3.0B or later can use generic-compat-pmu.c.
>
> In addition to the existing events for cycles and instructions, this
> adds several other architected events, including alternative encodings
> for some events.  In order to make it possible to measure cycles and
> instructions at the same time as each other, we set the CC5-6RUN bit
> in MMCR0, which makes PMC5 and PMC6 count instructions and cycles
> regardless of the run bit, so their events are now PM_CYC and
> PM_INST_CMPL rather than PM_RUN_CYC and PM_RUN_INST_CMPL (the latter
> are still available via other event codes).
>
> Note that POWER9 has an erratum where one architected event
> (PM_FLOP_CMPL, floating-point operations completed, code 0x100f4) does
> not work correctly.  Given that there is a specific PMU driver for P9
> which will be used in preference to generic-compat-pmu.c, that is not
> a real problem.
>
> Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
> ---
>   arch/powerpc/perf/generic-compat-pmu.c | 170 +++++++++++++++++++------
>   1 file changed, 134 insertions(+), 36 deletions(-)
>
> diff --git a/arch/powerpc/perf/generic-compat-pmu.c b/arch/powerpc/perf/generic-compat-pmu.c
> index eb8a6aaf4cc1..695975227e60 100644
> --- a/arch/powerpc/perf/generic-compat-pmu.c
> +++ b/arch/powerpc/perf/generic-compat-pmu.c
> @@ -14,45 +14,119 @@
>    *
>    *        28        24        20        16        12         8         4         0
>    * | - - - - | - - - - | - - - - | - - - - | - - - - | - - - - | - - - - | - - - - |
> - *                                 [ pmc ]   [unit ]   [ ]   m   [    pmcxsel    ]
> - *                                                     |     |
> - *                                                     |     *- mark
> - *                                                     |
> - *                                                     |
> - *                                                     *- combine
> - *
> - * Below uses IBM bit numbering.
> - *
> - * MMCR1[x:y] = unit    (PMCxUNIT)
> - * MMCR1[24]   = pmc1combine[0]
> - * MMCR1[25]   = pmc1combine[1]
> - * MMCR1[26]   = pmc2combine[0]
> - * MMCR1[27]   = pmc2combine[1]
> - * MMCR1[28]   = pmc3combine[0]
> - * MMCR1[29]   = pmc3combine[1]
> - * MMCR1[30]   = pmc4combine[0]
> - * MMCR1[31]   = pmc4combine[1]
> - *
> + *                                 [ pmc ]                       [    pmcxsel    ]
>    */
>   
>   /*
> - * Some power9 event codes.
> + * Event codes defined in ISA v3.0B
>    */
>   #define EVENT(_name, _code)	_name = _code,
>   
>   enum {
> -EVENT(PM_CYC,					0x0001e)
> -EVENT(PM_INST_CMPL,				0x00002)
> +	/* Cycles, alternate code */
> +	EVENT(PM_CYC_ALT,			0x100f0)
> +	/* One or more instructions completed in a cycle */
> +	EVENT(PM_CYC_INST_CMPL,			0x100f2)
> +	/* Floating-point instruction completed */
> +	EVENT(PM_FLOP_CMPL,			0x100f4)
> +	/* Instruction ERAT/L1-TLB miss */
> +	EVENT(PM_L1_ITLB_MISS,			0x100f6)
> +	/* All instructions completed and none available */
> +	EVENT(PM_NO_INST_AVAIL,			0x100f8)
> +	/* A load-type instruction completed (ISA v3.0+) */
> +	EVENT(PM_LD_CMPL,			0x100fc)
> +	/* Instruction completed, alternate code (ISA v3.0+) */
> +	EVENT(PM_INST_CMPL_ALT,			0x100fe)
> +	/* A store-type instruction completed */
> +	EVENT(PM_ST_CMPL,			0x200f0)
> +	/* Instruction Dispatched */
> +	EVENT(PM_INST_DISP,			0x200f2)
> +	/* Run_cycles */
> +	EVENT(PM_RUN_CYC,			0x200f4)
> +	/* Data ERAT/L1-TLB miss/reload */
> +	EVENT(PM_L1_DTLB_RELOAD,		0x200f6)
> +	/* Taken branch completed */
> +	EVENT(PM_BR_TAKEN_CMPL,			0x200fa)
> +	/* Demand iCache Miss */
> +	EVENT(PM_L1_ICACHE_MISS,		0x200fc)
> +	/* L1 Dcache reload from memory */
> +	EVENT(PM_L1_RELOAD_FROM_MEM,		0x200fe)
> +	/* L1 Dcache store miss */
> +	EVENT(PM_ST_MISS_L1,			0x300f0)
> +	/* Alternate code for PM_INST_DISP */
> +	EVENT(PM_INST_DISP_ALT,			0x300f2)
> +	/* Branch direction or target mispredicted */
> +	EVENT(PM_BR_MISPREDICT,			0x300f6)
> +	/* Data TLB miss/reload */
> +	EVENT(PM_DTLB_MISS,			0x300fc)
> +	/* Demand LD - L3 Miss (not L2 hit and not L3 hit) */
> +	EVENT(PM_DATA_FROM_L3MISS,		0x300fe)
> +	/* L1 Dcache load miss */
> +	EVENT(PM_LD_MISS_L1,			0x400f0)
> +	/* Cycle when instruction(s) dispatched */
> +	EVENT(PM_CYC_INST_DISP,			0x400f2)
> +	/* Branch or branch target mispredicted */
> +	EVENT(PM_BR_MPRED_CMPL,			0x400f6)
> +	/* Instructions completed with run latch set */
> +	EVENT(PM_RUN_INST_CMPL,			0x400fa)
> +	/* Instruction TLB miss/reload */
> +	EVENT(PM_ITLB_MISS,			0x400fc)
> +	/* Load data not cached */
> +	EVENT(PM_LD_NOT_CACHED,			0x400fe)
> +	/* Instructions */
> +	EVENT(PM_INST_CMPL,			0x500fa)
> +	/* Cycles */
> +	EVENT(PM_CYC,				0x600f4)
>   };
>   
>   #undef EVENT
>   
> +/* Table of alternatives, sorted in increasing order of column 0 */
> +/* Note that in each row, column 0 must be the smallest */
> +static const unsigned int generic_event_alternatives[][MAX_ALT] = {
> +	{ PM_CYC_ALT,			PM_CYC },
> +	{ PM_INST_CMPL_ALT,		PM_INST_CMPL },
> +	{ PM_INST_DISP,			PM_INST_DISP_ALT },
> +};
> +
> +static int generic_get_alternatives(u64 event, unsigned int flags, u64 alt[])
> +{
> +	int num_alt = 0;
> +
> +	num_alt = isa207_get_alternatives(event, alt,
> +					  ARRAY_SIZE(generic_event_alternatives), flags,
> +					  generic_event_alternatives);
> +
> +	return num_alt;
> +}
> +
>   GENERIC_EVENT_ATTR(cpu-cycles,			PM_CYC);
>   GENERIC_EVENT_ATTR(instructions,		PM_INST_CMPL);
> +GENERIC_EVENT_ATTR(stalled-cycles-frontend,	PM_NO_INST_AVAIL);
> +GENERIC_EVENT_ATTR(branch-misses,		PM_BR_MPRED_CMPL);
> +GENERIC_EVENT_ATTR(cache-misses,		PM_LD_MISS_L1);
> +
> +CACHE_EVENT_ATTR(L1-dcache-load-misses,		PM_LD_MISS_L1);
> +CACHE_EVENT_ATTR(L1-dcache-store-misses,	PM_ST_MISS_L1);
> +CACHE_EVENT_ATTR(L1-icache-load-misses,		PM_L1_ICACHE_MISS);
> +CACHE_EVENT_ATTR(LLC-load-misses,		PM_DATA_FROM_L3MISS);
> +CACHE_EVENT_ATTR(branch-load-misses,		PM_BR_MPRED_CMPL);
> +CACHE_EVENT_ATTR(dTLB-load-misses,		PM_DTLB_MISS);
> +CACHE_EVENT_ATTR(iTLB-load-misses,		PM_ITLB_MISS);
>   
>   static struct attribute *generic_compat_events_attr[] = {
>   	GENERIC_EVENT_PTR(PM_CYC),
>   	GENERIC_EVENT_PTR(PM_INST_CMPL),
> +	GENERIC_EVENT_PTR(PM_NO_INST_AVAIL),
> +	GENERIC_EVENT_PTR(PM_BR_MPRED_CMPL),
> +	GENERIC_EVENT_PTR(PM_LD_MISS_L1),
> +	CACHE_EVENT_PTR(PM_LD_MISS_L1),
> +	CACHE_EVENT_PTR(PM_ST_MISS_L1),
> +	CACHE_EVENT_PTR(PM_L1_ICACHE_MISS),
> +	CACHE_EVENT_PTR(PM_DATA_FROM_L3MISS),
> +	CACHE_EVENT_PTR(PM_BR_MPRED_CMPL),
> +	CACHE_EVENT_PTR(PM_DTLB_MISS),
> +	CACHE_EVENT_PTR(PM_ITLB_MISS),
>   	NULL
>   };
>   
> @@ -63,17 +137,11 @@ static struct attribute_group generic_compat_pmu_events_group = {
>   
>   PMU_FORMAT_ATTR(event,		"config:0-19");
>   PMU_FORMAT_ATTR(pmcxsel,	"config:0-7");
> -PMU_FORMAT_ATTR(mark,		"config:8");
> -PMU_FORMAT_ATTR(combine,	"config:10-11");
> -PMU_FORMAT_ATTR(unit,		"config:12-15");
>   PMU_FORMAT_ATTR(pmc,		"config:16-19");
>   
>   static struct attribute *generic_compat_pmu_format_attr[] = {
>   	&format_attr_event.attr,
>   	&format_attr_pmcxsel.attr,
> -	&format_attr_mark.attr,
> -	&format_attr_combine.attr,
> -	&format_attr_unit.attr,
>   	&format_attr_pmc.attr,
>   	NULL,
>   };
> @@ -92,6 +160,9 @@ static const struct attribute_group *generic_compat_pmu_attr_groups[] = {
>   static int compat_generic_events[] = {
>   	[PERF_COUNT_HW_CPU_CYCLES] =			PM_CYC,
>   	[PERF_COUNT_HW_INSTRUCTIONS] =			PM_INST_CMPL,
> +	[PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] =	PM_NO_INST_AVAIL,
> +	[PERF_COUNT_HW_BRANCH_MISSES] =			PM_BR_MPRED_CMPL,
> +	[PERF_COUNT_HW_CACHE_MISSES] =			PM_LD_MISS_L1,
>   };
>   
>   #define C(x)	PERF_COUNT_HW_CACHE_##x
> @@ -105,11 +176,11 @@ static u64 generic_compat_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = {
>   	[ C(L1D) ] = {
>   		[ C(OP_READ) ] = {
>   			[ C(RESULT_ACCESS) ] = 0,
> -			[ C(RESULT_MISS)   ] = 0,
> +			[ C(RESULT_MISS)   ] = PM_LD_MISS_L1,
>   		},
>   		[ C(OP_WRITE) ] = {
>   			[ C(RESULT_ACCESS) ] = 0,
> -			[ C(RESULT_MISS)   ] = 0,
> +			[ C(RESULT_MISS)   ] = PM_ST_MISS_L1,
>   		},
>   		[ C(OP_PREFETCH) ] = {
>   			[ C(RESULT_ACCESS) ] = 0,
> @@ -119,7 +190,7 @@ static u64 generic_compat_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = {
>   	[ C(L1I) ] = {
>   		[ C(OP_READ) ] = {
>   			[ C(RESULT_ACCESS) ] = 0,
> -			[ C(RESULT_MISS)   ] = 0,
> +			[ C(RESULT_MISS)   ] = PM_L1_ICACHE_MISS,
>   		},
>   		[ C(OP_WRITE) ] = {
>   			[ C(RESULT_ACCESS) ] = 0,
> @@ -133,7 +204,7 @@ static u64 generic_compat_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = {
>   	[ C(LL) ] = {
>   		[ C(OP_READ) ] = {
>   			[ C(RESULT_ACCESS) ] = 0,
> -			[ C(RESULT_MISS)   ] = 0,
> +			[ C(RESULT_MISS)   ] = PM_DATA_FROM_L3MISS,
>   		},
>   		[ C(OP_WRITE) ] = {
>   			[ C(RESULT_ACCESS) ] = 0,
> @@ -147,7 +218,7 @@ static u64 generic_compat_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = {
>   	[ C(DTLB) ] = {
>   		[ C(OP_READ) ] = {
>   			[ C(RESULT_ACCESS) ] = 0,
> -			[ C(RESULT_MISS)   ] = 0,
> +			[ C(RESULT_MISS)   ] = PM_DTLB_MISS,
>   		},
>   		[ C(OP_WRITE) ] = {
>   			[ C(RESULT_ACCESS) ] = -1,
> @@ -161,7 +232,7 @@ static u64 generic_compat_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = {
>   	[ C(ITLB) ] = {
>   		[ C(OP_READ) ] = {
>   			[ C(RESULT_ACCESS) ] = 0,
> -			[ C(RESULT_MISS)   ] = 0,
> +			[ C(RESULT_MISS)   ] = PM_ITLB_MISS,
>   		},
>   		[ C(OP_WRITE) ] = {
>   			[ C(RESULT_ACCESS) ] = -1,
> @@ -175,7 +246,7 @@ static u64 generic_compat_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = {
>   	[ C(BPU) ] = {
>   		[ C(OP_READ) ] = {
>   			[ C(RESULT_ACCESS) ] = 0,
> -			[ C(RESULT_MISS)   ] = 0,
> +			[ C(RESULT_MISS)   ] = PM_BR_MPRED_CMPL,
>   		},
>   		[ C(OP_WRITE) ] = {
>   			[ C(RESULT_ACCESS) ] = -1,
> @@ -204,13 +275,30 @@ static u64 generic_compat_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = {
>   
>   #undef C
>   
> +/*
> + * We set MMCR0[CC5-6RUN] so we can use counters 5 and 6 for
> + * PM_INST_CMPL and PM_CYC.
> + */
> +static int generic_compute_mmcr(u64 event[], int n_ev,
> +				unsigned int hwc[], struct mmcr_regs *mmcr,
> +				struct perf_event *pevents[], u32 flags)
> +{
> +	int ret;
> +
> +	ret = isa207_compute_mmcr(event, n_ev, hwc, mmcr, pevents, flags);
> +	if (!ret)
> +		mmcr->mmcr0 |= MMCR0_C56RUN;
> +	return ret;
> +}
> +
>   static struct power_pmu generic_compat_pmu = {
>   	.name			= "GENERIC_COMPAT",
>   	.n_counter		= MAX_PMU_COUNTERS,
>   	.add_fields		= ISA207_ADD_FIELDS,
>   	.test_adder		= ISA207_TEST_ADDER,
> -	.compute_mmcr		= isa207_compute_mmcr,
> +	.compute_mmcr		= generic_compute_mmcr,
>   	.get_constraint		= isa207_get_constraint,
> +	.get_alternatives	= generic_get_alternatives,
>   	.disable_pmc		= isa207_disable_pmc,
>   	.flags			= PPMU_HAS_SIER | PPMU_ARCH_207S,
>   	.n_generic		= ARRAY_SIZE(compat_generic_events),
> @@ -223,6 +311,16 @@ int init_generic_compat_pmu(void)
>   {
>   	int rc = 0;
>   
> +	/*
> +	 * From ISA v2.07 on, PMU features are architected;
> +	 * we require >= v3.0 because (a) that has PM_LD_CMPL and
> +	 * PM_INST_CMPL_ALT, which v2.07 doesn't have, and
> +	 * (b) we don't expect any non-IBM Power ISA
> +	 * implementations that conform to v2.07 but not v3.0.
> +	 */
> +	if (!cpu_has_feature(CPU_FTR_ARCH_300))
> +		return -ENODEV;
> +
>   	rc = register_power_pmu(&generic_compat_pmu);
>   	if (rc)
>   		return rc;

^ permalink raw reply

* [PATCH v2 1/2] ASoC: imx-akcodec: Add imx-akcodec machine driver
From: Shengjiu Wang @ 2021-05-10  7:07 UTC (permalink / raw)
  To: lgirdwood, broonie, perex, tiwai, timur, nicoleotsuka, Xiubo.Lee,
	festevam, shawnguo, s.hauer, kernel, linux-imx, linux-kernel,
	alsa-devel, linuxppc-dev, linux-arm-kernel, robh+dt, devicetree

Add machine driver for i.MX boards that have
AK4458/AK5558/AK4497/AK5552 DAC/ADC attached to
SAI interface.

Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
Signed-off-by: Mihai Serban <mihai.serban@nxp.com>
Signed-off-by: Cosmin-Gabriel Samoila <cosmin.samoila@nxp.com>
Signed-off-by: Viorel Suman <viorel.suman@nxp.com>
Signed-off-by: Daniel Baluta <daniel.baluta@nxp.com>
Signed-off-by: Adrian Alonso <adrian.alonso@nxp.com>
---
changes in v2:
- update the code according to the change of binding doc

 sound/soc/fsl/Kconfig       |  12 +
 sound/soc/fsl/Makefile      |   2 +
 sound/soc/fsl/imx-akcodec.c | 799 ++++++++++++++++++++++++++++++++++++
 3 files changed, 813 insertions(+)
 create mode 100644 sound/soc/fsl/imx-akcodec.c

diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 0917d65d6921..5720ad36494f 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -350,6 +350,18 @@ config SND_SOC_IMX_RPMSG
 	  Say Y if you want to add support for SoC audio on an i.MX board with
 	  a rpmsg devices.
 
+config SND_SOC_IMX_AKCODEC
+	tristate "SoC Audio support for i.MX boards with AKCODEC"
+	depends on OF && I2C
+	select SND_SOC_AK4458
+	select SND_SOC_AK5558
+	select SND_SOC_IMX_PCM_DMA
+	select SND_SOC_FSL_SAI
+	help
+	  SoC Audio support for i.MX boards with AK4458/AK4497/AK5558/AK5552
+	  Say Y if you want to add support for SoC audio on an i.MX board with
+	  an AK4458/AK4497/AK5558/AK5552 DAC/ADC.
+
 endif # SND_IMX_SOC
 
 endmenu
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index f146ce464acd..ee7caed0c758 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -71,6 +71,7 @@ snd-soc-imx-spdif-objs := imx-spdif.o
 snd-soc-imx-audmix-objs := imx-audmix.o
 snd-soc-imx-hdmi-objs := imx-hdmi.o
 snd-soc-imx-rpmsg-objs := imx-rpmsg.o
+snd-soc-imx-akcodec-objs := imx-akcodec.o
 
 obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o
 obj-$(CONFIG_SND_SOC_IMX_ES8328) += snd-soc-imx-es8328.o
@@ -79,3 +80,4 @@ obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o
 obj-$(CONFIG_SND_SOC_IMX_AUDMIX) += snd-soc-imx-audmix.o
 obj-$(CONFIG_SND_SOC_IMX_HDMI) += snd-soc-imx-hdmi.o
 obj-$(CONFIG_SND_SOC_IMX_RPMSG) += snd-soc-imx-rpmsg.o
+obj-$(CONFIG_SND_SOC_IMX_AKCODEC) += snd-soc-imx-akcodec.o
diff --git a/sound/soc/fsl/imx-akcodec.c b/sound/soc/fsl/imx-akcodec.c
new file mode 100644
index 000000000000..586ecac8c0bf
--- /dev/null
+++ b/sound/soc/fsl/imx-akcodec.c
@@ -0,0 +1,799 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright 2017-2021 NXP
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of_device.h>
+#include <linux/i2c.h>
+#include <linux/of_gpio.h>
+#include <linux/clk.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include <sound/pcm.h>
+#include <sound/soc-dapm.h>
+#include <sound/simple_card_utils.h>
+
+#include "fsl_sai.h"
+
+enum codec_type {
+	CODEC_AK5558 = 1,
+	CODEC_AK4458,
+	CODEC_AK4497,
+	CODEC_AK5552,
+};
+
+/*
+ * Mapping LRCK fs and frame width, table 3 & 4 in datasheet
+ * @rmin: min rate
+ * @rmax: max rate
+ * @wmin: min frame ratio
+ * @wmax: max frame ratio
+ */
+struct imx_akcodec_fs_mul {
+	unsigned int rmin;
+	unsigned int rmax;
+	unsigned int wmin;
+	unsigned int wmax;
+};
+
+/*
+ * Mapping TDM mode and frame width
+ */
+struct imx_akcodec_tdm_fs_mul {
+	unsigned int min;
+	unsigned int max;
+	unsigned int mul;
+};
+
+/*
+ * struct imx_akcodec_plat_data - specific info for codecs
+ *
+ * @fs_mul: ratio of mclk/fs for normal mode
+ * @tdm_fs_mul: ratio of mclk/fs for tdm mode
+ * @support_rates: supported sample rate
+ * @support_tdm_rates: supported sample rate for tdm mode
+ * @support_channels: supported channels
+ * @support_tdm_channels: supported channels for tdm mode
+ * @num_fs_mul: ARRAY_SIZE of fs_mul
+ * @num_tdm_fs_mul: ARRAY_SIZE of tdm_fs_mul
+ * @num_rates: ARRAY_SIZE of support_rates
+ * @num_tdm_rates: ARRAY_SIZE of support_tdm_rates
+ * @num_channels: ARRAY_SIZE of support_channels
+ * @num_tdm_channels: ARRAY_SIZE of support_tdm_channels
+ * @one2one_ratio: if mclk/bclk = 1 or not
+ * @type: codec type
+ */
+struct imx_akcodec_plat_data {
+	struct imx_akcodec_fs_mul  *fs_mul;
+	struct imx_akcodec_tdm_fs_mul  *tdm_fs_mul;
+	const u32 *support_rates;
+	const u32 *support_tdm_rates;
+	const u32 *support_channels;
+	const u32 *support_tdm_channels;
+	unsigned int num_fs_mul;
+	unsigned int num_tdm_fs_mul;
+	unsigned int num_rates;
+	unsigned int num_tdm_rates;
+	unsigned int num_channels;
+	unsigned int num_tdm_channels;
+	unsigned int num_codecs;
+	bool one2one_ratio;
+	enum codec_type type;
+};
+
+/*
+ * struct imx_akcodec_data - platform device data
+ *
+ * @card: card instance
+ * @plat_data: pointer of imx_akcodec_plat_data
+ * @dapm_routes: pointer of dapm_routes
+ * @num_dapm_routes: number of dapm_routes
+ * @slots: slots value
+ * @slot_width: slot width value
+ * @tdm_mode: tdm mode enabled or not
+ * @asrc_rate: asrc rates
+ * @asrc_format: asrc format
+ */
+struct imx_akcodec_data {
+	struct snd_soc_card card;
+	const struct imx_akcodec_plat_data *plat_data;
+	struct snd_soc_dapm_route *dapm_routes;
+	int num_dapm_routes;
+	unsigned int slots;
+	unsigned int slot_width;
+	bool tdm_mode;
+	u32 asrc_rate;
+	u32 asrc_format;
+};
+
+struct imx_akcodec_fs_mul ak4458_fs_mul[] = {
+	/* Normal, < 32kHz */
+	{ .rmin = 8000,   .rmax = 24000,  .wmin = 1024, .wmax = 1024, },
+	/* Normal, 32kHz */
+	{ .rmin = 32000,  .rmax = 32000,  .wmin = 256,  .wmax = 1024, },
+	/* Normal */
+	{ .rmin = 44100,  .rmax = 48000,  .wmin = 256,  .wmax = 768,  },
+	/* Double */
+	{ .rmin = 88200,  .rmax = 96000,  .wmin = 256,  .wmax = 512,  },
+	/* Quad */
+	{ .rmin = 176400, .rmax = 192000, .wmin = 128,  .wmax = 256,  },
+	/* Oct */
+	{ .rmin = 352800, .rmax = 384000, .wmin = 32,   .wmax = 128,  },
+	/* Hex */
+	{ .rmin = 705600, .rmax = 768000, .wmin = 16,   .wmax = 64,   },
+};
+
+struct imx_akcodec_tdm_fs_mul ak4458_tdm_fs_mul[] = {
+	/*
+	 * Table 13	- Audio Interface Format
+	 * For TDM mode, MCLK should is set to
+	 * obtained from 2 * slots * slot_width
+	 */
+	{ .min = 128,	.max = 128,	.mul = 256  }, /* TDM128 */
+	{ .min = 256,	.max = 256,	.mul = 512  }, /* TDM256 */
+	{ .min = 512,	.max = 512,	.mul = 1024  }, /* TDM512 */
+};
+
+struct imx_akcodec_fs_mul ak4497_fs_mul[] = {
+	/**
+	 * Table 7      - mapping multiplier and speed mode
+	 * Tables 8 & 9 - mapping speed mode and LRCK fs
+	 */
+	{ .rmin = 8000,   .rmax = 32000,  .wmin = 1024, .wmax = 1024, }, /* Normal, <= 32kHz */
+	{ .rmin = 44100,  .rmax = 48000,  .wmin = 512,  .wmax = 512, }, /* Normal */
+	{ .rmin = 88200,  .rmax = 96000,  .wmin = 256,  .wmax = 256, }, /* Double */
+	{ .rmin = 176400, .rmax = 192000, .wmin = 128,  .wmax = 128, }, /* Quad */
+	{ .rmin = 352800, .rmax = 384000, .wmin = 128,  .wmax = 128, }, /* Oct */
+	{ .rmin = 705600, .rmax = 768000, .wmin = 64,   .wmax = 64, }, /* Hex */
+};
+
+/*
+ * Auto MCLK selection based on LRCK for Normal Mode
+ * (Table 4 from datasheet)
+ */
+struct imx_akcodec_fs_mul ak5558_fs_mul[] = {
+	{ .rmin = 8000,   .rmax = 32000,  .wmin = 1024, .wmax = 1024, },
+	{ .rmin = 44100,  .rmax = 48000,  .wmin = 512,  .wmax = 512, },
+	{ .rmin = 88200,  .rmax = 96000,  .wmin = 256,  .wmax = 256, },
+	{ .rmin = 176400, .rmax = 192000, .wmin = 128,  .wmax = 128, },
+	{ .rmin = 352800, .rmax = 384000, .wmin = 64,   .wmax = 64, },
+	{ .rmin = 705600, .rmax = 768000, .wmin = 32,   .wmax = 32, },
+};
+
+/*
+ * MCLK and BCLK selection based on TDM mode
+ * because of SAI we also add the restriction: MCLK >= 2 * BCLK
+ * (Table 9 from datasheet)
+ */
+struct imx_akcodec_tdm_fs_mul ak5558_tdm_fs_mul[] = {
+	{ .min = 128,	.max = 128,	.mul = 256 },
+	{ .min = 256,	.max = 256,	.mul = 512 },
+	{ .min = 512,	.max = 512,	.mul = 1024 },
+};
+
+static const u32 akcodec_rates[] = {
+	8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200,
+	96000, 176400, 192000, 352800, 384000, 705600, 768000,
+};
+
+static const u32 akcodec_tdm_rates[] = {
+	8000, 16000, 32000, 48000, 96000,
+};
+
+static const u32 ak4458_channels[] = {
+	1, 2, 4, 6, 8, 10, 12, 14, 16,
+};
+
+static const u32 ak4458_tdm_channels[] = {
+	1, 2, 3, 4, 5, 6, 7, 8, 16,
+};
+
+static const u32 ak5558_channels[] = {
+	1, 2, 4, 6, 8,
+};
+
+static const u32 ak5558_tdm_channels[] = {
+	1, 2, 3, 4, 5, 6, 7, 8,
+};
+
+static const struct imx_akcodec_plat_data imx8mq_akcodec = {
+	.support_rates = akcodec_rates,
+	.num_rates = ARRAY_SIZE(akcodec_rates),
+	.support_tdm_rates = akcodec_tdm_rates,
+	.num_tdm_rates = ARRAY_SIZE(akcodec_tdm_rates),
+	.one2one_ratio = false,
+};
+
+static const struct imx_akcodec_plat_data imx_akcodec = {
+	.support_rates = akcodec_rates,
+	.num_rates = ARRAY_SIZE(akcodec_rates),
+	.support_tdm_rates = akcodec_tdm_rates,
+	.num_tdm_rates = ARRAY_SIZE(akcodec_tdm_rates),
+	.one2one_ratio = true,
+};
+
+static bool format_is_dsd(struct snd_pcm_hw_params *params)
+{
+	snd_pcm_format_t format = params_format(params);
+
+	switch (format) {
+	case SNDRV_PCM_FORMAT_DSD_U8:
+	case SNDRV_PCM_FORMAT_DSD_U16_LE:
+	case SNDRV_PCM_FORMAT_DSD_U16_BE:
+	case SNDRV_PCM_FORMAT_DSD_U32_LE:
+	case SNDRV_PCM_FORMAT_DSD_U32_BE:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool format_is_tdm(unsigned int dai_fmt)
+{
+	if ((dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_DSP_B)
+		return true;
+	else
+		return false;
+}
+
+static unsigned long akcodec_get_mclk_rate(struct snd_pcm_substream *substream,
+					   struct snd_pcm_hw_params *params,
+					   bool tdm_mode)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct imx_akcodec_data *data = snd_soc_card_get_drvdata(rtd->card);
+	const struct imx_akcodec_plat_data *plat_data = data->plat_data;
+	unsigned int width = data->slots * data->slot_width;
+	unsigned int rate = params_rate(params);
+	int i;
+
+	if (data->tdm_mode) {
+		for (i = 0; i < plat_data->num_tdm_fs_mul; i++) {
+			/* min = max = slots * slots_width */
+			if (width != plat_data->tdm_fs_mul[i].min)
+				continue;
+			return rate * plat_data->tdm_fs_mul[i].mul;
+		}
+	} else {
+		for (i = 0; i < plat_data->num_fs_mul; i++) {
+			if (rate >= plat_data->fs_mul[i].rmin &&
+			    rate <= plat_data->fs_mul[i].rmax) {
+				width = max(width, plat_data->fs_mul[i].wmin);
+				width = min(width, plat_data->fs_mul[i].wmax);
+
+				/* Adjust SAI bclk:mclk ratio */
+				width *= plat_data->one2one_ratio ? 1 : 2;
+
+				return rate * width;
+			}
+		}
+	}
+
+	/* Let DAI manage clk frequency by default */
+	return 0;
+}
+
+static int imx_aif_hw_params(struct snd_pcm_substream *substream,
+			     struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+	struct snd_soc_card *card = rtd->card;
+	struct imx_akcodec_data *data = snd_soc_card_get_drvdata(card);
+	bool is_dsd = format_is_dsd(params);
+	struct device *dev = card->dev;
+	struct snd_soc_dai *codec_dai;
+	unsigned long mclk_freq;
+	unsigned int fmt;
+	bool tdm_mode;
+	int ret, i;
+
+	tdm_mode = format_is_tdm(rtd->dai_link->dai_fmt);
+
+	if (tdm_mode) {
+		data->slots = 8;
+		data->slot_width = 32;
+		fmt = rtd->dai_link->dai_fmt;
+	} else {
+		if (is_dsd) {
+			data->slots = 1;
+			data->slot_width = params_width(params);
+			fmt = (rtd->dai_link->dai_fmt & ~SND_SOC_DAIFMT_FORMAT_MASK) |
+			      SND_SOC_DAIFMT_PDM;
+		} else {
+			data->slots = 2;
+			data->slot_width = params_physical_width(params);
+			fmt = (rtd->dai_link->dai_fmt & ~SND_SOC_DAIFMT_FORMAT_MASK) |
+			      SND_SOC_DAIFMT_I2S;
+		}
+	}
+
+	ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+	if (ret && ret != -ENOTSUPP) {
+		dev_err(dev, "failed to set cpu dai fmt: %d\n", ret);
+		return ret;
+	}
+	ret = snd_soc_dai_set_tdm_slot(cpu_dai,
+				       BIT(data->slots) - 1,
+				       BIT(data->slots) - 1,
+				       data->slots, data->slot_width);
+	if (ret && ret != -ENOTSUPP) {
+		dev_err(dev, "failed to set cpu dai tdm slot: %d\n", ret);
+		return ret;
+	}
+
+	for_each_rtd_codec_dais(rtd, i, codec_dai) {
+		ret = snd_soc_dai_set_fmt(codec_dai, fmt);
+		if (ret && ret != -ENOTSUPP) {
+			dev_err(dev, "failed to set codec dai[%d] fmt: %d\n", i, ret);
+			return ret;
+		}
+
+		ret = snd_soc_dai_set_tdm_slot(codec_dai,
+					       BIT(data->slots) - 1,
+					       BIT(data->slots) - 1,
+					       data->slots, data->slot_width);
+		if (ret && ret != -ENOTSUPP) {
+			dev_err(dev, "failed to set codec dai[%d] tdm slot: %d\n", i, ret);
+			return ret;
+		}
+	}
+
+	/* Set MCLK freq */
+	mclk_freq = akcodec_get_mclk_rate(substream, params, tdm_mode);
+	/* Use the maximum freq from DSD512 (512*44100 = 22579200) */
+	if (is_dsd)
+		mclk_freq = 22579200;
+
+	ret = snd_soc_dai_set_sysclk(cpu_dai, FSL_SAI_CLK_MAST1, mclk_freq,
+				     SND_SOC_CLOCK_OUT);
+	if (ret && ret != -ENOTSUPP)
+		dev_err(dev, "failed to set cpui dai mclk1 rate (%lu): %d\n",
+			mclk_freq, ret);
+	return ret;
+}
+
+static int ak5558_hw_rule_rate(struct snd_pcm_hw_params *p, struct snd_pcm_hw_rule *r)
+{
+	struct imx_akcodec_data *data = r->private;
+	struct snd_interval t = { .min = 8000, .max = 8000, };
+	unsigned long mclk_freq;
+	unsigned int fs;
+	int i;
+
+	fs = hw_param_interval(p, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min;
+	fs *= data->tdm_mode ? 8 : 2;
+
+	/* Identify maximum supported rate */
+	for (i = 0; i < ARRAY_SIZE(akcodec_rates); i++) {
+		mclk_freq = fs * akcodec_rates[i];
+		/* Adjust SAI bclk:mclk ratio */
+		mclk_freq *= data->plat_data->one2one_ratio ? 1 : 2;
+
+		/* Skip rates for which MCLK is beyond supported value */
+		if (mclk_freq > 36864000)
+			continue;
+
+		if (t.max < akcodec_rates[i])
+			t.max = akcodec_rates[i];
+	}
+
+	return snd_interval_refine(hw_param_interval(p, r->var), &t);
+}
+
+static int imx_aif_startup(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_card *card = rtd->card;
+	struct imx_akcodec_data *data = snd_soc_card_get_drvdata(card);
+	static struct snd_pcm_hw_constraint_list constraint_rates;
+	static struct snd_pcm_hw_constraint_list constraint_channels;
+	int ret = 0;
+
+	data->tdm_mode = format_is_tdm(rtd->dai_link->dai_fmt);
+	if (data->tdm_mode) {
+		constraint_channels.list = data->plat_data->support_tdm_channels;
+		constraint_channels.count = data->plat_data->num_tdm_channels;
+		constraint_rates.list = data->plat_data->support_tdm_rates;
+		constraint_rates.count = data->plat_data->num_tdm_rates;
+	} else {
+		constraint_channels.list = data->plat_data->support_channels;
+		constraint_channels.count = data->plat_data->num_channels;
+		constraint_rates.list = data->plat_data->support_rates;
+		constraint_rates.count = data->plat_data->num_rates;
+	}
+
+	ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+					 &constraint_channels);
+	if (ret)
+		return ret;
+
+	ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+					 &constraint_rates);
+	if (ret)
+		return ret;
+
+	if (data->plat_data->type == CODEC_AK5558)
+		ret = snd_pcm_hw_rule_add(substream->runtime, 0,
+					  SNDRV_PCM_HW_PARAM_RATE,
+					  ak5558_hw_rule_rate, data,
+					  SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1);
+
+	return ret;
+}
+
+static struct snd_soc_ops imx_aif_ops = {
+	.hw_params = imx_aif_hw_params,
+	.startup = imx_aif_startup,
+};
+
+static struct snd_soc_ops imx_aif_ops_be = {
+	.hw_params = imx_aif_hw_params,
+};
+
+static int be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+			      struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_card *card = rtd->card;
+	struct imx_akcodec_data *data = snd_soc_card_get_drvdata(card);
+	struct snd_interval *rate;
+	struct snd_mask *mask;
+
+	rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+	rate->max = data->asrc_rate;
+	rate->min = data->asrc_rate;
+
+	mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+	snd_mask_none(mask);
+	snd_mask_set(mask, data->asrc_format);
+
+	return 0;
+}
+
+static int imx_akcodec_parse_of(struct snd_soc_card *card,
+				u32 *asrc_format,
+				u32 *asrc_rate,
+				enum codec_type *codec_type,
+				unsigned int *num_codecs)
+{
+	struct device_node *np;
+	struct device_node *codec = NULL;
+	struct device_node *platform = NULL;
+	struct device_node *cpu = NULL;
+	struct device *dev = card->dev;
+	struct snd_soc_dai_link *link;
+	struct of_phandle_args args;
+	struct snd_soc_dai_link_component *dlc;
+	int ret, num_links;
+	u32 width;
+
+	ret = snd_soc_of_parse_card_name(card, "model");
+	if (ret) {
+		dev_err(dev, "Error parsing card name: %d\n", ret);
+		return ret;
+	}
+
+	/* DAPM routes */
+	if (of_property_read_bool(dev->of_node, "audio-routing")) {
+		ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
+		if (ret)
+			return ret;
+	}
+
+	/* Populate links */
+	num_links = of_get_child_count(dev->of_node);
+
+	/* Allocate the DAI link array */
+	card->dai_link = devm_kcalloc(dev, num_links, sizeof(*link), GFP_KERNEL);
+	if (!card->dai_link)
+		return -ENOMEM;
+
+	card->num_links = num_links;
+	link = card->dai_link;
+
+	for_each_child_of_node(dev->of_node, np) {
+		dlc = devm_kzalloc(dev, 2 * sizeof(*dlc), GFP_KERNEL);
+		if (!dlc) {
+			ret = -ENOMEM;
+			goto err_put_np;
+		}
+
+		link->cpus	= &dlc[0];
+		link->platforms	= &dlc[1];
+
+		link->num_cpus		= 1;
+		link->num_platforms	= 1;
+
+		ret = of_property_read_string(np, "link-name", &link->name);
+		if (ret) {
+			dev_err(card->dev, "error getting codec dai_link name\n");
+			goto err_put_np;
+		}
+
+		cpu = of_get_child_by_name(np, "cpu");
+		if (!cpu) {
+			dev_err(dev, "%s: Can't find cpu DT node\n", link->name);
+			ret = -EINVAL;
+			goto err;
+		}
+
+		ret = of_parse_phandle_with_args(cpu, "sound-dai",
+						 "#sound-dai-cells", 0, &args);
+		if (ret) {
+			dev_err(card->dev, "%s: error getting cpu phandle\n", link->name);
+			goto err;
+		}
+		link->cpus->of_node = args.np;
+		link->platforms->of_node = link->cpus->of_node;
+		link->id = args.args[0];
+
+		ret = snd_soc_of_get_dai_name(cpu, &link->cpus->dai_name);
+		if (ret) {
+			if (ret != -EPROBE_DEFER)
+				dev_err(card->dev, "%s: error getting cpu dai name: %d\n",
+					link->name, ret);
+			goto err;
+		}
+
+		codec = of_get_child_by_name(np, "codec");
+		if (codec) {
+			ret = snd_soc_of_get_dai_link_codecs(dev, codec, link);
+			if (ret < 0) {
+				if (ret != -EPROBE_DEFER)
+					dev_err(dev, "%s: codec dai not found: %d\n",
+						link->name, ret);
+				goto err;
+			}
+
+			*num_codecs = link->num_codecs;
+
+			if (!strcmp(link->codecs->dai_name, "ak4458-aif"))
+				*codec_type = CODEC_AK4458;
+			else if (!strcmp(link->codecs->dai_name, "ak4497-aif"))
+				*codec_type = CODEC_AK4497;
+			else if (!strcmp(link->codecs->dai_name, "ak5558-aif"))
+				*codec_type = CODEC_AK5558;
+			else if (!strcmp(link->codecs->dai_name, "ak5552-aif"))
+				*codec_type = CODEC_AK5552;
+
+		} else {
+			dlc = devm_kzalloc(dev, sizeof(*dlc), GFP_KERNEL);
+			if (!dlc) {
+				ret = -ENOMEM;
+				goto err;
+			}
+
+			link->codecs	 = dlc;
+			link->num_codecs = 1;
+
+			link->codecs->dai_name = "snd-soc-dummy-dai";
+			link->codecs->name = "snd-soc-dummy";
+		}
+
+		if (!strncmp(link->name, "HiFi-ASRC-FE", 12)) {
+			/* DPCM frontend */
+			link->dynamic = 1;
+			link->dpcm_merged_chan = 1;
+
+			ret = of_property_read_u32(args.np, "fsl,asrc-rate", asrc_rate);
+			if (ret) {
+				dev_err(dev, "failed to get output rate\n");
+				ret = -EINVAL;
+				goto err;
+			}
+
+			ret = of_property_read_u32(args.np, "fsl,asrc-format", asrc_format);
+			if (ret) {
+				/* Fallback to old binding; translate to asrc_format */
+				ret = of_property_read_u32(args.np, "fsl,asrc-width", &width);
+				if (ret) {
+					dev_err(dev,
+						"failed to decide output format\n");
+					goto err;
+				}
+
+				if (width == 24)
+					*asrc_format = SNDRV_PCM_FORMAT_S24_LE;
+				else
+					*asrc_format = SNDRV_PCM_FORMAT_S16_LE;
+			}
+		} else if (!strncmp(link->name, "HiFi-ASRC-BE", 12)) {
+			/* DPCM backend */
+			link->no_pcm = 1;
+			link->platforms->of_node = NULL;
+			link->platforms->name = "snd-soc-dummy";
+
+			link->be_hw_params_fixup = be_hw_params_fixup;
+			link->ops = &imx_aif_ops_be;
+		} else {
+			link->ops = &imx_aif_ops;
+		}
+
+		if (link->no_pcm || link->dynamic)
+			snd_soc_dai_link_set_capabilities(link);
+
+		ret = asoc_simple_parse_daifmt(dev, np, codec,
+					       NULL, &link->dai_fmt);
+		if (ret)
+			link->dai_fmt = SND_SOC_DAIFMT_NB_NF |
+					SND_SOC_DAIFMT_CBS_CFS |
+					SND_SOC_DAIFMT_I2S;
+
+		link->ignore_pmdown_time = 1;
+		link->stream_name = link->name;
+		link++;
+
+		of_node_put(cpu);
+		of_node_put(codec);
+		of_node_put(platform);
+	}
+
+	return 0;
+err:
+	of_node_put(cpu);
+	of_node_put(codec);
+	of_node_put(platform);
+err_put_np:
+	of_node_put(np);
+	return ret;
+}
+
+static int imx_akcodec_probe(struct platform_device *pdev)
+{
+	struct snd_soc_dai_link *link_be = NULL, *link;
+	struct imx_akcodec_plat_data *plat_data;
+	struct imx_akcodec_data *data;
+	int ret, i;
+
+	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	plat_data = devm_kzalloc(&pdev->dev, sizeof(*plat_data), GFP_KERNEL);
+	if (!plat_data)
+		return -ENOMEM;
+
+	data->plat_data = plat_data;
+	data->card.dev = &pdev->dev;
+
+	memcpy(plat_data, of_device_get_match_data(&pdev->dev),
+	       sizeof(struct imx_akcodec_plat_data));
+
+	dev_set_drvdata(&pdev->dev, &data->card);
+	snd_soc_card_set_drvdata(&data->card, data);
+	ret = imx_akcodec_parse_of(&data->card,
+				   &data->asrc_format,
+				   &data->asrc_rate,
+				   &plat_data->type,
+				   &plat_data->num_codecs);
+	if (ret)
+		return ret;
+
+	data->num_dapm_routes = plat_data->num_codecs + 1;
+	data->dapm_routes = devm_kcalloc(&pdev->dev, data->num_dapm_routes,
+					 sizeof(struct snd_soc_dapm_route),
+					 GFP_KERNEL);
+	if (!data->dapm_routes)
+		return -ENOMEM;
+
+	/* configure the dapm routes */
+	switch (plat_data->type) {
+	case CODEC_AK4458:
+	case CODEC_AK4497:
+		if (plat_data->num_codecs == 1) {
+			data->dapm_routes[0].sink = "Playback";
+			data->dapm_routes[0].source = "CPU-Playback";
+			i = 1;
+		} else {
+			for (i = 0; i < plat_data->num_codecs; i++) {
+				data->dapm_routes[i].sink =
+					devm_kasprintf(&pdev->dev, GFP_KERNEL, "%d %s",
+						       i + 1, "Playback");
+				data->dapm_routes[i].source = "CPU-Playback";
+			}
+		}
+		data->dapm_routes[i].sink = "CPU-Playback";
+		data->dapm_routes[i].source = "ASRC-Playback";
+		break;
+	case CODEC_AK5558:
+	case CODEC_AK5552:
+		if (plat_data->num_codecs == 1) {
+			data->dapm_routes[0].sink = "CPU-Capture";
+			data->dapm_routes[0].source = "Capture";
+			i = 1;
+		} else {
+			for (i = 0; i < plat_data->num_codecs; i++) {
+				data->dapm_routes[i].source =
+					devm_kasprintf(&pdev->dev, GFP_KERNEL, "%d %s",
+						       i + 1, "Capture");
+				data->dapm_routes[i].sink = "CPU-Capture";
+			}
+		}
+		data->dapm_routes[i].sink = "ASRC-Capture";
+		data->dapm_routes[i].source = "CPU-Capture";
+		break;
+	default:
+		break;
+	}
+
+	switch (plat_data->type) {
+	case CODEC_AK4458:
+		plat_data->fs_mul = ak4458_fs_mul;
+		plat_data->num_fs_mul = ARRAY_SIZE(ak4458_fs_mul);
+		plat_data->tdm_fs_mul = ak4458_tdm_fs_mul;
+		plat_data->num_tdm_fs_mul = ARRAY_SIZE(ak4458_tdm_fs_mul);
+		plat_data->support_channels = ak4458_channels;
+		plat_data->num_channels = ARRAY_SIZE(ak4458_channels);
+		plat_data->support_tdm_channels = ak4458_tdm_channels;
+		plat_data->num_tdm_channels = ARRAY_SIZE(ak4458_tdm_channels);
+		break;
+	case CODEC_AK4497:
+		plat_data->fs_mul = ak4497_fs_mul;
+		plat_data->num_fs_mul = ARRAY_SIZE(ak4497_fs_mul);
+		plat_data->support_channels = ak4458_channels;
+		plat_data->num_channels = ARRAY_SIZE(ak4458_channels);
+		break;
+	case CODEC_AK5558:
+	case CODEC_AK5552:
+		plat_data->fs_mul = ak5558_fs_mul;
+		plat_data->num_fs_mul = ARRAY_SIZE(ak5558_fs_mul);
+		plat_data->tdm_fs_mul = ak5558_tdm_fs_mul;
+		plat_data->num_tdm_fs_mul = ARRAY_SIZE(ak5558_tdm_fs_mul);
+		plat_data->support_channels = ak5558_channels;
+		plat_data->num_channels = ARRAY_SIZE(ak5558_channels);
+		plat_data->support_tdm_channels = ak5558_tdm_channels;
+		plat_data->num_tdm_channels = ARRAY_SIZE(ak5558_tdm_channels);
+		break;
+	default:
+		break;
+	}
+
+	/* with asrc as front end */
+	if (data->card.num_links == 3) {
+		data->card.dapm_routes = data->dapm_routes;
+		data->card.num_dapm_routes = data->num_dapm_routes;
+		for_each_card_prelinks(&data->card, i, link) {
+			if (link->no_pcm == 1)
+				link_be = link;
+		}
+		for_each_card_prelinks(&data->card, i, link) {
+			if (link->dynamic == 1 && link_be) {
+				link->dpcm_playback = link_be->dpcm_playback;
+				link->dpcm_capture = link_be->dpcm_capture;
+			}
+		}
+	}
+
+	ret = devm_snd_soc_register_card(&pdev->dev, &data->card);
+	if (ret) {
+		if (ret != -EPROBE_DEFER)
+			dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct of_device_id imx_akcodec_dt_ids[] = {
+	{ .compatible = "fsl,imx8mq-audio-akcodec", .data = &imx8mq_akcodec, },
+	{ .compatible = "fsl,imx-audio-akcodec", .data = &imx_akcodec, },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, imx_akcodec_dt_ids);
+
+static struct platform_driver imx_akcodec_driver = {
+	.driver = {
+		.name = "imx-akcodec",
+		.pm = &snd_soc_pm_ops,
+		.of_match_table = imx_akcodec_dt_ids,
+	},
+	.probe = imx_akcodec_probe,
+};
+module_platform_driver(imx_akcodec_driver);
+
+MODULE_DESCRIPTION("Freescale i.MX AK CODEC ASoC machine driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:imx-akcodec");
-- 
2.27.0


^ permalink raw reply related

* [PATCH v2 2/2] ASoC: dt-bindings: imx-akcodec: Add binding doc for akcodec machine driver
From: Shengjiu Wang @ 2021-05-10  7:07 UTC (permalink / raw)
  To: lgirdwood, broonie, perex, tiwai, timur, nicoleotsuka, Xiubo.Lee,
	festevam, shawnguo, s.hauer, kernel, linux-imx, linux-kernel,
	alsa-devel, linuxppc-dev, linux-arm-kernel, robh+dt, devicetree
In-Reply-To: <1620630441-14358-1-git-send-email-shengjiu.wang@nxp.com>

Imx-akcodec is a new added machine driver for supporting
ak4458/ak5558/ak5552/ak4497 codec on i.MX platforms.

Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
---
changes in v2:
- update doc accoring to Rob's comment, use the common porperties.

 .../bindings/sound/imx-audio-akcodec.yaml     | 107 ++++++++++++++++++
 1 file changed, 107 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/imx-audio-akcodec.yaml

diff --git a/Documentation/devicetree/bindings/sound/imx-audio-akcodec.yaml b/Documentation/devicetree/bindings/sound/imx-audio-akcodec.yaml
new file mode 100644
index 000000000000..0812c4779b59
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/imx-audio-akcodec.yaml
@@ -0,0 +1,107 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/imx-audio-akcodec.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NXP i.MX audio complex with AK4458/AK5558/AK5552/AK4497 codec
+
+maintainers:
+  - Shengjiu Wang <shengjiu.wang@nxp.com>
+
+properties:
+  compatible:
+    enum:
+      - fsl,imx8mq-audio-akcodec
+      - fsl,imx-audio-akcodec
+
+  model:
+    $ref: /schemas/types.yaml#/definitions/string
+    description: User specified audio sound card name
+
+  audio-routing:
+    $ref: /schemas/types.yaml#/definitions/non-unique-string-array
+    description:
+      A list of the connections between audio components. Each entry is a
+      pair of strings, the first being the connection's sink, the second
+      being the connection's source. Valid names could be power supplies,
+      MicBias of codec and the jacks on the board.
+
+patternProperties:
+  ".*-dai-link$":
+    description:
+      Each subnode represents a dai link. Subnodes of each dai links would be
+      cpu/codec dais.
+
+    type: object
+
+    properties:
+      link-name:
+        description: Indicates dai-link name and PCM stream name.
+        $ref: /schemas/types.yaml#/definitions/string
+        maxItems: 1
+
+      format:
+        description: audio format.
+        items:
+          enum:
+            - i2s
+            - dsp_b
+
+      cpu:
+        description: Holds subnode which indicates cpu dai.
+        type: object
+        properties:
+          sound-dai: true
+
+      codec:
+        description: Holds subnode which indicates codec dai.
+        type: object
+        properties:
+          sound-dai: true
+
+    required:
+      - link-name
+      - cpu
+
+    additionalProperties: false
+
+required:
+  - compatible
+  - model
+
+additionalProperties: false
+
+examples:
+  - |
+    sound-ak4458 {
+        compatible = "fsl,imx-audio-akcodec";
+        model = "ak4458-audio";
+        pri-dai-link {
+            link-name = "akcodec";
+            format = "i2s";
+            cpu {
+                 sound-dai = <&sai1>;
+            };
+            codec {
+                 sound-dai = <&ak4458_1>, <&ak4458_2>;
+            };
+        };
+        fe-dai-link {
+            link-name = "HiFi-ASRC-FE";
+            format = "i2s";
+            cpu {
+                sound-dai = <&easrc>;
+            };
+        };
+        be-dai-link {
+            link-name = "HiFi-ASRC-BE";
+            format = "dsp_b";
+            cpu {
+                sound-dai = <&sai1>;
+            };
+            codec {
+                sound-dai = <&ak4458_1>, <&ak4458_2>;
+            };
+        };
+    };
-- 
2.27.0


^ permalink raw reply related

* Re: [PATCH v4 02/11] powerpc/kernel/iommu: Add new iommu_table_in_use() helper
From: Alexey Kardashevskiy @ 2021-05-10  7:34 UTC (permalink / raw)
  To: Leonardo Bras, Michael Ellerman, Benjamin Herrenschmidt,
	Paul Mackerras, Joel Stanley, Christophe Leroy, Nicolin Chen,
	Niklas Schnelle
  Cc: linuxppc-dev, linux-kernel
In-Reply-To: <20210430163145.146984-3-leobras.c@gmail.com>



On 5/1/21 02:31, Leonardo Bras wrote:
> Having a function to check if the iommu table has any allocation helps
> deciding if a tbl can be reset for using a new DMA window.
> 
> It should be enough to replace all instances of !bitmap_empty(tbl...).
> 
> iommu_table_in_use() skips reserved memory, so we don't need to worry about
> releasing it before testing. This causes iommu_table_release_pages() to
> become unnecessary, given it is only used to remove reserved memory for
> testing.
> 
> Also, only allow storing reserved memory values in tbl if they are valid
> in the table, so there is no need to check it in the new helper.
> 
> Signed-off-by: Leonardo Bras <leobras.c@gmail.com>
> ---
>   arch/powerpc/include/asm/iommu.h |  1 +
>   arch/powerpc/kernel/iommu.c      | 65 ++++++++++++++++----------------
>   2 files changed, 34 insertions(+), 32 deletions(-)
> 
> diff --git a/arch/powerpc/include/asm/iommu.h b/arch/powerpc/include/asm/iommu.h
> index deef7c94d7b6..bf3b84128525 100644
> --- a/arch/powerpc/include/asm/iommu.h
> +++ b/arch/powerpc/include/asm/iommu.h
> @@ -154,6 +154,7 @@ extern int iommu_tce_table_put(struct iommu_table *tbl);
>    */
>   extern struct iommu_table *iommu_init_table(struct iommu_table *tbl,
>   		int nid, unsigned long res_start, unsigned long res_end);
> +bool iommu_table_in_use(struct iommu_table *tbl);
>   
>   #define IOMMU_TABLE_GROUP_MAX_TABLES	2
>   
> diff --git a/arch/powerpc/kernel/iommu.c b/arch/powerpc/kernel/iommu.c
> index ad82dda81640..5e168bd91401 100644
> --- a/arch/powerpc/kernel/iommu.c
> +++ b/arch/powerpc/kernel/iommu.c
> @@ -691,32 +691,24 @@ static void iommu_table_reserve_pages(struct iommu_table *tbl,
>   	if (tbl->it_offset == 0)
>   		set_bit(0, tbl->it_map);
>   
> -	tbl->it_reserved_start = res_start;
> -	tbl->it_reserved_end = res_end;
> -
> -	/* Check if res_start..res_end isn't empty and overlaps the table */
> -	if (res_start && res_end &&
> -			(tbl->it_offset + tbl->it_size < res_start ||
> -			 res_end < tbl->it_offset))
> -		return;
> +	if (res_start < tbl->it_offset)
> +		res_start = tbl->it_offset;
>   
> -	for (i = tbl->it_reserved_start; i < tbl->it_reserved_end; ++i)
> -		set_bit(i - tbl->it_offset, tbl->it_map);
> -}
> +	if (res_end > (tbl->it_offset + tbl->it_size))
> +		res_end = tbl->it_offset + tbl->it_size;
>   
> -static void iommu_table_release_pages(struct iommu_table *tbl)
> -{
> -	int i;
> +	/* Check if res_start..res_end is a valid range in the table */
> +	if (res_start >= res_end) {
> +		tbl->it_reserved_start = tbl->it_offset;
> +		tbl->it_reserved_end = tbl->it_offset;
> +		return;
> +	}
>   
> -	/*
> -	 * In case we have reserved the first bit, we should not emit
> -	 * the warning below.
> -	 */
> -	if (tbl->it_offset == 0)
> -		clear_bit(0, tbl->it_map);
> +	tbl->it_reserved_start = res_start;
> +	tbl->it_reserved_end = res_end;
>   
>   	for (i = tbl->it_reserved_start; i < tbl->it_reserved_end; ++i)
> -		clear_bit(i - tbl->it_offset, tbl->it_map);
> +		set_bit(i - tbl->it_offset, tbl->it_map);


git produced a messy chunk here. The new logic is:


static void iommu_table_reserve_pages(struct iommu_table *tbl,
		unsigned long res_start, unsigned long res_end)
{
	int i;

	WARN_ON_ONCE(res_end < res_start);
	/*
	 * Reserve page 0 so it will not be used for any mappings.
	 * This avoids buggy drivers that consider page 0 to be invalid
	 * to crash the machine or even lose data.
	 */
	if (tbl->it_offset == 0)
		set_bit(0, tbl->it_map);

	if (res_start < tbl->it_offset)
		res_start = tbl->it_offset;

	if (res_end > (tbl->it_offset + tbl->it_size))
		res_end = tbl->it_offset + tbl->it_size;

	/* Check if res_start..res_end is a valid range in the table */
	if (res_start >= res_end) {
		tbl->it_reserved_start = tbl->it_offset;
		tbl->it_reserved_end = tbl->it_offset;
		return;
	}


It is just hard to read. A code reviewer would assume res_end >= 
res_start (as there is WARN_ON) but later we allow res_end to be lesser 
than res_start.

but may be it is just me :)
Otherwise looks good.

Reviewed-by: Alexey Kardashevskiy <aik@ozlabs.ru>


>   }
>   
>   /*
> @@ -781,6 +773,22 @@ struct iommu_table *iommu_init_table(struct iommu_table *tbl, int nid,
>   	return tbl;
>   }
>   
> +bool iommu_table_in_use(struct iommu_table *tbl)
> +{
> +	unsigned long start = 0, end;
> +
> +	/* ignore reserved bit0 */
> +	if (tbl->it_offset == 0)
> +		start = 1;
> +	end = tbl->it_reserved_start - tbl->it_offset;
> +	if (find_next_bit(tbl->it_map, end, start) != end)
> +		return true;
> +
> +	start = tbl->it_reserved_end - tbl->it_offset;
> +	end = tbl->it_size;
> +	return find_next_bit(tbl->it_map, end, start) != end;
> +}
> +
>   static void iommu_table_free(struct kref *kref)
>   {
>   	unsigned long bitmap_sz;
> @@ -799,10 +807,8 @@ static void iommu_table_free(struct kref *kref)
>   
>   	iommu_debugfs_del(tbl);
>   
> -	iommu_table_release_pages(tbl);
> -
>   	/* verify that table contains no entries */
> -	if (!bitmap_empty(tbl->it_map, tbl->it_size))
> +	if (iommu_table_in_use(tbl))
>   		pr_warn("%s: Unexpected TCEs\n", __func__);
>   
>   	/* calculate bitmap size in bytes */
> @@ -1108,18 +1114,13 @@ int iommu_take_ownership(struct iommu_table *tbl)
>   	for (i = 0; i < tbl->nr_pools; i++)
>   		spin_lock(&tbl->pools[i].lock);
>   
> -	iommu_table_release_pages(tbl);
> -
> -	if (!bitmap_empty(tbl->it_map, tbl->it_size)) {
> +	if (iommu_table_in_use(tbl)) {
>   		pr_err("iommu_tce: it_map is not empty");
>   		ret = -EBUSY;
> -		/* Undo iommu_table_release_pages, i.e. restore bit#0, etc */
> -		iommu_table_reserve_pages(tbl, tbl->it_reserved_start,
> -				tbl->it_reserved_end);
> -	} else {
> -		memset(tbl->it_map, 0xff, sz);
>   	}
>   
> +	memset(tbl->it_map, 0xff, sz);
> +
>   	for (i = 0; i < tbl->nr_pools; i++)
>   		spin_unlock(&tbl->pools[i].lock);
>   	spin_unlock_irqrestore(&tbl->large_pool.lock, flags);
> 

-- 
Alexey

^ permalink raw reply

* Re: [PATCH v4 07/11] powerpc/pseries/iommu: Reorganize iommu_table_setparms*() with new helper
From: Alexey Kardashevskiy @ 2021-05-10  7:34 UTC (permalink / raw)
  To: Leonardo Bras, Michael Ellerman, Benjamin Herrenschmidt,
	Paul Mackerras, Joel Stanley, Christophe Leroy, Nicolin Chen,
	Niklas Schnelle
  Cc: linuxppc-dev, linux-kernel
In-Reply-To: <20210430163145.146984-8-leobras.c@gmail.com>



On 5/1/21 02:31, Leonardo Bras wrote:
> Add a new helper _iommu_table_setparms(), and use it in
> iommu_table_setparms() and iommu_table_setparms_lpar() to avoid duplicated
> code.
> 
> Also, setting tbl->it_ops was happening outsite iommu_table_setparms*(),
> so move it to the new helper. Since we need the iommu_table_ops to be
> declared before used, move iommu_table_lpar_multi_ops and
> iommu_table_pseries_ops to before their respective iommu_table_setparms*().
> 
> Signed-off-by: Leonardo Bras <leobras.c@gmail.com>


This does not apply anymore as it conflicts with my 4be518d838809e2135.


> ---
>   arch/powerpc/platforms/pseries/iommu.c | 100 ++++++++++++-------------
>   1 file changed, 50 insertions(+), 50 deletions(-)
> 
> diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c
> index 5a70ecd579b8..89cb6e9e9f31 100644
> --- a/arch/powerpc/platforms/pseries/iommu.c
> +++ b/arch/powerpc/platforms/pseries/iommu.c
> @@ -53,6 +53,11 @@ enum {
>   	DDW_EXT_QUERY_OUT_SIZE = 2
>   };
>   
> +#ifdef CONFIG_IOMMU_API
> +static int tce_exchange_pseries(struct iommu_table *tbl, long index, unsigned long *tce,
> +				enum dma_data_direction *direction, bool realmode);
> +#endif


Instead of declaring this so far from the the code which needs it, may 
be add

struct iommu_table_ops iommu_table_lpar_multi_ops;

right before iommu_table_setparms() (as the sctruct is what you actually 
want there), and you won't need to move iommu_table_pseries_ops as well.


> +
>   static struct iommu_table *iommu_pseries_alloc_table(int node)
>   {
>   	struct iommu_table *tbl;
> @@ -501,6 +506,28 @@ static int tce_setrange_multi_pSeriesLP_walk(unsigned long start_pfn,
>   	return tce_setrange_multi_pSeriesLP(start_pfn, num_pfn, arg);
>   }
>   
> +static inline void _iommu_table_setparms(struct iommu_table *tbl, unsigned long busno,


The underscore is confusing, may be iommu_table_do_setparms()? 
iommu_table_setparms_common()? Not sure. I cannot recall a single 
function with just one leading underscore, I suspect I was pushed back 
when I tried adding one ages ago :) "inline" seems excessive, the 
compiler will probably figure it out anyway.



> +					 unsigned long liobn, unsigned long win_addr,
> +					 unsigned long window_size, unsigned long page_shift,
> +					 unsigned long base, struct iommu_table_ops *table_ops)


Make "base" a pointer. Or, better, just keep setting it directly in 
iommu_table_setparms() rather than passing 0 around.

The same comment about "liobn" - set it in iommu_table_setparms_lpar(). 
The reviewer will see what field atters in what situation imho.



> +{
> +	tbl->it_busno = busno;
> +	tbl->it_index = liobn;
> +	tbl->it_offset = win_addr >> page_shift;
> +	tbl->it_size = window_size >> page_shift;
> +	tbl->it_page_shift = page_shift;
> +	tbl->it_base = base;
> +	tbl->it_blocksize = 16;
> +	tbl->it_type = TCE_PCI;
> +	tbl->it_ops = table_ops;
> +}
> +
> +struct iommu_table_ops iommu_table_pseries_ops = {
> +	.set = tce_build_pSeries,
> +	.clear = tce_free_pSeries,
> +	.get = tce_get_pseries
> +};
> +
>   static void iommu_table_setparms(struct pci_controller *phb,
>   				 struct device_node *dn,
>   				 struct iommu_table *tbl)
> @@ -509,8 +536,13 @@ static void iommu_table_setparms(struct pci_controller *phb,
>   	const unsigned long *basep;
>   	const u32 *sizep;
>   
> -	node = phb->dn;
> +	/* Test if we are going over 2GB of DMA space */
> +	if (phb->dma_window_base_cur + phb->dma_window_size > SZ_2G) {
> +		udbg_printf("PCI_DMA: Unexpected number of IOAs under this PHB.\n");
> +		panic("PCI_DMA: Unexpected number of IOAs under this PHB.\n");
> +	}
>   
> +	node = phb->dn;
>   	basep = of_get_property(node, "linux,tce-base", NULL);
>   	sizep = of_get_property(node, "linux,tce-size", NULL);
>   	if (basep == NULL || sizep == NULL) {
> @@ -519,33 +551,25 @@ static void iommu_table_setparms(struct pci_controller *phb,
>   		return;
>   	}
>   
> -	tbl->it_base = (unsigned long)__va(*basep);
> +	_iommu_table_setparms(tbl, phb->bus->number, 0, phb->dma_window_base_cur,
> +			      phb->dma_window_size, IOMMU_PAGE_SHIFT_4K,
> +			      (unsigned long)__va(*basep), &iommu_table_pseries_ops);
>   
>   	if (!is_kdump_kernel())
>   		memset((void *)tbl->it_base, 0, *sizep);
>   
> -	tbl->it_busno = phb->bus->number;
> -	tbl->it_page_shift = IOMMU_PAGE_SHIFT_4K;
> -
> -	/* Units of tce entries */
> -	tbl->it_offset = phb->dma_window_base_cur >> tbl->it_page_shift;
> -
> -	/* Test if we are going over 2GB of DMA space */
> -	if (phb->dma_window_base_cur + phb->dma_window_size > 0x80000000ul) {
> -		udbg_printf("PCI_DMA: Unexpected number of IOAs under this PHB.\n");
> -		panic("PCI_DMA: Unexpected number of IOAs under this PHB.\n");
> -	}
> -
>   	phb->dma_window_base_cur += phb->dma_window_size;
> -
> -	/* Set the tce table size - measured in entries */
> -	tbl->it_size = phb->dma_window_size >> tbl->it_page_shift;
> -
> -	tbl->it_index = 0;
> -	tbl->it_blocksize = 16;
> -	tbl->it_type = TCE_PCI;
>   }
>   
> +struct iommu_table_ops iommu_table_lpar_multi_ops = {
> +	.set = tce_buildmulti_pSeriesLP,
> +#ifdef CONFIG_IOMMU_API
> +	.xchg_no_kill = tce_exchange_pseries,
> +#endif
> +	.clear = tce_freemulti_pSeriesLP,
> +	.get = tce_get_pSeriesLP
> +};
> +
>   /*
>    * iommu_table_setparms_lpar
>    *
> @@ -557,28 +581,17 @@ static void iommu_table_setparms_lpar(struct pci_controller *phb,
>   				      struct iommu_table_group *table_group,
>   				      const __be32 *dma_window)
>   {
> -	unsigned long offset, size;
> +	unsigned long offset, size, liobn;
>   
> -	of_parse_dma_window(dn, dma_window, &tbl->it_index, &offset, &size);
> +	of_parse_dma_window(dn, dma_window, &liobn, &offset, &size);
>   
> -	tbl->it_busno = phb->bus->number;
> -	tbl->it_page_shift = IOMMU_PAGE_SHIFT_4K;
> -	tbl->it_base   = 0;
> -	tbl->it_blocksize  = 16;
> -	tbl->it_type = TCE_PCI;
> -	tbl->it_offset = offset >> tbl->it_page_shift;
> -	tbl->it_size = size >> tbl->it_page_shift;
> +	_iommu_table_setparms(tbl, phb->bus->number, liobn, offset, size, IOMMU_PAGE_SHIFT_4K, 0,
> +			      &iommu_table_lpar_multi_ops);
>   
>   	table_group->tce32_start = offset;
>   	table_group->tce32_size = size;
>   }
>   
> -struct iommu_table_ops iommu_table_pseries_ops = {
> -	.set = tce_build_pSeries,
> -	.clear = tce_free_pSeries,
> -	.get = tce_get_pseries
> -};
> -
>   static void pci_dma_bus_setup_pSeries(struct pci_bus *bus)
>   {
>   	struct device_node *dn;
> @@ -647,7 +660,6 @@ static void pci_dma_bus_setup_pSeries(struct pci_bus *bus)
>   	tbl = pci->table_group->tables[0];
>   
>   	iommu_table_setparms(pci->phb, dn, tbl);
> -	tbl->it_ops = &iommu_table_pseries_ops;
>   	iommu_init_table(tbl, pci->phb->node, 0, 0);
>   
>   	/* Divide the rest (1.75GB) among the children */
> @@ -664,7 +676,7 @@ static int tce_exchange_pseries(struct iommu_table *tbl, long index, unsigned
>   				bool realmode)
>   {
>   	long rc;
> -	unsigned long ioba = (unsigned long) index << tbl->it_page_shift;
> +	unsigned long ioba = (unsigned long)index << tbl->it_page_shift;


Unrelated change, why, did checkpatch.pl complain?


>   	unsigned long flags, oldtce = 0;
>   	u64 proto_tce = iommu_direction_to_tce_perm(*direction);
>   	unsigned long newtce = *tce | proto_tce;
> @@ -686,15 +698,6 @@ static int tce_exchange_pseries(struct iommu_table *tbl, long index, unsigned
>   }
>   #endif
>   
> -struct iommu_table_ops iommu_table_lpar_multi_ops = {
> -	.set = tce_buildmulti_pSeriesLP,
> -#ifdef CONFIG_IOMMU_API
> -	.xchg_no_kill = tce_exchange_pseries,
> -#endif
> -	.clear = tce_freemulti_pSeriesLP,
> -	.get = tce_get_pSeriesLP
> -};
> -
>   static void pci_dma_bus_setup_pSeriesLP(struct pci_bus *bus)
>   {
>   	struct iommu_table *tbl;
> @@ -729,7 +732,6 @@ static void pci_dma_bus_setup_pSeriesLP(struct pci_bus *bus)
>   		tbl = ppci->table_group->tables[0];
>   		iommu_table_setparms_lpar(ppci->phb, pdn, tbl,
>   				ppci->table_group, dma_window);
> -		tbl->it_ops = &iommu_table_lpar_multi_ops;
>   		iommu_init_table(tbl, ppci->phb->node, 0, 0);
>   		iommu_register_group(ppci->table_group,
>   				pci_domain_nr(bus), 0);
> @@ -758,7 +760,6 @@ static void pci_dma_dev_setup_pSeries(struct pci_dev *dev)
>   		PCI_DN(dn)->table_group = iommu_pseries_alloc_group(phb->node);
>   		tbl = PCI_DN(dn)->table_group->tables[0];
>   		iommu_table_setparms(phb, dn, tbl);
> -		tbl->it_ops = &iommu_table_pseries_ops;
>   		iommu_init_table(tbl, phb->node, 0, 0);
>   		set_iommu_table_base(&dev->dev, tbl);
>   		return;
> @@ -1436,7 +1437,6 @@ static void pci_dma_dev_setup_pSeriesLP(struct pci_dev *dev)
>   		tbl = pci->table_group->tables[0];
>   		iommu_table_setparms_lpar(pci->phb, pdn, tbl,
>   				pci->table_group, dma_window);
> -		tbl->it_ops = &iommu_table_lpar_multi_ops;
>   		iommu_init_table(tbl, pci->phb->node, 0, 0);
>   		iommu_register_group(pci->table_group,
>   				pci_domain_nr(pci->phb->bus), 0);
> 

-- 
Alexey

^ permalink raw reply

* Re: [PATCH v4 06/11] powerpc/pseries/iommu: Add ddw_property_create() and refactor enable_ddw()
From: Alexey Kardashevskiy @ 2021-05-10  7:34 UTC (permalink / raw)
  To: Leonardo Bras, Michael Ellerman, Benjamin Herrenschmidt,
	Paul Mackerras, Joel Stanley, Christophe Leroy, Nicolin Chen,
	Niklas Schnelle
  Cc: linuxppc-dev, linux-kernel
In-Reply-To: <20210430163145.146984-7-leobras.c@gmail.com>



On 5/1/21 02:31, Leonardo Bras wrote:
> Code used to create a ddw property that was previously scattered in
> enable_ddw() is now gathered in ddw_property_create(), which deals with
> allocation and filling the property, letting it ready for
> of_property_add(), which now occurs in sequence.
> 
> This created an opportunity to reorganize the second part of enable_ddw():
> 
> Without this patch enable_ddw() does, in order:
> kzalloc() property & members, create_ddw(), fill ddwprop inside property,
> ddw_list_new_entry(), do tce_setrange_multi_pSeriesLP_walk in all memory,
> of_add_property(), and list_add().
> 
> With this patch enable_ddw() does, in order:
> create_ddw(), ddw_property_create(), of_add_property(),
> ddw_list_new_entry(), do tce_setrange_multi_pSeriesLP_walk in all memory,
> and list_add().
> 
> This change requires of_remove_property() in case anything fails after
> of_add_property(), but we get to do tce_setrange_multi_pSeriesLP_walk
> in all memory, which looks the most expensive operation, only if
> everything else succeeds.
> 
> Signed-off-by: Leonardo Bras <leobras.c@gmail.com>


Reviewed-by: Alexey Kardashevskiy <aik@ozlabs.ru>



> ---
>   arch/powerpc/platforms/pseries/iommu.c | 93 ++++++++++++++++----------
>   1 file changed, 57 insertions(+), 36 deletions(-)
> 
> diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c
> index 955cf095416c..5a70ecd579b8 100644
> --- a/arch/powerpc/platforms/pseries/iommu.c
> +++ b/arch/powerpc/platforms/pseries/iommu.c
> @@ -1122,6 +1122,35 @@ static void reset_dma_window(struct pci_dev *dev, struct device_node *par_dn)
>   			 ret);
>   }
>   
> +static struct property *ddw_property_create(const char *propname, u32 liobn, u64 dma_addr,
> +					    u32 page_shift, u32 window_shift)
> +{
> +	struct dynamic_dma_window_prop *ddwprop;
> +	struct property *win64;
> +
> +	win64 = kzalloc(sizeof(*win64), GFP_KERNEL);
> +	if (!win64)
> +		return NULL;
> +
> +	win64->name = kstrdup(propname, GFP_KERNEL);
> +	ddwprop = kzalloc(sizeof(*ddwprop), GFP_KERNEL);
> +	win64->value = ddwprop;
> +	win64->length = sizeof(*ddwprop);
> +	if (!win64->name || !win64->value) {
> +		kfree(win64->name);
> +		kfree(win64->value);
> +		kfree(win64);
> +		return NULL;
> +	}
> +
> +	ddwprop->liobn = cpu_to_be32(liobn);
> +	ddwprop->dma_base = cpu_to_be64(dma_addr);
> +	ddwprop->tce_shift = cpu_to_be32(page_shift);
> +	ddwprop->window_shift = cpu_to_be32(window_shift);
> +
> +	return win64;
> +}
> +
>   /* Return largest page shift based on "IO Page Sizes" output of ibm,query-pe-dma-window. */
>   static int iommu_get_page_shift(u32 query_page_size)
>   {
> @@ -1167,11 +1196,11 @@ static bool enable_ddw(struct pci_dev *dev, struct device_node *pdn)
>   	struct ddw_query_response query;
>   	struct ddw_create_response create;
>   	int page_shift;
> +	u64 win_addr;
>   	struct device_node *dn;
>   	u32 ddw_avail[DDW_APPLICABLE_SIZE];
>   	struct direct_window *window;
>   	struct property *win64 = NULL;
> -	struct dynamic_dma_window_prop *ddwprop;
>   	struct failed_ddw_pdn *fpdn;
>   	bool default_win_removed = false;
>   	bool pmem_present;
> @@ -1286,65 +1315,54 @@ static bool enable_ddw(struct pci_dev *dev, struct device_node *pdn)
>   			1ULL << page_shift);
>   		goto out_failed;
>   	}
> -	win64 = kzalloc(sizeof(struct property), GFP_KERNEL);
> -	if (!win64) {
> -		dev_info(&dev->dev,
> -			"couldn't allocate property for 64bit dma window\n");
> -		goto out_failed;
> -	}
> -	win64->name = kstrdup(DIRECT64_PROPNAME, GFP_KERNEL);
> -	win64->value = ddwprop = kmalloc(sizeof(*ddwprop), GFP_KERNEL);
> -	win64->length = sizeof(*ddwprop);
> -	if (!win64->name || !win64->value) {
> -		dev_info(&dev->dev,
> -			"couldn't allocate property name and value\n");
> -		goto out_free_prop;
> -	}
>   
>   	ret = create_ddw(dev, ddw_avail, &create, page_shift, len);
>   	if (ret != 0)
> -		goto out_free_prop;
> -
> -	ddwprop->liobn = cpu_to_be32(create.liobn);
> -	ddwprop->dma_base = cpu_to_be64(((u64)create.addr_hi << 32) |
> -			create.addr_lo);
> -	ddwprop->tce_shift = cpu_to_be32(page_shift);
> -	ddwprop->window_shift = cpu_to_be32(len);
> +		goto out_failed;
>   
>   	dev_dbg(&dev->dev, "created tce table LIOBN 0x%x for %pOF\n",
>   		  create.liobn, dn);
>   
> -	window = ddw_list_new_entry(pdn, ddwprop);
> +	win_addr = ((u64)create.addr_hi << 32) | create.addr_lo;
> +	win64 = ddw_property_create(DIRECT64_PROPNAME, create.liobn, win_addr,
> +				    page_shift, len);
> +	if (!win64) {
> +		dev_info(&dev->dev,
> +			 "couldn't allocate property, property name, or value\n");
> +		goto out_remove_win;
> +	}
> +
> +	ret = of_add_property(pdn, win64);
> +	if (ret) {
> +		dev_err(&dev->dev, "unable to add dma window property for %pOF: %d",
> +			pdn, ret);
> +		goto out_free_prop;
> +	}
> +
> +	window = ddw_list_new_entry(pdn, win64->value);
>   	if (!window)
> -		goto out_clear_window;
> +		goto out_del_prop;
>   
>   	ret = walk_system_ram_range(0, memblock_end_of_DRAM() >> PAGE_SHIFT,
>   			win64->value, tce_setrange_multi_pSeriesLP_walk);
>   	if (ret) {
>   		dev_info(&dev->dev, "failed to map direct window for %pOF: %d\n",
>   			 dn, ret);
> -		goto out_free_window;
> -	}
> -
> -	ret = of_add_property(pdn, win64);
> -	if (ret) {
> -		dev_err(&dev->dev, "unable to add dma window property for %pOF: %d",
> -			 pdn, ret);
> -		goto out_free_window;
> +		goto out_del_list;
>   	}
>   
>   	spin_lock(&direct_window_list_lock);
>   	list_add(&window->list, &direct_window_list);
>   	spin_unlock(&direct_window_list_lock);
>   
> -	dev->dev.archdata.dma_offset = be64_to_cpu(ddwprop->dma_base);
> +	dev->dev.archdata.dma_offset = win_addr;
>   	goto out_unlock;
>   
> -out_free_window:
> +out_del_list:
>   	kfree(window);
>   
> -out_clear_window:
> -	remove_ddw(pdn, true);
> +out_del_prop:
> +	of_remove_property(pdn, win64);
>   
>   out_free_prop:
>   	kfree(win64->name);
> @@ -1352,6 +1370,9 @@ static bool enable_ddw(struct pci_dev *dev, struct device_node *pdn)
>   	kfree(win64);
>   	win64 = NULL;
>   
> +out_remove_win:
> +	remove_ddw(pdn, true);
> +
>   out_failed:
>   	if (default_win_removed)
>   		reset_dma_window(dev, pdn);
> 

-- 
Alexey

^ permalink raw reply

* [PATCH v6 00/15] Restricted DMA
From: Claire Chang @ 2021-05-10  9:50 UTC (permalink / raw)
  To: Rob Herring, mpe, Joerg Roedel, Will Deacon, Frank Rowand,
	Konrad Rzeszutek Wilk, boris.ostrovsky, jgross, Christoph Hellwig,
	Marek Szyprowski
  Cc: heikki.krogerus, thomas.hellstrom, peterz, joonas.lahtinen,
	dri-devel, chris, grant.likely, paulus, mingo, jxgao, sstabellini,
	Saravana Kannan, xypron.glpk, Rafael J . Wysocki,
	Bartosz Golaszewski, bskeggs, linux-pci, xen-devel,
	Thierry Reding, intel-gfx, matthew.auld, linux-devicetree, daniel,
	airlied, maarten.lankhorst, linuxppc-dev, jani.nikula,
	Nicolas Boichat, rodrigo.vivi, bhelgaas, tientzu, Dan Williams,
	Andy Shevchenko, Claire Chang, nouveau, Greg KH, Randy Dunlap,
	lkml, tfiga, list@263.net:IOMMU DRIVERS, Jim Quinlan,
	Robin Murphy, bauerman

From: Claire Chang <tientzu@google.com>

This series implements mitigations for lack of DMA access control on
systems without an IOMMU, which could result in the DMA accessing the
system memory at unexpected times and/or unexpected addresses, possibly
leading to data leakage or corruption.

For example, we plan to use the PCI-e bus for Wi-Fi and that PCI-e bus is
not behind an IOMMU. As PCI-e, by design, gives the device full access to
system memory, a vulnerability in the Wi-Fi firmware could easily escalate
to a full system exploit (remote wifi exploits: [1a], [1b] that shows a
full chain of exploits; [2], [3]).

To mitigate the security concerns, we introduce restricted DMA. Restricted
DMA utilizes the existing swiotlb to bounce streaming DMA in and out of a
specially allocated region and does memory allocation from the same region.
The feature on its own provides a basic level of protection against the DMA
overwriting buffer contents at unexpected times. However, to protect
against general data leakage and system memory corruption, the system needs
to provide a way to restrict the DMA to a predefined memory region (this is
usually done at firmware level, e.g. MPU in ATF on some ARM platforms [4]).

[1a] https://googleprojectzero.blogspot.com/2017/04/over-air-exploiting-broadcoms-wi-fi_4.html
[1b] https://googleprojectzero.blogspot.com/2017/04/over-air-exploiting-broadcoms-wi-fi_11.html
[2] https://blade.tencent.com/en/advisories/qualpwn/
[3] https://www.bleepingcomputer.com/news/security/vulnerabilities-found-in-highly-popular-firmware-for-wifi-chips/
[4] https://github.com/ARM-software/arm-trusted-firmware/blob/master/plat/mediatek/mt8183/drivers/emi_mpu/emi_mpu.c#L132

v6:
Address the comments in v5

v5:
Rebase on latest linux-next
https://lore.kernel.org/patchwork/cover/1416899/

v4:
- Fix spinlock bad magic
- Use rmem->name for debugfs entry
- Address the comments in v3
https://lore.kernel.org/patchwork/cover/1378113/

v3:
Using only one reserved memory region for both streaming DMA and memory
allocation.
https://lore.kernel.org/patchwork/cover/1360992/

v2:
Building on top of swiotlb.
https://lore.kernel.org/patchwork/cover/1280705/

v1:
Using dma_map_ops.
https://lore.kernel.org/patchwork/cover/1271660/
*** BLURB HERE ***

Claire Chang (15):
  swiotlb: Refactor swiotlb init functions
  swiotlb: Refactor swiotlb_create_debugfs
  swiotlb: Add DMA_RESTRICTED_POOL
  swiotlb: Add restricted DMA pool initialization
  swiotlb: Add a new get_io_tlb_mem getter
  swiotlb: Update is_swiotlb_buffer to add a struct device argument
  swiotlb: Update is_swiotlb_active to add a struct device argument
  swiotlb: Bounce data from/to restricted DMA pool if available
  swiotlb: Move alloc_size to find_slots
  swiotlb: Refactor swiotlb_tbl_unmap_single
  dma-direct: Add a new wrapper __dma_direct_free_pages()
  swiotlb: Add restricted DMA alloc/free support.
  dma-direct: Allocate memory from restricted DMA pool if available
  dt-bindings: of: Add restricted DMA pool
  of: Add plumbing for restricted DMA pool

 .../reserved-memory/reserved-memory.txt       |  27 ++
 drivers/gpu/drm/i915/gem/i915_gem_internal.c  |   2 +-
 drivers/gpu/drm/nouveau/nouveau_ttm.c         |   2 +-
 drivers/iommu/dma-iommu.c                     |  12 +-
 drivers/of/address.c                          |  25 ++
 drivers/of/device.c                           |   3 +
 drivers/of/of_private.h                       |   5 +
 drivers/pci/xen-pcifront.c                    |   2 +-
 drivers/xen/swiotlb-xen.c                     |   2 +-
 include/linux/device.h                        |   4 +
 include/linux/swiotlb.h                       |  41 ++-
 kernel/dma/Kconfig                            |  14 +
 kernel/dma/direct.c                           |  63 +++--
 kernel/dma/direct.h                           |   9 +-
 kernel/dma/swiotlb.c                          | 242 +++++++++++++-----
 15 files changed, 356 insertions(+), 97 deletions(-)

-- 
2.31.1.607.g51e8a6a459-goog


^ permalink raw reply

* [PATCH v6 01/15] swiotlb: Refactor swiotlb init functions
From: Claire Chang @ 2021-05-10  9:50 UTC (permalink / raw)
  To: Rob Herring, mpe, Joerg Roedel, Will Deacon, Frank Rowand,
	Konrad Rzeszutek Wilk, boris.ostrovsky, jgross, Christoph Hellwig,
	Marek Szyprowski
  Cc: heikki.krogerus, thomas.hellstrom, peterz, joonas.lahtinen,
	dri-devel, chris, grant.likely, paulus, mingo, jxgao, sstabellini,
	Saravana Kannan, xypron.glpk, Rafael J . Wysocki,
	Bartosz Golaszewski, bskeggs, linux-pci, xen-devel,
	Thierry Reding, intel-gfx, matthew.auld, linux-devicetree, daniel,
	airlied, maarten.lankhorst, linuxppc-dev, jani.nikula,
	Nicolas Boichat, rodrigo.vivi, bhelgaas, tientzu, Dan Williams,
	Andy Shevchenko, nouveau, Greg KH, Randy Dunlap, lkml, tfiga,
	list@263.net:IOMMU DRIVERS, Jim Quinlan, Robin Murphy, bauerman
In-Reply-To: <20210510095026.3477496-1-tientzu@chromium.org>

Add a new function, swiotlb_init_io_tlb_mem, for the io_tlb_mem struct
initialization to make the code reusable.

Note that we now also call set_memory_decrypted in swiotlb_init_with_tbl.

Signed-off-by: Claire Chang <tientzu@chromium.org>
---
 kernel/dma/swiotlb.c | 51 ++++++++++++++++++++++----------------------
 1 file changed, 25 insertions(+), 26 deletions(-)

diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c
index 8ca7d505d61c..d3232fc19385 100644
--- a/kernel/dma/swiotlb.c
+++ b/kernel/dma/swiotlb.c
@@ -168,9 +168,30 @@ void __init swiotlb_update_mem_attributes(void)
 	memset(vaddr, 0, bytes);
 }
 
-int __init swiotlb_init_with_tbl(char *tlb, unsigned long nslabs, int verbose)
+static void swiotlb_init_io_tlb_mem(struct io_tlb_mem *mem, phys_addr_t start,
+				    unsigned long nslabs, bool late_alloc)
 {
+	void *vaddr = phys_to_virt(start);
 	unsigned long bytes = nslabs << IO_TLB_SHIFT, i;
+
+	mem->nslabs = nslabs;
+	mem->start = start;
+	mem->end = mem->start + bytes;
+	mem->index = 0;
+	mem->late_alloc = late_alloc;
+	spin_lock_init(&mem->lock);
+	for (i = 0; i < mem->nslabs; i++) {
+		mem->slots[i].list = IO_TLB_SEGSIZE - io_tlb_offset(i);
+		mem->slots[i].orig_addr = INVALID_PHYS_ADDR;
+		mem->slots[i].alloc_size = 0;
+	}
+
+	set_memory_decrypted((unsigned long)vaddr, bytes >> PAGE_SHIFT);
+	memset(vaddr, 0, bytes);
+}
+
+int __init swiotlb_init_with_tbl(char *tlb, unsigned long nslabs, int verbose)
+{
 	struct io_tlb_mem *mem;
 	size_t alloc_size;
 
@@ -186,16 +207,8 @@ int __init swiotlb_init_with_tbl(char *tlb, unsigned long nslabs, int verbose)
 	if (!mem)
 		panic("%s: Failed to allocate %zu bytes align=0x%lx\n",
 		      __func__, alloc_size, PAGE_SIZE);
-	mem->nslabs = nslabs;
-	mem->start = __pa(tlb);
-	mem->end = mem->start + bytes;
-	mem->index = 0;
-	spin_lock_init(&mem->lock);
-	for (i = 0; i < mem->nslabs; i++) {
-		mem->slots[i].list = IO_TLB_SEGSIZE - io_tlb_offset(i);
-		mem->slots[i].orig_addr = INVALID_PHYS_ADDR;
-		mem->slots[i].alloc_size = 0;
-	}
+
+	swiotlb_init_io_tlb_mem(mem, __pa(tlb), nslabs, false);
 
 	io_tlb_default_mem = mem;
 	if (verbose)
@@ -282,7 +295,6 @@ swiotlb_late_init_with_default_size(size_t default_size)
 int
 swiotlb_late_init_with_tbl(char *tlb, unsigned long nslabs)
 {
-	unsigned long bytes = nslabs << IO_TLB_SHIFT, i;
 	struct io_tlb_mem *mem;
 
 	if (swiotlb_force == SWIOTLB_NO_FORCE)
@@ -297,20 +309,7 @@ swiotlb_late_init_with_tbl(char *tlb, unsigned long nslabs)
 	if (!mem)
 		return -ENOMEM;
 
-	mem->nslabs = nslabs;
-	mem->start = virt_to_phys(tlb);
-	mem->end = mem->start + bytes;
-	mem->index = 0;
-	mem->late_alloc = 1;
-	spin_lock_init(&mem->lock);
-	for (i = 0; i < mem->nslabs; i++) {
-		mem->slots[i].list = IO_TLB_SEGSIZE - io_tlb_offset(i);
-		mem->slots[i].orig_addr = INVALID_PHYS_ADDR;
-		mem->slots[i].alloc_size = 0;
-	}
-
-	set_memory_decrypted((unsigned long)tlb, bytes >> PAGE_SHIFT);
-	memset(tlb, 0, bytes);
+	swiotlb_init_io_tlb_mem(mem, virt_to_phys(tlb), nslabs, true);
 
 	io_tlb_default_mem = mem;
 	swiotlb_print_info();
-- 
2.31.1.607.g51e8a6a459-goog


^ permalink raw reply related

* [PATCH v6 02/15] swiotlb: Refactor swiotlb_create_debugfs
From: Claire Chang @ 2021-05-10  9:50 UTC (permalink / raw)
  To: Rob Herring, mpe, Joerg Roedel, Will Deacon, Frank Rowand,
	Konrad Rzeszutek Wilk, boris.ostrovsky, jgross, Christoph Hellwig,
	Marek Szyprowski
  Cc: heikki.krogerus, thomas.hellstrom, peterz, joonas.lahtinen,
	dri-devel, chris, grant.likely, paulus, mingo, jxgao, sstabellini,
	Saravana Kannan, xypron.glpk, Rafael J . Wysocki,
	Bartosz Golaszewski, bskeggs, linux-pci, xen-devel,
	Thierry Reding, intel-gfx, matthew.auld, linux-devicetree, daniel,
	airlied, maarten.lankhorst, linuxppc-dev, jani.nikula,
	Nicolas Boichat, rodrigo.vivi, bhelgaas, tientzu, Dan Williams,
	Andy Shevchenko, nouveau, Greg KH, Randy Dunlap, lkml, tfiga,
	list@263.net:IOMMU DRIVERS, Jim Quinlan, Robin Murphy, bauerman
In-Reply-To: <20210510095026.3477496-1-tientzu@chromium.org>

Split the debugfs creation to make the code reusable for supporting
different bounce buffer pools, e.g. restricted DMA pool.

Signed-off-by: Claire Chang <tientzu@chromium.org>
---
 kernel/dma/swiotlb.c | 22 ++++++++++++++++------
 1 file changed, 16 insertions(+), 6 deletions(-)

diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c
index d3232fc19385..858475bd6923 100644
--- a/kernel/dma/swiotlb.c
+++ b/kernel/dma/swiotlb.c
@@ -64,6 +64,7 @@
 enum swiotlb_force swiotlb_force;
 
 struct io_tlb_mem *io_tlb_default_mem;
+static struct dentry *debugfs_dir;
 
 /*
  * Max segment that we can provide which (if pages are contingous) will
@@ -662,18 +663,27 @@ EXPORT_SYMBOL_GPL(is_swiotlb_active);
 
 #ifdef CONFIG_DEBUG_FS
 
-static int __init swiotlb_create_debugfs(void)
+static void swiotlb_create_debugfs(struct io_tlb_mem *mem, const char *name,
+				   struct dentry *node)
 {
-	struct io_tlb_mem *mem = io_tlb_default_mem;
-
 	if (!mem)
-		return 0;
-	mem->debugfs = debugfs_create_dir("swiotlb", NULL);
+		return;
+
+	mem->debugfs = debugfs_create_dir(name, node);
 	debugfs_create_ulong("io_tlb_nslabs", 0400, mem->debugfs, &mem->nslabs);
 	debugfs_create_ulong("io_tlb_used", 0400, mem->debugfs, &mem->used);
+}
+
+static int __init swiotlb_create_default_debugfs(void)
+{
+	struct io_tlb_mem *mem = io_tlb_default_mem;
+
+	swiotlb_create_debugfs(mem, "swiotlb", NULL);
+	debugfs_dir = mem->debugfs;
+
 	return 0;
 }
 
-late_initcall(swiotlb_create_debugfs);
+late_initcall(swiotlb_create_default_debugfs);
 
 #endif
-- 
2.31.1.607.g51e8a6a459-goog


^ permalink raw reply related

* [PATCH v6 03/15] swiotlb: Add DMA_RESTRICTED_POOL
From: Claire Chang @ 2021-05-10  9:50 UTC (permalink / raw)
  To: Rob Herring, mpe, Joerg Roedel, Will Deacon, Frank Rowand,
	Konrad Rzeszutek Wilk, boris.ostrovsky, jgross, Christoph Hellwig,
	Marek Szyprowski
  Cc: heikki.krogerus, thomas.hellstrom, peterz, joonas.lahtinen,
	dri-devel, chris, grant.likely, paulus, mingo, jxgao, sstabellini,
	Saravana Kannan, xypron.glpk, Rafael J . Wysocki,
	Bartosz Golaszewski, bskeggs, linux-pci, xen-devel,
	Thierry Reding, intel-gfx, matthew.auld, linux-devicetree, daniel,
	airlied, maarten.lankhorst, linuxppc-dev, jani.nikula,
	Nicolas Boichat, rodrigo.vivi, bhelgaas, tientzu, Dan Williams,
	Andy Shevchenko, nouveau, Greg KH, Randy Dunlap, lkml, tfiga,
	list@263.net:IOMMU DRIVERS, Jim Quinlan, Robin Murphy, bauerman
In-Reply-To: <20210510095026.3477496-1-tientzu@chromium.org>

Add a new kconfig symbol, DMA_RESTRICTED_POOL, for restricted DMA pool.

Signed-off-by: Claire Chang <tientzu@chromium.org>
---
 kernel/dma/Kconfig | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/kernel/dma/Kconfig b/kernel/dma/Kconfig
index 77b405508743..3e961dc39634 100644
--- a/kernel/dma/Kconfig
+++ b/kernel/dma/Kconfig
@@ -80,6 +80,20 @@ config SWIOTLB
 	bool
 	select NEED_DMA_MAP_STATE
 
+config DMA_RESTRICTED_POOL
+	bool "DMA Restricted Pool"
+	depends on OF && OF_RESERVED_MEM
+	select SWIOTLB
+	help
+	  This enables support for restricted DMA pools which provide a level of
+	  DMA memory protection on systems with limited hardware protection
+	  capabilities, such as those lacking an IOMMU.
+
+	  For more information see
+	  <Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt>
+	  and <kernel/dma/swiotlb.c>.
+	  If unsure, say "n".
+
 #
 # Should be selected if we can mmap non-coherent mappings to userspace.
 # The only thing that is really required is a way to set an uncached bit
-- 
2.31.1.607.g51e8a6a459-goog


^ permalink raw reply related

* [PATCH v6 04/15] swiotlb: Add restricted DMA pool initialization
From: Claire Chang @ 2021-05-10  9:50 UTC (permalink / raw)
  To: Rob Herring, mpe, Joerg Roedel, Will Deacon, Frank Rowand,
	Konrad Rzeszutek Wilk, boris.ostrovsky, jgross, Christoph Hellwig,
	Marek Szyprowski
  Cc: heikki.krogerus, thomas.hellstrom, peterz, joonas.lahtinen,
	dri-devel, chris, grant.likely, paulus, mingo, jxgao, sstabellini,
	Saravana Kannan, xypron.glpk, Rafael J . Wysocki,
	Bartosz Golaszewski, bskeggs, linux-pci, xen-devel,
	Thierry Reding, intel-gfx, matthew.auld, linux-devicetree, daniel,
	airlied, maarten.lankhorst, linuxppc-dev, jani.nikula,
	Nicolas Boichat, rodrigo.vivi, bhelgaas, tientzu, Dan Williams,
	Andy Shevchenko, nouveau, Greg KH, Randy Dunlap, lkml, tfiga,
	list@263.net:IOMMU DRIVERS, Jim Quinlan, Robin Murphy, bauerman
In-Reply-To: <20210510095026.3477496-1-tientzu@chromium.org>

Add the initialization function to create restricted DMA pools from
matching reserved-memory nodes.

Signed-off-by: Claire Chang <tientzu@chromium.org>
---
 include/linux/device.h  |  4 +++
 include/linux/swiotlb.h |  3 +-
 kernel/dma/swiotlb.c    | 79 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 85 insertions(+), 1 deletion(-)

diff --git a/include/linux/device.h b/include/linux/device.h
index 38a2071cf776..4987608ea4ff 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -416,6 +416,7 @@ struct dev_links_info {
  * @dma_pools:	Dma pools (if dma'ble device).
  * @dma_mem:	Internal for coherent mem override.
  * @cma_area:	Contiguous memory area for dma allocations
+ * @dma_io_tlb_mem: Internal for swiotlb io_tlb_mem override.
  * @archdata:	For arch-specific additions.
  * @of_node:	Associated device tree node.
  * @fwnode:	Associated device node supplied by platform firmware.
@@ -521,6 +522,9 @@ struct device {
 #ifdef CONFIG_DMA_CMA
 	struct cma *cma_area;		/* contiguous memory area for dma
 					   allocations */
+#endif
+#ifdef CONFIG_DMA_RESTRICTED_POOL
+	struct io_tlb_mem *dma_io_tlb_mem;
 #endif
 	/* arch specific additions */
 	struct dev_archdata	archdata;
diff --git a/include/linux/swiotlb.h b/include/linux/swiotlb.h
index 216854a5e513..03ad6e3b4056 100644
--- a/include/linux/swiotlb.h
+++ b/include/linux/swiotlb.h
@@ -72,7 +72,8 @@ extern enum swiotlb_force swiotlb_force;
  *		range check to see if the memory was in fact allocated by this
  *		API.
  * @nslabs:	The number of IO TLB blocks (in groups of 64) between @start and
- *		@end. This is command line adjustable via setup_io_tlb_npages.
+ *		@end. For default swiotlb, this is command line adjustable via
+ *		setup_io_tlb_npages.
  * @used:	The number of used IO TLB block.
  * @list:	The free list describing the number of free entries available
  *		from each index.
diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c
index 858475bd6923..4ea027b75013 100644
--- a/kernel/dma/swiotlb.c
+++ b/kernel/dma/swiotlb.c
@@ -39,6 +39,13 @@
 #ifdef CONFIG_DEBUG_FS
 #include <linux/debugfs.h>
 #endif
+#ifdef CONFIG_DMA_RESTRICTED_POOL
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/slab.h>
+#endif
 
 #include <asm/io.h>
 #include <asm/dma.h>
@@ -687,3 +694,75 @@ static int __init swiotlb_create_default_debugfs(void)
 late_initcall(swiotlb_create_default_debugfs);
 
 #endif
+
+#ifdef CONFIG_DMA_RESTRICTED_POOL
+static int rmem_swiotlb_device_init(struct reserved_mem *rmem,
+				    struct device *dev)
+{
+	struct io_tlb_mem *mem = rmem->priv;
+	unsigned long nslabs = rmem->size >> IO_TLB_SHIFT;
+
+	if (dev->dma_io_tlb_mem)
+		return 0;
+
+	/* Since multiple devices can share the same pool, the private data,
+	 * io_tlb_mem struct, will be initialized by the first device attached
+	 * to it.
+	 */
+	if (!mem) {
+		mem = kzalloc(struct_size(mem, slots, nslabs), GFP_KERNEL);
+		if (!mem)
+			return -ENOMEM;
+#ifdef CONFIG_ARM
+		if (!PageHighMem(pfn_to_page(PHYS_PFN(rmem->base)))) {
+			kfree(mem);
+			return -EINVAL;
+		}
+#endif /* CONFIG_ARM */
+		swiotlb_init_io_tlb_mem(mem, rmem->base, nslabs, false);
+
+		rmem->priv = mem;
+
+#ifdef CONFIG_DEBUG_FS
+		if (!debugfs_dir)
+			debugfs_dir = debugfs_create_dir("swiotlb", NULL);
+
+		swiotlb_create_debugfs(mem, rmem->name, debugfs_dir);
+#endif /* CONFIG_DEBUG_FS */
+	}
+
+	dev->dma_io_tlb_mem = mem;
+
+	return 0;
+}
+
+static void rmem_swiotlb_device_release(struct reserved_mem *rmem,
+					struct device *dev)
+{
+	if (dev)
+		dev->dma_io_tlb_mem = NULL;
+}
+
+static const struct reserved_mem_ops rmem_swiotlb_ops = {
+	.device_init = rmem_swiotlb_device_init,
+	.device_release = rmem_swiotlb_device_release,
+};
+
+static int __init rmem_swiotlb_setup(struct reserved_mem *rmem)
+{
+	unsigned long node = rmem->fdt_node;
+
+	if (of_get_flat_dt_prop(node, "reusable", NULL) ||
+	    of_get_flat_dt_prop(node, "linux,cma-default", NULL) ||
+	    of_get_flat_dt_prop(node, "linux,dma-default", NULL) ||
+	    of_get_flat_dt_prop(node, "no-map", NULL))
+		return -EINVAL;
+
+	rmem->ops = &rmem_swiotlb_ops;
+	pr_info("Reserved memory: created device swiotlb memory pool at %pa, size %ld MiB\n",
+		&rmem->base, (unsigned long)rmem->size / SZ_1M);
+	return 0;
+}
+
+RESERVEDMEM_OF_DECLARE(dma, "restricted-dma-pool", rmem_swiotlb_setup);
+#endif /* CONFIG_DMA_RESTRICTED_POOL */
-- 
2.31.1.607.g51e8a6a459-goog


^ permalink raw reply related

* [PATCH v6 05/15] swiotlb: Add a new get_io_tlb_mem getter
From: Claire Chang @ 2021-05-10  9:50 UTC (permalink / raw)
  To: Rob Herring, mpe, Joerg Roedel, Will Deacon, Frank Rowand,
	Konrad Rzeszutek Wilk, boris.ostrovsky, jgross, Christoph Hellwig,
	Marek Szyprowski
  Cc: heikki.krogerus, thomas.hellstrom, peterz, joonas.lahtinen,
	dri-devel, chris, grant.likely, paulus, mingo, jxgao, sstabellini,
	Saravana Kannan, xypron.glpk, Rafael J . Wysocki,
	Bartosz Golaszewski, bskeggs, linux-pci, xen-devel,
	Thierry Reding, intel-gfx, matthew.auld, linux-devicetree, daniel,
	airlied, maarten.lankhorst, linuxppc-dev, jani.nikula,
	Nicolas Boichat, rodrigo.vivi, bhelgaas, tientzu, Dan Williams,
	Andy Shevchenko, nouveau, Greg KH, Randy Dunlap, lkml, tfiga,
	list@263.net:IOMMU DRIVERS, Jim Quinlan, Robin Murphy, bauerman
In-Reply-To: <20210510095026.3477496-1-tientzu@chromium.org>

Add a new getter, get_io_tlb_mem, to help select the io_tlb_mem struct.
The restricted DMA pool is preferred if available.

Signed-off-by: Claire Chang <tientzu@chromium.org>
---
 include/linux/swiotlb.h | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/include/linux/swiotlb.h b/include/linux/swiotlb.h
index 03ad6e3b4056..b469f04cca26 100644
--- a/include/linux/swiotlb.h
+++ b/include/linux/swiotlb.h
@@ -2,6 +2,7 @@
 #ifndef __LINUX_SWIOTLB_H
 #define __LINUX_SWIOTLB_H
 
+#include <linux/device.h>
 #include <linux/dma-direction.h>
 #include <linux/init.h>
 #include <linux/types.h>
@@ -102,6 +103,16 @@ struct io_tlb_mem {
 };
 extern struct io_tlb_mem *io_tlb_default_mem;
 
+static inline struct io_tlb_mem *get_io_tlb_mem(struct device *dev)
+{
+#ifdef CONFIG_DMA_RESTRICTED_POOL
+	if (dev && dev->dma_io_tlb_mem)
+		return dev->dma_io_tlb_mem;
+#endif /* CONFIG_DMA_RESTRICTED_POOL */
+
+	return io_tlb_default_mem;
+}
+
 static inline bool is_swiotlb_buffer(phys_addr_t paddr)
 {
 	struct io_tlb_mem *mem = io_tlb_default_mem;
-- 
2.31.1.607.g51e8a6a459-goog


^ permalink raw reply related

* [PATCH v6 06/15] swiotlb: Update is_swiotlb_buffer to add a struct device argument
From: Claire Chang @ 2021-05-10  9:50 UTC (permalink / raw)
  To: Rob Herring, mpe, Joerg Roedel, Will Deacon, Frank Rowand,
	Konrad Rzeszutek Wilk, boris.ostrovsky, jgross, Christoph Hellwig,
	Marek Szyprowski
  Cc: heikki.krogerus, thomas.hellstrom, peterz, joonas.lahtinen,
	dri-devel, chris, grant.likely, paulus, mingo, jxgao, sstabellini,
	Saravana Kannan, xypron.glpk, Rafael J . Wysocki,
	Bartosz Golaszewski, bskeggs, linux-pci, xen-devel,
	Thierry Reding, intel-gfx, matthew.auld, linux-devicetree, daniel,
	airlied, maarten.lankhorst, linuxppc-dev, jani.nikula,
	Nicolas Boichat, rodrigo.vivi, bhelgaas, tientzu, Dan Williams,
	Andy Shevchenko, nouveau, Greg KH, Randy Dunlap, lkml, tfiga,
	list@263.net:IOMMU DRIVERS, Jim Quinlan, Robin Murphy, bauerman
In-Reply-To: <20210510095026.3477496-1-tientzu@chromium.org>

Update is_swiotlb_buffer to add a struct device argument. This will be
useful later to allow for restricted DMA pool.

Signed-off-by: Claire Chang <tientzu@chromium.org>
---
 drivers/iommu/dma-iommu.c | 12 ++++++------
 drivers/xen/swiotlb-xen.c |  2 +-
 include/linux/swiotlb.h   |  6 +++---
 kernel/dma/direct.c       |  6 +++---
 kernel/dma/direct.h       |  6 +++---
 5 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 7bcdd1205535..a5df35bfd150 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -504,7 +504,7 @@ static void __iommu_dma_unmap_swiotlb(struct device *dev, dma_addr_t dma_addr,
 
 	__iommu_dma_unmap(dev, dma_addr, size);
 
-	if (unlikely(is_swiotlb_buffer(phys)))
+	if (unlikely(is_swiotlb_buffer(dev, phys)))
 		swiotlb_tbl_unmap_single(dev, phys, size, dir, attrs);
 }
 
@@ -575,7 +575,7 @@ static dma_addr_t __iommu_dma_map_swiotlb(struct device *dev, phys_addr_t phys,
 	}
 
 	iova = __iommu_dma_map(dev, phys, aligned_size, prot, dma_mask);
-	if (iova == DMA_MAPPING_ERROR && is_swiotlb_buffer(phys))
+	if (iova == DMA_MAPPING_ERROR && is_swiotlb_buffer(dev, phys))
 		swiotlb_tbl_unmap_single(dev, phys, org_size, dir, attrs);
 	return iova;
 }
@@ -781,7 +781,7 @@ static void iommu_dma_sync_single_for_cpu(struct device *dev,
 	if (!dev_is_dma_coherent(dev))
 		arch_sync_dma_for_cpu(phys, size, dir);
 
-	if (is_swiotlb_buffer(phys))
+	if (is_swiotlb_buffer(dev, phys))
 		swiotlb_sync_single_for_cpu(dev, phys, size, dir);
 }
 
@@ -794,7 +794,7 @@ static void iommu_dma_sync_single_for_device(struct device *dev,
 		return;
 
 	phys = iommu_iova_to_phys(iommu_get_dma_domain(dev), dma_handle);
-	if (is_swiotlb_buffer(phys))
+	if (is_swiotlb_buffer(dev, phys))
 		swiotlb_sync_single_for_device(dev, phys, size, dir);
 
 	if (!dev_is_dma_coherent(dev))
@@ -815,7 +815,7 @@ static void iommu_dma_sync_sg_for_cpu(struct device *dev,
 		if (!dev_is_dma_coherent(dev))
 			arch_sync_dma_for_cpu(sg_phys(sg), sg->length, dir);
 
-		if (is_swiotlb_buffer(sg_phys(sg)))
+		if (is_swiotlb_buffer(dev, sg_phys(sg)))
 			swiotlb_sync_single_for_cpu(dev, sg_phys(sg),
 						    sg->length, dir);
 	}
@@ -832,7 +832,7 @@ static void iommu_dma_sync_sg_for_device(struct device *dev,
 		return;
 
 	for_each_sg(sgl, sg, nelems, i) {
-		if (is_swiotlb_buffer(sg_phys(sg)))
+		if (is_swiotlb_buffer(dev, sg_phys(sg)))
 			swiotlb_sync_single_for_device(dev, sg_phys(sg),
 						       sg->length, dir);
 
diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c
index 4c89afc0df62..0c6ed09f8513 100644
--- a/drivers/xen/swiotlb-xen.c
+++ b/drivers/xen/swiotlb-xen.c
@@ -100,7 +100,7 @@ static int is_xen_swiotlb_buffer(struct device *dev, dma_addr_t dma_addr)
 	 * in our domain. Therefore _only_ check address within our domain.
 	 */
 	if (pfn_valid(PFN_DOWN(paddr)))
-		return is_swiotlb_buffer(paddr);
+		return is_swiotlb_buffer(dev, paddr);
 	return 0;
 }
 
diff --git a/include/linux/swiotlb.h b/include/linux/swiotlb.h
index b469f04cca26..2a6cca07540b 100644
--- a/include/linux/swiotlb.h
+++ b/include/linux/swiotlb.h
@@ -113,9 +113,9 @@ static inline struct io_tlb_mem *get_io_tlb_mem(struct device *dev)
 	return io_tlb_default_mem;
 }
 
-static inline bool is_swiotlb_buffer(phys_addr_t paddr)
+static inline bool is_swiotlb_buffer(struct device *dev, phys_addr_t paddr)
 {
-	struct io_tlb_mem *mem = io_tlb_default_mem;
+	struct io_tlb_mem *mem = get_io_tlb_mem(dev);
 
 	return mem && paddr >= mem->start && paddr < mem->end;
 }
@@ -127,7 +127,7 @@ bool is_swiotlb_active(void);
 void __init swiotlb_adjust_size(unsigned long size);
 #else
 #define swiotlb_force SWIOTLB_NO_FORCE
-static inline bool is_swiotlb_buffer(phys_addr_t paddr)
+static inline bool is_swiotlb_buffer(struct device *dev, phys_addr_t paddr)
 {
 	return false;
 }
diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c
index f737e3347059..84c9feb5474a 100644
--- a/kernel/dma/direct.c
+++ b/kernel/dma/direct.c
@@ -343,7 +343,7 @@ void dma_direct_sync_sg_for_device(struct device *dev,
 	for_each_sg(sgl, sg, nents, i) {
 		phys_addr_t paddr = dma_to_phys(dev, sg_dma_address(sg));
 
-		if (unlikely(is_swiotlb_buffer(paddr)))
+		if (unlikely(is_swiotlb_buffer(dev, paddr)))
 			swiotlb_sync_single_for_device(dev, paddr, sg->length,
 						       dir);
 
@@ -369,7 +369,7 @@ void dma_direct_sync_sg_for_cpu(struct device *dev,
 		if (!dev_is_dma_coherent(dev))
 			arch_sync_dma_for_cpu(paddr, sg->length, dir);
 
-		if (unlikely(is_swiotlb_buffer(paddr)))
+		if (unlikely(is_swiotlb_buffer(dev, paddr)))
 			swiotlb_sync_single_for_cpu(dev, paddr, sg->length,
 						    dir);
 
@@ -504,7 +504,7 @@ size_t dma_direct_max_mapping_size(struct device *dev)
 bool dma_direct_need_sync(struct device *dev, dma_addr_t dma_addr)
 {
 	return !dev_is_dma_coherent(dev) ||
-		is_swiotlb_buffer(dma_to_phys(dev, dma_addr));
+	       is_swiotlb_buffer(dev, dma_to_phys(dev, dma_addr));
 }
 
 /**
diff --git a/kernel/dma/direct.h b/kernel/dma/direct.h
index 50afc05b6f1d..13e9e7158d94 100644
--- a/kernel/dma/direct.h
+++ b/kernel/dma/direct.h
@@ -56,7 +56,7 @@ static inline void dma_direct_sync_single_for_device(struct device *dev,
 {
 	phys_addr_t paddr = dma_to_phys(dev, addr);
 
-	if (unlikely(is_swiotlb_buffer(paddr)))
+	if (unlikely(is_swiotlb_buffer(dev, paddr)))
 		swiotlb_sync_single_for_device(dev, paddr, size, dir);
 
 	if (!dev_is_dma_coherent(dev))
@@ -73,7 +73,7 @@ static inline void dma_direct_sync_single_for_cpu(struct device *dev,
 		arch_sync_dma_for_cpu_all();
 	}
 
-	if (unlikely(is_swiotlb_buffer(paddr)))
+	if (unlikely(is_swiotlb_buffer(dev, paddr)))
 		swiotlb_sync_single_for_cpu(dev, paddr, size, dir);
 
 	if (dir == DMA_FROM_DEVICE)
@@ -113,7 +113,7 @@ static inline void dma_direct_unmap_page(struct device *dev, dma_addr_t addr,
 	if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC))
 		dma_direct_sync_single_for_cpu(dev, addr, size, dir);
 
-	if (unlikely(is_swiotlb_buffer(phys)))
+	if (unlikely(is_swiotlb_buffer(dev, phys)))
 		swiotlb_tbl_unmap_single(dev, phys, size, dir, attrs);
 }
 #endif /* _KERNEL_DMA_DIRECT_H */
-- 
2.31.1.607.g51e8a6a459-goog


^ permalink raw reply related

* [PATCH v6 07/15] swiotlb: Update is_swiotlb_active to add a struct device argument
From: Claire Chang @ 2021-05-10  9:50 UTC (permalink / raw)
  To: Rob Herring, mpe, Joerg Roedel, Will Deacon, Frank Rowand,
	Konrad Rzeszutek Wilk, boris.ostrovsky, jgross, Christoph Hellwig,
	Marek Szyprowski
  Cc: heikki.krogerus, thomas.hellstrom, peterz, joonas.lahtinen,
	dri-devel, chris, grant.likely, paulus, mingo, jxgao, sstabellini,
	Saravana Kannan, xypron.glpk, Rafael J . Wysocki,
	Bartosz Golaszewski, bskeggs, linux-pci, xen-devel,
	Thierry Reding, intel-gfx, matthew.auld, linux-devicetree, daniel,
	airlied, maarten.lankhorst, linuxppc-dev, jani.nikula,
	Nicolas Boichat, rodrigo.vivi, bhelgaas, tientzu, Dan Williams,
	Andy Shevchenko, nouveau, Greg KH, Randy Dunlap, lkml, tfiga,
	list@263.net:IOMMU DRIVERS, Jim Quinlan, Robin Murphy, bauerman
In-Reply-To: <20210510095026.3477496-1-tientzu@chromium.org>

Update is_swiotlb_active to add a struct device argument. This will be
useful later to allow for restricted DMA pool.

Signed-off-by: Claire Chang <tientzu@chromium.org>
---
 drivers/gpu/drm/i915/gem/i915_gem_internal.c | 2 +-
 drivers/gpu/drm/nouveau/nouveau_ttm.c        | 2 +-
 drivers/pci/xen-pcifront.c                   | 2 +-
 include/linux/swiotlb.h                      | 4 ++--
 kernel/dma/direct.c                          | 2 +-
 kernel/dma/swiotlb.c                         | 4 ++--
 6 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/i915/gem/i915_gem_internal.c b/drivers/gpu/drm/i915/gem/i915_gem_internal.c
index ce6b664b10aa..7d48c433446b 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_internal.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_internal.c
@@ -42,7 +42,7 @@ static int i915_gem_object_get_pages_internal(struct drm_i915_gem_object *obj)
 
 	max_order = MAX_ORDER;
 #ifdef CONFIG_SWIOTLB
-	if (is_swiotlb_active()) {
+	if (is_swiotlb_active(NULL)) {
 		unsigned int max_segment;
 
 		max_segment = swiotlb_max_segment();
diff --git a/drivers/gpu/drm/nouveau/nouveau_ttm.c b/drivers/gpu/drm/nouveau/nouveau_ttm.c
index e8b506a6685b..2a2ae6d6cf6d 100644
--- a/drivers/gpu/drm/nouveau/nouveau_ttm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_ttm.c
@@ -321,7 +321,7 @@ nouveau_ttm_init(struct nouveau_drm *drm)
 	}
 
 #if IS_ENABLED(CONFIG_SWIOTLB) && IS_ENABLED(CONFIG_X86)
-	need_swiotlb = is_swiotlb_active();
+	need_swiotlb = is_swiotlb_active(NULL);
 #endif
 
 	ret = ttm_device_init(&drm->ttm.bdev, &nouveau_bo_driver, drm->dev->dev,
diff --git a/drivers/pci/xen-pcifront.c b/drivers/pci/xen-pcifront.c
index b7a8f3a1921f..6d548ce53ce7 100644
--- a/drivers/pci/xen-pcifront.c
+++ b/drivers/pci/xen-pcifront.c
@@ -693,7 +693,7 @@ static int pcifront_connect_and_init_dma(struct pcifront_device *pdev)
 
 	spin_unlock(&pcifront_dev_lock);
 
-	if (!err && !is_swiotlb_active()) {
+	if (!err && !is_swiotlb_active(NULL)) {
 		err = pci_xen_swiotlb_init_late();
 		if (err)
 			dev_err(&pdev->xdev->dev, "Could not setup SWIOTLB!\n");
diff --git a/include/linux/swiotlb.h b/include/linux/swiotlb.h
index 2a6cca07540b..c530c976d18b 100644
--- a/include/linux/swiotlb.h
+++ b/include/linux/swiotlb.h
@@ -123,7 +123,7 @@ static inline bool is_swiotlb_buffer(struct device *dev, phys_addr_t paddr)
 void __init swiotlb_exit(void);
 unsigned int swiotlb_max_segment(void);
 size_t swiotlb_max_mapping_size(struct device *dev);
-bool is_swiotlb_active(void);
+bool is_swiotlb_active(struct device *dev);
 void __init swiotlb_adjust_size(unsigned long size);
 #else
 #define swiotlb_force SWIOTLB_NO_FORCE
@@ -143,7 +143,7 @@ static inline size_t swiotlb_max_mapping_size(struct device *dev)
 	return SIZE_MAX;
 }
 
-static inline bool is_swiotlb_active(void)
+static inline bool is_swiotlb_active(struct device *dev)
 {
 	return false;
 }
diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c
index 84c9feb5474a..7a88c34d0867 100644
--- a/kernel/dma/direct.c
+++ b/kernel/dma/direct.c
@@ -495,7 +495,7 @@ int dma_direct_supported(struct device *dev, u64 mask)
 size_t dma_direct_max_mapping_size(struct device *dev)
 {
 	/* If SWIOTLB is active, use its maximum mapping size */
-	if (is_swiotlb_active() &&
+	if (is_swiotlb_active(dev) &&
 	    (dma_addressing_limited(dev) || swiotlb_force == SWIOTLB_FORCE))
 		return swiotlb_max_mapping_size(dev);
 	return SIZE_MAX;
diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c
index 4ea027b75013..81bed3d2c771 100644
--- a/kernel/dma/swiotlb.c
+++ b/kernel/dma/swiotlb.c
@@ -662,9 +662,9 @@ size_t swiotlb_max_mapping_size(struct device *dev)
 	return ((size_t)IO_TLB_SIZE) * IO_TLB_SEGSIZE;
 }
 
-bool is_swiotlb_active(void)
+bool is_swiotlb_active(struct device *dev)
 {
-	return io_tlb_default_mem != NULL;
+	return get_io_tlb_mem(dev) != NULL;
 }
 EXPORT_SYMBOL_GPL(is_swiotlb_active);
 
-- 
2.31.1.607.g51e8a6a459-goog


^ permalink raw reply related

* [PATCH v6 08/15] swiotlb: Bounce data from/to restricted DMA pool if available
From: Claire Chang @ 2021-05-10  9:50 UTC (permalink / raw)
  To: Rob Herring, mpe, Joerg Roedel, Will Deacon, Frank Rowand,
	Konrad Rzeszutek Wilk, boris.ostrovsky, jgross, Christoph Hellwig,
	Marek Szyprowski
  Cc: heikki.krogerus, thomas.hellstrom, peterz, joonas.lahtinen,
	dri-devel, chris, grant.likely, paulus, mingo, jxgao, sstabellini,
	Saravana Kannan, xypron.glpk, Rafael J . Wysocki,
	Bartosz Golaszewski, bskeggs, linux-pci, xen-devel,
	Thierry Reding, intel-gfx, matthew.auld, linux-devicetree, daniel,
	airlied, maarten.lankhorst, linuxppc-dev, jani.nikula,
	Nicolas Boichat, rodrigo.vivi, bhelgaas, tientzu, Dan Williams,
	Andy Shevchenko, nouveau, Greg KH, Randy Dunlap, lkml, tfiga,
	list@263.net:IOMMU DRIVERS, Jim Quinlan, Robin Murphy, bauerman
In-Reply-To: <20210510095026.3477496-1-tientzu@chromium.org>

Regardless of swiotlb setting, the restricted DMA pool is preferred if
available.

The restricted DMA pools provide a basic level of protection against the
DMA overwriting buffer contents at unexpected times. However, to protect
against general data leakage and system memory corruption, the system
needs to provide a way to lock down the memory access, e.g., MPU.

Note that is_dev_swiotlb_force doesn't check if
swiotlb_force == SWIOTLB_FORCE. Otherwise the memory allocation behavior
with default swiotlb will be changed by the following patche
("dma-direct: Allocate memory from restricted DMA pool if available").

Signed-off-by: Claire Chang <tientzu@chromium.org>
---
 include/linux/swiotlb.h | 13 +++++++++++++
 kernel/dma/direct.c     |  3 ++-
 kernel/dma/direct.h     |  3 ++-
 kernel/dma/swiotlb.c    |  8 ++++----
 4 files changed, 21 insertions(+), 6 deletions(-)

diff --git a/include/linux/swiotlb.h b/include/linux/swiotlb.h
index c530c976d18b..0c5a18d9cf89 100644
--- a/include/linux/swiotlb.h
+++ b/include/linux/swiotlb.h
@@ -120,6 +120,15 @@ static inline bool is_swiotlb_buffer(struct device *dev, phys_addr_t paddr)
 	return mem && paddr >= mem->start && paddr < mem->end;
 }
 
+static inline bool is_dev_swiotlb_force(struct device *dev)
+{
+#ifdef CONFIG_DMA_RESTRICTED_POOL
+	if (dev->dma_io_tlb_mem)
+		return true;
+#endif /* CONFIG_DMA_RESTRICTED_POOL */
+	return false;
+}
+
 void __init swiotlb_exit(void);
 unsigned int swiotlb_max_segment(void);
 size_t swiotlb_max_mapping_size(struct device *dev);
@@ -131,6 +140,10 @@ static inline bool is_swiotlb_buffer(struct device *dev, phys_addr_t paddr)
 {
 	return false;
 }
+static inline bool is_dev_swiotlb_force(struct device *dev)
+{
+	return false;
+}
 static inline void swiotlb_exit(void)
 {
 }
diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c
index 7a88c34d0867..078f7087e466 100644
--- a/kernel/dma/direct.c
+++ b/kernel/dma/direct.c
@@ -496,7 +496,8 @@ size_t dma_direct_max_mapping_size(struct device *dev)
 {
 	/* If SWIOTLB is active, use its maximum mapping size */
 	if (is_swiotlb_active(dev) &&
-	    (dma_addressing_limited(dev) || swiotlb_force == SWIOTLB_FORCE))
+	    (dma_addressing_limited(dev) || swiotlb_force == SWIOTLB_FORCE ||
+	     is_dev_swiotlb_force(dev)))
 		return swiotlb_max_mapping_size(dev);
 	return SIZE_MAX;
 }
diff --git a/kernel/dma/direct.h b/kernel/dma/direct.h
index 13e9e7158d94..f94813674e23 100644
--- a/kernel/dma/direct.h
+++ b/kernel/dma/direct.h
@@ -87,7 +87,8 @@ static inline dma_addr_t dma_direct_map_page(struct device *dev,
 	phys_addr_t phys = page_to_phys(page) + offset;
 	dma_addr_t dma_addr = phys_to_dma(dev, phys);
 
-	if (unlikely(swiotlb_force == SWIOTLB_FORCE))
+	if (unlikely(swiotlb_force == SWIOTLB_FORCE) ||
+	    is_dev_swiotlb_force(dev))
 		return swiotlb_map(dev, phys, size, dir, attrs);
 
 	if (unlikely(!dma_capable(dev, dma_addr, size, true))) {
diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c
index 81bed3d2c771..3f1ad080a4bc 100644
--- a/kernel/dma/swiotlb.c
+++ b/kernel/dma/swiotlb.c
@@ -347,7 +347,7 @@ void __init swiotlb_exit(void)
 static void swiotlb_bounce(struct device *dev, phys_addr_t tlb_addr, size_t size,
 			   enum dma_data_direction dir)
 {
-	struct io_tlb_mem *mem = io_tlb_default_mem;
+	struct io_tlb_mem *mem = get_io_tlb_mem(dev);
 	int index = (tlb_addr - mem->start) >> IO_TLB_SHIFT;
 	phys_addr_t orig_addr = mem->slots[index].orig_addr;
 	size_t alloc_size = mem->slots[index].alloc_size;
@@ -429,7 +429,7 @@ static unsigned int wrap_index(struct io_tlb_mem *mem, unsigned int index)
 static int find_slots(struct device *dev, phys_addr_t orig_addr,
 		size_t alloc_size)
 {
-	struct io_tlb_mem *mem = io_tlb_default_mem;
+	struct io_tlb_mem *mem = get_io_tlb_mem(dev);
 	unsigned long boundary_mask = dma_get_seg_boundary(dev);
 	dma_addr_t tbl_dma_addr =
 		phys_to_dma_unencrypted(dev, mem->start) & boundary_mask;
@@ -506,7 +506,7 @@ phys_addr_t swiotlb_tbl_map_single(struct device *dev, phys_addr_t orig_addr,
 		size_t mapping_size, size_t alloc_size,
 		enum dma_data_direction dir, unsigned long attrs)
 {
-	struct io_tlb_mem *mem = io_tlb_default_mem;
+	struct io_tlb_mem *mem = get_io_tlb_mem(dev);
 	unsigned int offset = swiotlb_align_offset(dev, orig_addr);
 	unsigned int i;
 	int index;
@@ -557,7 +557,7 @@ void swiotlb_tbl_unmap_single(struct device *hwdev, phys_addr_t tlb_addr,
 			      size_t mapping_size, enum dma_data_direction dir,
 			      unsigned long attrs)
 {
-	struct io_tlb_mem *mem = io_tlb_default_mem;
+	struct io_tlb_mem *mem = get_io_tlb_mem(hwdev);
 	unsigned long flags;
 	unsigned int offset = swiotlb_align_offset(hwdev, tlb_addr);
 	int index = (tlb_addr - offset - mem->start) >> IO_TLB_SHIFT;
-- 
2.31.1.607.g51e8a6a459-goog


^ permalink raw reply related

* [PATCH v6 09/15] swiotlb: Move alloc_size to find_slots
From: Claire Chang @ 2021-05-10  9:50 UTC (permalink / raw)
  To: Rob Herring, mpe, Joerg Roedel, Will Deacon, Frank Rowand,
	Konrad Rzeszutek Wilk, boris.ostrovsky, jgross, Christoph Hellwig,
	Marek Szyprowski
  Cc: heikki.krogerus, thomas.hellstrom, peterz, joonas.lahtinen,
	dri-devel, chris, grant.likely, paulus, mingo, jxgao, sstabellini,
	Saravana Kannan, xypron.glpk, Rafael J . Wysocki,
	Bartosz Golaszewski, bskeggs, linux-pci, xen-devel,
	Thierry Reding, intel-gfx, matthew.auld, linux-devicetree, daniel,
	airlied, maarten.lankhorst, linuxppc-dev, jani.nikula,
	Nicolas Boichat, rodrigo.vivi, bhelgaas, tientzu, Dan Williams,
	Andy Shevchenko, nouveau, Greg KH, Randy Dunlap, lkml, tfiga,
	list@263.net:IOMMU DRIVERS, Jim Quinlan, Robin Murphy, bauerman
In-Reply-To: <20210510095026.3477496-1-tientzu@chromium.org>

Move the maintenance of alloc_size to find_slots for better code
reusability later.

Signed-off-by: Claire Chang <tientzu@chromium.org>
---
 kernel/dma/swiotlb.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c
index 3f1ad080a4bc..ef04d8f7708f 100644
--- a/kernel/dma/swiotlb.c
+++ b/kernel/dma/swiotlb.c
@@ -482,8 +482,11 @@ static int find_slots(struct device *dev, phys_addr_t orig_addr,
 	return -1;
 
 found:
-	for (i = index; i < index + nslots; i++)
+	for (i = index; i < index + nslots; i++) {
 		mem->slots[i].list = 0;
+		mem->slots[i].alloc_size =
+			alloc_size - ((i - index) << IO_TLB_SHIFT);
+	}
 	for (i = index - 1;
 	     io_tlb_offset(i) != IO_TLB_SEGSIZE - 1 &&
 	     mem->slots[i].list; i--)
@@ -538,11 +541,8 @@ phys_addr_t swiotlb_tbl_map_single(struct device *dev, phys_addr_t orig_addr,
 	 * This is needed when we sync the memory.  Then we sync the buffer if
 	 * needed.
 	 */
-	for (i = 0; i < nr_slots(alloc_size + offset); i++) {
+	for (i = 0; i < nr_slots(alloc_size + offset); i++)
 		mem->slots[index + i].orig_addr = slot_addr(orig_addr, i);
-		mem->slots[index + i].alloc_size =
-			alloc_size - (i << IO_TLB_SHIFT);
-	}
 	tlb_addr = slot_addr(mem->start, index) + offset;
 	if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC) &&
 	    (dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL))
-- 
2.31.1.607.g51e8a6a459-goog


^ permalink raw reply related

* [PATCH v6 10/15] swiotlb: Refactor swiotlb_tbl_unmap_single
From: Claire Chang @ 2021-05-10  9:50 UTC (permalink / raw)
  To: Rob Herring, mpe, Joerg Roedel, Will Deacon, Frank Rowand,
	Konrad Rzeszutek Wilk, boris.ostrovsky, jgross, Christoph Hellwig,
	Marek Szyprowski
  Cc: heikki.krogerus, thomas.hellstrom, peterz, joonas.lahtinen,
	dri-devel, chris, grant.likely, paulus, mingo, jxgao, sstabellini,
	Saravana Kannan, xypron.glpk, Rafael J . Wysocki,
	Bartosz Golaszewski, bskeggs, linux-pci, xen-devel,
	Thierry Reding, intel-gfx, matthew.auld, linux-devicetree, daniel,
	airlied, maarten.lankhorst, linuxppc-dev, jani.nikula,
	Nicolas Boichat, rodrigo.vivi, bhelgaas, tientzu, Dan Williams,
	Andy Shevchenko, nouveau, Greg KH, Randy Dunlap, lkml, tfiga,
	list@263.net:IOMMU DRIVERS, Jim Quinlan, Robin Murphy, bauerman
In-Reply-To: <20210510095026.3477496-1-tientzu@chromium.org>

Add a new function, release_slots, to make the code reusable for supporting
different bounce buffer pools, e.g. restricted DMA pool.

Signed-off-by: Claire Chang <tientzu@chromium.org>
---
 kernel/dma/swiotlb.c | 35 ++++++++++++++++++++---------------
 1 file changed, 20 insertions(+), 15 deletions(-)

diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c
index ef04d8f7708f..fa11787e4b95 100644
--- a/kernel/dma/swiotlb.c
+++ b/kernel/dma/swiotlb.c
@@ -550,27 +550,15 @@ phys_addr_t swiotlb_tbl_map_single(struct device *dev, phys_addr_t orig_addr,
 	return tlb_addr;
 }
 
-/*
- * tlb_addr is the physical address of the bounce buffer to unmap.
- */
-void swiotlb_tbl_unmap_single(struct device *hwdev, phys_addr_t tlb_addr,
-			      size_t mapping_size, enum dma_data_direction dir,
-			      unsigned long attrs)
+static void release_slots(struct device *dev, phys_addr_t tlb_addr)
 {
-	struct io_tlb_mem *mem = get_io_tlb_mem(hwdev);
+	struct io_tlb_mem *mem = get_io_tlb_mem(dev);
 	unsigned long flags;
-	unsigned int offset = swiotlb_align_offset(hwdev, tlb_addr);
+	unsigned int offset = swiotlb_align_offset(dev, tlb_addr);
 	int index = (tlb_addr - offset - mem->start) >> IO_TLB_SHIFT;
 	int nslots = nr_slots(mem->slots[index].alloc_size + offset);
 	int count, i;
 
-	/*
-	 * First, sync the memory before unmapping the entry
-	 */
-	if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC) &&
-	    (dir == DMA_FROM_DEVICE || dir == DMA_BIDIRECTIONAL))
-		swiotlb_bounce(hwdev, tlb_addr, mapping_size, DMA_FROM_DEVICE);
-
 	/*
 	 * Return the buffer to the free list by setting the corresponding
 	 * entries to indicate the number of contiguous entries available.
@@ -605,6 +593,23 @@ void swiotlb_tbl_unmap_single(struct device *hwdev, phys_addr_t tlb_addr,
 	spin_unlock_irqrestore(&mem->lock, flags);
 }
 
+/*
+ * tlb_addr is the physical address of the bounce buffer to unmap.
+ */
+void swiotlb_tbl_unmap_single(struct device *dev, phys_addr_t tlb_addr,
+			      size_t mapping_size, enum dma_data_direction dir,
+			      unsigned long attrs)
+{
+	/*
+	 * First, sync the memory before unmapping the entry
+	 */
+	if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC) &&
+	    (dir == DMA_FROM_DEVICE || dir == DMA_BIDIRECTIONAL))
+		swiotlb_bounce(dev, tlb_addr, mapping_size, DMA_FROM_DEVICE);
+
+	release_slots(dev, tlb_addr);
+}
+
 void swiotlb_sync_single_for_device(struct device *dev, phys_addr_t tlb_addr,
 		size_t size, enum dma_data_direction dir)
 {
-- 
2.31.1.607.g51e8a6a459-goog


^ permalink raw reply related

* [PATCH v6 11/15] dma-direct: Add a new wrapper __dma_direct_free_pages()
From: Claire Chang @ 2021-05-10  9:50 UTC (permalink / raw)
  To: Rob Herring, mpe, Joerg Roedel, Will Deacon, Frank Rowand,
	Konrad Rzeszutek Wilk, boris.ostrovsky, jgross, Christoph Hellwig,
	Marek Szyprowski
  Cc: heikki.krogerus, thomas.hellstrom, peterz, joonas.lahtinen,
	dri-devel, chris, grant.likely, paulus, mingo, jxgao, sstabellini,
	Saravana Kannan, xypron.glpk, Rafael J . Wysocki,
	Bartosz Golaszewski, bskeggs, linux-pci, xen-devel,
	Thierry Reding, intel-gfx, matthew.auld, linux-devicetree, daniel,
	airlied, maarten.lankhorst, linuxppc-dev, jani.nikula,
	Nicolas Boichat, rodrigo.vivi, bhelgaas, tientzu, Dan Williams,
	Andy Shevchenko, nouveau, Greg KH, Randy Dunlap, lkml, tfiga,
	list@263.net:IOMMU DRIVERS, Jim Quinlan, Robin Murphy, bauerman
In-Reply-To: <20210510095026.3477496-1-tientzu@chromium.org>

Add a new wrapper __dma_direct_free_pages() that will be useful later
for swiotlb_free().

Signed-off-by: Claire Chang <tientzu@chromium.org>
---
 kernel/dma/direct.c | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c
index 078f7087e466..eb4098323bbc 100644
--- a/kernel/dma/direct.c
+++ b/kernel/dma/direct.c
@@ -75,6 +75,12 @@ static bool dma_coherent_ok(struct device *dev, phys_addr_t phys, size_t size)
 		min_not_zero(dev->coherent_dma_mask, dev->bus_dma_limit);
 }
 
+static void __dma_direct_free_pages(struct device *dev, struct page *page,
+				    size_t size)
+{
+	dma_free_contiguous(dev, page, size);
+}
+
 static struct page *__dma_direct_alloc_pages(struct device *dev, size_t size,
 		gfp_t gfp)
 {
@@ -237,7 +243,7 @@ void *dma_direct_alloc(struct device *dev, size_t size,
 			return NULL;
 	}
 out_free_pages:
-	dma_free_contiguous(dev, page, size);
+	__dma_direct_free_pages(dev, page, size);
 	return NULL;
 }
 
@@ -273,7 +279,7 @@ void dma_direct_free(struct device *dev, size_t size,
 	else if (IS_ENABLED(CONFIG_ARCH_HAS_DMA_CLEAR_UNCACHED))
 		arch_dma_clear_uncached(cpu_addr, size);
 
-	dma_free_contiguous(dev, dma_direct_to_page(dev, dma_addr), size);
+	__dma_direct_free_pages(dev, dma_direct_to_page(dev, dma_addr), size);
 }
 
 struct page *dma_direct_alloc_pages(struct device *dev, size_t size,
@@ -310,7 +316,7 @@ struct page *dma_direct_alloc_pages(struct device *dev, size_t size,
 	*dma_handle = phys_to_dma_direct(dev, page_to_phys(page));
 	return page;
 out_free_pages:
-	dma_free_contiguous(dev, page, size);
+	__dma_direct_free_pages(dev, page, size);
 	return NULL;
 }
 
@@ -329,7 +335,7 @@ void dma_direct_free_pages(struct device *dev, size_t size,
 	if (force_dma_unencrypted(dev))
 		set_memory_encrypted((unsigned long)vaddr, 1 << page_order);
 
-	dma_free_contiguous(dev, page, size);
+	__dma_direct_free_pages(dev, page, size);
 }
 
 #if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \
-- 
2.31.1.607.g51e8a6a459-goog


^ permalink raw reply related

* [PATCH v6 12/15] swiotlb: Add restricted DMA alloc/free support.
From: Claire Chang @ 2021-05-10  9:50 UTC (permalink / raw)
  To: Rob Herring, mpe, Joerg Roedel, Will Deacon, Frank Rowand,
	Konrad Rzeszutek Wilk, boris.ostrovsky, jgross, Christoph Hellwig,
	Marek Szyprowski
  Cc: heikki.krogerus, thomas.hellstrom, peterz, joonas.lahtinen,
	dri-devel, chris, grant.likely, paulus, mingo, jxgao, sstabellini,
	Saravana Kannan, xypron.glpk, Rafael J . Wysocki,
	Bartosz Golaszewski, bskeggs, linux-pci, xen-devel,
	Thierry Reding, intel-gfx, matthew.auld, linux-devicetree, daniel,
	airlied, maarten.lankhorst, linuxppc-dev, jani.nikula,
	Nicolas Boichat, rodrigo.vivi, bhelgaas, tientzu, Dan Williams,
	Andy Shevchenko, nouveau, Greg KH, Randy Dunlap, lkml, tfiga,
	list@263.net:IOMMU DRIVERS, Jim Quinlan, Robin Murphy, bauerman
In-Reply-To: <20210510095026.3477496-1-tientzu@chromium.org>

Add the functions, swiotlb_{alloc,free} to support the memory allocation
from restricted DMA pool.

Signed-off-by: Claire Chang <tientzu@chromium.org>
---
 include/linux/swiotlb.h |  4 ++++
 kernel/dma/swiotlb.c    | 35 +++++++++++++++++++++++++++++++++--
 2 files changed, 37 insertions(+), 2 deletions(-)

diff --git a/include/linux/swiotlb.h b/include/linux/swiotlb.h
index 0c5a18d9cf89..e8cf49bd90c5 100644
--- a/include/linux/swiotlb.h
+++ b/include/linux/swiotlb.h
@@ -134,6 +134,10 @@ unsigned int swiotlb_max_segment(void);
 size_t swiotlb_max_mapping_size(struct device *dev);
 bool is_swiotlb_active(struct device *dev);
 void __init swiotlb_adjust_size(unsigned long size);
+#ifdef CONFIG_DMA_RESTRICTED_POOL
+struct page *swiotlb_alloc(struct device *dev, size_t size);
+bool swiotlb_free(struct device *dev, struct page *page, size_t size);
+#endif /* CONFIG_DMA_RESTRICTED_POOL */
 #else
 #define swiotlb_force SWIOTLB_NO_FORCE
 static inline bool is_swiotlb_buffer(struct device *dev, phys_addr_t paddr)
diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c
index fa11787e4b95..d99d5f902259 100644
--- a/kernel/dma/swiotlb.c
+++ b/kernel/dma/swiotlb.c
@@ -457,8 +457,9 @@ static int find_slots(struct device *dev, phys_addr_t orig_addr,
 
 	index = wrap = wrap_index(mem, ALIGN(mem->index, stride));
 	do {
-		if ((slot_addr(tbl_dma_addr, index) & iotlb_align_mask) !=
-		    (orig_addr & iotlb_align_mask)) {
+		if (orig_addr &&
+		    (slot_addr(tbl_dma_addr, index) & iotlb_align_mask) !=
+			    (orig_addr & iotlb_align_mask)) {
 			index = wrap_index(mem, index + 1);
 			continue;
 		}
@@ -701,6 +702,36 @@ late_initcall(swiotlb_create_default_debugfs);
 #endif
 
 #ifdef CONFIG_DMA_RESTRICTED_POOL
+struct page *swiotlb_alloc(struct device *dev, size_t size)
+{
+	struct io_tlb_mem *mem = dev->dma_io_tlb_mem;
+	phys_addr_t tlb_addr;
+	int index;
+
+	if (!mem)
+		return NULL;
+
+	index = find_slots(dev, 0, size);
+	if (index == -1)
+		return NULL;
+
+	tlb_addr = slot_addr(mem->start, index);
+
+	return pfn_to_page(PFN_DOWN(tlb_addr));
+}
+
+bool swiotlb_free(struct device *dev, struct page *page, size_t size)
+{
+	phys_addr_t tlb_addr = page_to_phys(page);
+
+	if (!is_swiotlb_buffer(dev, tlb_addr))
+		return false;
+
+	release_slots(dev, tlb_addr);
+
+	return true;
+}
+
 static int rmem_swiotlb_device_init(struct reserved_mem *rmem,
 				    struct device *dev)
 {
-- 
2.31.1.607.g51e8a6a459-goog


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox