ldc.c

来自「linux 内核源代码」· C语言 代码 · 共 2,379 行 · 第 1/4 页

C
2,379
字号
/* ldc.c: Logical Domain Channel link-layer protocol driver. * * Copyright (C) 2007 David S. Miller <davem@davemloft.net> */#include <linux/kernel.h>#include <linux/module.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 <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.0"#define DRV_MODULE_RELDATE	"June 25, 2007"static char version[] __devinitdata =	DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";#define LDC_PACKET_SIZE		64/* 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 arena alloc/free.  */	spinlock_t			lock;	struct iommu_arena		arena;	struct ldc_mtable_entry		*page_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)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 void ldc_set_state(struct ldc_channel *lp, u8 state){	ldcdbg(STATE, "STATE (%s) --> (%s)\n",	       state_to_str(lp->state),	       state_to_str(state));	lp->state = state;}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;	ldcdbg(HS, "SEND DATA NACK type[0x%x] ctl[0x%x] seq[0x%x] ack[0x%x]\n",	       p->type, p->ctrl, p->seqid, p->u.r.ackid);	err = send_tx_packet(lp, p, new_tail);	if (!err)		lp->snd_nxt++;	return err;}static int ldc_abort(struct ldc_channel *lp){	unsigned long hv_err;	ldcdbg(STATE, "ABORT\n");	/* We report but do not act upon the hypervisor errors because	 * there really isn't much we can do if they fail at this point.	 */	hv_err = sun4v_ldc_tx_qconf(lp->id, lp->tx_ra, lp->tx_num_entries);	if (hv_err)		printk(KERN_ERR PFX "ldc_abort: "		       "sun4v_ldc_tx_qconf(%lx,%lx,%lx) failed, err=%lu\n",		       lp->id, lp->tx_ra, lp->tx_num_entries, hv_err);	hv_err = sun4v_ldc_tx_get_state(lp->id,					&lp->tx_head,					&lp->tx_tail,					&lp->chan_state);	if (hv_err)		printk(KERN_ERR PFX "ldc_abort: "		       "sun4v_ldc_tx_get_state(%lx,...) failed, err=%lu\n",		       lp->id, hv_err);	hv_err = sun4v_ldc_rx_qconf(lp->id, lp->rx_ra, lp->rx_num_entries);	if (hv_err)		printk(KERN_ERR PFX "ldc_abort: "		       "sun4v_ldc_rx_qconf(%lx,%lx,%lx) failed, err=%lu\n",		       lp->id, lp->rx_ra, lp->rx_num_entries, hv_err);	/* Refetch the RX queue state as well, because we could be invoked	 * here in the queue processing context.	 */	hv_err = sun4v_ldc_rx_get_state(lp->id,					&lp->rx_head,					&lp->rx_tail,					&lp->chan_state);	if (hv_err)		printk(KERN_ERR PFX "ldc_abort: "		       "sun4v_ldc_rx_get_state(%lx,...) failed, err=%lu\n",		       lp->id, hv_err);	return -ECONNRESET;}static struct ldc_version *find_by_major(u16 major){	struct ldc_version *ret = NULL;	int i;	for (i = 0; i < ARRAY_SIZE(ver_arr); i++) {		struct ldc_version *v = &ver_arr[i];		if (v->major <= major) {			ret = v;			break;		}	}	return ret;}static int process_ver_info(struct ldc_channel *lp, struct ldc_version *vp){	struct ldc_version *vap;	int err;	ldcdbg(HS, "GOT VERSION INFO major[%x] minor[%x]\n",	       vp->major, vp->minor);	if (lp->hs_state == LDC_HS_GOTVERS) {		lp->hs_state = LDC_HS_OPEN;		memset(&lp->ver, 0, sizeof(lp->ver));	}	vap = find_by_major(vp->major);	if (!vap) {		err = send_version_nack(lp, 0, 0);	} else if (vap->major != vp->major) {		err = send_version_nack(lp, vap->major, vap->minor);	} else {		struct ldc_version ver = *vp;		if (ver.minor > vap->minor)			ver.minor = vap->minor;		err = send_version_ack(lp, &ver);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?