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 + -
显示快捷键?