Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 59b55a4d authored by Eugene Crosser's avatar Eugene Crosser Committed by David S. Miller
Browse files

s390/qdio: bridgeport support - CHSC part



Introduce function for the "Perform network-subchannel operation"
CHSC command with operation code "bridgeport information",
and bit definitions for "characteristics" pertaning to this command.

Signed-off-by: default avatarEugene Crosser <eugene.crosser@ru.ibm.com>
Signed-off-by: default avatarFrank Blaschka <frank.blaschka@de.ibm.com>
Reviewed-by: default avatarSebastian Ott <sebott@linux.vnet.ibm.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent b4d72c08
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -29,6 +29,8 @@ struct css_general_char {
	u32 fcx : 1;	 /* bit 88 */
	u32 : 19;
	u32 alt_ssi : 1; /* bit 108 */
	u32:1;
	u32 narf:1;	 /* bit 110 */
} __packed;

extern struct css_general_char css_general_characteristics;
+33 −0
Original line number Diff line number Diff line
@@ -378,6 +378,34 @@ struct qdio_initialize {
	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_ESTABLISHED		0x00000004 /* after qdio_establish */
#define QDIO_STATE_ACTIVE		0x00000008 /* after qdio_activate */
@@ -399,5 +427,10 @@ extern int qdio_get_next_buffers(struct ccw_device *, int, int *, int *);
extern int qdio_shutdown(struct ccw_device *, int);
extern int qdio_free(struct ccw_device *);
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__ */
+33 −0
Original line number Diff line number Diff line
@@ -55,6 +55,7 @@ int chsc_error_from_response(int response)
	case 0x0004:
		return -EOPNOTSUPP;
	case 0x000b:
	case 0x0107:		/* "Channel busy" for the op 0x003d */
		return -EBUSY;
	case 0x0100:
	case 0x0102:
@@ -1234,3 +1235,35 @@ int chsc_scm_info(struct chsc_scm_info *scm_area, u64 token)
	return ret;
}
EXPORT_SYMBOL_GPL(chsc_scm_info);

/**
 * chsc_pnso_brinfo() - Perform Network-Subchannel Operation, Bridge Info.
 * @schid:		id of the subchannel on which PNSO is performed
 * @brinfo_area:	request and response block for the operation
 * @resume_token:	resume token for multiblock response
 * @cnc:		Boolean change-notification control
 *
 * brinfo_area must be allocated by the caller with get_zeroed_page(GFP_KERNEL)
 *
 * Returns 0 on success.
 */
int chsc_pnso_brinfo(struct subchannel_id schid,
		struct chsc_pnso_area *brinfo_area,
		struct chsc_brinfo_resume_token resume_token,
		int cnc)
{
	memset(brinfo_area, 0, sizeof(*brinfo_area));
	brinfo_area->request.length = 0x0030;
	brinfo_area->request.code = 0x003d; /* network-subchannel operation */
	brinfo_area->m	   = schid.m;
	brinfo_area->ssid  = schid.ssid;
	brinfo_area->sch   = schid.sch_no;
	brinfo_area->cssid = schid.cssid;
	brinfo_area->oc    = 0; /* Store-network-bridging-information list */
	brinfo_area->resume_token = resume_token;
	brinfo_area->n	   = (cnc != 0);
	if (chsc(brinfo_area))
		return -EIO;
	return chsc_error_from_response(brinfo_area->response.code);
}
EXPORT_SYMBOL_GPL(chsc_pnso_brinfo);
+50 −1
Original line number Diff line number Diff line
@@ -61,7 +61,9 @@ struct css_chsc_char {
	u32 : 20;
	u32 scssc : 1;  /* bit 107 */
	u32 scsscf : 1; /* bit 108 */
	u32 : 19;
	u32:7;
	u32 pnso:1; /* bit 116 */
	u32:11;
}__attribute__((packed));

extern struct css_chsc_char css_chsc_characteristics;
@@ -188,6 +190,53 @@ struct chsc_scm_info {

int chsc_scm_info(struct chsc_scm_info *scm_area, u64 token);

struct chsc_brinfo_resume_token {
	u64 t1;
	u64 t2;
} __packed;

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;

int chsc_pnso_brinfo(struct subchannel_id schid,
		struct chsc_pnso_area *brinfo_area,
		struct chsc_brinfo_resume_token resume_token,
		int cnc);

#ifdef CONFIG_SCM_BUS
int scm_update_information(void);
int scm_process_availability_information(void);
+91 −0
Original line number Diff line number Diff line
@@ -1752,6 +1752,97 @@ int qdio_stop_irq(struct ccw_device *cdev, int nr)
}
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 passed from the caller to qdio_pnso_brinfo()
 * @type:		Type of the address entry passed to the callback
 * @entry:		Entry containg the address of the specified type
 * @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)
{
	int rc;