| From 863b5b6941f9f43b924393b6ba2b36647e7dee42 Mon Sep 17 00:00:00 2001 |
| From: Russell King <rmk+kernel@armlinux.org.uk> |
| Date: Thu, 7 Nov 2019 17:06:08 +0000 |
| Subject: [PATCH 615/660] net: sfp: rework upstream interface |
| |
| The current upstream interface is an all-or-nothing, which is |
| sub-optimal for future changes, as it doesn't allow the upstream driver |
| to prepare for the SFP module becoming available, as it is at boot. |
| |
| Switch to a find-sfp-bus, add-upstream, del-upstream, put-sfp-bus |
| interface structure instead, which allows the upstream driver to |
| prepare for a module being available as soon as add-upstream is called. |
| |
| Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> |
| --- |
| drivers/net/phy/phylink.c | 10 +++-- |
| drivers/net/phy/sfp-bus.c | 92 +++++++++++++++++++++++++++------------ |
| include/linux/sfp.h | 25 +++++++---- |
| 3 files changed, 88 insertions(+), 39 deletions(-) |
| |
| --- a/drivers/net/phy/phylink.c |
| +++ b/drivers/net/phy/phylink.c |
| @@ -568,7 +568,7 @@ static int phylink_register_sfp(struct p |
| struct sfp_bus *bus; |
| int ret; |
| |
| - bus = sfp_register_upstream_node(fwnode, pl, &sfp_phylink_ops); |
| + bus = sfp_bus_find_fwnode(fwnode); |
| if (IS_ERR(bus)) { |
| ret = PTR_ERR(bus); |
| phylink_err(pl, "unable to attach SFP bus: %d\n", ret); |
| @@ -577,7 +577,10 @@ static int phylink_register_sfp(struct p |
| |
| pl->sfp_bus = bus; |
| |
| - return 0; |
| + ret = sfp_bus_add_upstream(bus, pl, &sfp_phylink_ops); |
| + sfp_bus_put(bus); |
| + |
| + return ret; |
| } |
| |
| /** |
| @@ -675,8 +678,7 @@ EXPORT_SYMBOL_GPL(phylink_create); |
| */ |
| void phylink_destroy(struct phylink *pl) |
| { |
| - if (pl->sfp_bus) |
| - sfp_unregister_upstream(pl->sfp_bus); |
| + sfp_bus_del_upstream(pl->sfp_bus); |
| if (pl->link_gpio) |
| gpiod_put(pl->link_gpio); |
| |
| --- a/drivers/net/phy/sfp-bus.c |
| +++ b/drivers/net/phy/sfp-bus.c |
| @@ -404,10 +404,19 @@ static void sfp_bus_release(struct kref |
| kfree(bus); |
| } |
| |
| -static void sfp_bus_put(struct sfp_bus *bus) |
| +/** |
| + * sfp_bus_put() - put a reference on the &struct sfp_bus |
| + * bus: the &struct sfp_bus found via sfp_bus_find_fwnode() |
| + * |
| + * Put a reference on the &struct sfp_bus and free the underlying structure |
| + * if this was the last reference. |
| + */ |
| +void sfp_bus_put(struct sfp_bus *bus) |
| { |
| - kref_put_mutex(&bus->kref, sfp_bus_release, &sfp_mutex); |
| + if (bus) |
| + kref_put_mutex(&bus->kref, sfp_bus_release, &sfp_mutex); |
| } |
| +EXPORT_SYMBOL_GPL(sfp_bus_put); |
| |
| static int sfp_register_bus(struct sfp_bus *bus) |
| { |
| @@ -423,11 +432,11 @@ static int sfp_register_bus(struct sfp_b |
| return ret; |
| } |
| } |
| + bus->registered = true; |
| bus->socket_ops->attach(bus->sfp); |
| if (bus->started) |
| bus->socket_ops->start(bus->sfp); |
| bus->upstream_ops->attach(bus->upstream, bus); |
| - bus->registered = true; |
| return 0; |
| } |
| |
| @@ -521,13 +530,12 @@ static void sfp_upstream_clear(struct sf |
| } |
| |
| /** |
| - * sfp_register_upstream_node() - parse and register the neighbouring device |
| + * sfp_bus_find_fwnode() - parse and locate the SFP bus from fwnode |
| * @fwnode: firmware node for the parent device (MAC or PHY) |
| - * @upstream: the upstream private data |
| - * @ops: the upstream's &struct sfp_upstream_ops |
| * |
| - * Parse the parent device's firmware node for a SFP bus, and register the |
| - * SFP bus using sfp_register_upstream(). |
| + * Parse the parent device's firmware node for a SFP bus, and locate |
| + * the sfp_bus structure, incrementing its reference count. This must |
| + * be put via sfp_bus_put() when done. |
| * |
| * Returns: on success, a pointer to the sfp_bus structure, |
| * %NULL if no SFP is specified, |
| @@ -537,9 +545,7 @@ static void sfp_upstream_clear(struct sf |
| * %-ENOMEM if we failed to allocate the bus. |
| * an error from the upstream's connect_phy() method. |
| */ |
| -struct sfp_bus *sfp_register_upstream_node(struct fwnode_handle *fwnode, |
| - void *upstream, |
| - const struct sfp_upstream_ops *ops) |
| +struct sfp_bus *sfp_bus_find_fwnode(struct fwnode_handle *fwnode) |
| { |
| struct fwnode_reference_args ref; |
| struct sfp_bus *bus; |
| @@ -562,7 +568,39 @@ struct sfp_bus *sfp_register_upstream_no |
| if (!bus) |
| return ERR_PTR(-ENOMEM); |
| |
| + return bus; |
| +} |
| +EXPORT_SYMBOL_GPL(sfp_bus_find_fwnode); |
| + |
| +/** |
| + * sfp_bus_add_upstream() - parse and register the neighbouring device |
| + * @bus: the &struct sfp_bus found via sfp_bus_find_fwnode() |
| + * @upstream: the upstream private data |
| + * @ops: the upstream's &struct sfp_upstream_ops |
| + * |
| + * Add upstream driver for the SFP bus, and if the bus is complete, register |
| + * the SFP bus using sfp_register_upstream(). This takes a reference on the |
| + * bus, so it is safe to put the bus after this call. |
| + * |
| + * Returns: on success, a pointer to the sfp_bus structure, |
| + * %NULL if no SFP is specified, |
| + * on failure, an error pointer value: |
| + * corresponding to the errors detailed for |
| + * fwnode_property_get_reference_args(). |
| + * %-ENOMEM if we failed to allocate the bus. |
| + * an error from the upstream's connect_phy() method. |
| + */ |
| +int sfp_bus_add_upstream(struct sfp_bus *bus, void *upstream, |
| + const struct sfp_upstream_ops *ops) |
| +{ |
| + int ret; |
| + |
| + /* If no bus, return success */ |
| + if (!bus) |
| + return 0; |
| + |
| rtnl_lock(); |
| + kref_get(&bus->kref); |
| bus->upstream_ops = ops; |
| bus->upstream = upstream; |
| |
| @@ -575,33 +613,33 @@ struct sfp_bus *sfp_register_upstream_no |
| } |
| rtnl_unlock(); |
| |
| - if (ret) { |
| + if (ret) |
| sfp_bus_put(bus); |
| - bus = ERR_PTR(ret); |
| - } |
| |
| - return bus; |
| + return ret; |
| } |
| -EXPORT_SYMBOL_GPL(sfp_register_upstream_node); |
| +EXPORT_SYMBOL_GPL(sfp_bus_add_upstream); |
| |
| /** |
| - * sfp_unregister_upstream() - Unregister sfp bus |
| + * sfp_bus_del_upstream() - Delete a sfp bus |
| * @bus: a pointer to the &struct sfp_bus structure for the sfp module |
| * |
| - * Unregister a previously registered upstream connection for the SFP |
| - * module. @bus is returned from sfp_register_upstream(). |
| + * Delete a previously registered upstream connection for the SFP |
| + * module. @bus should have been added by sfp_bus_add_upstream(). |
| */ |
| -void sfp_unregister_upstream(struct sfp_bus *bus) |
| +void sfp_bus_del_upstream(struct sfp_bus *bus) |
| { |
| - rtnl_lock(); |
| - if (bus->sfp) |
| - sfp_unregister_bus(bus); |
| - sfp_upstream_clear(bus); |
| - rtnl_unlock(); |
| + if (bus) { |
| + rtnl_lock(); |
| + if (bus->sfp) |
| + sfp_unregister_bus(bus); |
| + sfp_upstream_clear(bus); |
| + rtnl_unlock(); |
| |
| - sfp_bus_put(bus); |
| + sfp_bus_put(bus); |
| + } |
| } |
| -EXPORT_SYMBOL_GPL(sfp_unregister_upstream); |
| +EXPORT_SYMBOL_GPL(sfp_bus_del_upstream); |
| |
| /* Socket driver entry points */ |
| int sfp_add_phy(struct sfp_bus *bus, struct phy_device *phydev) |
| --- a/include/linux/sfp.h |
| +++ b/include/linux/sfp.h |
| @@ -508,10 +508,11 @@ int sfp_get_module_eeprom(struct sfp_bus |
| u8 *data); |
| void sfp_upstream_start(struct sfp_bus *bus); |
| void sfp_upstream_stop(struct sfp_bus *bus); |
| -struct sfp_bus *sfp_register_upstream_node(struct fwnode_handle *fwnode, |
| - void *upstream, |
| - const struct sfp_upstream_ops *ops); |
| -void sfp_unregister_upstream(struct sfp_bus *bus); |
| +void sfp_bus_put(struct sfp_bus *bus); |
| +struct sfp_bus *sfp_bus_find_fwnode(struct fwnode_handle *fwnode); |
| +int sfp_bus_add_upstream(struct sfp_bus *bus, void *upstream, |
| + const struct sfp_upstream_ops *ops); |
| +void sfp_bus_del_upstream(struct sfp_bus *bus); |
| #else |
| static inline int sfp_parse_port(struct sfp_bus *bus, |
| const struct sfp_eeprom_id *id, |
| @@ -553,14 +554,22 @@ static inline void sfp_upstream_stop(str |
| { |
| } |
| |
| -static inline struct sfp_bus *sfp_register_upstream_node( |
| - struct fwnode_handle *fwnode, void *upstream, |
| - const struct sfp_upstream_ops *ops) |
| +static inline void sfp_bus_put(struct sfp_bus *bus) |
| +{ |
| +} |
| + |
| +static inline struct sfp_bus *sfp_bus_find_fwnode(struct fwnode_handle *fwnode) |
| { |
| return NULL; |
| } |
| |
| -static inline void sfp_unregister_upstream(struct sfp_bus *bus) |
| +static int sfp_bus_add_upstream(struct sfp_bus *bus, void *upstream, |
| + const struct sfp_upstream_ops *ops) |
| +{ |
| + return 0; |
| +} |
| + |
| +static inline void sfp_bus_del_upstream(struct sfp_bus *bus) |
| { |
| } |
| #endif |