Commit 4c814829 authored by Julian Wiedmann's avatar Julian Wiedmann Committed by Greg Kroah-Hartman

s390/qeth: unbreak OSM and OSN support


[ Upstream commit 2d2ebb3e ]

commit b4d72c08 ("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: b4d72c08 ("qeth: bridgeport support - basic control")
Signed-off-by: default avatarJulian Wiedmann <jwi@linux.vnet.ibm.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 03effc4a
...@@ -722,6 +722,7 @@ enum qeth_discipline_id { ...@@ -722,6 +722,7 @@ enum qeth_discipline_id {
}; };
struct qeth_discipline { struct qeth_discipline {
const struct device_type *devtype;
void (*start_poll)(struct ccw_device *, int, unsigned long); void (*start_poll)(struct ccw_device *, int, unsigned long);
qdio_handler_t *input_handler; qdio_handler_t *input_handler;
qdio_handler_t *output_handler; qdio_handler_t *output_handler;
...@@ -887,6 +888,9 @@ extern struct qeth_discipline qeth_l2_discipline; ...@@ -887,6 +888,9 @@ extern struct qeth_discipline qeth_l2_discipline;
extern struct qeth_discipline qeth_l3_discipline; extern struct qeth_discipline qeth_l3_discipline;
extern const struct attribute_group *qeth_generic_attr_groups[]; extern const struct attribute_group *qeth_generic_attr_groups[];
extern const struct attribute_group *qeth_osn_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; extern struct workqueue_struct *qeth_wq;
int qeth_card_hw_is_reachable(struct qeth_card *); int qeth_card_hw_is_reachable(struct qeth_card *);
......
...@@ -5304,10 +5304,12 @@ void qeth_core_free_discipline(struct qeth_card *card) ...@@ -5304,10 +5304,12 @@ void qeth_core_free_discipline(struct qeth_card *card)
card->discipline = NULL; card->discipline = NULL;
} }
static const struct device_type qeth_generic_devtype = { const struct device_type qeth_generic_devtype = {
.name = "qeth_generic", .name = "qeth_generic",
.groups = qeth_generic_attr_groups, .groups = qeth_generic_attr_groups,
}; };
EXPORT_SYMBOL_GPL(qeth_generic_devtype);
static const struct device_type qeth_osn_devtype = { static const struct device_type qeth_osn_devtype = {
.name = "qeth_osn", .name = "qeth_osn",
.groups = qeth_osn_attr_groups, .groups = qeth_osn_attr_groups,
...@@ -5433,23 +5435,22 @@ static int qeth_core_probe_device(struct ccwgroup_device *gdev) ...@@ -5433,23 +5435,22 @@ static int qeth_core_probe_device(struct ccwgroup_device *gdev)
goto err_card; 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) { switch (card->info.type) {
case QETH_CARD_TYPE_OSN: case QETH_CARD_TYPE_OSN:
case QETH_CARD_TYPE_OSM: case QETH_CARD_TYPE_OSM:
rc = qeth_core_load_discipline(card, QETH_DISCIPLINE_LAYER2); rc = qeth_core_load_discipline(card, QETH_DISCIPLINE_LAYER2);
if (rc) if (rc)
goto err_card; 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); rc = card->discipline->setup(card->gdev);
if (rc) if (rc)
goto err_disc; goto err_disc;
case QETH_CARD_TYPE_OSD: break;
case QETH_CARD_TYPE_OSX:
default: default:
gdev->dev.type = &qeth_generic_devtype;
break; break;
} }
......
...@@ -446,12 +446,16 @@ static ssize_t qeth_dev_layer2_store(struct device *dev, ...@@ -446,12 +446,16 @@ static ssize_t qeth_dev_layer2_store(struct device *dev,
if (card->options.layer2 == newdis) if (card->options.layer2 == newdis)
goto out; goto out;
else { if (card->info.type == QETH_CARD_TYPE_OSM) {
card->info.mac_bits = 0; /* fixed layer, can't switch */
if (card->discipline) { rc = -EOPNOTSUPP;
card->discipline->remove(card->gdev); goto out;
qeth_core_free_discipline(card); }
}
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); rc = qeth_core_load_discipline(card, newdis);
...@@ -745,10 +749,11 @@ static struct attribute *qeth_blkt_device_attrs[] = { ...@@ -745,10 +749,11 @@ static struct attribute *qeth_blkt_device_attrs[] = {
&dev_attr_inter_jumbo.attr, &dev_attr_inter_jumbo.attr,
NULL, NULL,
}; };
static struct attribute_group qeth_device_blkt_group = { const struct attribute_group qeth_device_blkt_group = {
.name = "blkt", .name = "blkt",
.attrs = qeth_blkt_device_attrs, .attrs = qeth_blkt_device_attrs,
}; };
EXPORT_SYMBOL_GPL(qeth_device_blkt_group);
static struct attribute *qeth_device_attrs[] = { static struct attribute *qeth_device_attrs[] = {
&dev_attr_state.attr, &dev_attr_state.attr,
...@@ -768,9 +773,10 @@ static struct attribute *qeth_device_attrs[] = { ...@@ -768,9 +773,10 @@ static struct attribute *qeth_device_attrs[] = {
&dev_attr_switch_attrs.attr, &dev_attr_switch_attrs.attr,
NULL, NULL,
}; };
static struct attribute_group qeth_device_attr_group = { const struct attribute_group qeth_device_attr_group = {
.attrs = qeth_device_attrs, .attrs = qeth_device_attrs,
}; };
EXPORT_SYMBOL_GPL(qeth_device_attr_group);
const struct attribute_group *qeth_generic_attr_groups[] = { const struct attribute_group *qeth_generic_attr_groups[] = {
&qeth_device_attr_group, &qeth_device_attr_group,
......
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
#include "qeth_core.h" #include "qeth_core.h"
extern const struct attribute_group *qeth_l2_attr_groups[];
int qeth_l2_create_device_attributes(struct device *); int qeth_l2_create_device_attributes(struct device *);
void qeth_l2_remove_device_attributes(struct device *); void qeth_l2_remove_device_attributes(struct device *);
void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card); void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card);
......
...@@ -887,14 +887,21 @@ static int qeth_l2_stop(struct net_device *dev) ...@@ -887,14 +887,21 @@ static int qeth_l2_stop(struct net_device *dev)
return 0; 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) static int qeth_l2_probe_device(struct ccwgroup_device *gdev)
{ {
struct qeth_card *card = dev_get_drvdata(&gdev->dev); struct qeth_card *card = dev_get_drvdata(&gdev->dev);
int rc; int rc;
rc = qeth_l2_create_device_attributes(&gdev->dev); if (gdev->dev.type == &qeth_generic_devtype) {
if (rc) rc = qeth_l2_create_device_attributes(&gdev->dev);
return rc; if (rc)
return rc;
}
INIT_LIST_HEAD(&card->vid_list); INIT_LIST_HEAD(&card->vid_list);
INIT_LIST_HEAD(&card->mc_list); INIT_LIST_HEAD(&card->mc_list);
card->options.layer2 = 1; card->options.layer2 = 1;
...@@ -906,7 +913,8 @@ static void qeth_l2_remove_device(struct ccwgroup_device *cgdev) ...@@ -906,7 +913,8 @@ static void qeth_l2_remove_device(struct ccwgroup_device *cgdev)
{ {
struct qeth_card *card = dev_get_drvdata(&cgdev->dev); 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); qeth_set_allowed_threads(card, 0, 1);
wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0); wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0);
...@@ -1276,6 +1284,7 @@ static int qeth_l2_control_event(struct qeth_card *card, ...@@ -1276,6 +1284,7 @@ static int qeth_l2_control_event(struct qeth_card *card,
} }
struct qeth_discipline qeth_l2_discipline = { struct qeth_discipline qeth_l2_discipline = {
.devtype = &qeth_l2_devtype,
.start_poll = qeth_qdio_start_poll, .start_poll = qeth_qdio_start_poll,
.input_handler = (qdio_handler_t *) qeth_qdio_input_handler, .input_handler = (qdio_handler_t *) qeth_qdio_input_handler,
.output_handler = (qdio_handler_t *) qeth_qdio_output_handler, .output_handler = (qdio_handler_t *) qeth_qdio_output_handler,
......
...@@ -216,3 +216,11 @@ void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card) ...@@ -216,3 +216,11 @@ void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card)
} else } else
qeth_bridgeport_an_set(card, 0); 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,
};
...@@ -3612,6 +3612,7 @@ static int qeth_l3_control_event(struct qeth_card *card, ...@@ -3612,6 +3612,7 @@ static int qeth_l3_control_event(struct qeth_card *card,
} }
struct qeth_discipline qeth_l3_discipline = { struct qeth_discipline qeth_l3_discipline = {
.devtype = &qeth_generic_devtype,
.start_poll = qeth_qdio_start_poll, .start_poll = qeth_qdio_start_poll,
.input_handler = (qdio_handler_t *) qeth_qdio_input_handler, .input_handler = (qdio_handler_t *) qeth_qdio_input_handler,
.output_handler = (qdio_handler_t *) qeth_qdio_output_handler, .output_handler = (qdio_handler_t *) qeth_qdio_output_handler,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment