2428 lines · 50.4 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* ldc.c: Logical Domain Channel link-layer protocol driver. * * Copyright (C) 2007, 2008 David S. Miller <davem@davemloft.net> */ #include <linux/kernel.h>#include <linux/export.h>#include <linux/slab.h>#include <linux/spinlock.h>#include <linux/delay.h>#include <linux/errno.h>#include <linux/string.h>#include <linux/scatterlist.h>#include <linux/interrupt.h>#include <linux/list.h>#include <linux/init.h>#include <linux/bitmap.h>#include <asm/iommu-common.h> #include <asm/hypervisor.h>#include <asm/iommu.h>#include <asm/page.h>#include <asm/ldc.h>#include <asm/mdesc.h> #define DRV_MODULE_NAME		"ldc"#define PFX DRV_MODULE_NAME	": "#define DRV_MODULE_VERSION	"1.1"#define DRV_MODULE_RELDATE	"July 22, 2008" #define COOKIE_PGSZ_CODE	0xf000000000000000ULL#define COOKIE_PGSZ_CODE_SHIFT	60ULL  static char version[] =	DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; /* Packet header layout for unreliable and reliable mode frames. * When in RAW mode, packets are simply straight 64-byte payloads * with no headers. */struct ldc_packet {	u8			type;#define LDC_CTRL		0x01#define LDC_DATA		0x02#define LDC_ERR			0x10 	u8			stype;#define LDC_INFO		0x01#define LDC_ACK			0x02#define LDC_NACK		0x04 	u8			ctrl;#define LDC_VERS		0x01 /* Link Version		*/#define LDC_RTS			0x02 /* Request To Send		*/#define LDC_RTR			0x03 /* Ready To Receive	*/#define LDC_RDX			0x04 /* Ready for Data eXchange	*/#define LDC_CTRL_MSK		0x0f 	u8			env;#define LDC_LEN			0x3f#define LDC_FRAG_MASK		0xc0#define LDC_START		0x40#define LDC_STOP		0x80 	u32			seqid; 	union {		u8		u_data[LDC_PACKET_SIZE - 8];		struct {			u32	pad;			u32	ackid;			u8	r_data[LDC_PACKET_SIZE - 8 - 8];		} r;	} u;}; struct ldc_version {	u16 major;	u16 minor;}; /* Ordered from largest major to lowest.  */static struct ldc_version ver_arr[] = {	{ .major = 1, .minor = 0 },}; #define LDC_DEFAULT_MTU			(4 * LDC_PACKET_SIZE)#define LDC_DEFAULT_NUM_ENTRIES		(PAGE_SIZE / LDC_PACKET_SIZE) struct ldc_channel; struct ldc_mode_ops {	int (*write)(struct ldc_channel *, const void *, unsigned int);	int (*read)(struct ldc_channel *, void *, unsigned int);}; static const struct ldc_mode_ops raw_ops;static const struct ldc_mode_ops nonraw_ops;static const struct ldc_mode_ops stream_ops; int ldom_domaining_enabled; struct ldc_iommu {	/* Protects ldc_unmap.  */	spinlock_t			lock;	struct ldc_mtable_entry		*page_table;	struct iommu_map_table		iommu_map_table;}; struct ldc_channel {	/* Protects all operations that depend upon channel state.  */	spinlock_t			lock; 	unsigned long			id; 	u8				*mssbuf;	u32				mssbuf_len;	u32				mssbuf_off; 	struct ldc_packet		*tx_base;	unsigned long			tx_head;	unsigned long			tx_tail;	unsigned long			tx_num_entries;	unsigned long			tx_ra; 	unsigned long			tx_acked; 	struct ldc_packet		*rx_base;	unsigned long			rx_head;	unsigned long			rx_tail;	unsigned long			rx_num_entries;	unsigned long			rx_ra; 	u32				rcv_nxt;	u32				snd_nxt; 	unsigned long			chan_state; 	struct ldc_channel_config	cfg;	void				*event_arg; 	const struct ldc_mode_ops	*mops; 	struct ldc_iommu		iommu; 	struct ldc_version		ver; 	u8				hs_state;#define LDC_HS_CLOSED			0x00#define LDC_HS_OPEN			0x01#define LDC_HS_GOTVERS			0x02#define LDC_HS_SENTRTR			0x03#define LDC_HS_GOTRTR			0x04#define LDC_HS_COMPLETE			0x10 	u8				flags;#define LDC_FLAG_ALLOCED_QUEUES		0x01#define LDC_FLAG_REGISTERED_QUEUES	0x02#define LDC_FLAG_REGISTERED_IRQS	0x04#define LDC_FLAG_RESET			0x10 	u8				mss;	u8				state; #define LDC_IRQ_NAME_MAX		32	char				rx_irq_name[LDC_IRQ_NAME_MAX];	char				tx_irq_name[LDC_IRQ_NAME_MAX]; 	struct hlist_head		mh_list; 	struct hlist_node		list;}; #define ldcdbg(TYPE, f, a...) \do {	if (lp->cfg.debug & LDC_DEBUG_##TYPE) \		printk(KERN_INFO PFX "ID[%lu] " f, lp->id, ## a); \} while (0) #define	LDC_ABORT(lp)	ldc_abort((lp), __func__) static const char *state_to_str(u8 state){	switch (state) {	case LDC_STATE_INVALID:		return "INVALID";	case LDC_STATE_INIT:		return "INIT";	case LDC_STATE_BOUND:		return "BOUND";	case LDC_STATE_READY:		return "READY";	case LDC_STATE_CONNECTED:		return "CONNECTED";	default:		return "<UNKNOWN>";	}} static unsigned long __advance(unsigned long off, unsigned long num_entries){	off += LDC_PACKET_SIZE;	if (off == (num_entries * LDC_PACKET_SIZE))		off = 0; 	return off;} static unsigned long rx_advance(struct ldc_channel *lp, unsigned long off){	return __advance(off, lp->rx_num_entries);} static unsigned long tx_advance(struct ldc_channel *lp, unsigned long off){	return __advance(off, lp->tx_num_entries);} static struct ldc_packet *handshake_get_tx_packet(struct ldc_channel *lp,						  unsigned long *new_tail){	struct ldc_packet *p;	unsigned long t; 	t = tx_advance(lp, lp->tx_tail);	if (t == lp->tx_head)		return NULL; 	*new_tail = t; 	p = lp->tx_base;	return p + (lp->tx_tail / LDC_PACKET_SIZE);} /* When we are in reliable or stream mode, have to track the next packet * we haven't gotten an ACK for in the TX queue using tx_acked.  We have * to be careful not to stomp over the queue past that point.  During * the handshake, we don't have TX data packets pending in the queue * and that's why handshake_get_tx_packet() need not be mindful of * lp->tx_acked. */static unsigned long head_for_data(struct ldc_channel *lp){	if (lp->cfg.mode == LDC_MODE_STREAM)		return lp->tx_acked;	return lp->tx_head;} static int tx_has_space_for(struct ldc_channel *lp, unsigned int size){	unsigned long limit, tail, new_tail, diff;	unsigned int mss; 	limit = head_for_data(lp);	tail = lp->tx_tail;	new_tail = tx_advance(lp, tail);	if (new_tail == limit)		return 0; 	if (limit > new_tail)		diff = limit - new_tail;	else		diff = (limit +			((lp->tx_num_entries * LDC_PACKET_SIZE) - new_tail));	diff /= LDC_PACKET_SIZE;	mss = lp->mss; 	if (diff * mss < size)		return 0; 	return 1;} static struct ldc_packet *data_get_tx_packet(struct ldc_channel *lp,					     unsigned long *new_tail){	struct ldc_packet *p;	unsigned long h, t; 	h = head_for_data(lp);	t = tx_advance(lp, lp->tx_tail);	if (t == h)		return NULL; 	*new_tail = t; 	p = lp->tx_base;	return p + (lp->tx_tail / LDC_PACKET_SIZE);} static int set_tx_tail(struct ldc_channel *lp, unsigned long tail){	unsigned long orig_tail = lp->tx_tail;	int limit = 1000; 	lp->tx_tail = tail;	while (limit-- > 0) {		unsigned long err; 		err = sun4v_ldc_tx_set_qtail(lp->id, tail);		if (!err)			return 0; 		if (err != HV_EWOULDBLOCK) {			lp->tx_tail = orig_tail;			return -EINVAL;		}		udelay(1);	} 	lp->tx_tail = orig_tail;	return -EBUSY;} /* This just updates the head value in the hypervisor using * a polling loop with a timeout.  The caller takes care of * upating software state representing the head change, if any. */static int __set_rx_head(struct ldc_channel *lp, unsigned long head){	int limit = 1000; 	while (limit-- > 0) {		unsigned long err; 		err = sun4v_ldc_rx_set_qhead(lp->id, head);		if (!err)			return 0; 		if (err != HV_EWOULDBLOCK)			return -EINVAL; 		udelay(1);	} 	return -EBUSY;} static int send_tx_packet(struct ldc_channel *lp,			  struct ldc_packet *p,			  unsigned long new_tail){	BUG_ON(p != (lp->tx_base + (lp->tx_tail / LDC_PACKET_SIZE))); 	return set_tx_tail(lp, new_tail);} static struct ldc_packet *handshake_compose_ctrl(struct ldc_channel *lp,						 u8 stype, u8 ctrl,						 void *data, int dlen,						 unsigned long *new_tail){	struct ldc_packet *p = handshake_get_tx_packet(lp, new_tail); 	if (p) {		memset(p, 0, sizeof(*p));		p->type = LDC_CTRL;		p->stype = stype;		p->ctrl = ctrl;		if (data)			memcpy(p->u.u_data, data, dlen);	}	return p;} static int start_handshake(struct ldc_channel *lp){	struct ldc_packet *p;	struct ldc_version *ver;	unsigned long new_tail; 	ver = &ver_arr[0]; 	ldcdbg(HS, "SEND VER INFO maj[%u] min[%u]\n",	       ver->major, ver->minor); 	p = handshake_compose_ctrl(lp, LDC_INFO, LDC_VERS,				   ver, sizeof(*ver), &new_tail);	if (p) {		int err = send_tx_packet(lp, p, new_tail);		if (!err)			lp->flags &= ~LDC_FLAG_RESET;		return err;	}	return -EBUSY;} static int send_version_nack(struct ldc_channel *lp,			     u16 major, u16 minor){	struct ldc_packet *p;	struct ldc_version ver;	unsigned long new_tail; 	ver.major = major;	ver.minor = minor; 	p = handshake_compose_ctrl(lp, LDC_NACK, LDC_VERS,				   &ver, sizeof(ver), &new_tail);	if (p) {		ldcdbg(HS, "SEND VER NACK maj[%u] min[%u]\n",		       ver.major, ver.minor); 		return send_tx_packet(lp, p, new_tail);	}	return -EBUSY;} static int send_version_ack(struct ldc_channel *lp,			    struct ldc_version *vp){	struct ldc_packet *p;	unsigned long new_tail; 	p = handshake_compose_ctrl(lp, LDC_ACK, LDC_VERS,				   vp, sizeof(*vp), &new_tail);	if (p) {		ldcdbg(HS, "SEND VER ACK maj[%u] min[%u]\n",		       vp->major, vp->minor); 		return send_tx_packet(lp, p, new_tail);	}	return -EBUSY;} static int send_rts(struct ldc_channel *lp){	struct ldc_packet *p;	unsigned long new_tail; 	p = handshake_compose_ctrl(lp, LDC_INFO, LDC_RTS, NULL, 0,				   &new_tail);	if (p) {		p->env = lp->cfg.mode;		p->seqid = 0;		lp->rcv_nxt = 0; 		ldcdbg(HS, "SEND RTS env[0x%x] seqid[0x%x]\n",		       p->env, p->seqid); 		return send_tx_packet(lp, p, new_tail);	}	return -EBUSY;} static int send_rtr(struct ldc_channel *lp){	struct ldc_packet *p;	unsigned long new_tail; 	p = handshake_compose_ctrl(lp, LDC_INFO, LDC_RTR, NULL, 0,				   &new_tail);	if (p) {		p->env = lp->cfg.mode;		p->seqid = 0; 		ldcdbg(HS, "SEND RTR env[0x%x] seqid[0x%x]\n",		       p->env, p->seqid); 		return send_tx_packet(lp, p, new_tail);	}	return -EBUSY;} static int send_rdx(struct ldc_channel *lp){	struct ldc_packet *p;	unsigned long new_tail; 	p = handshake_compose_ctrl(lp, LDC_INFO, LDC_RDX, NULL, 0,				   &new_tail);	if (p) {		p->env = 0;		p->seqid = ++lp->snd_nxt;		p->u.r.ackid = lp->rcv_nxt; 		ldcdbg(HS, "SEND RDX env[0x%x] seqid[0x%x] ackid[0x%x]\n",		       p->env, p->seqid, p->u.r.ackid); 		return send_tx_packet(lp, p, new_tail);	}	return -EBUSY;} static int send_data_nack(struct ldc_channel *lp, struct ldc_packet *data_pkt){	struct ldc_packet *p;	unsigned long new_tail;	int err; 	p = data_get_tx_packet(lp, &new_tail);	if (!p)		return -EBUSY;	memset(p, 0, sizeof(*p));	p->type = data_pkt->type;	p->stype = LDC_NACK;	p->ctrl = data_pkt->ctrl & LDC_CTRL_MSK;	p->seqid = lp->snd_nxt + 1;	p->u.r.ackid = lp->rcv_nxt;

Lines 1–500 of 2428.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t