mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-08 14:13:53 +00:00
Merge branch 's390-qeth-next'
Julian Wiedmann says: ==================== s390/qeth: updates 2020-09-10 subject to positive review by the bridge maintainers on patch 5, please apply the following patch series to netdev's net-next tree. Alexandra adds BR_LEARNING_SYNC support to qeth. In addition to the main qeth changes (controlling the feature, and raising switchdev events), this also needs - Patch 1 and 2 for some s390/cio infrastructure improvements (acked by Heiko to go in via net-next), and - Patch 5 to introduce a new switchdev_notifier_type, so that a driver can clear all previously learned entries from the bridge FDB in case things go out-of-sync later on. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
0f9ad4e759
@ -238,7 +238,10 @@ extern void ccw_device_get_schid(struct ccw_device *, struct subchannel_id *);
|
||||
struct channel_path_desc_fmt0 *ccw_device_get_chp_desc(struct ccw_device *, int);
|
||||
u8 *ccw_device_get_util_str(struct ccw_device *cdev, int chp_idx);
|
||||
int ccw_device_pnso(struct ccw_device *cdev,
|
||||
struct chsc_pnso_area *pnso_area,
|
||||
struct chsc_pnso_resume_token resume_token,
|
||||
int cnc);
|
||||
struct chsc_pnso_area *pnso_area, u8 oc,
|
||||
struct chsc_pnso_resume_token resume_token, int cnc);
|
||||
int ccw_device_get_cssid(struct ccw_device *cdev, u8 *cssid);
|
||||
int ccw_device_get_iid(struct ccw_device *cdev, u8 *iid);
|
||||
int ccw_device_get_chpid(struct ccw_device *cdev, int chp_idx, u8 *chpid);
|
||||
int ccw_device_get_chid(struct ccw_device *cdev, int chp_idx, u16 *chid);
|
||||
#endif /* _S390_CCWDEV_H_ */
|
||||
|
@ -11,6 +11,13 @@
|
||||
|
||||
#include <uapi/asm/chsc.h>
|
||||
|
||||
/**
|
||||
* Operation codes for CHSC PNSO:
|
||||
* PNSO_OC_NET_BRIDGE_INFO - only addresses that are visible to a bridgeport
|
||||
* PNSO_OC_NET_ADDR_INFO - all addresses
|
||||
*/
|
||||
#define PNSO_OC_NET_BRIDGE_INFO 0
|
||||
#define PNSO_OC_NET_ADDR_INFO 3
|
||||
/**
|
||||
* struct chsc_pnso_naid_l2 - network address information descriptor
|
||||
* @nit: Network interface token
|
||||
|
@ -36,7 +36,9 @@ struct css_general_char {
|
||||
u64 alt_ssi : 1; /* bit 108 */
|
||||
u64 : 1;
|
||||
u64 narf : 1; /* bit 110 */
|
||||
u64 : 12;
|
||||
u64 : 5;
|
||||
u64 enarf: 1; /* bit 116 */
|
||||
u64 : 6;
|
||||
u64 util_str : 1;/* bit 123 */
|
||||
} __packed;
|
||||
|
||||
|
@ -65,6 +65,8 @@ int chsc_error_from_response(int response)
|
||||
case 0x0100:
|
||||
case 0x0102:
|
||||
return -ENOMEM;
|
||||
case 0x0108: /* "HW limit exceeded" for the op 0x003d */
|
||||
return -EUSERS;
|
||||
default:
|
||||
return -EIO;
|
||||
}
|
||||
@ -1114,7 +1116,7 @@ int chsc_enable_facility(int operation_code)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int __init chsc_get_cssid(int idx)
|
||||
int __init chsc_get_cssid_iid(int idx, u8 *cssid, u8 *iid)
|
||||
{
|
||||
struct {
|
||||
struct chsc_header request;
|
||||
@ -1125,7 +1127,8 @@ int __init chsc_get_cssid(int idx)
|
||||
u32 reserved2[3];
|
||||
struct {
|
||||
u8 cssid;
|
||||
u32 : 24;
|
||||
u8 iid;
|
||||
u32 : 16;
|
||||
} list[0];
|
||||
} *sdcal_area;
|
||||
int ret;
|
||||
@ -1151,8 +1154,10 @@ int __init chsc_get_cssid(int idx)
|
||||
}
|
||||
|
||||
if ((addr_t) &sdcal_area->list[idx] <
|
||||
(addr_t) &sdcal_area->response + sdcal_area->response.length)
|
||||
ret = sdcal_area->list[idx].cssid;
|
||||
(addr_t) &sdcal_area->response + sdcal_area->response.length) {
|
||||
*cssid = sdcal_area->list[idx].cssid;
|
||||
*iid = sdcal_area->list[idx].iid;
|
||||
}
|
||||
else
|
||||
ret = -ENODEV;
|
||||
exit:
|
||||
@ -1340,6 +1345,7 @@ EXPORT_SYMBOL_GPL(chsc_scm_info);
|
||||
* chsc_pnso() - Perform Network-Subchannel Operation
|
||||
* @schid: id of the subchannel on which PNSO is performed
|
||||
* @pnso_area: request and response block for the operation
|
||||
* @oc: Operation Code
|
||||
* @resume_token: resume token for multiblock response
|
||||
* @cnc: Boolean change-notification control
|
||||
*
|
||||
@ -1347,10 +1353,8 @@ EXPORT_SYMBOL_GPL(chsc_scm_info);
|
||||
*
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
int chsc_pnso(struct subchannel_id schid,
|
||||
struct chsc_pnso_area *pnso_area,
|
||||
struct chsc_pnso_resume_token resume_token,
|
||||
int cnc)
|
||||
int chsc_pnso(struct subchannel_id schid, struct chsc_pnso_area *pnso_area,
|
||||
u8 oc, struct chsc_pnso_resume_token resume_token, int cnc)
|
||||
{
|
||||
memset(pnso_area, 0, sizeof(*pnso_area));
|
||||
pnso_area->request.length = 0x0030;
|
||||
@ -1359,7 +1363,7 @@ int chsc_pnso(struct subchannel_id schid,
|
||||
pnso_area->ssid = schid.ssid;
|
||||
pnso_area->sch = schid.sch_no;
|
||||
pnso_area->cssid = schid.cssid;
|
||||
pnso_area->oc = 0; /* Store-network-bridging-information list */
|
||||
pnso_area->oc = oc;
|
||||
pnso_area->resume_token = resume_token;
|
||||
pnso_area->n = (cnc != 0);
|
||||
if (chsc(pnso_area))
|
||||
|
@ -205,12 +205,10 @@ struct chsc_scm_info {
|
||||
|
||||
int chsc_scm_info(struct chsc_scm_info *scm_area, u64 token);
|
||||
|
||||
int chsc_pnso(struct subchannel_id schid,
|
||||
struct chsc_pnso_area *pnso_area,
|
||||
struct chsc_pnso_resume_token resume_token,
|
||||
int cnc);
|
||||
int chsc_pnso(struct subchannel_id schid, struct chsc_pnso_area *pnso_area,
|
||||
u8 oc, struct chsc_pnso_resume_token resume_token, int cnc);
|
||||
|
||||
int __init chsc_get_cssid(int idx);
|
||||
int __init chsc_get_cssid_iid(int idx, u8 *cssid, u8 *iid);
|
||||
|
||||
#ifdef CONFIG_SCM_BUS
|
||||
int scm_update_information(void);
|
||||
|
@ -854,7 +854,7 @@ css_generate_pgid(struct channel_subsystem *css, u32 tod_high)
|
||||
if (css_general_characteristics.mcss) {
|
||||
css->global_pgid.pgid_high.ext_cssid.version = 0x80;
|
||||
css->global_pgid.pgid_high.ext_cssid.cssid =
|
||||
(css->cssid < 0) ? 0 : css->cssid;
|
||||
css->id_valid ? css->cssid : 0;
|
||||
} else {
|
||||
css->global_pgid.pgid_high.cpu_addr = stap();
|
||||
}
|
||||
@ -877,7 +877,7 @@ static ssize_t real_cssid_show(struct device *dev, struct device_attribute *a,
|
||||
{
|
||||
struct channel_subsystem *css = to_css(dev);
|
||||
|
||||
if (css->cssid < 0)
|
||||
if (!css->id_valid)
|
||||
return -EINVAL;
|
||||
|
||||
return sprintf(buf, "%x\n", css->cssid);
|
||||
@ -975,7 +975,12 @@ static int __init setup_css(int nr)
|
||||
css->device.dma_mask = &css->device.coherent_dma_mask;
|
||||
|
||||
mutex_init(&css->mutex);
|
||||
css->cssid = chsc_get_cssid(nr);
|
||||
ret = chsc_get_cssid_iid(nr, &css->cssid, &css->iid);
|
||||
if (!ret) {
|
||||
css->id_valid = true;
|
||||
pr_info("Partition identifier %01x.%01x\n", css->cssid,
|
||||
css->iid);
|
||||
}
|
||||
css_generate_pgid(css, (u32) (get_tod_clock() >> 32));
|
||||
|
||||
ret = device_register(&css->device);
|
||||
|
@ -115,7 +115,9 @@ extern int for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *);
|
||||
void css_update_ssd_info(struct subchannel *sch);
|
||||
|
||||
struct channel_subsystem {
|
||||
int cssid;
|
||||
u8 cssid;
|
||||
u8 iid;
|
||||
bool id_valid; /* cssid,iid */
|
||||
struct channel_path *chps[__MAX_CHPID + 1];
|
||||
struct device device;
|
||||
struct pgid global_pgid;
|
||||
|
@ -714,6 +714,7 @@ EXPORT_SYMBOL_GPL(ccw_device_get_schid);
|
||||
* ccw_device_pnso() - Perform Network-Subchannel Operation
|
||||
* @cdev: device on which PNSO is performed
|
||||
* @pnso_area: request and response block for the operation
|
||||
* @oc: Operation Code
|
||||
* @resume_token: resume token for multiblock response
|
||||
* @cnc: Boolean change-notification control
|
||||
*
|
||||
@ -722,17 +723,101 @@ EXPORT_SYMBOL_GPL(ccw_device_get_schid);
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
int ccw_device_pnso(struct ccw_device *cdev,
|
||||
struct chsc_pnso_area *pnso_area,
|
||||
struct chsc_pnso_resume_token resume_token,
|
||||
int cnc)
|
||||
struct chsc_pnso_area *pnso_area, u8 oc,
|
||||
struct chsc_pnso_resume_token resume_token, int cnc)
|
||||
{
|
||||
struct subchannel_id schid;
|
||||
|
||||
ccw_device_get_schid(cdev, &schid);
|
||||
return chsc_pnso(schid, pnso_area, resume_token, cnc);
|
||||
return chsc_pnso(schid, pnso_area, oc, resume_token, cnc);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ccw_device_pnso);
|
||||
|
||||
/**
|
||||
* ccw_device_get_cssid() - obtain Channel Subsystem ID
|
||||
* @cdev: device to obtain the CSSID for
|
||||
* @cssid: The resulting Channel Subsystem ID
|
||||
*/
|
||||
int ccw_device_get_cssid(struct ccw_device *cdev, u8 *cssid)
|
||||
{
|
||||
struct device *sch_dev = cdev->dev.parent;
|
||||
struct channel_subsystem *css = to_css(sch_dev->parent);
|
||||
|
||||
if (css->id_valid)
|
||||
*cssid = css->cssid;
|
||||
return css->id_valid ? 0 : -ENODEV;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ccw_device_get_cssid);
|
||||
|
||||
/**
|
||||
* ccw_device_get_iid() - obtain MIF-image ID
|
||||
* @cdev: device to obtain the MIF-image ID for
|
||||
* @iid: The resulting MIF-image ID
|
||||
*/
|
||||
int ccw_device_get_iid(struct ccw_device *cdev, u8 *iid)
|
||||
{
|
||||
struct device *sch_dev = cdev->dev.parent;
|
||||
struct channel_subsystem *css = to_css(sch_dev->parent);
|
||||
|
||||
if (css->id_valid)
|
||||
*iid = css->iid;
|
||||
return css->id_valid ? 0 : -ENODEV;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ccw_device_get_iid);
|
||||
|
||||
/**
|
||||
* ccw_device_get_chpid() - obtain Channel Path ID
|
||||
* @cdev: device to obtain the Channel Path ID for
|
||||
* @chp_idx: Index of the channel path
|
||||
* @chpid: The resulting Channel Path ID
|
||||
*/
|
||||
int ccw_device_get_chpid(struct ccw_device *cdev, int chp_idx, u8 *chpid)
|
||||
{
|
||||
struct subchannel *sch = to_subchannel(cdev->dev.parent);
|
||||
int mask;
|
||||
|
||||
if ((chp_idx < 0) || (chp_idx > 7))
|
||||
return -EINVAL;
|
||||
mask = 0x80 >> chp_idx;
|
||||
if (!(sch->schib.pmcw.pim & mask))
|
||||
return -ENODEV;
|
||||
|
||||
*chpid = sch->schib.pmcw.chpid[chp_idx];
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ccw_device_get_chpid);
|
||||
|
||||
/**
|
||||
* ccw_device_get_chid() - obtain Channel ID associated with specified CHPID
|
||||
* @cdev: device to obtain the Channel ID for
|
||||
* @chp_idx: Index of the channel path
|
||||
* @chid: The resulting Channel ID
|
||||
*/
|
||||
int ccw_device_get_chid(struct ccw_device *cdev, int chp_idx, u16 *chid)
|
||||
{
|
||||
struct chp_id cssid_chpid;
|
||||
struct channel_path *chp;
|
||||
int rc;
|
||||
|
||||
chp_id_init(&cssid_chpid);
|
||||
rc = ccw_device_get_chpid(cdev, chp_idx, &cssid_chpid.id);
|
||||
if (rc)
|
||||
return rc;
|
||||
chp = chpid_to_chp(cssid_chpid);
|
||||
if (!chp)
|
||||
return -ENODEV;
|
||||
|
||||
mutex_lock(&chp->lock);
|
||||
if (chp->desc_fmt1.flags & 0x10)
|
||||
*chid = chp->desc_fmt1.chid;
|
||||
else
|
||||
rc = -ENODEV;
|
||||
mutex_unlock(&chp->lock);
|
||||
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ccw_device_get_chid);
|
||||
|
||||
/*
|
||||
* Allocate zeroed dma coherent 31 bit addressable memory using
|
||||
* the subchannels dma pool. Maximal size of allocation supported
|
||||
|
@ -677,6 +677,7 @@ struct qeth_card_blkt {
|
||||
enum qeth_pnso_mode {
|
||||
QETH_PNSO_NONE,
|
||||
QETH_PNSO_BRIDGEPORT,
|
||||
QETH_PNSO_ADDR_INFO,
|
||||
};
|
||||
|
||||
#define QETH_BROADCAST_WITH_ECHO 0x01
|
||||
@ -684,9 +685,16 @@ enum qeth_pnso_mode {
|
||||
struct qeth_card_info {
|
||||
unsigned short unit_addr2;
|
||||
unsigned short cula;
|
||||
u8 chpid;
|
||||
__u16 func_level;
|
||||
char mcl_level[QETH_MCL_LENGTH + 1];
|
||||
/* doubleword below corresponds to net_if_token */
|
||||
u16 ddev_devno;
|
||||
u8 cssid;
|
||||
u8 iid;
|
||||
u8 ssid;
|
||||
u8 chpid;
|
||||
u16 chid;
|
||||
u8 ids_valid:1; /* cssid,iid,chid */
|
||||
u8 dev_addr_is_registered:1;
|
||||
u8 open_when_online:1;
|
||||
u8 promisc_mode:1;
|
||||
@ -780,6 +788,8 @@ struct qeth_switch_info {
|
||||
|
||||
struct qeth_priv {
|
||||
unsigned int rx_copybreak;
|
||||
u32 brport_hw_features;
|
||||
u32 brport_features;
|
||||
};
|
||||
|
||||
#define QETH_NAPI_WEIGHT NAPI_POLL_WEIGHT
|
||||
|
@ -2311,12 +2311,10 @@ static void qeth_idx_setup_activate_cmd(struct qeth_card *card,
|
||||
u16 addr = (card->info.cula << 8) + card->info.unit_addr2;
|
||||
u8 port = ((u8)card->dev->dev_port) | 0x80;
|
||||
struct ccw1 *ccw = __ccw_from_cmd(iob);
|
||||
struct ccw_dev_id dev_id;
|
||||
|
||||
qeth_setup_ccw(&ccw[0], CCW_CMD_WRITE, CCW_FLAG_CC, IDX_ACTIVATE_SIZE,
|
||||
iob->data);
|
||||
qeth_setup_ccw(&ccw[1], CCW_CMD_READ, 0, iob->length, iob->data);
|
||||
ccw_device_get_id(CARD_DDEV(card), &dev_id);
|
||||
iob->finalize = qeth_idx_finalize_cmd;
|
||||
|
||||
port |= QETH_IDX_ACT_INVAL_FRAME;
|
||||
@ -2325,7 +2323,7 @@ static void qeth_idx_setup_activate_cmd(struct qeth_card *card,
|
||||
&card->token.issuer_rm_w, QETH_MPC_TOKEN_LENGTH);
|
||||
memcpy(QETH_IDX_ACT_FUNC_LEVEL(iob->data),
|
||||
&card->info.func_level, 2);
|
||||
memcpy(QETH_IDX_ACT_QDIO_DEV_CUA(iob->data), &dev_id.devno, 2);
|
||||
memcpy(QETH_IDX_ACT_QDIO_DEV_CUA(iob->data), &card->info.ddev_devno, 2);
|
||||
memcpy(QETH_IDX_ACT_QDIO_DEV_REALADDR(iob->data), &addr, 2);
|
||||
}
|
||||
|
||||
@ -2599,7 +2597,6 @@ static int qeth_ulp_setup(struct qeth_card *card)
|
||||
{
|
||||
__u16 temp;
|
||||
struct qeth_cmd_buffer *iob;
|
||||
struct ccw_dev_id dev_id;
|
||||
|
||||
QETH_CARD_TEXT(card, 2, "ulpsetup");
|
||||
|
||||
@ -2614,8 +2611,7 @@ static int qeth_ulp_setup(struct qeth_card *card)
|
||||
memcpy(QETH_ULP_SETUP_FILTER_TOKEN(iob->data),
|
||||
&card->token.ulp_filter_r, QETH_MPC_TOKEN_LENGTH);
|
||||
|
||||
ccw_device_get_id(CARD_DDEV(card), &dev_id);
|
||||
memcpy(QETH_ULP_SETUP_CUA(iob->data), &dev_id.devno, 2);
|
||||
memcpy(QETH_ULP_SETUP_CUA(iob->data), &card->info.ddev_devno, 2);
|
||||
temp = (card->info.cula << 8) + card->info.unit_addr2;
|
||||
memcpy(QETH_ULP_SETUP_REAL_DEVADDR(iob->data), &temp, 2);
|
||||
return qeth_send_control_data(card, iob, qeth_ulp_setup_cb, NULL);
|
||||
@ -4920,7 +4916,6 @@ int qeth_vm_request_mac(struct qeth_card *card)
|
||||
{
|
||||
struct diag26c_mac_resp *response;
|
||||
struct diag26c_mac_req *request;
|
||||
struct ccw_dev_id id;
|
||||
int rc;
|
||||
|
||||
QETH_CARD_TEXT(card, 2, "vmreqmac");
|
||||
@ -4932,11 +4927,10 @@ int qeth_vm_request_mac(struct qeth_card *card)
|
||||
goto out;
|
||||
}
|
||||
|
||||
ccw_device_get_id(CARD_DDEV(card), &id);
|
||||
request->resp_buf_len = sizeof(*response);
|
||||
request->resp_version = DIAG26C_VERSION2;
|
||||
request->op_code = DIAG26C_GET_MAC;
|
||||
request->devno = id.devno;
|
||||
request->devno = card->info.ddev_devno;
|
||||
|
||||
QETH_DBF_HEX(CTRL, 2, request, sizeof(*request));
|
||||
rc = diag26c(request, response, DIAG26C_MAC_SERVICES);
|
||||
@ -5017,6 +5011,33 @@ static void qeth_determine_capabilities(struct qeth_card *card)
|
||||
return;
|
||||
}
|
||||
|
||||
static void qeth_read_ccw_conf_data(struct qeth_card *card)
|
||||
{
|
||||
struct qeth_card_info *info = &card->info;
|
||||
struct ccw_device *cdev = CARD_DDEV(card);
|
||||
struct ccw_dev_id dev_id;
|
||||
|
||||
QETH_CARD_TEXT(card, 2, "ccwconfd");
|
||||
ccw_device_get_id(cdev, &dev_id);
|
||||
|
||||
info->ddev_devno = dev_id.devno;
|
||||
info->ids_valid = !ccw_device_get_cssid(cdev, &info->cssid) &&
|
||||
!ccw_device_get_iid(cdev, &info->iid) &&
|
||||
!ccw_device_get_chid(cdev, 0, &info->chid);
|
||||
info->ssid = dev_id.ssid;
|
||||
|
||||
dev_info(&card->gdev->dev, "CHID: %x CHPID: %x\n",
|
||||
info->chid, info->chpid);
|
||||
|
||||
QETH_CARD_TEXT_(card, 3, "devn%x", info->ddev_devno);
|
||||
QETH_CARD_TEXT_(card, 3, "cssid:%x", info->cssid);
|
||||
QETH_CARD_TEXT_(card, 3, "iid:%x", info->iid);
|
||||
QETH_CARD_TEXT_(card, 3, "ssid:%x", info->ssid);
|
||||
QETH_CARD_TEXT_(card, 3, "chpid:%x", info->chpid);
|
||||
QETH_CARD_TEXT_(card, 3, "chid:%x", info->chid);
|
||||
QETH_CARD_TEXT_(card, 3, "idval%x", info->ids_valid);
|
||||
}
|
||||
|
||||
static int qeth_qdio_establish(struct qeth_card *card)
|
||||
{
|
||||
struct qdio_buffer **out_sbal_ptrs[QETH_MAX_OUT_QUEUES];
|
||||
@ -5185,6 +5206,7 @@ int qeth_core_hardsetup_card(struct qeth_card *card, bool *carrier_ok)
|
||||
}
|
||||
|
||||
qeth_determine_capabilities(card);
|
||||
qeth_read_ccw_conf_data(card);
|
||||
qeth_idx_init(card);
|
||||
|
||||
rc = qeth_idx_activate_read_channel(card);
|
||||
|
@ -23,7 +23,7 @@ int qeth_l2_vnicc_set_state(struct qeth_card *card, u32 vnicc, bool state);
|
||||
int qeth_l2_vnicc_get_state(struct qeth_card *card, u32 vnicc, bool *state);
|
||||
int qeth_l2_vnicc_set_timeout(struct qeth_card *card, u32 timeout);
|
||||
int qeth_l2_vnicc_get_timeout(struct qeth_card *card, u32 *timeout);
|
||||
bool qeth_l2_vnicc_is_in_use(struct qeth_card *card);
|
||||
bool qeth_bridgeport_allowed(struct qeth_card *card);
|
||||
|
||||
struct qeth_mac {
|
||||
u8 mac_addr[ETH_ALEN];
|
||||
|
@ -17,10 +17,13 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/if_bridge.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/hash.h>
|
||||
#include <linux/hashtable.h>
|
||||
#include <net/switchdev.h>
|
||||
#include <asm/chsc.h>
|
||||
#include <asm/css_chars.h>
|
||||
#include <asm/setup.h>
|
||||
#include "qeth_core.h"
|
||||
#include "qeth_l2.h"
|
||||
@ -30,6 +33,7 @@ static void qeth_bridge_state_change(struct qeth_card *card,
|
||||
struct qeth_ipa_cmd *cmd);
|
||||
static void qeth_addr_change_event(struct qeth_card *card,
|
||||
struct qeth_ipa_cmd *cmd);
|
||||
static bool qeth_bridgeport_is_in_use(struct qeth_card *card);
|
||||
static void qeth_l2_vnicc_set_defaults(struct qeth_card *card);
|
||||
static void qeth_l2_vnicc_init(struct qeth_card *card);
|
||||
static bool qeth_l2_vnicc_recover_timeout(struct qeth_card *card, u32 vnicc,
|
||||
@ -284,8 +288,26 @@ static void qeth_l2_set_pnso_mode(struct qeth_card *card,
|
||||
drain_workqueue(card->event_wq);
|
||||
}
|
||||
|
||||
static void qeth_l2_dev2br_fdb_flush(struct qeth_card *card)
|
||||
{
|
||||
struct switchdev_notifier_fdb_info info;
|
||||
|
||||
QETH_CARD_TEXT(card, 2, "fdbflush");
|
||||
|
||||
info.addr = NULL;
|
||||
/* flush all VLANs: */
|
||||
info.vid = 0;
|
||||
info.added_by_user = false;
|
||||
info.offloaded = true;
|
||||
|
||||
call_switchdev_notifiers(SWITCHDEV_FDB_FLUSH_TO_BRIDGE,
|
||||
card->dev, &info.info, NULL);
|
||||
}
|
||||
|
||||
static void qeth_l2_stop_card(struct qeth_card *card)
|
||||
{
|
||||
struct qeth_priv *priv = netdev_priv(card->dev);
|
||||
|
||||
QETH_CARD_TEXT(card, 2, "stopcard");
|
||||
|
||||
qeth_set_allowed_threads(card, 0, 1);
|
||||
@ -304,6 +326,12 @@ static void qeth_l2_stop_card(struct qeth_card *card)
|
||||
qeth_l2_set_pnso_mode(card, QETH_PNSO_NONE);
|
||||
qeth_flush_local_addrs(card);
|
||||
card->info.promisc_mode = 0;
|
||||
|
||||
if (priv->brport_features & BR_LEARNING_SYNC) {
|
||||
rtnl_lock();
|
||||
qeth_l2_dev2br_fdb_flush(card);
|
||||
rtnl_unlock();
|
||||
}
|
||||
}
|
||||
|
||||
static int qeth_l2_request_initial_mac(struct qeth_card *card)
|
||||
@ -642,6 +670,7 @@ static void qeth_l2_set_rx_mode(struct net_device *dev)
|
||||
/**
|
||||
* qeth_l2_pnso() - perform network subchannel operation
|
||||
* @card: qeth_card structure pointer
|
||||
* @oc: Operation Code
|
||||
* @cnc: Boolean Change-Notification Control
|
||||
* @cb: Callback function will be executed for each element
|
||||
* of the address list
|
||||
@ -652,7 +681,7 @@ static void qeth_l2_set_rx_mode(struct net_device *dev)
|
||||
* control" is set, further changes in the address list will be reported
|
||||
* via the IPA command.
|
||||
*/
|
||||
static int qeth_l2_pnso(struct qeth_card *card, int cnc,
|
||||
static int qeth_l2_pnso(struct qeth_card *card, u8 oc, int cnc,
|
||||
void (*cb)(void *priv, struct chsc_pnso_naid_l2 *entry),
|
||||
void *priv)
|
||||
{
|
||||
@ -663,13 +692,14 @@ static int qeth_l2_pnso(struct qeth_card *card, int cnc,
|
||||
int i, size, elems;
|
||||
int rc;
|
||||
|
||||
QETH_CARD_TEXT(card, 2, "PNSO");
|
||||
rr = (struct chsc_pnso_area *)get_zeroed_page(GFP_KERNEL);
|
||||
if (rr == NULL)
|
||||
return -ENOMEM;
|
||||
do {
|
||||
QETH_CARD_TEXT(card, 2, "PNSO");
|
||||
/* on the first iteration, naihdr.resume_token will be zero */
|
||||
rc = ccw_device_pnso(ddev, rr, rr->naihdr.resume_token, cnc);
|
||||
rc = ccw_device_pnso(ddev, rr, oc, rr->naihdr.resume_token,
|
||||
cnc);
|
||||
if (rc)
|
||||
continue;
|
||||
if (cb == NULL)
|
||||
@ -705,6 +735,218 @@ static int qeth_l2_pnso(struct qeth_card *card, int cnc,
|
||||
return rc;
|
||||
}
|
||||
|
||||
static bool qeth_is_my_net_if_token(struct qeth_card *card,
|
||||
struct net_if_token *token)
|
||||
{
|
||||
return ((card->info.ddev_devno == token->devnum) &&
|
||||
(card->info.cssid == token->cssid) &&
|
||||
(card->info.iid == token->iid) &&
|
||||
(card->info.ssid == token->ssid) &&
|
||||
(card->info.chpid == token->chpid) &&
|
||||
(card->info.chid == token->chid));
|
||||
}
|
||||
|
||||
/**
|
||||
* qeth_l2_dev2br_fdb_notify() - update fdb of master bridge
|
||||
* @card: qeth_card structure pointer
|
||||
* @code: event bitmask: high order bit 0x80 set to
|
||||
* 1 - removal of an object
|
||||
* 0 - addition of an object
|
||||
* Object type(s):
|
||||
* 0x01 - VLAN, 0x02 - MAC, 0x03 - VLAN and MAC
|
||||
* @token: "network token" structure identifying 'physical' location
|
||||
* of the target
|
||||
* @addr_lnid: structure with MAC address and VLAN ID of the target
|
||||
*/
|
||||
static void qeth_l2_dev2br_fdb_notify(struct qeth_card *card, u8 code,
|
||||
struct net_if_token *token,
|
||||
struct mac_addr_lnid *addr_lnid)
|
||||
{
|
||||
struct switchdev_notifier_fdb_info info;
|
||||
u8 ntfy_mac[ETH_ALEN];
|
||||
|
||||
ether_addr_copy(ntfy_mac, addr_lnid->mac);
|
||||
/* Ignore VLAN only changes */
|
||||
if (!(code & IPA_ADDR_CHANGE_CODE_MACADDR))
|
||||
return;
|
||||
/* Ignore mcast entries */
|
||||
if (is_multicast_ether_addr(ntfy_mac))
|
||||
return;
|
||||
/* Ignore my own addresses */
|
||||
if (qeth_is_my_net_if_token(card, token))
|
||||
return;
|
||||
|
||||
info.addr = ntfy_mac;
|
||||
/* don't report VLAN IDs */
|
||||
info.vid = 0;
|
||||
info.added_by_user = false;
|
||||
info.offloaded = true;
|
||||
|
||||
if (code & IPA_ADDR_CHANGE_CODE_REMOVAL) {
|
||||
call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE,
|
||||
card->dev, &info.info, NULL);
|
||||
QETH_CARD_TEXT(card, 4, "andelmac");
|
||||
QETH_CARD_TEXT_(card, 4,
|
||||
"mc%012lx", ether_addr_to_u64(ntfy_mac));
|
||||
} else {
|
||||
call_switchdev_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE,
|
||||
card->dev, &info.info, NULL);
|
||||
QETH_CARD_TEXT(card, 4, "anaddmac");
|
||||
QETH_CARD_TEXT_(card, 4,
|
||||
"mc%012lx", ether_addr_to_u64(ntfy_mac));
|
||||
}
|
||||
}
|
||||
|
||||
static void qeth_l2_dev2br_an_set_cb(void *priv,
|
||||
struct chsc_pnso_naid_l2 *entry)
|
||||
{
|
||||
u8 code = IPA_ADDR_CHANGE_CODE_MACADDR;
|
||||
struct qeth_card *card = priv;
|
||||
|
||||
if (entry->addr_lnid.lnid < VLAN_N_VID)
|
||||
code |= IPA_ADDR_CHANGE_CODE_VLANID;
|
||||
qeth_l2_dev2br_fdb_notify(card, code,
|
||||
(struct net_if_token *)&entry->nit,
|
||||
(struct mac_addr_lnid *)&entry->addr_lnid);
|
||||
}
|
||||
|
||||
/**
|
||||
* qeth_l2_dev2br_an_set() -
|
||||
* Enable or disable 'dev to bridge network address notification'
|
||||
* @card: qeth_card structure pointer
|
||||
* @enable: Enable or disable 'dev to bridge network address notification'
|
||||
*
|
||||
* Returns negative errno-compatible error indication or 0 on success.
|
||||
*
|
||||
* On enable, emits a series of address notifications for all
|
||||
* currently registered hosts.
|
||||
*
|
||||
* Must be called under rtnl_lock
|
||||
*/
|
||||
static int qeth_l2_dev2br_an_set(struct qeth_card *card, bool enable)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (enable) {
|
||||
QETH_CARD_TEXT(card, 2, "anseton");
|
||||
rc = qeth_l2_pnso(card, PNSO_OC_NET_ADDR_INFO, 1,
|
||||
qeth_l2_dev2br_an_set_cb, card);
|
||||
if (rc == -EAGAIN)
|
||||
/* address notification enabled, but inconsistent
|
||||
* addresses reported -> disable address notification
|
||||
*/
|
||||
qeth_l2_pnso(card, PNSO_OC_NET_ADDR_INFO, 0,
|
||||
NULL, NULL);
|
||||
} else {
|
||||
QETH_CARD_TEXT(card, 2, "ansetoff");
|
||||
rc = qeth_l2_pnso(card, PNSO_OC_NET_ADDR_INFO, 0, NULL, NULL);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int qeth_l2_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
|
||||
struct net_device *dev, u32 filter_mask,
|
||||
int nlflags)
|
||||
{
|
||||
struct qeth_priv *priv = netdev_priv(dev);
|
||||
struct qeth_card *card = dev->ml_priv;
|
||||
u16 mode = BRIDGE_MODE_UNDEF;
|
||||
|
||||
/* Do not even show qeth devs that cannot do bridge_setlink */
|
||||
if (!priv->brport_hw_features || !netif_device_present(dev) ||
|
||||
qeth_bridgeport_is_in_use(card))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return ndo_dflt_bridge_getlink(skb, pid, seq, dev,
|
||||
mode, priv->brport_features,
|
||||
priv->brport_hw_features,
|
||||
nlflags, filter_mask, NULL);
|
||||
}
|
||||
|
||||
static const struct nla_policy qeth_brport_policy[IFLA_BRPORT_MAX + 1] = {
|
||||
[IFLA_BRPORT_LEARNING_SYNC] = { .type = NLA_U8 },
|
||||
};
|
||||
|
||||
/**
|
||||
* qeth_l2_bridge_setlink() - set bridgeport attributes
|
||||
* @dev: netdevice
|
||||
* @nlh: netlink message header
|
||||
* @flags: bridge flags (here: BRIDGE_FLAGS_SELF)
|
||||
* @extack: extended ACK report struct
|
||||
*
|
||||
* Called under rtnl_lock
|
||||
*/
|
||||
static int qeth_l2_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh,
|
||||
u16 flags, struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct qeth_priv *priv = netdev_priv(dev);
|
||||
struct nlattr *bp_tb[IFLA_BRPORT_MAX + 1];
|
||||
struct qeth_card *card = dev->ml_priv;
|
||||
struct nlattr *attr, *nested_attr;
|
||||
bool enable, has_protinfo = false;
|
||||
int rem1, rem2;
|
||||
int rc;
|
||||
|
||||
if (!netif_device_present(dev))
|
||||
return -ENODEV;
|
||||
if (!(priv->brport_hw_features))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
nlmsg_for_each_attr(attr, nlh, sizeof(struct ifinfomsg), rem1) {
|
||||
if (nla_type(attr) == IFLA_PROTINFO) {
|
||||
rc = nla_parse_nested(bp_tb, IFLA_BRPORT_MAX, attr,
|
||||
qeth_brport_policy, extack);
|
||||
if (rc)
|
||||
return rc;
|
||||
has_protinfo = true;
|
||||
} else if (nla_type(attr) == IFLA_AF_SPEC) {
|
||||
nla_for_each_nested(nested_attr, attr, rem2) {
|
||||
if (nla_type(nested_attr) == IFLA_BRIDGE_FLAGS)
|
||||
continue;
|
||||
NL_SET_ERR_MSG_ATTR(extack, nested_attr,
|
||||
"Unsupported attribute");
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
NL_SET_ERR_MSG_ATTR(extack, attr, "Unsupported attribute");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
if (!has_protinfo)
|
||||
return 0;
|
||||
if (!bp_tb[IFLA_BRPORT_LEARNING_SYNC])
|
||||
return -EINVAL;
|
||||
enable = !!nla_get_u8(bp_tb[IFLA_BRPORT_LEARNING_SYNC]);
|
||||
|
||||
if (enable == !!(priv->brport_features & BR_LEARNING_SYNC))
|
||||
return 0;
|
||||
|
||||
mutex_lock(&card->sbp_lock);
|
||||
/* do not change anything if BridgePort is enabled */
|
||||
if (qeth_bridgeport_is_in_use(card)) {
|
||||
NL_SET_ERR_MSG(extack, "n/a (BridgePort)");
|
||||
rc = -EBUSY;
|
||||
} else if (enable) {
|
||||
qeth_l2_set_pnso_mode(card, QETH_PNSO_ADDR_INFO);
|
||||
rc = qeth_l2_dev2br_an_set(card, true);
|
||||
if (rc)
|
||||
qeth_l2_set_pnso_mode(card, QETH_PNSO_NONE);
|
||||
else
|
||||
priv->brport_features |= BR_LEARNING_SYNC;
|
||||
} else {
|
||||
rc = qeth_l2_dev2br_an_set(card, false);
|
||||
if (!rc) {
|
||||
qeth_l2_set_pnso_mode(card, QETH_PNSO_NONE);
|
||||
priv->brport_features ^= BR_LEARNING_SYNC;
|
||||
qeth_l2_dev2br_fdb_flush(card);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&card->sbp_lock);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static const struct net_device_ops qeth_l2_netdev_ops = {
|
||||
.ndo_open = qeth_open,
|
||||
.ndo_stop = qeth_stop,
|
||||
@ -720,7 +962,9 @@ static const struct net_device_ops qeth_l2_netdev_ops = {
|
||||
.ndo_vlan_rx_kill_vid = qeth_l2_vlan_rx_kill_vid,
|
||||
.ndo_tx_timeout = qeth_tx_timeout,
|
||||
.ndo_fix_features = qeth_fix_features,
|
||||
.ndo_set_features = qeth_set_features
|
||||
.ndo_set_features = qeth_set_features,
|
||||
.ndo_bridge_getlink = qeth_l2_bridge_getlink,
|
||||
.ndo_bridge_setlink = qeth_l2_bridge_setlink,
|
||||
};
|
||||
|
||||
static const struct net_device_ops qeth_osn_netdev_ops = {
|
||||
@ -824,6 +1068,78 @@ static void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* qeth_l2_detect_dev2br_support() -
|
||||
* Detect whether this card supports 'dev to bridge fdb network address
|
||||
* change notification' and thus can support the learning_sync bridgeport
|
||||
* attribute
|
||||
* @card: qeth_card structure pointer
|
||||
*
|
||||
* This is a destructive test and must be called before dev2br or
|
||||
* bridgeport address notification is enabled!
|
||||
*/
|
||||
static void qeth_l2_detect_dev2br_support(struct qeth_card *card)
|
||||
{
|
||||
struct qeth_priv *priv = netdev_priv(card->dev);
|
||||
bool dev2br_supported;
|
||||
int rc;
|
||||
|
||||
QETH_CARD_TEXT(card, 2, "d2brsup");
|
||||
if (!IS_IQD(card))
|
||||
return;
|
||||
|
||||
/* dev2br requires valid cssid,iid,chid */
|
||||
if (!card->info.ids_valid) {
|
||||
dev2br_supported = false;
|
||||
} else if (css_general_characteristics.enarf) {
|
||||
dev2br_supported = true;
|
||||
} else {
|
||||
/* Old machines don't have the feature bit:
|
||||
* Probe by testing whether a disable succeeds
|
||||
*/
|
||||
rc = qeth_l2_pnso(card, PNSO_OC_NET_ADDR_INFO, 0, NULL, NULL);
|
||||
dev2br_supported = !rc;
|
||||
}
|
||||
QETH_CARD_TEXT_(card, 2, "D2Bsup%02x", dev2br_supported);
|
||||
|
||||
if (dev2br_supported)
|
||||
priv->brport_hw_features |= BR_LEARNING_SYNC;
|
||||
else
|
||||
priv->brport_hw_features &= ~BR_LEARNING_SYNC;
|
||||
}
|
||||
|
||||
static void qeth_l2_enable_brport_features(struct qeth_card *card)
|
||||
{
|
||||
struct qeth_priv *priv = netdev_priv(card->dev);
|
||||
int rc;
|
||||
|
||||
if (priv->brport_features & BR_LEARNING_SYNC) {
|
||||
if (priv->brport_hw_features & BR_LEARNING_SYNC) {
|
||||
qeth_l2_set_pnso_mode(card, QETH_PNSO_ADDR_INFO);
|
||||
rc = qeth_l2_dev2br_an_set(card, true);
|
||||
if (rc == -EAGAIN) {
|
||||
/* Recoverable error, retry once */
|
||||
qeth_l2_set_pnso_mode(card, QETH_PNSO_NONE);
|
||||
qeth_l2_dev2br_fdb_flush(card);
|
||||
qeth_l2_set_pnso_mode(card, QETH_PNSO_ADDR_INFO);
|
||||
rc = qeth_l2_dev2br_an_set(card, true);
|
||||
}
|
||||
if (rc) {
|
||||
netdev_err(card->dev,
|
||||
"failed to enable bridge learning_sync: %d\n",
|
||||
rc);
|
||||
qeth_l2_set_pnso_mode(card, QETH_PNSO_NONE);
|
||||
qeth_l2_dev2br_fdb_flush(card);
|
||||
priv->brport_features ^= BR_LEARNING_SYNC;
|
||||
}
|
||||
} else {
|
||||
dev_warn(&card->gdev->dev,
|
||||
"bridge learning_sync not supported\n");
|
||||
priv->brport_features ^= BR_LEARNING_SYNC;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int qeth_l2_set_online(struct qeth_card *card)
|
||||
{
|
||||
struct ccwgroup_device *gdev = card->gdev;
|
||||
@ -838,6 +1154,9 @@ static int qeth_l2_set_online(struct qeth_card *card)
|
||||
goto out_remove;
|
||||
}
|
||||
|
||||
/* query before bridgeport_notification may be enabled */
|
||||
qeth_l2_detect_dev2br_support(card);
|
||||
|
||||
mutex_lock(&card->sbp_lock);
|
||||
qeth_bridgeport_query_support(card);
|
||||
if (card->options.sbp.supported_funcs) {
|
||||
@ -880,6 +1199,7 @@ static int qeth_l2_set_online(struct qeth_card *card)
|
||||
|
||||
netif_device_attach(dev);
|
||||
qeth_enable_hw_features(dev);
|
||||
qeth_l2_enable_brport_features(card);
|
||||
|
||||
if (card->info.open_when_online) {
|
||||
card->info.open_when_online = 0;
|
||||
@ -1169,6 +1489,81 @@ struct qeth_addr_change_data {
|
||||
struct qeth_ipacmd_addr_change ac_event;
|
||||
};
|
||||
|
||||
static void qeth_l2_dev2br_worker(struct work_struct *work)
|
||||
{
|
||||
struct delayed_work *dwork = to_delayed_work(work);
|
||||
struct qeth_addr_change_data *data;
|
||||
struct qeth_card *card;
|
||||
struct qeth_priv *priv;
|
||||
unsigned int i;
|
||||
int rc;
|
||||
|
||||
data = container_of(dwork, struct qeth_addr_change_data, dwork);
|
||||
card = data->card;
|
||||
priv = netdev_priv(card->dev);
|
||||
|
||||
QETH_CARD_TEXT(card, 4, "dev2brew");
|
||||
|
||||
if (READ_ONCE(card->info.pnso_mode) == QETH_PNSO_NONE)
|
||||
goto free;
|
||||
|
||||
/* Potential re-config in progress, try again later: */
|
||||
if (!rtnl_trylock()) {
|
||||
queue_delayed_work(card->event_wq, dwork,
|
||||
msecs_to_jiffies(100));
|
||||
return;
|
||||
}
|
||||
if (!netif_device_present(card->dev))
|
||||
goto out_unlock;
|
||||
|
||||
if (data->ac_event.lost_event_mask) {
|
||||
QETH_DBF_MESSAGE(3,
|
||||
"Address change notification overflow on device %x\n",
|
||||
CARD_DEVID(card));
|
||||
/* Card fdb and bridge fdb are out of sync, card has stopped
|
||||
* notifications (no need to drain_workqueue). Purge all
|
||||
* 'extern_learn' entries from the parent bridge and restart
|
||||
* the notifications.
|
||||
*/
|
||||
qeth_l2_dev2br_fdb_flush(card);
|
||||
rc = qeth_l2_dev2br_an_set(card, true);
|
||||
if (rc) {
|
||||
/* TODO: if we want to retry after -EAGAIN, be
|
||||
* aware there could be stale entries in the
|
||||
* workqueue now, that need to be drained.
|
||||
* For now we give up:
|
||||
*/
|
||||
netdev_err(card->dev,
|
||||
"bridge learning_sync failed to recover: %d\n",
|
||||
rc);
|
||||
WRITE_ONCE(card->info.pnso_mode,
|
||||
QETH_PNSO_NONE);
|
||||
/* To remove fdb entries reported by an_set: */
|
||||
qeth_l2_dev2br_fdb_flush(card);
|
||||
priv->brport_features ^= BR_LEARNING_SYNC;
|
||||
} else {
|
||||
QETH_DBF_MESSAGE(3,
|
||||
"Address Notification resynced on device %x\n",
|
||||
CARD_DEVID(card));
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < data->ac_event.num_entries; i++) {
|
||||
struct qeth_ipacmd_addr_change_entry *entry =
|
||||
&data->ac_event.entry[i];
|
||||
qeth_l2_dev2br_fdb_notify(card,
|
||||
entry->change_code,
|
||||
&entry->token,
|
||||
&entry->addr_lnid);
|
||||
}
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
rtnl_unlock();
|
||||
|
||||
free:
|
||||
kfree(data);
|
||||
}
|
||||
|
||||
static void qeth_addr_change_event_worker(struct work_struct *work)
|
||||
{
|
||||
struct delayed_work *dwork = to_delayed_work(work);
|
||||
@ -1251,7 +1646,10 @@ static void qeth_addr_change_event(struct qeth_card *card,
|
||||
QETH_CARD_TEXT(card, 2, "ACNalloc");
|
||||
return;
|
||||
}
|
||||
INIT_DELAYED_WORK(&data->dwork, qeth_addr_change_event_worker);
|
||||
if (card->info.pnso_mode == QETH_PNSO_BRIDGEPORT)
|
||||
INIT_DELAYED_WORK(&data->dwork, qeth_addr_change_event_worker);
|
||||
else
|
||||
INIT_DELAYED_WORK(&data->dwork, qeth_l2_dev2br_worker);
|
||||
data->card = card;
|
||||
memcpy(&data->ac_event, hostevs,
|
||||
sizeof(struct qeth_ipacmd_addr_change) + extrasize);
|
||||
@ -1578,11 +1976,12 @@ int qeth_bridgeport_an_set(struct qeth_card *card, int enable)
|
||||
if (enable) {
|
||||
qeth_bridge_emit_host_event(card, anev_reset, 0, NULL, NULL);
|
||||
qeth_l2_set_pnso_mode(card, QETH_PNSO_BRIDGEPORT);
|
||||
rc = qeth_l2_pnso(card, 1, qeth_bridgeport_an_set_cb, card);
|
||||
rc = qeth_l2_pnso(card, PNSO_OC_NET_BRIDGE_INFO, 1,
|
||||
qeth_bridgeport_an_set_cb, card);
|
||||
if (rc)
|
||||
qeth_l2_set_pnso_mode(card, QETH_PNSO_NONE);
|
||||
} else {
|
||||
rc = qeth_l2_pnso(card, 0, NULL, NULL);
|
||||
rc = qeth_l2_pnso(card, PNSO_OC_NET_BRIDGE_INFO, 0, NULL, NULL);
|
||||
qeth_l2_set_pnso_mode(card, QETH_PNSO_NONE);
|
||||
}
|
||||
return rc;
|
||||
@ -1879,7 +2278,7 @@ int qeth_l2_vnicc_get_timeout(struct qeth_card *card, u32 *timeout)
|
||||
}
|
||||
|
||||
/* check if VNICC is currently enabled */
|
||||
bool qeth_l2_vnicc_is_in_use(struct qeth_card *card)
|
||||
static bool _qeth_l2_vnicc_is_in_use(struct qeth_card *card)
|
||||
{
|
||||
if (!card->options.vnicc.sup_chars)
|
||||
return false;
|
||||
@ -1894,6 +2293,21 @@ bool qeth_l2_vnicc_is_in_use(struct qeth_card *card)
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* qeth_bridgeport_allowed - are any qeth_bridgeport functions allowed?
|
||||
* @card: qeth_card structure pointer
|
||||
*
|
||||
* qeth_bridgeport functionality is mutually exclusive with usage of the
|
||||
* VNIC Characteristics and dev2br address notifications
|
||||
*/
|
||||
bool qeth_bridgeport_allowed(struct qeth_card *card)
|
||||
{
|
||||
struct qeth_priv *priv = netdev_priv(card->dev);
|
||||
|
||||
return (!_qeth_l2_vnicc_is_in_use(card) &&
|
||||
!(priv->brport_features & BR_LEARNING_SYNC));
|
||||
}
|
||||
|
||||
/* recover user timeout setting */
|
||||
static bool qeth_l2_vnicc_recover_timeout(struct qeth_card *card, u32 vnicc,
|
||||
u32 *timeout)
|
||||
|
@ -18,7 +18,7 @@ static ssize_t qeth_bridge_port_role_state_show(struct device *dev,
|
||||
int rc = 0;
|
||||
char *word;
|
||||
|
||||
if (qeth_l2_vnicc_is_in_use(card))
|
||||
if (!qeth_bridgeport_allowed(card))
|
||||
return sprintf(buf, "n/a (VNIC characteristics)\n");
|
||||
|
||||
mutex_lock(&card->sbp_lock);
|
||||
@ -65,7 +65,7 @@ static ssize_t qeth_bridge_port_role_show(struct device *dev,
|
||||
{
|
||||
struct qeth_card *card = dev_get_drvdata(dev);
|
||||
|
||||
if (qeth_l2_vnicc_is_in_use(card))
|
||||
if (!qeth_bridgeport_allowed(card))
|
||||
return sprintf(buf, "n/a (VNIC characteristics)\n");
|
||||
|
||||
return qeth_bridge_port_role_state_show(dev, attr, buf, 0);
|
||||
@ -90,7 +90,7 @@ static ssize_t qeth_bridge_port_role_store(struct device *dev,
|
||||
mutex_lock(&card->conf_mutex);
|
||||
mutex_lock(&card->sbp_lock);
|
||||
|
||||
if (qeth_l2_vnicc_is_in_use(card))
|
||||
if (!qeth_bridgeport_allowed(card))
|
||||
rc = -EBUSY;
|
||||
else if (card->options.sbp.reflect_promisc)
|
||||
/* Forbid direct manipulation */
|
||||
@ -116,7 +116,7 @@ static ssize_t qeth_bridge_port_state_show(struct device *dev,
|
||||
{
|
||||
struct qeth_card *card = dev_get_drvdata(dev);
|
||||
|
||||
if (qeth_l2_vnicc_is_in_use(card))
|
||||
if (!qeth_bridgeport_allowed(card))
|
||||
return sprintf(buf, "n/a (VNIC characteristics)\n");
|
||||
|
||||
return qeth_bridge_port_role_state_show(dev, attr, buf, 1);
|
||||
@ -131,7 +131,7 @@ static ssize_t qeth_bridgeport_hostnotification_show(struct device *dev,
|
||||
struct qeth_card *card = dev_get_drvdata(dev);
|
||||
int enabled;
|
||||
|
||||
if (qeth_l2_vnicc_is_in_use(card))
|
||||
if (!qeth_bridgeport_allowed(card))
|
||||
return sprintf(buf, "n/a (VNIC characteristics)\n");
|
||||
|
||||
enabled = card->options.sbp.hostnotification;
|
||||
@ -153,7 +153,7 @@ static ssize_t qeth_bridgeport_hostnotification_store(struct device *dev,
|
||||
mutex_lock(&card->conf_mutex);
|
||||
mutex_lock(&card->sbp_lock);
|
||||
|
||||
if (qeth_l2_vnicc_is_in_use(card))
|
||||
if (!qeth_bridgeport_allowed(card))
|
||||
rc = -EBUSY;
|
||||
else if (qeth_card_hw_is_reachable(card)) {
|
||||
rc = qeth_bridgeport_an_set(card, enable);
|
||||
@ -179,7 +179,7 @@ static ssize_t qeth_bridgeport_reflect_show(struct device *dev,
|
||||
struct qeth_card *card = dev_get_drvdata(dev);
|
||||
char *state;
|
||||
|
||||
if (qeth_l2_vnicc_is_in_use(card))
|
||||
if (!qeth_bridgeport_allowed(card))
|
||||
return sprintf(buf, "n/a (VNIC characteristics)\n");
|
||||
|
||||
if (card->options.sbp.reflect_promisc) {
|
||||
@ -215,7 +215,7 @@ static ssize_t qeth_bridgeport_reflect_store(struct device *dev,
|
||||
mutex_lock(&card->conf_mutex);
|
||||
mutex_lock(&card->sbp_lock);
|
||||
|
||||
if (qeth_l2_vnicc_is_in_use(card))
|
||||
if (!qeth_bridgeport_allowed(card))
|
||||
rc = -EBUSY;
|
||||
else if (card->options.sbp.role != QETH_SBP_ROLE_NONE)
|
||||
rc = -EPERM;
|
||||
|
@ -203,6 +203,7 @@ enum switchdev_notifier_type {
|
||||
SWITCHDEV_FDB_ADD_TO_DEVICE,
|
||||
SWITCHDEV_FDB_DEL_TO_DEVICE,
|
||||
SWITCHDEV_FDB_OFFLOADED,
|
||||
SWITCHDEV_FDB_FLUSH_TO_BRIDGE,
|
||||
|
||||
SWITCHDEV_PORT_OBJ_ADD, /* Blocking. */
|
||||
SWITCHDEV_PORT_OBJ_DEL, /* Blocking. */
|
||||
|
@ -183,6 +183,11 @@ static int br_switchdev_event(struct notifier_block *unused,
|
||||
br_fdb_offloaded_set(br, p, fdb_info->addr,
|
||||
fdb_info->vid, fdb_info->offloaded);
|
||||
break;
|
||||
case SWITCHDEV_FDB_FLUSH_TO_BRIDGE:
|
||||
fdb_info = ptr;
|
||||
/* Don't delete static entries */
|
||||
br_fdb_delete_by_port(br, p, fdb_info->vid, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
|
Loading…
Reference in New Issue
Block a user