Subject: Re: [RFC 04/20] RDMA/irdma: Add driver framework definitions

From: Leon Romanovsky <[email-masked]>
Date: Thu, 26 Sep 2019 20:30:46 +0300

On Thu, Sep 26, 2019 at 09:45:03AM -0700, Jeff Kirsher wrote:
> From: Mustafa Ismail <[email-masked]>
>
> Register irdma as a platform driver capable of supporting platform
> devices from multi-generation RDMA capable Intel HW. Establish the
> interface with all supported netdev peer devices and initialize HW.
>
> Signed-off-by: Mustafa Ismail <[email-masked]>
> Signed-off-by: Shiraz Saleem <[email-masked]>
> Signed-off-by: Jeff Kirsher <[email-masked]>
> ---
>  drivers/infiniband/hw/irdma/i40iw_if.c | 270 +++++++++++
>  drivers/infiniband/hw/irdma/irdma_if.c | 436 +++++++++++++++++
>  drivers/infiniband/hw/irdma/main.c     | 531 ++++++++++++++++++++
>  drivers/infiniband/hw/irdma/main.h     | 639 +++++++++++++++++++++++++
>  4 files changed, 1876 insertions(+)
>  create mode 100644 drivers/infiniband/hw/irdma/i40iw_if.c
>  create mode 100644 drivers/infiniband/hw/irdma/irdma_if.c
>  create mode 100644 drivers/infiniband/hw/irdma/main.c
>  create mode 100644 drivers/infiniband/hw/irdma/main.h
>
> diff --git a/drivers/infiniband/hw/irdma/i40iw_if.c b/drivers/infiniband/hw/irdma/i40iw_if.c
> new file mode 100644
> index 000000000000..3cddb091acfb
> --- /dev/null
> +++ b/drivers/infiniband/hw/irdma/i40iw_if.c
> @@ -0,0 +1,270 @@
> +// SPDX-License-Identifier: GPL-2.0 or Linux-OpenIB
> +/* Copyright (c) 2019, Intel Corporation. */
> +
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>
> +#include <linux/netdevice.h>
> +#include <linux/etherdevice.h>
> +#include <net/addrconf.h>
> +#include "main.h"
> +#include "i40iw_hw.h"
> +#include <linux/net/intel/i40e_client.h>
> +
> +/**
> + * i40iw_request_reset - Request a reset
> + * @rf: RDMA PCI function
> + *
> + */
> +void i40iw_request_reset(struct irdma_pci_f *rf)
> +{
> +	struct i40e_info *ldev = (struct i40e_info *)rf->ldev.if_ldev;
> +
> +	ldev->ops->request_reset(ldev, rf->ldev.if_client, 1);
> +}
> +
> +/**
> + * i40iw_open - client interface operation open for iwarp/uda device
> + * @ldev: LAN device information
> + * @client: iwarp client information, provided during registration
> + *
> + * Called by the LAN driver during the processing of client register
> + * Create device resources, set up queues, pble and hmc objects and
> + * register the device with the ib verbs interface
> + * Return 0 if successful, otherwise return error
> + */
> +static int i40iw_open(struct i40e_info *ldev, struct i40e_client *client)
> +{
> +	struct irdma_l2params l2params = {};
> +	struct irdma_device *iwdev = NULL;
> +	struct irdma_handler *hdl = NULL;
> +	struct irdma_priv_ldev *pldev;
> +	u16 last_qset = IRDMA_NO_QSET;
> +	struct irdma_sc_dev *dev;
> +	struct irdma_pci_f *rf;
> +	int err_code = -EIO;
> +	u16 qset;
> +	int i;
> +
> +	hdl = irdma_find_handler(ldev->pcidev);
> +	if (hdl)
> +		return 0;
> +
> +	hdl = kzalloc((sizeof(*hdl) + sizeof(*iwdev)), GFP_KERNEL);
> +	if (!hdl)
> +		return -ENOMEM;
> +
> +	iwdev = (struct irdma_device *)((u8 *)hdl + sizeof(*hdl));
> +
> +	iwdev->param_wq = alloc_ordered_workqueue("l2params", WQ_MEM_RECLAIM);
> +	if (!iwdev->param_wq)
> +		goto error;
> +
> +	rf = &hdl->rf;
> +	rf->hdl = hdl;
> +	dev = &rf->sc_dev;
> +	dev->back_dev = rf;
> +	rf->rdma_ver = IRDMA_GEN_1;
> +	hdl->platform_dev = ldev->platform_dev;
> +	irdma_init_rf_config_params(rf);
> +	rf->init_hw = i40iw_init_hw;
> +	rf->hw.hw_addr = ldev->hw_addr;
> +	rf->pdev = ldev->pcidev;
> +	rf->netdev = ldev->netdev;
> +	dev->pci_rev = rf->pdev->revision;
> +	iwdev->rf = rf;
> +	iwdev->hdl = hdl;
> +	iwdev->ldev = &rf->ldev;
> +	iwdev->init_state = INITIAL_STATE;
> +	iwdev->rcv_wnd = IRDMA_CM_DEFAULT_RCV_WND_SCALED;
> +	iwdev->rcv_wscale = IRDMA_CM_DEFAULT_RCV_WND_SCALE;
> +	iwdev->netdev = ldev->netdev;
> +	iwdev->create_ilq = true;
> +	iwdev->vsi_num = 0;
> +
> +	pldev = &rf->ldev;
> +	hdl->ldev = pldev;
> +	pldev->if_client = client;
> +	pldev->if_ldev = ldev;
> +	pldev->fn_num = ldev->fid;
> +	pldev->ftype = ldev->ftype;
> +	pldev->pf_vsi_num = 0;
> +	pldev->msix_count = ldev->msix_count;
> +	pldev->msix_entries = ldev->msix_entries;
> +
> +	if (irdma_ctrl_init_hw(rf))
> +		goto error;
> +
> +	l2params.mtu =
> +		(ldev->params.mtu) ? ldev->params.mtu : IRDMA_DEFAULT_MTU;
> +	for (i = 0; i < I40E_CLIENT_MAX_USER_PRIORITY; i++) {
> +		qset = ldev->params.qos.prio_qos[i].qs_handle;
> +		l2params.up2tc[i] = ldev->params.qos.prio_qos[i].tc;
> +		l2params.qs_handle_list[i] = qset;
> +		if (last_qset == IRDMA_NO_QSET)
> +			last_qset = qset;
> +		else if ((qset != last_qset) && (qset != IRDMA_NO_QSET))
> +			iwdev->dcb = true;
> +	}
> +
> +	if (irdma_rt_init_hw(rf, iwdev, &l2params)) {
> +		irdma_deinit_ctrl_hw(rf);
> +		goto error;
> +	}
> +
> +	irdma_add_handler(hdl);
> +	return 0;
> +error:
> +	kfree(hdl);
> +	return err_code;
> +}
> +
> +/**
> + * i40iw_l2params_worker - worker for l2 params change
> + * @work: work pointer for l2 params
> + */
> +static void i40iw_l2params_worker(struct work_struct *work)
> +{
> +	struct l2params_work *dwork =
> +		container_of(work, struct l2params_work, work);
> +	struct irdma_device *iwdev = dwork->iwdev;
> +
> +	irdma_change_l2params(&iwdev->vsi, &dwork->l2params);
> +	atomic_dec(&iwdev->params_busy);
> +	kfree(work);
> +}
> +
> +/**
> + * i40iw_l2param_change - handle qs handles for QoS and MSS change
> + * @ldev: LAN device information
> + * @client: client for parameter change
> + * @params: new parameters from L2
> + */
> +static void i40iw_l2param_change(struct i40e_info *ldev,
> +				 struct i40e_client *client,
> +				 struct i40e_params *params)
> +{
> +	struct irdma_l2params *l2params;
> +	struct l2params_work *work;
> +	struct irdma_device *iwdev;
> +	struct irdma_handler *hdl;
> +	int i;
> +
> +	hdl = irdma_find_handler(ldev->pcidev);
> +	if (!hdl)
> +		return;
> +
> +	iwdev = (struct irdma_device *)((u8 *)hdl + sizeof(*hdl));
> +
> +	if (atomic_read(&iwdev->params_busy))
> +		return;
> +	work = kzalloc(sizeof(*work), GFP_KERNEL);
> +	if (!work)
> +		return;
> +
> +	atomic_inc(&iwdev->params_busy);

Changing parameters through workqueue and perform locking with atomic_t, exciting.
Please do proper locking scheme and better to avoid workqueue at all.

<...>

> +/* client interface functions */
> +static const struct i40e_client_ops i40e_ops = {
> +	.open = i40iw_open,
> +	.close = i40iw_close,
> +	.l2_param_change = i40iw_l2param_change
> +};
> +
> +static struct i40e_client i40iw_client = {
> +	.name = "irdma",
> +	.ops = &i40e_ops,
> +	.version.major = I40E_CLIENT_VERSION_MAJOR,
> +	.version.minor = I40E_CLIENT_VERSION_MINOR,
> +	.version.build = I40E_CLIENT_VERSION_BUILD,
> +	.type = I40E_CLIENT_IWARP,
> +};
> +
> +int i40iw_probe(struct platform_device *pdev)
> +{
> +	struct i40e_peer_dev_platform_data *pdata =
> +		dev_get_platdata(&pdev->dev);
> +	struct i40e_info *ldev;
> +
> +	if (!pdata)
> +		return -EINVAL;
> +
> +	ldev = pdata->ldev;
> +
> +	if (ldev->version.major != I40E_CLIENT_VERSION_MAJOR ||
> +	    ldev->version.minor != I40E_CLIENT_VERSION_MINOR) {
> +		pr_err("version mismatch:\n");
> +		pr_err("expected major ver %d, caller specified major ver %d\n",
> +		       I40E_CLIENT_VERSION_MAJOR, ldev->version.major);
> +		pr_err("expected minor ver %d, caller specified minor ver %d\n",
> +		       I40E_CLIENT_VERSION_MINOR, ldev->version.minor);
> +		return -EINVAL;
> +	}

This is can't be in upstream code, we don't support out-of-tree modules,
everything else will have proper versions.

Thanks