| From foo@baz Wed May 31 09:13:34 JST 2017 |
| From: Julian Wiedmann <jwi@linux.vnet.ibm.com> |
| Date: Wed, 10 May 2017 19:07:52 +0200 |
| Subject: s390/qeth: unbreak OSM and OSN support |
| |
| From: Julian Wiedmann <jwi@linux.vnet.ibm.com> |
| |
| |
| [ Upstream commit 2d2ebb3ed0c6acfb014f98e427298673a5d07b82 ] |
| |
| commit b4d72c08b358 ("qeth: bridgeport support - basic control") |
| broke the support for OSM and OSN devices as follows: |
| |
| As OSM and OSN are L2 only, qeth_core_probe_device() does an early |
| setup by loading the l2 discipline and calling qeth_l2_probe_device(). |
| In this context, adding the l2-specific bridgeport sysfs attributes |
| via qeth_l2_create_device_attributes() hits a BUG_ON in fs/sysfs/group.c, |
| since the basic sysfs infrastructure for the device hasn't been |
| established yet. |
| |
| Note that OSN actually has its own unique sysfs attributes |
| (qeth_osn_devtype), so the additional attributes shouldn't be created |
| at all. |
| For OSM, add a new qeth_l2_devtype that contains all the common |
| and l2-specific sysfs attributes. |
| When qeth_core_probe_device() does early setup for OSM or OSN, assign |
| the corresponding devtype so that the ccwgroup probe code creates the |
| full set of sysfs attributes. |
| This allows us to skip qeth_l2_create_device_attributes() in case |
| of an early setup. |
| |
| Any device that can't do early setup will initially have only the |
| generic sysfs attributes, and when it's probed later |
| qeth_l2_probe_device() adds the l2-specific attributes. |
| |
| If an early-setup device is removed (by calling ccwgroup_ungroup()), |
| device_unregister() will - using the devtype - delete the |
| l2-specific attributes before qeth_l2_remove_device() is called. |
| So make sure to not remove them twice. |
| |
| What complicates the issue is that qeth_l2_probe_device() and |
| qeth_l2_remove_device() is also called on a device when its |
| layer2 attribute changes (ie. its layer mode is switched). |
| For early-setup devices this wouldn't work properly - we wouldn't |
| remove the l2-specific attributes when switching to L3. |
| But switching the layer mode doesn't actually make any sense; |
| we already decided that the device can only operate in L2! |
| So just refuse to switch the layer mode on such devices. Note that |
| OSN doesn't have a layer2 attribute, so we only need to special-case |
| OSM. |
| |
| Based on an initial patch by Ursula Braun. |
| |
| Fixes: b4d72c08b358 ("qeth: bridgeport support - basic control") |
| Signed-off-by: Julian Wiedmann <jwi@linux.vnet.ibm.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/s390/net/qeth_core.h | 4 ++++ |
| drivers/s390/net/qeth_core_main.c | 17 +++++++++-------- |
| drivers/s390/net/qeth_core_sys.c | 22 ++++++++++++++-------- |
| drivers/s390/net/qeth_l2.h | 2 ++ |
| drivers/s390/net/qeth_l2_main.c | 17 +++++++++++++---- |
| drivers/s390/net/qeth_l2_sys.c | 8 ++++++++ |
| drivers/s390/net/qeth_l3_main.c | 1 + |
| 7 files changed, 51 insertions(+), 20 deletions(-) |
| |
| --- a/drivers/s390/net/qeth_core.h |
| +++ b/drivers/s390/net/qeth_core.h |
| @@ -718,6 +718,7 @@ enum qeth_discipline_id { |
| }; |
| |
| struct qeth_discipline { |
| + const struct device_type *devtype; |
| void (*start_poll)(struct ccw_device *, int, unsigned long); |
| qdio_handler_t *input_handler; |
| qdio_handler_t *output_handler; |
| @@ -893,6 +894,9 @@ extern struct qeth_discipline qeth_l2_di |
| extern struct qeth_discipline qeth_l3_discipline; |
| extern const struct attribute_group *qeth_generic_attr_groups[]; |
| extern const struct attribute_group *qeth_osn_attr_groups[]; |
| +extern const struct attribute_group qeth_device_attr_group; |
| +extern const struct attribute_group qeth_device_blkt_group; |
| +extern const struct device_type qeth_generic_devtype; |
| extern struct workqueue_struct *qeth_wq; |
| |
| int qeth_card_hw_is_reachable(struct qeth_card *); |
| --- a/drivers/s390/net/qeth_core_main.c |
| +++ b/drivers/s390/net/qeth_core_main.c |
| @@ -5462,10 +5462,12 @@ void qeth_core_free_discipline(struct qe |
| card->discipline = NULL; |
| } |
| |
| -static const struct device_type qeth_generic_devtype = { |
| +const struct device_type qeth_generic_devtype = { |
| .name = "qeth_generic", |
| .groups = qeth_generic_attr_groups, |
| }; |
| +EXPORT_SYMBOL_GPL(qeth_generic_devtype); |
| + |
| static const struct device_type qeth_osn_devtype = { |
| .name = "qeth_osn", |
| .groups = qeth_osn_attr_groups, |
| @@ -5591,23 +5593,22 @@ static int qeth_core_probe_device(struct |
| goto err_card; |
| } |
| |
| - if (card->info.type == QETH_CARD_TYPE_OSN) |
| - gdev->dev.type = &qeth_osn_devtype; |
| - else |
| - gdev->dev.type = &qeth_generic_devtype; |
| - |
| switch (card->info.type) { |
| case QETH_CARD_TYPE_OSN: |
| case QETH_CARD_TYPE_OSM: |
| rc = qeth_core_load_discipline(card, QETH_DISCIPLINE_LAYER2); |
| if (rc) |
| goto err_card; |
| + |
| + gdev->dev.type = (card->info.type != QETH_CARD_TYPE_OSN) |
| + ? card->discipline->devtype |
| + : &qeth_osn_devtype; |
| rc = card->discipline->setup(card->gdev); |
| if (rc) |
| goto err_disc; |
| - case QETH_CARD_TYPE_OSD: |
| - case QETH_CARD_TYPE_OSX: |
| + break; |
| default: |
| + gdev->dev.type = &qeth_generic_devtype; |
| break; |
| } |
| |
| --- a/drivers/s390/net/qeth_core_sys.c |
| +++ b/drivers/s390/net/qeth_core_sys.c |
| @@ -413,12 +413,16 @@ static ssize_t qeth_dev_layer2_store(str |
| |
| if (card->options.layer2 == newdis) |
| goto out; |
| - else { |
| - card->info.mac_bits = 0; |
| - if (card->discipline) { |
| - card->discipline->remove(card->gdev); |
| - qeth_core_free_discipline(card); |
| - } |
| + if (card->info.type == QETH_CARD_TYPE_OSM) { |
| + /* fixed layer, can't switch */ |
| + rc = -EOPNOTSUPP; |
| + goto out; |
| + } |
| + |
| + card->info.mac_bits = 0; |
| + if (card->discipline) { |
| + card->discipline->remove(card->gdev); |
| + qeth_core_free_discipline(card); |
| } |
| |
| rc = qeth_core_load_discipline(card, newdis); |
| @@ -705,10 +709,11 @@ static struct attribute *qeth_blkt_devic |
| &dev_attr_inter_jumbo.attr, |
| NULL, |
| }; |
| -static struct attribute_group qeth_device_blkt_group = { |
| +const struct attribute_group qeth_device_blkt_group = { |
| .name = "blkt", |
| .attrs = qeth_blkt_device_attrs, |
| }; |
| +EXPORT_SYMBOL_GPL(qeth_device_blkt_group); |
| |
| static struct attribute *qeth_device_attrs[] = { |
| &dev_attr_state.attr, |
| @@ -728,9 +733,10 @@ static struct attribute *qeth_device_att |
| &dev_attr_switch_attrs.attr, |
| NULL, |
| }; |
| -static struct attribute_group qeth_device_attr_group = { |
| +const struct attribute_group qeth_device_attr_group = { |
| .attrs = qeth_device_attrs, |
| }; |
| +EXPORT_SYMBOL_GPL(qeth_device_attr_group); |
| |
| const struct attribute_group *qeth_generic_attr_groups[] = { |
| &qeth_device_attr_group, |
| --- a/drivers/s390/net/qeth_l2.h |
| +++ b/drivers/s390/net/qeth_l2.h |
| @@ -8,6 +8,8 @@ |
| |
| #include "qeth_core.h" |
| |
| +extern const struct attribute_group *qeth_l2_attr_groups[]; |
| + |
| int qeth_l2_create_device_attributes(struct device *); |
| void qeth_l2_remove_device_attributes(struct device *); |
| void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card); |
| --- a/drivers/s390/net/qeth_l2_main.c |
| +++ b/drivers/s390/net/qeth_l2_main.c |
| @@ -1021,14 +1021,21 @@ static int qeth_l2_stop(struct net_devic |
| return 0; |
| } |
| |
| +static const struct device_type qeth_l2_devtype = { |
| + .name = "qeth_layer2", |
| + .groups = qeth_l2_attr_groups, |
| +}; |
| + |
| static int qeth_l2_probe_device(struct ccwgroup_device *gdev) |
| { |
| struct qeth_card *card = dev_get_drvdata(&gdev->dev); |
| int rc; |
| |
| - rc = qeth_l2_create_device_attributes(&gdev->dev); |
| - if (rc) |
| - return rc; |
| + if (gdev->dev.type == &qeth_generic_devtype) { |
| + rc = qeth_l2_create_device_attributes(&gdev->dev); |
| + if (rc) |
| + return rc; |
| + } |
| INIT_LIST_HEAD(&card->vid_list); |
| hash_init(card->mac_htable); |
| card->options.layer2 = 1; |
| @@ -1040,7 +1047,8 @@ static void qeth_l2_remove_device(struct |
| { |
| struct qeth_card *card = dev_get_drvdata(&cgdev->dev); |
| |
| - qeth_l2_remove_device_attributes(&cgdev->dev); |
| + if (cgdev->dev.type == &qeth_generic_devtype) |
| + qeth_l2_remove_device_attributes(&cgdev->dev); |
| qeth_set_allowed_threads(card, 0, 1); |
| wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0); |
| |
| @@ -1437,6 +1445,7 @@ static int qeth_l2_control_event(struct |
| } |
| |
| struct qeth_discipline qeth_l2_discipline = { |
| + .devtype = &qeth_l2_devtype, |
| .start_poll = qeth_qdio_start_poll, |
| .input_handler = (qdio_handler_t *) qeth_qdio_input_handler, |
| .output_handler = (qdio_handler_t *) qeth_qdio_output_handler, |
| --- a/drivers/s390/net/qeth_l2_sys.c |
| +++ b/drivers/s390/net/qeth_l2_sys.c |
| @@ -272,3 +272,11 @@ void qeth_l2_setup_bridgeport_attrs(stru |
| } else |
| qeth_bridgeport_an_set(card, 0); |
| } |
| + |
| +const struct attribute_group *qeth_l2_attr_groups[] = { |
| + &qeth_device_attr_group, |
| + &qeth_device_blkt_group, |
| + /* l2 specific, see l2_{create,remove}_device_attributes(): */ |
| + &qeth_l2_bridgeport_attr_group, |
| + NULL, |
| +}; |
| --- a/drivers/s390/net/qeth_l3_main.c |
| +++ b/drivers/s390/net/qeth_l3_main.c |
| @@ -3453,6 +3453,7 @@ static int qeth_l3_control_event(struct |
| } |
| |
| struct qeth_discipline qeth_l3_discipline = { |
| + .devtype = &qeth_generic_devtype, |
| .start_poll = qeth_qdio_start_poll, |
| .input_handler = (qdio_handler_t *) qeth_qdio_input_handler, |
| .output_handler = (qdio_handler_t *) qeth_qdio_output_handler, |