qeth: bridgeport support - basic control
Introduce functions to assign roles and check state of bridgeport-capable HiperSocket devices, and sysfs attributes providing access to these functions from userspace. Introduce udev events emitted when the state of a bridgeport device changes. Signed-off-by: Eugene Crosser <eugene.crosser@ru.ibm.com> Signed-off-by: Frank Blaschka <frank.blaschka@de.ibm.com> Reviewed-by: Ursula Braun <ursula.braun@de.ibm.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
3977458c9c
commit
b4d72c08b3
9 changed files with 685 additions and 2 deletions
21
Documentation/s390/qeth.txt
Normal file
21
Documentation/s390/qeth.txt
Normal file
|
@ -0,0 +1,21 @@
|
|||
IBM s390 QDIO Ethernet Driver
|
||||
|
||||
HiperSockets Bridge Port Support
|
||||
|
||||
Uevents
|
||||
|
||||
To generate the events the device must be assigned a role of either
|
||||
a primary or a secondary Bridge Port. For more information, see
|
||||
"z/VM Connectivity, SC24-6174".
|
||||
|
||||
When run on HiperSockets Bridge Capable Port hardware, and the state
|
||||
of some configured Bridge Port device on the channel changes, a udev
|
||||
event with ACTION=CHANGE is emitted on behalf of the corresponding
|
||||
ccwgroup device. The event has the following attributes:
|
||||
|
||||
BRIDGEPORT=statechange - indicates that the Bridge Port device changed
|
||||
its state.
|
||||
|
||||
ROLE={primary|secondary|none} - the role assigned to the port.
|
||||
|
||||
STATE={active|standby|inactive} - the newly assumed state of the port.
|
|
@ -11,7 +11,7 @@ obj-$(CONFIG_LCS) += lcs.o
|
|||
obj-$(CONFIG_CLAW) += claw.o
|
||||
qeth-y += qeth_core_sys.o qeth_core_main.o qeth_core_mpc.o
|
||||
obj-$(CONFIG_QETH) += qeth.o
|
||||
qeth_l2-y += qeth_l2_main.o
|
||||
qeth_l2-y += qeth_l2_main.o qeth_l2_sys.o
|
||||
obj-$(CONFIG_QETH_L2) += qeth_l2.o
|
||||
qeth_l3-y += qeth_l3_main.o qeth_l3_sys.o
|
||||
obj-$(CONFIG_QETH_L3) += qeth_l3.o
|
||||
|
|
|
@ -156,6 +156,24 @@ struct qeth_ipa_info {
|
|||
__u32 enabled_funcs;
|
||||
};
|
||||
|
||||
/* SETBRIDGEPORT stuff */
|
||||
enum qeth_sbp_roles {
|
||||
QETH_SBP_ROLE_NONE = 0,
|
||||
QETH_SBP_ROLE_PRIMARY = 1,
|
||||
QETH_SBP_ROLE_SECONDARY = 2,
|
||||
};
|
||||
|
||||
enum qeth_sbp_states {
|
||||
QETH_SBP_STATE_INACTIVE = 0,
|
||||
QETH_SBP_STATE_STANDBY = 1,
|
||||
QETH_SBP_STATE_ACTIVE = 2,
|
||||
};
|
||||
|
||||
struct qeth_sbp_info {
|
||||
__u32 supported_funcs;
|
||||
enum qeth_sbp_roles role;
|
||||
};
|
||||
|
||||
static inline int qeth_is_ipa_supported(struct qeth_ipa_info *ipa,
|
||||
enum qeth_ipa_funcs func)
|
||||
{
|
||||
|
@ -672,6 +690,7 @@ struct qeth_card_options {
|
|||
struct qeth_ipa_info adp; /*Adapter parameters*/
|
||||
struct qeth_routing_info route6;
|
||||
struct qeth_ipa_info ipa6;
|
||||
struct qeth_sbp_info sbp; /* SETBRIDGEPORT options */
|
||||
int fake_broadcast;
|
||||
int add_hhlen;
|
||||
int layer2;
|
||||
|
@ -857,6 +876,7 @@ extern struct qeth_discipline qeth_l2_discipline;
|
|||
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 struct workqueue_struct *qeth_wq;
|
||||
|
||||
const char *qeth_get_cardname_short(struct qeth_card *);
|
||||
int qeth_realloc_buffer_pool(struct qeth_card *, int);
|
||||
|
@ -925,6 +945,11 @@ int qeth_query_card_info(struct qeth_card *card,
|
|||
int qeth_send_control_data(struct qeth_card *, int, struct qeth_cmd_buffer *,
|
||||
int (*reply_cb)(struct qeth_card *, struct qeth_reply*, unsigned long),
|
||||
void *reply_param);
|
||||
void qeth_bridge_state_change(struct qeth_card *card, struct qeth_ipa_cmd *cmd);
|
||||
void qeth_bridgeport_query_support(struct qeth_card *card);
|
||||
int qeth_bridgeport_query_ports(struct qeth_card *card,
|
||||
enum qeth_sbp_roles *role, enum qeth_sbp_states *state);
|
||||
int qeth_bridgeport_setrole(struct qeth_card *card, enum qeth_sbp_roles role);
|
||||
int qeth_get_priority_queue(struct qeth_card *, struct sk_buff *, int, int);
|
||||
int qeth_get_elements_no(struct qeth_card *, struct sk_buff *, int);
|
||||
int qeth_get_elements_for_frags(struct sk_buff *);
|
||||
|
|
|
@ -68,7 +68,7 @@ static void qeth_clear_output_buffer(struct qeth_qdio_out_q *queue,
|
|||
enum qeth_qdio_buffer_states newbufstate);
|
||||
static int qeth_init_qdio_out_buf(struct qeth_qdio_out_q *, int);
|
||||
|
||||
static struct workqueue_struct *qeth_wq;
|
||||
struct workqueue_struct *qeth_wq;
|
||||
|
||||
static void qeth_close_dev_handler(struct work_struct *work)
|
||||
{
|
||||
|
@ -615,6 +615,13 @@ static struct qeth_ipa_cmd *qeth_check_ipa_data(struct qeth_card *card,
|
|||
card->info.hwtrap = 2;
|
||||
qeth_schedule_recovery(card);
|
||||
return NULL;
|
||||
case IPA_CMD_SETBRIDGEPORT:
|
||||
if (cmd->data.sbp.hdr.command_code ==
|
||||
IPA_SBP_BRIDGE_PORT_STATE_CHANGE) {
|
||||
qeth_bridge_state_change(card, cmd);
|
||||
return NULL;
|
||||
} else
|
||||
return cmd;
|
||||
case IPA_CMD_MODCCID:
|
||||
return cmd;
|
||||
case IPA_CMD_REGISTER_LOCAL_ADDR:
|
||||
|
@ -4956,12 +4963,17 @@ int qeth_core_hardsetup_card(struct qeth_card *card)
|
|||
|
||||
card->options.ipa4.supported_funcs = 0;
|
||||
card->options.adp.supported_funcs = 0;
|
||||
card->options.sbp.supported_funcs = 0;
|
||||
card->info.diagass_support = 0;
|
||||
qeth_query_ipassists(card, QETH_PROT_IPV4);
|
||||
if (qeth_is_supported(card, IPA_SETADAPTERPARMS))
|
||||
qeth_query_setadapterparms(card);
|
||||
if (qeth_adp_supported(card, IPA_SETADP_SET_DIAG_ASSIST))
|
||||
qeth_query_setdiagass(card);
|
||||
qeth_bridgeport_query_support(card);
|
||||
if (card->options.sbp.supported_funcs)
|
||||
dev_info(&card->gdev->dev,
|
||||
"The device represents a HiperSockets Bridge Capable Port\n");
|
||||
return 0;
|
||||
out:
|
||||
dev_warn(&card->gdev->dev, "The qeth device driver failed to recover "
|
||||
|
|
|
@ -249,6 +249,7 @@ static struct ipa_cmd_names qeth_ipa_cmd_names[] = {
|
|||
{IPA_CMD_DELIP, "delip"},
|
||||
{IPA_CMD_SETADAPTERPARMS, "setadapterparms"},
|
||||
{IPA_CMD_SET_DIAG_ASS, "set_diag_ass"},
|
||||
{IPA_CMD_SETBRIDGEPORT, "set_bridge_port"},
|
||||
{IPA_CMD_CREATE_ADDR, "create_addr"},
|
||||
{IPA_CMD_DESTROY_ADDR, "destroy_addr"},
|
||||
{IPA_CMD_REGISTER_LOCAL_ADDR, "register_local_addr"},
|
||||
|
|
|
@ -104,6 +104,7 @@ enum qeth_ipa_cmds {
|
|||
IPA_CMD_DELIP = 0xb7,
|
||||
IPA_CMD_SETADAPTERPARMS = 0xb8,
|
||||
IPA_CMD_SET_DIAG_ASS = 0xb9,
|
||||
IPA_CMD_SETBRIDGEPORT = 0xbe,
|
||||
IPA_CMD_CREATE_ADDR = 0xc3,
|
||||
IPA_CMD_DESTROY_ADDR = 0xc4,
|
||||
IPA_CMD_REGISTER_LOCAL_ADDR = 0xd1,
|
||||
|
@ -500,6 +501,88 @@ struct qeth_ipacmd_diagass {
|
|||
__u8 cdata[64];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* SETBRIDGEPORT IPA Command: *********************************************/
|
||||
enum qeth_ipa_sbp_cmd {
|
||||
IPA_SBP_QUERY_COMMANDS_SUPPORTED = 0x00000000L,
|
||||
IPA_SBP_RESET_BRIDGE_PORT_ROLE = 0x00000001L,
|
||||
IPA_SBP_SET_PRIMARY_BRIDGE_PORT = 0x00000002L,
|
||||
IPA_SBP_SET_SECONDARY_BRIDGE_PORT = 0x00000004L,
|
||||
IPA_SBP_QUERY_BRIDGE_PORTS = 0x00000008L,
|
||||
IPA_SBP_BRIDGE_PORT_STATE_CHANGE = 0x00000010L,
|
||||
};
|
||||
|
||||
struct net_if_token {
|
||||
__u16 devnum;
|
||||
__u8 cssid;
|
||||
__u8 iid;
|
||||
__u8 ssid;
|
||||
__u8 chpid;
|
||||
__u16 chid;
|
||||
} __packed;
|
||||
|
||||
struct qeth_ipacmd_sbp_hdr {
|
||||
__u32 supported_sbp_cmds;
|
||||
__u32 enabled_sbp_cmds;
|
||||
__u16 cmdlength;
|
||||
__u16 reserved1;
|
||||
__u32 command_code;
|
||||
__u16 return_code;
|
||||
__u8 used_total;
|
||||
__u8 seq_no;
|
||||
__u32 reserved2;
|
||||
} __packed;
|
||||
|
||||
struct qeth_sbp_query_cmds_supp {
|
||||
__u32 supported_cmds;
|
||||
__u32 reserved;
|
||||
} __packed;
|
||||
|
||||
struct qeth_sbp_reset_role {
|
||||
} __packed;
|
||||
|
||||
struct qeth_sbp_set_primary {
|
||||
struct net_if_token token;
|
||||
} __packed;
|
||||
|
||||
struct qeth_sbp_set_secondary {
|
||||
} __packed;
|
||||
|
||||
struct qeth_sbp_port_entry {
|
||||
__u8 role;
|
||||
__u8 state;
|
||||
__u8 reserved1;
|
||||
__u8 reserved2;
|
||||
struct net_if_token token;
|
||||
} __packed;
|
||||
|
||||
struct qeth_sbp_query_ports {
|
||||
__u8 primary_bp_supported;
|
||||
__u8 secondary_bp_supported;
|
||||
__u8 num_entries;
|
||||
__u8 entry_length;
|
||||
struct qeth_sbp_port_entry entry[];
|
||||
} __packed;
|
||||
|
||||
struct qeth_sbp_state_change {
|
||||
__u8 primary_bp_supported;
|
||||
__u8 secondary_bp_supported;
|
||||
__u8 num_entries;
|
||||
__u8 entry_length;
|
||||
struct qeth_sbp_port_entry entry[];
|
||||
} __packed;
|
||||
|
||||
struct qeth_ipacmd_setbridgeport {
|
||||
struct qeth_ipacmd_sbp_hdr hdr;
|
||||
union {
|
||||
struct qeth_sbp_query_cmds_supp query_cmds_supp;
|
||||
struct qeth_sbp_reset_role reset_role;
|
||||
struct qeth_sbp_set_primary set_primary;
|
||||
struct qeth_sbp_set_secondary set_secondary;
|
||||
struct qeth_sbp_query_ports query_ports;
|
||||
struct qeth_sbp_state_change state_change;
|
||||
} data;
|
||||
} __packed;
|
||||
|
||||
/* Header for each IPA command */
|
||||
struct qeth_ipacmd_hdr {
|
||||
__u8 command;
|
||||
|
@ -529,6 +612,7 @@ struct qeth_ipa_cmd {
|
|||
struct qeth_ipacmd_setadpparms setadapterparms;
|
||||
struct qeth_set_routing setrtg;
|
||||
struct qeth_ipacmd_diagass diagass;
|
||||
struct qeth_ipacmd_setbridgeport sbp;
|
||||
} data;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
|
15
drivers/s390/net/qeth_l2.h
Normal file
15
drivers/s390/net/qeth_l2.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* Copyright IBM Corp. 2013
|
||||
* Author(s): Eugene Crosser <eugene.crosser@ru.ibm.com>
|
||||
*/
|
||||
|
||||
#ifndef __QETH_L2_H__
|
||||
#define __QETH_L2_H__
|
||||
|
||||
#include "qeth_core.h"
|
||||
|
||||
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);
|
||||
|
||||
#endif /* __QETH_L2_H__ */
|
|
@ -21,6 +21,7 @@
|
|||
#include <linux/list.h>
|
||||
|
||||
#include "qeth_core.h"
|
||||
#include "qeth_l2.h"
|
||||
|
||||
static int qeth_l2_set_offline(struct ccwgroup_device *);
|
||||
static int qeth_l2_stop(struct net_device *);
|
||||
|
@ -880,6 +881,7 @@ static int qeth_l2_probe_device(struct ccwgroup_device *gdev)
|
|||
{
|
||||
struct qeth_card *card = dev_get_drvdata(&gdev->dev);
|
||||
|
||||
qeth_l2_create_device_attributes(&gdev->dev);
|
||||
INIT_LIST_HEAD(&card->vid_list);
|
||||
INIT_LIST_HEAD(&card->mc_list);
|
||||
card->options.layer2 = 1;
|
||||
|
@ -891,6 +893,7 @@ static void qeth_l2_remove_device(struct ccwgroup_device *cgdev)
|
|||
{
|
||||
struct qeth_card *card = dev_get_drvdata(&cgdev->dev);
|
||||
|
||||
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);
|
||||
|
||||
|
@ -1003,6 +1006,8 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode)
|
|||
} else
|
||||
card->info.hwtrap = 0;
|
||||
|
||||
qeth_l2_setup_bridgeport_attrs(card);
|
||||
|
||||
card->state = CARD_STATE_HARDSETUP;
|
||||
memset(&card->rx, 0, sizeof(struct qeth_rx));
|
||||
qeth_print_status_message(card);
|
||||
|
@ -1347,6 +1352,365 @@ void qeth_osn_deregister(struct net_device *dev)
|
|||
}
|
||||
EXPORT_SYMBOL(qeth_osn_deregister);
|
||||
|
||||
/* SETBRIDGEPORT support, async notifications */
|
||||
|
||||
struct qeth_bridge_state_data {
|
||||
struct work_struct worker;
|
||||
struct qeth_card *card;
|
||||
struct qeth_sbp_state_change qports;
|
||||
};
|
||||
|
||||
static void qeth_bridge_state_change_worker(struct work_struct *work)
|
||||
{
|
||||
struct qeth_bridge_state_data *data =
|
||||
container_of(work, struct qeth_bridge_state_data, worker);
|
||||
/* We are only interested in the first entry - local port */
|
||||
struct qeth_sbp_port_entry *entry = &data->qports.entry[0];
|
||||
char env_locrem[32];
|
||||
char env_role[32];
|
||||
char env_state[32];
|
||||
char *env[] = {
|
||||
env_locrem,
|
||||
env_role,
|
||||
env_state,
|
||||
NULL
|
||||
};
|
||||
|
||||
/* Role should not change by itself, but if it did, */
|
||||
/* information from the hardware is authoritative. */
|
||||
mutex_lock(&data->card->conf_mutex);
|
||||
data->card->options.sbp.role = entry->role;
|
||||
mutex_unlock(&data->card->conf_mutex);
|
||||
|
||||
snprintf(env_locrem, sizeof(env_locrem), "BRIDGEPORT=statechange");
|
||||
snprintf(env_role, sizeof(env_role), "ROLE=%s",
|
||||
(entry->role == QETH_SBP_ROLE_NONE) ? "none" :
|
||||
(entry->role == QETH_SBP_ROLE_PRIMARY) ? "primary" :
|
||||
(entry->role == QETH_SBP_ROLE_SECONDARY) ? "secondary" :
|
||||
"<INVALID>");
|
||||
snprintf(env_state, sizeof(env_state), "STATE=%s",
|
||||
(entry->state == QETH_SBP_STATE_INACTIVE) ? "inactive" :
|
||||
(entry->state == QETH_SBP_STATE_STANDBY) ? "standby" :
|
||||
(entry->state == QETH_SBP_STATE_ACTIVE) ? "active" :
|
||||
"<INVALID>");
|
||||
kobject_uevent_env(&data->card->gdev->dev.kobj,
|
||||
KOBJ_CHANGE, env);
|
||||
kfree(data);
|
||||
}
|
||||
|
||||
void qeth_bridge_state_change(struct qeth_card *card, struct qeth_ipa_cmd *cmd)
|
||||
{
|
||||
struct qeth_sbp_state_change *qports =
|
||||
&cmd->data.sbp.data.state_change;
|
||||
struct qeth_bridge_state_data *data;
|
||||
int extrasize;
|
||||
|
||||
QETH_CARD_TEXT(card, 2, "brstchng");
|
||||
if (qports->entry_length != sizeof(struct qeth_sbp_port_entry)) {
|
||||
QETH_CARD_TEXT_(card, 2, "BPsz%.8d", qports->entry_length);
|
||||
return;
|
||||
}
|
||||
extrasize = sizeof(struct qeth_sbp_port_entry) * qports->num_entries;
|
||||
data = kzalloc(sizeof(struct qeth_bridge_state_data) + extrasize,
|
||||
GFP_ATOMIC);
|
||||
if (!data) {
|
||||
QETH_CARD_TEXT(card, 2, "BPSalloc");
|
||||
return;
|
||||
}
|
||||
INIT_WORK(&data->worker, qeth_bridge_state_change_worker);
|
||||
data->card = card;
|
||||
memcpy(&data->qports, qports,
|
||||
sizeof(struct qeth_sbp_state_change) + extrasize);
|
||||
queue_work(qeth_wq, &data->worker);
|
||||
}
|
||||
EXPORT_SYMBOL(qeth_bridge_state_change);
|
||||
|
||||
/* SETBRIDGEPORT support; sending commands */
|
||||
|
||||
struct _qeth_sbp_cbctl {
|
||||
u16 ipa_rc;
|
||||
u16 cmd_rc;
|
||||
union {
|
||||
u32 supported;
|
||||
struct {
|
||||
enum qeth_sbp_roles *role;
|
||||
enum qeth_sbp_states *state;
|
||||
} qports;
|
||||
} data;
|
||||
};
|
||||
|
||||
/**
|
||||
* qeth_bridgeport_makerc() - derive "traditional" error from hardware codes.
|
||||
* @card: qeth_card structure pointer, for debug messages.
|
||||
* @cbctl: state structure with hardware return codes.
|
||||
* @setcmd: IPA command code
|
||||
*
|
||||
* Returns negative errno-compatible error indication or 0 on success.
|
||||
*/
|
||||
static int qeth_bridgeport_makerc(struct qeth_card *card,
|
||||
struct _qeth_sbp_cbctl *cbctl, enum qeth_ipa_sbp_cmd setcmd)
|
||||
{
|
||||
int rc;
|
||||
|
||||
switch (cbctl->ipa_rc) {
|
||||
case IPA_RC_SUCCESS:
|
||||
switch (cbctl->cmd_rc) {
|
||||
case 0x0000:
|
||||
rc = 0;
|
||||
break;
|
||||
case 0x0004:
|
||||
rc = -ENOSYS;
|
||||
break;
|
||||
case 0x000C: /* Not configured as bridge Port */
|
||||
rc = -ENODEV; /* maybe not the best code here? */
|
||||
dev_err(&card->gdev->dev,
|
||||
"The HiperSockets device is not configured as a Bridge Port\n");
|
||||
break;
|
||||
case 0x0014: /* Another device is Primary */
|
||||
switch (setcmd) {
|
||||
case IPA_SBP_SET_PRIMARY_BRIDGE_PORT:
|
||||
rc = -EEXIST;
|
||||
dev_err(&card->gdev->dev,
|
||||
"The HiperSockets LAN already has a primary Bridge Port\n");
|
||||
break;
|
||||
case IPA_SBP_SET_SECONDARY_BRIDGE_PORT:
|
||||
rc = -EBUSY;
|
||||
dev_err(&card->gdev->dev,
|
||||
"The HiperSockets device is already a primary Bridge Port\n");
|
||||
break;
|
||||
default:
|
||||
rc = -EIO;
|
||||
}
|
||||
break;
|
||||
case 0x0018: /* This device is currently Secondary */
|
||||
rc = -EBUSY;
|
||||
dev_err(&card->gdev->dev,
|
||||
"The HiperSockets device is already a secondary Bridge Port\n");
|
||||
break;
|
||||
case 0x001C: /* Limit for Secondary devices reached */
|
||||
rc = -EEXIST;
|
||||
dev_err(&card->gdev->dev,
|
||||
"The HiperSockets LAN cannot have more secondary Bridge Ports\n");
|
||||
break;
|
||||
case 0x0024: /* This device is currently Primary */
|
||||
rc = -EBUSY;
|
||||
dev_err(&card->gdev->dev,
|
||||
"The HiperSockets device is already a primary Bridge Port\n");
|
||||
break;
|
||||
case 0x0020: /* Not authorized by zManager */
|
||||
rc = -EACCES;
|
||||
dev_err(&card->gdev->dev,
|
||||
"The HiperSockets device is not authorized to be a Bridge Port\n");
|
||||
break;
|
||||
default:
|
||||
rc = -EIO;
|
||||
}
|
||||
break;
|
||||
case IPA_RC_NOTSUPP:
|
||||
rc = -ENOSYS;
|
||||
break;
|
||||
case IPA_RC_UNSUPPORTED_COMMAND:
|
||||
rc = -ENOSYS;
|
||||
break;
|
||||
default:
|
||||
rc = -EIO;
|
||||
}
|
||||
if (rc) {
|
||||
QETH_CARD_TEXT_(card, 2, "SBPi%04x", cbctl->ipa_rc);
|
||||
QETH_CARD_TEXT_(card, 2, "SBPc%04x", cbctl->cmd_rc);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int qeth_bridgeport_query_support_cb(struct qeth_card *card,
|
||||
struct qeth_reply *reply, unsigned long data)
|
||||
{
|
||||
struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
|
||||
struct _qeth_sbp_cbctl *cbctl = (struct _qeth_sbp_cbctl *)reply->param;
|
||||
QETH_CARD_TEXT(card, 2, "brqsupcb");
|
||||
cbctl->ipa_rc = cmd->hdr.return_code;
|
||||
cbctl->cmd_rc = cmd->data.sbp.hdr.return_code;
|
||||
if ((cbctl->ipa_rc == 0) && (cbctl->cmd_rc == 0)) {
|
||||
cbctl->data.supported =
|
||||
cmd->data.sbp.data.query_cmds_supp.supported_cmds;
|
||||
} else {
|
||||
cbctl->data.supported = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* qeth_bridgeport_query_support() - store bitmask of supported subfunctions.
|
||||
* @card: qeth_card structure pointer.
|
||||
*
|
||||
* Sets bitmask of supported setbridgeport subfunctions in the qeth_card
|
||||
* strucutre: card->options.sbp.supported_funcs.
|
||||
*/
|
||||
void qeth_bridgeport_query_support(struct qeth_card *card)
|
||||
{
|
||||
struct qeth_cmd_buffer *iob;
|
||||
struct qeth_ipa_cmd *cmd;
|
||||
struct _qeth_sbp_cbctl cbctl;
|
||||
|
||||
QETH_CARD_TEXT(card, 2, "brqsuppo");
|
||||
iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETBRIDGEPORT, 0);
|
||||
cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
|
||||
cmd->data.sbp.hdr.cmdlength =
|
||||
sizeof(struct qeth_ipacmd_sbp_hdr) +
|
||||
sizeof(struct qeth_sbp_query_cmds_supp);
|
||||
cmd->data.sbp.hdr.command_code =
|
||||
IPA_SBP_QUERY_COMMANDS_SUPPORTED;
|
||||
cmd->data.sbp.hdr.used_total = 1;
|
||||
cmd->data.sbp.hdr.seq_no = 1;
|
||||
if (qeth_send_ipa_cmd(card, iob, qeth_bridgeport_query_support_cb,
|
||||
(void *)&cbctl) ||
|
||||
qeth_bridgeport_makerc(card, &cbctl,
|
||||
IPA_SBP_QUERY_COMMANDS_SUPPORTED)) {
|
||||
/* non-zero makerc signifies failure, and produce messages */
|
||||
card->options.sbp.role = QETH_SBP_ROLE_NONE;
|
||||
return;
|
||||
}
|
||||
card->options.sbp.supported_funcs = cbctl.data.supported;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qeth_bridgeport_query_support);
|
||||
|
||||
static int qeth_bridgeport_query_ports_cb(struct qeth_card *card,
|
||||
struct qeth_reply *reply, unsigned long data)
|
||||
{
|
||||
struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
|
||||
struct qeth_sbp_query_ports *qports = &cmd->data.sbp.data.query_ports;
|
||||
struct _qeth_sbp_cbctl *cbctl = (struct _qeth_sbp_cbctl *)reply->param;
|
||||
|
||||
QETH_CARD_TEXT(card, 2, "brqprtcb");
|
||||
cbctl->ipa_rc = cmd->hdr.return_code;
|
||||
cbctl->cmd_rc = cmd->data.sbp.hdr.return_code;
|
||||
if ((cbctl->ipa_rc != 0) || (cbctl->cmd_rc != 0))
|
||||
return 0;
|
||||
if (qports->entry_length != sizeof(struct qeth_sbp_port_entry)) {
|
||||
cbctl->cmd_rc = 0xffff;
|
||||
QETH_CARD_TEXT_(card, 2, "SBPs%04x", qports->entry_length);
|
||||
return 0;
|
||||
}
|
||||
/* first entry contains the state of the local port */
|
||||
if (qports->num_entries > 0) {
|
||||
if (cbctl->data.qports.role)
|
||||
*cbctl->data.qports.role = qports->entry[0].role;
|
||||
if (cbctl->data.qports.state)
|
||||
*cbctl->data.qports.state = qports->entry[0].state;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* qeth_bridgeport_query_ports() - query local bridgeport status.
|
||||
* @card: qeth_card structure pointer.
|
||||
* @role: Role of the port: 0-none, 1-primary, 2-secondary.
|
||||
* @state: State of the port: 0-inactive, 1-standby, 2-active.
|
||||
*
|
||||
* Returns negative errno-compatible error indication or 0 on success.
|
||||
*
|
||||
* 'role' and 'state' are not updated in case of hardware operation failure.
|
||||
*/
|
||||
int qeth_bridgeport_query_ports(struct qeth_card *card,
|
||||
enum qeth_sbp_roles *role, enum qeth_sbp_states *state)
|
||||
{
|
||||
int rc = 0;
|
||||
struct qeth_cmd_buffer *iob;
|
||||
struct qeth_ipa_cmd *cmd;
|
||||
struct _qeth_sbp_cbctl cbctl = {
|
||||
.data = {
|
||||
.qports = {
|
||||
.role = role,
|
||||
.state = state,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
QETH_CARD_TEXT(card, 2, "brqports");
|
||||
if (!(card->options.sbp.supported_funcs & IPA_SBP_QUERY_BRIDGE_PORTS))
|
||||
return -EOPNOTSUPP;
|
||||
iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETBRIDGEPORT, 0);
|
||||
cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
|
||||
cmd->data.sbp.hdr.cmdlength =
|
||||
sizeof(struct qeth_ipacmd_sbp_hdr);
|
||||
cmd->data.sbp.hdr.command_code =
|
||||
IPA_SBP_QUERY_BRIDGE_PORTS;
|
||||
cmd->data.sbp.hdr.used_total = 1;
|
||||
cmd->data.sbp.hdr.seq_no = 1;
|
||||
rc = qeth_send_ipa_cmd(card, iob, qeth_bridgeport_query_ports_cb,
|
||||
(void *)&cbctl);
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = qeth_bridgeport_makerc(card, &cbctl, IPA_SBP_QUERY_BRIDGE_PORTS);
|
||||
if (rc)
|
||||
return rc;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qeth_bridgeport_query_ports);
|
||||
|
||||
static int qeth_bridgeport_set_cb(struct qeth_card *card,
|
||||
struct qeth_reply *reply, unsigned long data)
|
||||
{
|
||||
struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *)data;
|
||||
struct _qeth_sbp_cbctl *cbctl = (struct _qeth_sbp_cbctl *)reply->param;
|
||||
QETH_CARD_TEXT(card, 2, "brsetrcb");
|
||||
cbctl->ipa_rc = cmd->hdr.return_code;
|
||||
cbctl->cmd_rc = cmd->data.sbp.hdr.return_code;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* qeth_bridgeport_setrole() - Assign primary role to the port.
|
||||
* @card: qeth_card structure pointer.
|
||||
* @role: Role to assign.
|
||||
*
|
||||
* Returns negative errno-compatible error indication or 0 on success.
|
||||
*/
|
||||
int qeth_bridgeport_setrole(struct qeth_card *card, enum qeth_sbp_roles role)
|
||||
{
|
||||
int rc = 0;
|
||||
int cmdlength;
|
||||
struct qeth_cmd_buffer *iob;
|
||||
struct qeth_ipa_cmd *cmd;
|
||||
struct _qeth_sbp_cbctl cbctl;
|
||||
enum qeth_ipa_sbp_cmd setcmd;
|
||||
|
||||
QETH_CARD_TEXT(card, 2, "brsetrol");
|
||||
switch (role) {
|
||||
case QETH_SBP_ROLE_NONE:
|
||||
setcmd = IPA_SBP_RESET_BRIDGE_PORT_ROLE;
|
||||
cmdlength = sizeof(struct qeth_ipacmd_sbp_hdr) +
|
||||
sizeof(struct qeth_sbp_reset_role);
|
||||
break;
|
||||
case QETH_SBP_ROLE_PRIMARY:
|
||||
setcmd = IPA_SBP_SET_PRIMARY_BRIDGE_PORT;
|
||||
cmdlength = sizeof(struct qeth_ipacmd_sbp_hdr) +
|
||||
sizeof(struct qeth_sbp_set_primary);
|
||||
break;
|
||||
case QETH_SBP_ROLE_SECONDARY:
|
||||
setcmd = IPA_SBP_SET_SECONDARY_BRIDGE_PORT;
|
||||
cmdlength = sizeof(struct qeth_ipacmd_sbp_hdr) +
|
||||
sizeof(struct qeth_sbp_set_secondary);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!(card->options.sbp.supported_funcs & setcmd))
|
||||
return -EOPNOTSUPP;
|
||||
iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETBRIDGEPORT, 0);
|
||||
cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
|
||||
cmd->data.sbp.hdr.cmdlength = cmdlength;
|
||||
cmd->data.sbp.hdr.command_code = setcmd;
|
||||
cmd->data.sbp.hdr.used_total = 1;
|
||||
cmd->data.sbp.hdr.seq_no = 1;
|
||||
rc = qeth_send_ipa_cmd(card, iob, qeth_bridgeport_set_cb,
|
||||
(void *)&cbctl);
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = qeth_bridgeport_makerc(card, &cbctl, setcmd);
|
||||
return rc;
|
||||
}
|
||||
|
||||
module_init(qeth_l2_init);
|
||||
module_exit(qeth_l2_exit);
|
||||
MODULE_AUTHOR("Frank Blaschka <frank.blaschka@de.ibm.com>");
|
||||
|
|
161
drivers/s390/net/qeth_l2_sys.c
Normal file
161
drivers/s390/net/qeth_l2_sys.c
Normal file
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* Copyright IBM Corp. 2013
|
||||
* Author(s): Eugene Crosser <eugene.crosser@ru.ibm.com>
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <asm/ebcdic.h>
|
||||
#include "qeth_l2.h"
|
||||
|
||||
#define QETH_DEVICE_ATTR(_id, _name, _mode, _show, _store) \
|
||||
struct device_attribute dev_attr_##_id = __ATTR(_name, _mode, _show, _store)
|
||||
|
||||
static int qeth_card_hw_is_reachable(struct qeth_card *card)
|
||||
{
|
||||
return (card->state == CARD_STATE_SOFTSETUP) ||
|
||||
(card->state == CARD_STATE_UP);
|
||||
}
|
||||
|
||||
static ssize_t qeth_bridge_port_role_state_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf,
|
||||
int show_state)
|
||||
{
|
||||
struct qeth_card *card = dev_get_drvdata(dev);
|
||||
enum qeth_sbp_states state = QETH_SBP_STATE_INACTIVE;
|
||||
int rc = 0;
|
||||
char *word;
|
||||
|
||||
if (!card)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&card->conf_mutex);
|
||||
|
||||
if (qeth_card_hw_is_reachable(card) &&
|
||||
card->options.sbp.supported_funcs)
|
||||
rc = qeth_bridgeport_query_ports(card,
|
||||
&card->options.sbp.role, &state);
|
||||
if (!rc) {
|
||||
if (show_state)
|
||||
switch (state) {
|
||||
case QETH_SBP_STATE_INACTIVE:
|
||||
word = "inactive"; break;
|
||||
case QETH_SBP_STATE_STANDBY:
|
||||
word = "standby"; break;
|
||||
case QETH_SBP_STATE_ACTIVE:
|
||||
word = "active"; break;
|
||||
default:
|
||||
rc = -EIO;
|
||||
}
|
||||
else
|
||||
switch (card->options.sbp.role) {
|
||||
case QETH_SBP_ROLE_NONE:
|
||||
word = "none"; break;
|
||||
case QETH_SBP_ROLE_PRIMARY:
|
||||
word = "primary"; break;
|
||||
case QETH_SBP_ROLE_SECONDARY:
|
||||
word = "secondary"; break;
|
||||
default:
|
||||
rc = -EIO;
|
||||
}
|
||||
if (rc)
|
||||
QETH_CARD_TEXT_(card, 2, "SBP%02x:%02x",
|
||||
card->options.sbp.role, state);
|
||||
else
|
||||
rc = sprintf(buf, "%s\n", word);
|
||||
}
|
||||
|
||||
mutex_unlock(&card->conf_mutex);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static ssize_t qeth_bridge_port_role_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return qeth_bridge_port_role_state_show(dev, attr, buf, 0);
|
||||
}
|
||||
|
||||
static ssize_t qeth_bridge_port_role_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct qeth_card *card = dev_get_drvdata(dev);
|
||||
int rc = 0;
|
||||
enum qeth_sbp_roles role;
|
||||
|
||||
if (!card)
|
||||
return -EINVAL;
|
||||
if (sysfs_streq(buf, "primary"))
|
||||
role = QETH_SBP_ROLE_PRIMARY;
|
||||
else if (sysfs_streq(buf, "secondary"))
|
||||
role = QETH_SBP_ROLE_SECONDARY;
|
||||
else if (sysfs_streq(buf, "none"))
|
||||
role = QETH_SBP_ROLE_NONE;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&card->conf_mutex);
|
||||
|
||||
if (qeth_card_hw_is_reachable(card)) {
|
||||
rc = qeth_bridgeport_setrole(card, role);
|
||||
if (!rc)
|
||||
card->options.sbp.role = role;
|
||||
} else
|
||||
card->options.sbp.role = role;
|
||||
|
||||
mutex_unlock(&card->conf_mutex);
|
||||
|
||||
return rc ? rc : count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(bridge_role, 0644, qeth_bridge_port_role_show,
|
||||
qeth_bridge_port_role_store);
|
||||
|
||||
static ssize_t qeth_bridge_port_state_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return qeth_bridge_port_role_state_show(dev, attr, buf, 1);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(bridge_state, 0644, qeth_bridge_port_state_show,
|
||||
NULL);
|
||||
|
||||
static struct attribute *qeth_l2_bridgeport_attrs[] = {
|
||||
&dev_attr_bridge_role.attr,
|
||||
&dev_attr_bridge_state.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group qeth_l2_bridgeport_attr_group = {
|
||||
.attrs = qeth_l2_bridgeport_attrs,
|
||||
};
|
||||
|
||||
int qeth_l2_create_device_attributes(struct device *dev)
|
||||
{
|
||||
return sysfs_create_group(&dev->kobj, &qeth_l2_bridgeport_attr_group);
|
||||
}
|
||||
|
||||
void qeth_l2_remove_device_attributes(struct device *dev)
|
||||
{
|
||||
sysfs_remove_group(&dev->kobj, &qeth_l2_bridgeport_attr_group);
|
||||
}
|
||||
|
||||
/**
|
||||
* qeth_l2_setup_bridgeport_attrs() - set/restore attrs when turning online.
|
||||
* @card: qeth_card structure pointer
|
||||
*
|
||||
* Note: this function is called with conf_mutex held by the caller
|
||||
*/
|
||||
void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card)
|
||||
{
|
||||
if (!card)
|
||||
return;
|
||||
if (!card->options.sbp.supported_funcs)
|
||||
return;
|
||||
if (card->options.sbp.role != QETH_SBP_ROLE_NONE) {
|
||||
/* Conditional to avoid spurious error messages */
|
||||
qeth_bridgeport_setrole(card, card->options.sbp.role);
|
||||
/* Let the callback function refresh the stored role value. */
|
||||
qeth_bridgeport_query_ports(card,
|
||||
&card->options.sbp.role, NULL);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue