[patch dpll-rfc 2/7] dpll: allow to call device register multiple times

Jiri Pirko
2023-03-26T17:01:02Z

From: Jiri Pirko <jiri@nvidia.com>

Some devices allow to control a single dpll instance over multiple
channels. In case of mlx5 for example, one dpll could be controlled over
multiple PFs that reside on a single ASIC. These are equal. Allow each
to register/unregister dpll device. Use the first ops and priv always
as the is no difference in between those and the rest.

Signed-off-by: Jiri Pirko <jiri@nvidia.com>
---
drivers/dpll/dpll_core.c | 85 +++++++++++++++++++++--
drivers/dpll/dpll_core.h | 11 ++-
drivers/dpll/dpll_netlink.c | 29 ++++----
drivers/net/ethernet/intel/ice/ice_dpll.c | 4 +-
drivers/ptp/ptp_ocp.c | 2 +-
include/linux/dpll.h | 5 +-
6 files changed, 113 insertions(+), 23 deletions(-)

diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
index 7f8442b73fd8..6e50216a636a 100644
--- a/drivers/dpll/dpll_core.c
+++ b/drivers/dpll/dpll_core.c
@@ -293,6 +293,7 @@ dpll_device_alloc(const u64 clock_id, u32 dev_driver_id,
if (!dpll)
return ERR_PTR(-ENOMEM);
refcount_set(&dpll->refcount, 1);
+ INIT_LIST_HEAD(&dpll->registration_list);
dpll->dev.class = &dpll_class;
dpll->dev_driver_id = dev_driver_id;
dpll->clock_id = clock_id;
@@ -366,12 +367,26 @@ void dpll_device_put(struct dpll_device *dpll)
WARN_ON_ONCE(!xa_empty(&dpll->pin_refs));
xa_destroy(&dpll->pin_refs);
xa_erase(&dpll_device_xa, dpll->id);
+ WARN_ON(!list_empty(&dpll->registration_list));
kfree(dpll);
}
mutex_unlock(&dpll_device_xa_lock);
}
EXPORT_SYMBOL_GPL(dpll_device_put);

+static struct dpll_device_registration *
+dpll_device_registration_find(struct dpll_device *dpll,
+ const struct dpll_device_ops *ops, void *priv)
+{
+ struct dpll_device_registration *reg;
+
+ list_for_each_entry(reg, &dpll->registration_list, list) {
+ if (reg->ops == ops && reg->priv == priv)
+ return reg;
+ }
+ return NULL;
+}
+
/**
* dpll_device_register - register the dpll device in the subsystem
* @dpll: pointer to a dpll
@@ -389,19 +404,39 @@ int dpll_device_register(struct dpll_device *dpll,
const struct dpll_device_ops *ops, void *priv,
struct device *owner)
{
+ struct dpll_device_registration *reg;
+ bool first_registration = false;
+
if (WARN_ON(!ops || !owner))
return -EINVAL;
+
mutex_lock(&dpll_device_xa_lock);
- if (ASSERT_DPLL_NOT_REGISTERED(dpll)) {
+ reg = dpll_device_registration_find(dpll, ops, priv);
+ if (reg) {
mutex_unlock(&dpll_device_xa_lock);
return -EEXIST;
}
+
+ reg = kzalloc(sizeof(*reg), GFP_KERNEL);
+ if (!reg) {
+ mutex_unlock(&dpll_device_xa_lock);
+ return -ENOMEM;
+ }
+ reg->ops = ops;
+ reg->priv = priv;
+
dpll->dev.bus = owner->bus;
dpll->parent = owner;
- dpll->ops = ops;
dev_set_name(&dpll->dev, "%s_%d", dev_name(owner),
dpll->dev_driver_id);
- dpll->priv = priv;
+
+ first_registration = list_empty(&dpll->registration_list);
+ list_add_tail(&reg->list, &dpll->registration_list);
+ if (!first_registration) {
+ mutex_unlock(&dpll_device_xa_lock);
+ return 0;
+ }
+
xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
mutex_unlock(&dpll_device_xa_lock);
dpll_notify_device_create(dpll);
@@ -413,14 +448,32 @@ EXPORT_SYMBOL_GPL(dpll_device_register);
/**
* dpll_device_unregister - deregister dpll device
* @dpll: registered dpll pointer
+ * @ops: ops for a dpll device
+ * @priv: pointer to private information of owner
*
* Deregister device, make it unavailable for userspace.
* Note: It does not free the memory
*/
-void dpll_device_unregister(struct dpll_device *dpll)
+void dpll_device_unregister(struct dpll_device *dpll,
+ const struct dpll_device_ops *ops, void *priv)
{
+ struct dpll_device_registration *reg;
+
mutex_lock(&dpll_device_xa_lock);
ASSERT_DPLL_REGISTERED(dpll);
+
+ reg = dpll_device_registration_find(dpll, ops, priv);
+ if (WARN_ON(!reg)) {
+ mutex_unlock(&dpll_device_xa_lock);
+ return;
+ }
+ list_del(&reg->list);
+ kfree(reg);
+
+ if (!list_empty(&dpll->registration_list)) {
+ mutex_unlock(&dpll_device_xa_lock);
+ return;
+ }
xa_clear_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
mutex_unlock(&dpll_device_xa_lock);
dpll_notify_device_delete(dpll);
@@ -760,6 +813,17 @@ struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, u32 idx)
return NULL;
}

+static struct dpll_device_registration *
+dpll_device_registration_first(struct dpll_device *dpll)
+{
+ struct dpll_device_registration *reg;
+
+ reg = list_first_entry_or_null((struct list_head *) &dpll->registration_list,
+ struct dpll_device_registration, list);
+ WARN_ON(!reg);
+ return reg;
+}
+
/**
* dpll_priv - get the dpll device private owner data
* @dpll: registered dpll pointer
@@ -768,10 +832,21 @@ struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, u32 idx)
*/
void *dpll_priv(const struct dpll_device *dpll)
{
- return dpll->priv;
+ struct dpll_device_registration *reg;
+
+ reg = dpll_device_registration_first((struct dpll_device *) dpll);
+ return reg->priv;
}
EXPORT_SYMBOL_GPL(dpll_priv);

+const struct dpll_device_ops *dpll_device_ops(struct dpll_device *dpll)
+{
+ struct dpll_device_registration *reg;
+
+ reg = dpll_device_registration_first(dpll);
+ return reg->ops;
+}
+
/**
* dpll_pin_on_dpll_priv - get the dpll device private owner data
* @dpll: registered dpll pointer
diff --git a/drivers/dpll/dpll_core.h b/drivers/dpll/dpll_core.h
index 876b6ac6f3a0..21ba31621b44 100644
--- a/drivers/dpll/dpll_core.h
+++ b/drivers/dpll/dpll_core.h
@@ -7,11 +7,18 @@
#define __DPLL_CORE_H__

#include <linux/dpll.h>
+#include <linux/list.h>
#include <linux/refcount.h>
#include "dpll_netlink.h"

#define DPLL_REGISTERED XA_MARK_1

+struct dpll_device_registration {
+ struct list_head list;
+ const struct dpll_device_ops *ops;
+ void *priv;
+};
+
/**
* struct dpll_device - structure for a DPLL device
* @id: unique id number for each device
@@ -34,9 +41,8 @@ struct dpll_device {
struct device dev;
struct device *parent;
struct module *module;
- struct dpll_device_ops *ops;
enum dpll_type type;
- void *priv;
+ struct list_head registration_list;
struct xarray pin_refs;
u64 clock_id;
unsigned long mode_supported_mask;
@@ -84,6 +90,7 @@ struct dpll_pin_ref {
refcount_t refcount;
};

+const struct dpll_device_ops *dpll_device_ops(struct dpll_device *dpll);
struct dpll_device *dpll_device_get_by_id(int id);
struct dpll_device *dpll_device_get_by_name(const char *bus_name,
const char *dev_name);
diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
index d2c699015215..430c009d0a71 100644
--- a/drivers/dpll/dpll_netlink.c
+++ b/drivers/dpll/dpll_netlink.c
@@ -18,7 +18,7 @@ static u32 dpll_pin_freq_value[] = {
};

static int
-dpll_msg_add_dev_handle(struct sk_buff *msg, const struct dpll_device *dpll)
+dpll_msg_add_dev_handle(struct sk_buff *msg, struct dpll_device *dpll)
{
if (nla_put_u32(msg, DPLL_A_ID, dpll->id))
return -EMSGSIZE;
@@ -31,14 +31,15 @@ dpll_msg_add_dev_handle(struct sk_buff *msg, const struct dpll_device *dpll)
}

static int
-dpll_msg_add_mode(struct sk_buff *msg, const struct dpll_device *dpll,
+dpll_msg_add_mode(struct sk_buff *msg, struct dpll_device *dpll,
struct netlink_ext_ack *extack)
{
+ const struct dpll_device_ops *ops = dpll_device_ops(dpll);
enum dpll_mode mode;

- if (WARN_ON(!dpll->ops->mode_get))
+ if (WARN_ON(!ops->mode_get))
return -EOPNOTSUPP;
- if (dpll->ops->mode_get(dpll, &mode, extack))
+ if (ops->mode_get(dpll, &mode, extack))
return -EFAULT;
if (nla_put_u8(msg, DPLL_A_MODE, mode))
return -EMSGSIZE;
@@ -50,11 +51,12 @@ static int
dpll_msg_add_source_pin_idx(struct sk_buff *msg, struct dpll_device *dpll,
struct netlink_ext_ack *extack)
{
+ const struct dpll_device_ops *ops = dpll_device_ops(dpll);
u32 source_pin_idx;

- if (!dpll->ops->source_pin_idx_get)
+ if (!ops->source_pin_idx_get)
return 0;
- if (dpll->ops->source_pin_idx_get(dpll, &source_pin_idx, extack))
+ if (ops->source_pin_idx_get(dpll, &source_pin_idx, extack))
return -EFAULT;
if (nla_put_u32(msg, DPLL_A_SOURCE_PIN_IDX, source_pin_idx))
return -EMSGSIZE;
@@ -66,11 +68,12 @@ static int
dpll_msg_add_lock_status(struct sk_buff *msg, struct dpll_device *dpll,
struct netlink_ext_ack *extack)
{
+ const struct dpll_device_ops *ops = dpll_device_ops(dpll);
enum dpll_lock_status status;

- if (WARN_ON(!dpll->ops->lock_status_get))
+ if (WARN_ON(!ops->lock_status_get))
return -EOPNOTSUPP;
- if (dpll->ops->lock_status_get(dpll, &status, extack))
+ if (ops->lock_status_get(dpll, &status, extack))
return -EFAULT;
if (nla_put_u8(msg, DPLL_A_LOCK_STATUS, status))
return -EMSGSIZE;
@@ -82,11 +85,12 @@ static int
dpll_msg_add_temp(struct sk_buff *msg, struct dpll_device *dpll,
struct netlink_ext_ack *extack)
{
+ const struct dpll_device_ops *ops = dpll_device_ops(dpll);
s32 temp;

- if (!dpll->ops->temp_get)
+ if (!ops->temp_get)
return -EOPNOTSUPP;
- if (dpll->ops->temp_get(dpll, &temp, extack))
+ if (ops->temp_get(dpll, &temp, extack))
return -EFAULT;
if (nla_put_s32(msg, DPLL_A_TEMP, temp))
return -EMSGSIZE;
@@ -686,6 +690,7 @@ int dpll_nl_pin_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
static int
dpll_set_from_nlattr(struct dpll_device *dpll, struct genl_info *info)
{
+ const struct dpll_device_ops *ops = dpll_device_ops(dpll);
struct nlattr *attr;
enum dpll_mode mode;
int rem, ret = 0;
@@ -696,9 +701,9 @@ dpll_set_from_nlattr(struct dpll_device *dpll, struct genl_info *info)
case DPLL_A_MODE:
mode = nla_get_u8(attr);

- if (!dpll->ops || !dpll->ops->mode_set)
+ if (!ops->mode_set)
return -EOPNOTSUPP;
- ret = dpll->ops->mode_set(dpll, mode, info->extack);
+ ret = ops->mode_set(dpll, mode, info->extack);
if (ret)
return ret;
break;
diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c
index 87572ecc21e4..532ad7314f49 100644
--- a/drivers/net/ethernet/intel/ice/ice_dpll.c
+++ b/drivers/net/ethernet/intel/ice/ice_dpll.c
@@ -1551,7 +1551,7 @@ static void ice_dpll_release_all(struct ice_pf *pf, bool cgu)
if (dp->dpll) {
mutex_lock(&pf->dplls.lock);
if (cgu)
- dpll_device_unregister(dp->dpll);
+ dpll_device_unregister(dp->dpll, &ice_dpll_ops, pf);
dpll_device_put(dp->dpll);
mutex_unlock(&pf->dplls.lock);
dev_dbg(ice_pf_to_dev(pf), "PPS dpll removed\n");
@@ -1560,7 +1560,7 @@ static void ice_dpll_release_all(struct ice_pf *pf, bool cgu)
if (de->dpll) {
mutex_lock(&pf->dplls.lock);
if (cgu)
- dpll_device_unregister(de->dpll);
+ dpll_device_unregister(de->dpll, &ice_dpll_ops, pf);
dpll_device_put(de->dpll);
mutex_unlock(&pf->dplls.lock);
dev_dbg(ice_pf_to_dev(pf), "EEC dpll removed\n");
diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c
index cc840d0e3265..5e7fceae2a6a 100644
--- a/drivers/ptp/ptp_ocp.c
+++ b/drivers/ptp/ptp_ocp.c
@@ -4431,7 +4431,7 @@ ptp_ocp_remove(struct pci_dev *pdev)
struct ptp_ocp *bp = pci_get_drvdata(pdev);
struct devlink *devlink = priv_to_devlink(bp);

- dpll_device_unregister(bp->dpll);
+ dpll_device_unregister(bp->dpll, &dpll_ops, bp);
dpll_device_put(bp->dpll);
devlink_unregister(devlink);
ptp_ocp_detach(bp);
diff --git a/include/linux/dpll.h b/include/linux/dpll.h
index 496358df83a9..09863d66a44c 100644
--- a/include/linux/dpll.h
+++ b/include/linux/dpll.h
@@ -130,11 +130,14 @@ int dpll_device_register(struct dpll_device *dpll,
/**
* dpll_device_unregister - deregister registered dpll
* @dpll: pointer to dpll
+ * @ops: ops for a dpll device
+ * @priv: pointer to private information of owner
*
* Unregister the dpll from the subsystem, make it unavailable for netlink
* API users.
*/
-void dpll_device_unregister(struct dpll_device *dpll);
+void dpll_device_unregister(struct dpll_device *dpll,
+ const struct dpll_device_ops *ops, void *priv);

/**
* dpll_priv - get dpll private data
--
2.39.0