s390/cio, s390/qeth: cleanup PNSO CHSC
CHSC3D (PNSO - perform network subchannel operation) is used for OC0 (Store-network-bridging-information) as well as for OC3 (Store-network-address-information). So common fields are renamed from *brinfo* to *pnso*. Also *_bridge_host_* is changed into *_addr_change_*, e.g. qeth_bridge_host_event to qeth_addr_change_event, for the same reasons. The keywords in the card traces are changed accordingly. Remove unused L3 types, as PNSO will only return Layer2 entries. Make PNSO CHSC implementation more consistent with existing API usage: Add new function ccw_device_pnso() to drivers/s390/cio/device_ops.c and the function declaration to arch/s390/include/asm/ccwdev.h, which takes a struct ccw_device * as parameter instead of schid and calls chsc_pnso(). PNSO CHSC has no strict relationship to qdio. So move the calling function from qdio to qeth_l2 and move the necessary structures to a new file arch/s390/include/asm/chsc.h. Do response code evaluation only in chsc_error_from_response() and use return code in all other places. qeth_anset_makerc() was meant to evaluate the PNSO response code, but never did, because pnso_rc was already non-zero. Indentation was corrected in some places. Signed-off-by: Alexandra Winter <wintera@linux.ibm.com> Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com> Reviewed-by: Vineeth Vijayan <vneethv@linux.ibm.com> Reviewed-by: Julian Wiedmann <jwi@linux.ibm.com> Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
This commit is contained in:
parent
cafebf8653
commit
a0138f5926
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/mod_devicetable.h>
|
#include <linux/mod_devicetable.h>
|
||||||
|
#include <asm/chsc.h>
|
||||||
#include <asm/fcx.h>
|
#include <asm/fcx.h>
|
||||||
#include <asm/irq.h>
|
#include <asm/irq.h>
|
||||||
#include <asm/schid.h>
|
#include <asm/schid.h>
|
||||||
|
@ -236,4 +237,8 @@ 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);
|
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);
|
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);
|
||||||
#endif /* _S390_CCWDEV_H_ */
|
#endif /* _S390_CCWDEV_H_ */
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
|
/*
|
||||||
|
* Copyright IBM Corp. 2020
|
||||||
|
*
|
||||||
|
* Author(s): Alexandra Winter <wintera@linux.ibm.com>
|
||||||
|
*
|
||||||
|
* Interface for Channel Subsystem Call
|
||||||
|
*/
|
||||||
|
#ifndef _ASM_S390_CHSC_H
|
||||||
|
#define _ASM_S390_CHSC_H
|
||||||
|
|
||||||
|
#include <uapi/asm/chsc.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct chsc_pnso_naid_l2 - network address information descriptor
|
||||||
|
* @nit: Network interface token
|
||||||
|
* @addr_lnid: network address and logical network id (VLAN ID)
|
||||||
|
*/
|
||||||
|
struct chsc_pnso_naid_l2 {
|
||||||
|
u64 nit;
|
||||||
|
struct { u8 mac[6]; u16 lnid; } addr_lnid;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct chsc_pnso_resume_token {
|
||||||
|
u64 t1;
|
||||||
|
u64 t2;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct chsc_pnso_naihdr {
|
||||||
|
struct chsc_pnso_resume_token resume_token;
|
||||||
|
u32:32;
|
||||||
|
u32 instance;
|
||||||
|
u32:24;
|
||||||
|
u8 naids;
|
||||||
|
u32 reserved[3];
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct chsc_pnso_area {
|
||||||
|
struct chsc_header request;
|
||||||
|
u8:2;
|
||||||
|
u8 m:1;
|
||||||
|
u8:5;
|
||||||
|
u8:2;
|
||||||
|
u8 ssid:2;
|
||||||
|
u8 fmt:4;
|
||||||
|
u16 sch;
|
||||||
|
u8:8;
|
||||||
|
u8 cssid;
|
||||||
|
u16:16;
|
||||||
|
u8 oc;
|
||||||
|
u32:24;
|
||||||
|
struct chsc_pnso_resume_token resume_token;
|
||||||
|
u32 n:1;
|
||||||
|
u32:31;
|
||||||
|
u32 reserved[3];
|
||||||
|
struct chsc_header response;
|
||||||
|
u32:32;
|
||||||
|
struct chsc_pnso_naihdr naihdr;
|
||||||
|
struct chsc_pnso_naid_l2 entries[0];
|
||||||
|
} __packed __aligned(PAGE_SIZE);
|
||||||
|
|
||||||
|
#endif /* _ASM_S390_CHSC_H */
|
|
@ -365,34 +365,6 @@ struct qdio_initialize {
|
||||||
struct qdio_outbuf_state *output_sbal_state_array;
|
struct qdio_outbuf_state *output_sbal_state_array;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* enum qdio_brinfo_entry_type - type of address entry for qdio_brinfo_desc()
|
|
||||||
* @l3_ipv6_addr: entry contains IPv6 address
|
|
||||||
* @l3_ipv4_addr: entry contains IPv4 address
|
|
||||||
* @l2_addr_lnid: entry contains MAC address and VLAN ID
|
|
||||||
*/
|
|
||||||
enum qdio_brinfo_entry_type {l3_ipv6_addr, l3_ipv4_addr, l2_addr_lnid};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* struct qdio_brinfo_entry_XXX - Address entry for qdio_brinfo_desc()
|
|
||||||
* @nit: Network interface token
|
|
||||||
* @addr: Address of one of the three types
|
|
||||||
*
|
|
||||||
* The struct is passed to the callback function by qdio_brinfo_desc()
|
|
||||||
*/
|
|
||||||
struct qdio_brinfo_entry_l3_ipv6 {
|
|
||||||
u64 nit;
|
|
||||||
struct { unsigned char _s6_addr[16]; } addr;
|
|
||||||
} __packed;
|
|
||||||
struct qdio_brinfo_entry_l3_ipv4 {
|
|
||||||
u64 nit;
|
|
||||||
struct { uint32_t _s_addr; } addr;
|
|
||||||
} __packed;
|
|
||||||
struct qdio_brinfo_entry_l2 {
|
|
||||||
u64 nit;
|
|
||||||
struct { u8 mac[6]; u16 lnid; } addr_lnid;
|
|
||||||
} __packed;
|
|
||||||
|
|
||||||
#define QDIO_STATE_INACTIVE 0x00000002 /* after qdio_cleanup */
|
#define QDIO_STATE_INACTIVE 0x00000002 /* after qdio_cleanup */
|
||||||
#define QDIO_STATE_ESTABLISHED 0x00000004 /* after qdio_establish */
|
#define QDIO_STATE_ESTABLISHED 0x00000004 /* after qdio_establish */
|
||||||
#define QDIO_STATE_ACTIVE 0x00000008 /* after qdio_activate */
|
#define QDIO_STATE_ACTIVE 0x00000008 /* after qdio_activate */
|
||||||
|
@ -423,10 +395,5 @@ extern int qdio_inspect_queue(struct ccw_device *cdev, unsigned int nr,
|
||||||
extern int qdio_shutdown(struct ccw_device *, int);
|
extern int qdio_shutdown(struct ccw_device *, int);
|
||||||
extern int qdio_free(struct ccw_device *);
|
extern int qdio_free(struct ccw_device *);
|
||||||
extern int qdio_get_ssqd_desc(struct ccw_device *, struct qdio_ssqd_desc *);
|
extern int qdio_get_ssqd_desc(struct ccw_device *, struct qdio_ssqd_desc *);
|
||||||
extern int qdio_pnso_brinfo(struct subchannel_id schid,
|
|
||||||
int cnc, u16 *response,
|
|
||||||
void (*cb)(void *priv, enum qdio_brinfo_entry_type type,
|
|
||||||
void *entry),
|
|
||||||
void *priv);
|
|
||||||
|
|
||||||
#endif /* __QDIO_H__ */
|
#endif /* __QDIO_H__ */
|
||||||
|
|
|
@ -57,6 +57,7 @@ int chsc_error_from_response(int response)
|
||||||
case 0x0104:
|
case 0x0104:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
case 0x0004:
|
case 0x0004:
|
||||||
|
case 0x0106: /* "Wrong Channel Parm" for the op 0x003d */
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
case 0x000b:
|
case 0x000b:
|
||||||
case 0x0107: /* "Channel busy" for the op 0x003d */
|
case 0x0107: /* "Channel busy" for the op 0x003d */
|
||||||
|
@ -1336,36 +1337,35 @@ out:
|
||||||
EXPORT_SYMBOL_GPL(chsc_scm_info);
|
EXPORT_SYMBOL_GPL(chsc_scm_info);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* chsc_pnso_brinfo() - Perform Network-Subchannel Operation, Bridge Info.
|
* chsc_pnso() - Perform Network-Subchannel Operation
|
||||||
* @schid: id of the subchannel on which PNSO is performed
|
* @schid: id of the subchannel on which PNSO is performed
|
||||||
* @brinfo_area: request and response block for the operation
|
* @pnso_area: request and response block for the operation
|
||||||
* @resume_token: resume token for multiblock response
|
* @resume_token: resume token for multiblock response
|
||||||
* @cnc: Boolean change-notification control
|
* @cnc: Boolean change-notification control
|
||||||
*
|
*
|
||||||
* brinfo_area must be allocated by the caller with get_zeroed_page(GFP_KERNEL)
|
* pnso_area must be allocated by the caller with get_zeroed_page(GFP_KERNEL)
|
||||||
*
|
*
|
||||||
* Returns 0 on success.
|
* Returns 0 on success.
|
||||||
*/
|
*/
|
||||||
int chsc_pnso_brinfo(struct subchannel_id schid,
|
int chsc_pnso(struct subchannel_id schid,
|
||||||
struct chsc_pnso_area *brinfo_area,
|
struct chsc_pnso_area *pnso_area,
|
||||||
struct chsc_brinfo_resume_token resume_token,
|
struct chsc_pnso_resume_token resume_token,
|
||||||
int cnc)
|
int cnc)
|
||||||
{
|
{
|
||||||
memset(brinfo_area, 0, sizeof(*brinfo_area));
|
memset(pnso_area, 0, sizeof(*pnso_area));
|
||||||
brinfo_area->request.length = 0x0030;
|
pnso_area->request.length = 0x0030;
|
||||||
brinfo_area->request.code = 0x003d; /* network-subchannel operation */
|
pnso_area->request.code = 0x003d; /* network-subchannel operation */
|
||||||
brinfo_area->m = schid.m;
|
pnso_area->m = schid.m;
|
||||||
brinfo_area->ssid = schid.ssid;
|
pnso_area->ssid = schid.ssid;
|
||||||
brinfo_area->sch = schid.sch_no;
|
pnso_area->sch = schid.sch_no;
|
||||||
brinfo_area->cssid = schid.cssid;
|
pnso_area->cssid = schid.cssid;
|
||||||
brinfo_area->oc = 0; /* Store-network-bridging-information list */
|
pnso_area->oc = 0; /* Store-network-bridging-information list */
|
||||||
brinfo_area->resume_token = resume_token;
|
pnso_area->resume_token = resume_token;
|
||||||
brinfo_area->n = (cnc != 0);
|
pnso_area->n = (cnc != 0);
|
||||||
if (chsc(brinfo_area))
|
if (chsc(pnso_area))
|
||||||
return -EIO;
|
return -EIO;
|
||||||
return chsc_error_from_response(brinfo_area->response.code);
|
return chsc_error_from_response(pnso_area->response.code);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(chsc_pnso_brinfo);
|
|
||||||
|
|
||||||
int chsc_sgib(u32 origin)
|
int chsc_sgib(u32 origin)
|
||||||
{
|
{
|
||||||
|
|
|
@ -205,52 +205,10 @@ struct chsc_scm_info {
|
||||||
|
|
||||||
int chsc_scm_info(struct chsc_scm_info *scm_area, u64 token);
|
int chsc_scm_info(struct chsc_scm_info *scm_area, u64 token);
|
||||||
|
|
||||||
struct chsc_brinfo_resume_token {
|
int chsc_pnso(struct subchannel_id schid,
|
||||||
u64 t1;
|
struct chsc_pnso_area *pnso_area,
|
||||||
u64 t2;
|
struct chsc_pnso_resume_token resume_token,
|
||||||
} __packed;
|
int cnc);
|
||||||
|
|
||||||
struct chsc_brinfo_naihdr {
|
|
||||||
struct chsc_brinfo_resume_token resume_token;
|
|
||||||
u32:32;
|
|
||||||
u32 instance;
|
|
||||||
u32:24;
|
|
||||||
u8 naids;
|
|
||||||
u32 reserved[3];
|
|
||||||
} __packed;
|
|
||||||
|
|
||||||
struct chsc_pnso_area {
|
|
||||||
struct chsc_header request;
|
|
||||||
u8:2;
|
|
||||||
u8 m:1;
|
|
||||||
u8:5;
|
|
||||||
u8:2;
|
|
||||||
u8 ssid:2;
|
|
||||||
u8 fmt:4;
|
|
||||||
u16 sch;
|
|
||||||
u8:8;
|
|
||||||
u8 cssid;
|
|
||||||
u16:16;
|
|
||||||
u8 oc;
|
|
||||||
u32:24;
|
|
||||||
struct chsc_brinfo_resume_token resume_token;
|
|
||||||
u32 n:1;
|
|
||||||
u32:31;
|
|
||||||
u32 reserved[3];
|
|
||||||
struct chsc_header response;
|
|
||||||
u32:32;
|
|
||||||
struct chsc_brinfo_naihdr naihdr;
|
|
||||||
union {
|
|
||||||
struct qdio_brinfo_entry_l3_ipv6 l3_ipv6[0];
|
|
||||||
struct qdio_brinfo_entry_l3_ipv4 l3_ipv4[0];
|
|
||||||
struct qdio_brinfo_entry_l2 l2[0];
|
|
||||||
} entries;
|
|
||||||
} __packed __aligned(PAGE_SIZE);
|
|
||||||
|
|
||||||
int chsc_pnso_brinfo(struct subchannel_id schid,
|
|
||||||
struct chsc_pnso_area *brinfo_area,
|
|
||||||
struct chsc_brinfo_resume_token resume_token,
|
|
||||||
int cnc);
|
|
||||||
|
|
||||||
int __init chsc_get_cssid(int idx);
|
int __init chsc_get_cssid(int idx);
|
||||||
|
|
||||||
|
|
|
@ -710,6 +710,29 @@ void ccw_device_get_schid(struct ccw_device *cdev, struct subchannel_id *schid)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(ccw_device_get_schid);
|
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
|
||||||
|
* @resume_token: resume token for multiblock response
|
||||||
|
* @cnc: Boolean change-notification control
|
||||||
|
*
|
||||||
|
* pnso_area must be allocated by the caller with get_zeroed_page(GFP_KERNEL)
|
||||||
|
*
|
||||||
|
* 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 subchannel_id schid;
|
||||||
|
|
||||||
|
ccw_device_get_schid(cdev, &schid);
|
||||||
|
return chsc_pnso(schid, pnso_area, resume_token, cnc);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(ccw_device_pnso);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allocate zeroed dma coherent 31 bit addressable memory using
|
* Allocate zeroed dma coherent 31 bit addressable memory using
|
||||||
* the subchannels dma pool. Maximal size of allocation supported
|
* the subchannels dma pool. Maximal size of allocation supported
|
||||||
|
|
|
@ -1705,94 +1705,6 @@ int qdio_stop_irq(struct ccw_device *cdev)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(qdio_stop_irq);
|
EXPORT_SYMBOL(qdio_stop_irq);
|
||||||
|
|
||||||
/**
|
|
||||||
* qdio_pnso_brinfo() - perform network subchannel op #0 - bridge info.
|
|
||||||
* @schid: Subchannel ID.
|
|
||||||
* @cnc: Boolean Change-Notification Control
|
|
||||||
* @response: Response code will be stored at this address
|
|
||||||
* @cb: Callback function will be executed for each element
|
|
||||||
* of the address list
|
|
||||||
* @priv: Pointer to pass to the callback function.
|
|
||||||
*
|
|
||||||
* Performs "Store-network-bridging-information list" operation and calls
|
|
||||||
* the callback function for every entry in the list. If "change-
|
|
||||||
* notification-control" is set, further changes in the address list
|
|
||||||
* will be reported via the IPA command.
|
|
||||||
*/
|
|
||||||
int qdio_pnso_brinfo(struct subchannel_id schid,
|
|
||||||
int cnc, u16 *response,
|
|
||||||
void (*cb)(void *priv, enum qdio_brinfo_entry_type type,
|
|
||||||
void *entry),
|
|
||||||
void *priv)
|
|
||||||
{
|
|
||||||
struct chsc_pnso_area *rr;
|
|
||||||
int rc;
|
|
||||||
u32 prev_instance = 0;
|
|
||||||
int isfirstblock = 1;
|
|
||||||
int i, size, elems;
|
|
||||||
|
|
||||||
rr = (struct chsc_pnso_area *)get_zeroed_page(GFP_KERNEL);
|
|
||||||
if (rr == NULL)
|
|
||||||
return -ENOMEM;
|
|
||||||
do {
|
|
||||||
/* on the first iteration, naihdr.resume_token will be zero */
|
|
||||||
rc = chsc_pnso_brinfo(schid, rr, rr->naihdr.resume_token, cnc);
|
|
||||||
if (rc != 0 && rc != -EBUSY)
|
|
||||||
goto out;
|
|
||||||
if (rr->response.code != 1) {
|
|
||||||
rc = -EIO;
|
|
||||||
continue;
|
|
||||||
} else
|
|
||||||
rc = 0;
|
|
||||||
|
|
||||||
if (cb == NULL)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
size = rr->naihdr.naids;
|
|
||||||
elems = (rr->response.length -
|
|
||||||
sizeof(struct chsc_header) -
|
|
||||||
sizeof(struct chsc_brinfo_naihdr)) /
|
|
||||||
size;
|
|
||||||
|
|
||||||
if (!isfirstblock && (rr->naihdr.instance != prev_instance)) {
|
|
||||||
/* Inform the caller that they need to scrap */
|
|
||||||
/* the data that was already reported via cb */
|
|
||||||
rc = -EAGAIN;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
isfirstblock = 0;
|
|
||||||
prev_instance = rr->naihdr.instance;
|
|
||||||
for (i = 0; i < elems; i++)
|
|
||||||
switch (size) {
|
|
||||||
case sizeof(struct qdio_brinfo_entry_l3_ipv6):
|
|
||||||
(*cb)(priv, l3_ipv6_addr,
|
|
||||||
&rr->entries.l3_ipv6[i]);
|
|
||||||
break;
|
|
||||||
case sizeof(struct qdio_brinfo_entry_l3_ipv4):
|
|
||||||
(*cb)(priv, l3_ipv4_addr,
|
|
||||||
&rr->entries.l3_ipv4[i]);
|
|
||||||
break;
|
|
||||||
case sizeof(struct qdio_brinfo_entry_l2):
|
|
||||||
(*cb)(priv, l2_addr_lnid,
|
|
||||||
&rr->entries.l2[i]);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
WARN_ON_ONCE(1);
|
|
||||||
rc = -EIO;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
} while (rr->response.code == 0x0107 || /* channel busy */
|
|
||||||
(rr->response.code == 1 && /* list stored */
|
|
||||||
/* resume token is non-zero => list incomplete */
|
|
||||||
(rr->naihdr.resume_token.t1 || rr->naihdr.resume_token.t2)));
|
|
||||||
(*response) = rr->response.code;
|
|
||||||
|
|
||||||
out:
|
|
||||||
free_page((unsigned long)rr);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(qdio_pnso_brinfo);
|
|
||||||
|
|
||||||
static int __init init_QDIO(void)
|
static int __init init_QDIO(void)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
#include <linux/hash.h>
|
#include <linux/hash.h>
|
||||||
#include <linux/hashtable.h>
|
#include <linux/hashtable.h>
|
||||||
|
#include <asm/chsc.h>
|
||||||
#include <asm/setup.h>
|
#include <asm/setup.h>
|
||||||
#include "qeth_core.h"
|
#include "qeth_core.h"
|
||||||
#include "qeth_l2.h"
|
#include "qeth_l2.h"
|
||||||
|
@ -27,8 +28,8 @@
|
||||||
static void qeth_bridgeport_query_support(struct qeth_card *card);
|
static void qeth_bridgeport_query_support(struct qeth_card *card);
|
||||||
static void qeth_bridge_state_change(struct qeth_card *card,
|
static void qeth_bridge_state_change(struct qeth_card *card,
|
||||||
struct qeth_ipa_cmd *cmd);
|
struct qeth_ipa_cmd *cmd);
|
||||||
static void qeth_bridge_host_event(struct qeth_card *card,
|
static void qeth_addr_change_event(struct qeth_card *card,
|
||||||
struct qeth_ipa_cmd *cmd);
|
struct qeth_ipa_cmd *cmd);
|
||||||
static void qeth_l2_vnicc_set_defaults(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 void qeth_l2_vnicc_init(struct qeth_card *card);
|
||||||
static bool qeth_l2_vnicc_recover_timeout(struct qeth_card *card, u32 vnicc,
|
static bool qeth_l2_vnicc_recover_timeout(struct qeth_card *card, u32 vnicc,
|
||||||
|
@ -628,6 +629,72 @@ static void qeth_l2_set_rx_mode(struct net_device *dev)
|
||||||
schedule_work(&card->rx_mode_work);
|
schedule_work(&card->rx_mode_work);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* qeth_l2_pnso() - perform network subchannel operation
|
||||||
|
* @card: qeth_card structure pointer
|
||||||
|
* @cnc: Boolean Change-Notification Control
|
||||||
|
* @cb: Callback function will be executed for each element
|
||||||
|
* of the address list
|
||||||
|
* @priv: Pointer to pass to the callback function.
|
||||||
|
*
|
||||||
|
* Collects network information in a network address list and calls the
|
||||||
|
* callback function for every entry in the list. If "change-notification-
|
||||||
|
* 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,
|
||||||
|
void (*cb)(void *priv, struct chsc_pnso_naid_l2 *entry),
|
||||||
|
void *priv)
|
||||||
|
{
|
||||||
|
struct ccw_device *ddev = CARD_DDEV(card);
|
||||||
|
struct chsc_pnso_area *rr;
|
||||||
|
u32 prev_instance = 0;
|
||||||
|
int isfirstblock = 1;
|
||||||
|
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 {
|
||||||
|
/* on the first iteration, naihdr.resume_token will be zero */
|
||||||
|
rc = ccw_device_pnso(ddev, rr, rr->naihdr.resume_token, cnc);
|
||||||
|
if (rc)
|
||||||
|
continue;
|
||||||
|
if (cb == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
size = rr->naihdr.naids;
|
||||||
|
if (size != sizeof(struct chsc_pnso_naid_l2)) {
|
||||||
|
WARN_ON_ONCE(1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
elems = (rr->response.length - sizeof(struct chsc_header) -
|
||||||
|
sizeof(struct chsc_pnso_naihdr)) / size;
|
||||||
|
|
||||||
|
if (!isfirstblock && (rr->naihdr.instance != prev_instance)) {
|
||||||
|
/* Inform the caller that they need to scrap */
|
||||||
|
/* the data that was already reported via cb */
|
||||||
|
rc = -EAGAIN;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
isfirstblock = 0;
|
||||||
|
prev_instance = rr->naihdr.instance;
|
||||||
|
for (i = 0; i < elems; i++)
|
||||||
|
(*cb)(priv, &rr->entries[i]);
|
||||||
|
} while ((rc == -EBUSY) || (!rc && /* list stored */
|
||||||
|
/* resume token is non-zero => list incomplete */
|
||||||
|
(rr->naihdr.resume_token.t1 || rr->naihdr.resume_token.t2)));
|
||||||
|
|
||||||
|
if (rc)
|
||||||
|
QETH_CARD_TEXT_(card, 2, "PNrp%04x", rr->response.code);
|
||||||
|
|
||||||
|
free_page((unsigned long)rr);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
static const struct net_device_ops qeth_l2_netdev_ops = {
|
static const struct net_device_ops qeth_l2_netdev_ops = {
|
||||||
.ndo_open = qeth_open,
|
.ndo_open = qeth_open,
|
||||||
.ndo_stop = qeth_stop,
|
.ndo_stop = qeth_stop,
|
||||||
|
@ -854,7 +921,7 @@ static int qeth_l2_control_event(struct qeth_card *card,
|
||||||
} else
|
} else
|
||||||
return 1;
|
return 1;
|
||||||
case IPA_CMD_ADDRESS_CHANGE_NOTIF:
|
case IPA_CMD_ADDRESS_CHANGE_NOTIF:
|
||||||
qeth_bridge_host_event(card, cmd);
|
qeth_addr_change_event(card, cmd);
|
||||||
return 0;
|
return 0;
|
||||||
default:
|
default:
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -971,8 +1038,10 @@ enum qeth_an_event_type {anev_reg_unreg, anev_abort, anev_reset};
|
||||||
* for all currently registered addresses.
|
* for all currently registered addresses.
|
||||||
*/
|
*/
|
||||||
static void qeth_bridge_emit_host_event(struct qeth_card *card,
|
static void qeth_bridge_emit_host_event(struct qeth_card *card,
|
||||||
enum qeth_an_event_type evtype,
|
enum qeth_an_event_type evtype,
|
||||||
u8 code, struct net_if_token *token, struct mac_addr_lnid *addr_lnid)
|
u8 code,
|
||||||
|
struct net_if_token *token,
|
||||||
|
struct mac_addr_lnid *addr_lnid)
|
||||||
{
|
{
|
||||||
char str[7][32];
|
char str[7][32];
|
||||||
char *env[8];
|
char *env[8];
|
||||||
|
@ -1089,74 +1158,76 @@ static void qeth_bridge_state_change(struct qeth_card *card,
|
||||||
queue_work(card->event_wq, &data->worker);
|
queue_work(card->event_wq, &data->worker);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct qeth_bridge_host_data {
|
struct qeth_addr_change_data {
|
||||||
struct work_struct worker;
|
struct work_struct worker;
|
||||||
struct qeth_card *card;
|
struct qeth_card *card;
|
||||||
struct qeth_ipacmd_addr_change hostevs;
|
struct qeth_ipacmd_addr_change ac_event;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void qeth_bridge_host_event_worker(struct work_struct *work)
|
static void qeth_addr_change_event_worker(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct qeth_bridge_host_data *data =
|
struct qeth_addr_change_data *data =
|
||||||
container_of(work, struct qeth_bridge_host_data, worker);
|
container_of(work, struct qeth_addr_change_data, worker);
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (data->hostevs.lost_event_mask) {
|
QETH_CARD_TEXT(data->card, 4, "adrchgew");
|
||||||
|
if (data->ac_event.lost_event_mask) {
|
||||||
dev_info(&data->card->gdev->dev,
|
dev_info(&data->card->gdev->dev,
|
||||||
"Address notification from the Bridge Port stopped %s (%s)\n",
|
"Address change notification stopped on %s (%s)\n",
|
||||||
data->card->dev->name,
|
data->card->dev->name,
|
||||||
(data->hostevs.lost_event_mask == 0x01)
|
(data->ac_event.lost_event_mask == 0x01)
|
||||||
? "Overflow"
|
? "Overflow"
|
||||||
: (data->hostevs.lost_event_mask == 0x02)
|
: (data->ac_event.lost_event_mask == 0x02)
|
||||||
? "Bridge port state change"
|
? "Bridge port state change"
|
||||||
: "Unknown reason");
|
: "Unknown reason");
|
||||||
mutex_lock(&data->card->sbp_lock);
|
mutex_lock(&data->card->sbp_lock);
|
||||||
data->card->options.sbp.hostnotification = 0;
|
data->card->options.sbp.hostnotification = 0;
|
||||||
mutex_unlock(&data->card->sbp_lock);
|
mutex_unlock(&data->card->sbp_lock);
|
||||||
qeth_bridge_emit_host_event(data->card, anev_abort,
|
qeth_bridge_emit_host_event(data->card, anev_abort,
|
||||||
0, NULL, NULL);
|
0, NULL, NULL);
|
||||||
} else
|
} else
|
||||||
for (i = 0; i < data->hostevs.num_entries; i++) {
|
for (i = 0; i < data->ac_event.num_entries; i++) {
|
||||||
struct qeth_ipacmd_addr_change_entry *entry =
|
struct qeth_ipacmd_addr_change_entry *entry =
|
||||||
&data->hostevs.entry[i];
|
&data->ac_event.entry[i];
|
||||||
qeth_bridge_emit_host_event(data->card,
|
qeth_bridge_emit_host_event(data->card,
|
||||||
anev_reg_unreg,
|
anev_reg_unreg,
|
||||||
entry->change_code,
|
entry->change_code,
|
||||||
&entry->token, &entry->addr_lnid);
|
&entry->token,
|
||||||
|
&entry->addr_lnid);
|
||||||
}
|
}
|
||||||
kfree(data);
|
kfree(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qeth_bridge_host_event(struct qeth_card *card,
|
static void qeth_addr_change_event(struct qeth_card *card,
|
||||||
struct qeth_ipa_cmd *cmd)
|
struct qeth_ipa_cmd *cmd)
|
||||||
{
|
{
|
||||||
struct qeth_ipacmd_addr_change *hostevs =
|
struct qeth_ipacmd_addr_change *hostevs =
|
||||||
&cmd->data.addrchange;
|
&cmd->data.addrchange;
|
||||||
struct qeth_bridge_host_data *data;
|
struct qeth_addr_change_data *data;
|
||||||
int extrasize;
|
int extrasize;
|
||||||
|
|
||||||
QETH_CARD_TEXT(card, 2, "brhostev");
|
QETH_CARD_TEXT(card, 4, "adrchgev");
|
||||||
if (cmd->hdr.return_code != 0x0000) {
|
if (cmd->hdr.return_code != 0x0000) {
|
||||||
if (cmd->hdr.return_code == 0x0010) {
|
if (cmd->hdr.return_code == 0x0010) {
|
||||||
if (hostevs->lost_event_mask == 0x00)
|
if (hostevs->lost_event_mask == 0x00)
|
||||||
hostevs->lost_event_mask = 0xff;
|
hostevs->lost_event_mask = 0xff;
|
||||||
} else {
|
} else {
|
||||||
QETH_CARD_TEXT_(card, 2, "BPHe%04x",
|
QETH_CARD_TEXT_(card, 2, "ACHN%04x",
|
||||||
cmd->hdr.return_code);
|
cmd->hdr.return_code);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
extrasize = sizeof(struct qeth_ipacmd_addr_change_entry) *
|
extrasize = sizeof(struct qeth_ipacmd_addr_change_entry) *
|
||||||
hostevs->num_entries;
|
hostevs->num_entries;
|
||||||
data = kzalloc(sizeof(struct qeth_bridge_host_data) + extrasize,
|
data = kzalloc(sizeof(struct qeth_addr_change_data) + extrasize,
|
||||||
GFP_ATOMIC);
|
GFP_ATOMIC);
|
||||||
if (!data) {
|
if (!data) {
|
||||||
QETH_CARD_TEXT(card, 2, "BPHalloc");
|
QETH_CARD_TEXT(card, 2, "ACNalloc");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
INIT_WORK(&data->worker, qeth_bridge_host_event_worker);
|
INIT_WORK(&data->worker, qeth_addr_change_event_worker);
|
||||||
data->card = card;
|
data->card = card;
|
||||||
memcpy(&data->hostevs, hostevs,
|
memcpy(&data->ac_event, hostevs,
|
||||||
sizeof(struct qeth_ipacmd_addr_change) + extrasize);
|
sizeof(struct qeth_ipacmd_addr_change) + extrasize);
|
||||||
queue_work(card->event_wq, &data->worker);
|
queue_work(card->event_wq, &data->worker);
|
||||||
}
|
}
|
||||||
|
@ -1446,63 +1517,18 @@ int qeth_bridgeport_setrole(struct qeth_card *card, enum qeth_sbp_roles role)
|
||||||
return qeth_send_ipa_cmd(card, iob, qeth_bridgeport_set_cb, NULL);
|
return qeth_send_ipa_cmd(card, iob, qeth_bridgeport_set_cb, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* qeth_anset_makerc() - derive "traditional" error from hardware codes.
|
|
||||||
* @card: qeth_card structure pointer, for debug messages.
|
|
||||||
*
|
|
||||||
* Returns negative errno-compatible error indication or 0 on success.
|
|
||||||
*/
|
|
||||||
static int qeth_anset_makerc(struct qeth_card *card, int pnso_rc, u16 response)
|
|
||||||
{
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
if (pnso_rc == 0)
|
|
||||||
switch (response) {
|
|
||||||
case 0x0001:
|
|
||||||
rc = 0;
|
|
||||||
break;
|
|
||||||
case 0x0004:
|
|
||||||
case 0x0100:
|
|
||||||
case 0x0106:
|
|
||||||
rc = -EOPNOTSUPP;
|
|
||||||
dev_err(&card->gdev->dev,
|
|
||||||
"Setting address notification failed\n");
|
|
||||||
break;
|
|
||||||
case 0x0107:
|
|
||||||
rc = -EAGAIN;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
rc = -EIO;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
rc = -EIO;
|
|
||||||
|
|
||||||
if (rc) {
|
|
||||||
QETH_CARD_TEXT_(card, 2, "SBPp%04x", pnso_rc);
|
|
||||||
QETH_CARD_TEXT_(card, 2, "SBPr%04x", response);
|
|
||||||
}
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void qeth_bridgeport_an_set_cb(void *priv,
|
static void qeth_bridgeport_an_set_cb(void *priv,
|
||||||
enum qdio_brinfo_entry_type type, void *entry)
|
struct chsc_pnso_naid_l2 *entry)
|
||||||
{
|
{
|
||||||
struct qeth_card *card = (struct qeth_card *)priv;
|
struct qeth_card *card = (struct qeth_card *)priv;
|
||||||
struct qdio_brinfo_entry_l2 *l2entry;
|
|
||||||
u8 code;
|
u8 code;
|
||||||
|
|
||||||
if (type != l2_addr_lnid) {
|
|
||||||
WARN_ON_ONCE(1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
l2entry = (struct qdio_brinfo_entry_l2 *)entry;
|
|
||||||
code = IPA_ADDR_CHANGE_CODE_MACADDR;
|
code = IPA_ADDR_CHANGE_CODE_MACADDR;
|
||||||
if (l2entry->addr_lnid.lnid < VLAN_N_VID)
|
if (entry->addr_lnid.lnid < VLAN_N_VID)
|
||||||
code |= IPA_ADDR_CHANGE_CODE_VLANID;
|
code |= IPA_ADDR_CHANGE_CODE_VLANID;
|
||||||
qeth_bridge_emit_host_event(card, anev_reg_unreg, code,
|
qeth_bridge_emit_host_event(card, anev_reg_unreg, code,
|
||||||
(struct net_if_token *)&l2entry->nit,
|
(struct net_if_token *)&entry->nit,
|
||||||
(struct mac_addr_lnid *)&l2entry->addr_lnid);
|
(struct mac_addr_lnid *)&entry->addr_lnid);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1518,22 +1544,16 @@ static void qeth_bridgeport_an_set_cb(void *priv,
|
||||||
int qeth_bridgeport_an_set(struct qeth_card *card, int enable)
|
int qeth_bridgeport_an_set(struct qeth_card *card, int enable)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
u16 response;
|
|
||||||
struct ccw_device *ddev;
|
|
||||||
struct subchannel_id schid;
|
|
||||||
|
|
||||||
if (!card->options.sbp.supported_funcs)
|
if (!card->options.sbp.supported_funcs)
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
ddev = CARD_DDEV(card);
|
|
||||||
ccw_device_get_schid(ddev, &schid);
|
|
||||||
|
|
||||||
if (enable) {
|
if (enable) {
|
||||||
qeth_bridge_emit_host_event(card, anev_reset, 0, NULL, NULL);
|
qeth_bridge_emit_host_event(card, anev_reset, 0, NULL, NULL);
|
||||||
rc = qdio_pnso_brinfo(schid, 1, &response,
|
rc = qeth_l2_pnso(card, 1, qeth_bridgeport_an_set_cb, card);
|
||||||
qeth_bridgeport_an_set_cb, card);
|
|
||||||
} else
|
} else
|
||||||
rc = qdio_pnso_brinfo(schid, 0, &response, NULL, NULL);
|
rc = qeth_l2_pnso(card, 0, NULL, NULL);
|
||||||
return qeth_anset_makerc(card, rc, response);
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool qeth_bridgeport_is_in_use(struct qeth_card *card)
|
static bool qeth_bridgeport_is_in_use(struct qeth_card *card)
|
||||||
|
|
Loading…
Reference in New Issue