static int send_dreg(struct vio_driver_state *vio) { struct vio_dring_state *dr = &vio->drings[VIO_DRIVER_TX_RING]; union { struct vio_dring_register pkt; char all[sizeof(struct vio_dring_register) + (sizeof(struct ldc_trans_cookie) * dr->ncookies)]; } u; int i; memset(&u, 0, sizeof(u)); init_tag(&u.pkt.tag, VIO_TYPE_CTRL, VIO_SUBTYPE_INFO, VIO_DRING_REG); u.pkt.dring_ident = 0; u.pkt.num_descr = dr->num_entries; u.pkt.descr_size = dr->entry_size; u.pkt.options = VIO_TX_DRING; u.pkt.num_cookies = dr->ncookies; viodbg(HS, "SEND DRING_REG INFO ndesc[%u] dsz[%u] opt[0x%x] " "ncookies[%u]\n", u.pkt.num_descr, u.pkt.descr_size, u.pkt.options, u.pkt.num_cookies); for (i = 0; i < dr->ncookies; i++) { u.pkt.cookies[i] = dr->cookies[i]; viodbg(HS, "DRING COOKIE(%d) [%016llx:%016llx]\n", i, (unsigned long long) u.pkt.cookies[i].cookie_addr, (unsigned long long) u.pkt.cookies[i].cookie_size); } return send_ctrl(vio, &u.pkt.tag, sizeof(u)); }
static int process_rdx_info(struct vio_driver_state *vio, struct vio_rdx *pkt) { viodbg(HS, "GOT RDX INFO\n"); pkt->tag.stype = VIO_SUBTYPE_ACK; viodbg(HS, "SEND RDX ACK\n"); if (send_ctrl(vio, &pkt->tag, sizeof(*pkt)) < 0) return handshake_failure(vio); vio->hs_state |= VIO_HS_SENT_RDX_ACK; return 0; }
static int process_dreg_ack(struct vio_driver_state *vio, struct vio_dring_register *pkt) { struct vio_dring_state *dr; viodbg(HS, "GOT DRING_REG ACK ident[%llx] " "ndesc[%u] dsz[%u] opt[0x%x] ncookies[%u]\n", (unsigned long long) pkt->dring_ident, pkt->num_descr, pkt->descr_size, pkt->options, pkt->num_cookies); dr = &vio->drings[VIO_DRIVER_TX_RING]; if (!(vio->dr_state & VIO_DR_STATE_TXREQ)) return handshake_failure(vio); dr->ident = pkt->dring_ident; vio->dr_state |= VIO_DR_STATE_TXREG; if (all_drings_registered(vio)) { if (send_rdx(vio) < 0) return handshake_failure(vio); vio->hs_state = VIO_HS_SENT_RDX; } return 0; }
static int handshake_failure(struct vio_driver_state *vio) { struct vio_dring_state *dr; /* XXX Put policy here... Perhaps start a timer to fire * XXX in 100 ms, which will bring the link up and retry * XXX the handshake. */ viodbg(HS, "HANDSHAKE FAILURE\n"); vio->dr_state &= ~(VIO_DR_STATE_TXREG | VIO_DR_STATE_RXREG); dr = &vio->drings[VIO_DRIVER_RX_RING]; memset(dr, 0, sizeof(*dr)); kfree(vio->desc_buf); vio->desc_buf = NULL; vio->desc_buf_len = 0; vio->hs_state = VIO_HS_INVALID; return -ECONNRESET; }
/* The issue is that the Solaris virtual disk server just mirrors the * SID values it gets from the client peer. So we work around that * here in vio_{validate,send}_sid() so that the drivers don't need * to be aware of this crap. */ int vio_validate_sid(struct vio_driver_state *vio, struct vio_msg_tag *tp) { u32 sid; /* Always let VERSION+INFO packets through unchecked, they * define the new SID. */ if (tp->type == VIO_TYPE_CTRL && tp->stype == VIO_SUBTYPE_INFO && tp->stype_env == VIO_VER_INFO) return 0; /* Ok, now figure out which SID to use. */ switch (vio->dev_class) { case VDEV_NETWORK: case VDEV_NETWORK_SWITCH: case VDEV_DISK_SERVER: default: sid = vio->_peer_sid; break; case VDEV_DISK: sid = vio->_local_sid; break; } if (sid == tp->sid) return 0; viodbg(DATA, "BAD SID tag->sid[%08x] peer_sid[%08x] local_sid[%08x]\n", tp->sid, vio->_peer_sid, vio->_local_sid); return -EINVAL; }
static int process_rdx_ack(struct vio_driver_state *vio, struct vio_rdx *pkt) { viodbg(HS, "GOT RDX ACK\n"); if (!(vio->hs_state & VIO_HS_SENT_RDX)) return handshake_failure(vio); vio->hs_state |= VIO_HS_GOT_RDX_ACK; return 0; }
static int process_dreg_nack(struct vio_driver_state *vio, struct vio_dring_register *pkt) { viodbg(HS, "GOT DRING_REG NACK ident[%llx] " "ndesc[%u] dsz[%u] opt[0x%x] ncookies[%u]\n", (unsigned long long) pkt->dring_ident, pkt->num_descr, pkt->descr_size, pkt->options, pkt->num_cookies); return handshake_failure(vio); }
static int send_rdx(struct vio_driver_state *vio) { struct vio_rdx pkt; memset(&pkt, 0, sizeof(pkt)); init_tag(&pkt.tag, VIO_TYPE_CTRL, VIO_SUBTYPE_INFO, VIO_RDX); viodbg(HS, "SEND RDX INFO\n"); return send_ctrl(vio, &pkt.tag, sizeof(pkt)); }
static int process_unknown(struct vio_driver_state *vio, void *arg) { struct vio_msg_tag *pkt = arg; viodbg(HS, "UNKNOWN CONTROL [%02x:%02x:%04x:%08x]\n", pkt->type, pkt->stype, pkt->stype_env, pkt->sid); printk(KERN_ERR "vio: ID[%lu] Resetting connection.\n", vio->vdev->channel_id); ldc_disconnect(vio->lp); return -ECONNRESET; }
static int start_handshake(struct vio_driver_state *vio) { int err; viodbg(HS, "START HANDSHAKE\n"); vio->hs_state = VIO_HS_INVALID; err = send_version(vio, vio->ver_table[0].major, vio->ver_table[0].minor); if (err < 0) return err; return 0; }
static int send_version(struct vio_driver_state *vio, u16 major, u16 minor) { struct vio_ver_info pkt; vio->_local_sid = (u32) sched_clock(); memset(&pkt, 0, sizeof(pkt)); init_tag(&pkt.tag, VIO_TYPE_CTRL, VIO_SUBTYPE_INFO, VIO_VER_INFO); pkt.major = major; pkt.minor = minor; pkt.dev_class = vio->dev_class; viodbg(HS, "SEND VERSION INFO maj[%u] min[%u] devclass[%u]\n", major, minor, vio->dev_class); return send_ctrl(vio, &pkt.tag, sizeof(pkt)); }
static int process_ver_nack(struct vio_driver_state *vio, struct vio_ver_info *pkt) { struct vio_version *nver; viodbg(HS, "GOT VERSION NACK maj[%u] min[%u] devclass[%u]\n", pkt->major, pkt->minor, pkt->dev_class); if (pkt->major == 0 && pkt->minor == 0) return handshake_failure(vio); nver = find_by_major(vio, pkt->major); if (!nver) return handshake_failure(vio); if (send_version(vio, nver->major, nver->minor) < 0) return handshake_failure(vio); return 0; }
static int process_dunreg(struct vio_driver_state *vio, struct vio_dring_unregister *pkt) { struct vio_dring_state *dr = &vio->drings[VIO_DRIVER_RX_RING]; viodbg(HS, "GOT DRING_UNREG\n"); if (pkt->dring_ident != dr->ident) return 0; vio->dr_state &= ~VIO_DR_STATE_RXREG; memset(dr, 0, sizeof(*dr)); kfree(vio->desc_buf); vio->desc_buf = NULL; vio->desc_buf_len = 0; return 0; }
static int handshake_failure(struct vio_driver_state *vio) { struct vio_dring_state *dr; viodbg(HS, "HANDSHAKE FAILURE\n"); vio->dr_state &= ~(VIO_DR_STATE_TXREG | VIO_DR_STATE_RXREG); dr = &vio->drings[VIO_DRIVER_RX_RING]; memset(dr, 0, sizeof(*dr)); kfree(vio->desc_buf); vio->desc_buf = NULL; vio->desc_buf_len = 0; vio->hs_state = VIO_HS_INVALID; return -ECONNRESET; }
static int process_ver_info(struct vio_driver_state *vio, struct vio_ver_info *pkt) { struct vio_version *vap; int err; viodbg(HS, "GOT VERSION INFO maj[%u] min[%u] devclass[%u]\n", pkt->major, pkt->minor, pkt->dev_class); if (vio->hs_state != VIO_HS_INVALID) { /* XXX Perhaps invoke start_handshake? XXX */ memset(&vio->ver, 0, sizeof(vio->ver)); vio->hs_state = VIO_HS_INVALID; } vap = find_by_major(vio, pkt->major); vio->_peer_sid = pkt->tag.sid; if (!vap) { pkt->tag.stype = VIO_SUBTYPE_NACK; pkt->major = 0; pkt->minor = 0; viodbg(HS, "SEND VERSION NACK maj[0] min[0]\n"); err = send_ctrl(vio, &pkt->tag, sizeof(*pkt)); } else if (vap->major != pkt->major) { pkt->tag.stype = VIO_SUBTYPE_NACK; pkt->major = vap->major; pkt->minor = vap->minor; viodbg(HS, "SEND VERSION NACK maj[%u] min[%u]\n", pkt->major, pkt->minor); err = send_ctrl(vio, &pkt->tag, sizeof(*pkt)); } else { struct vio_version ver = { .major = pkt->major, .minor = pkt->minor, }; if (ver.minor > vap->minor) ver.minor = vap->minor; pkt->minor = ver.minor; pkt->tag.stype = VIO_SUBTYPE_ACK; viodbg(HS, "SEND VERSION ACK maj[%u] min[%u]\n", pkt->major, pkt->minor); err = send_ctrl(vio, &pkt->tag, sizeof(*pkt)); if (err > 0) { vio->ver = ver; vio->hs_state = VIO_HS_GOTVERS; } } if (err < 0) return handshake_failure(vio); return 0; } static int process_ver_ack(struct vio_driver_state *vio, struct vio_ver_info *pkt) { viodbg(HS, "GOT VERSION ACK maj[%u] min[%u] devclass[%u]\n", pkt->major, pkt->minor, pkt->dev_class); if (vio->hs_state & VIO_HS_GOTVERS) { if (vio->ver.major != pkt->major || vio->ver.minor != pkt->minor) { pkt->tag.stype = VIO_SUBTYPE_NACK; (void) send_ctrl(vio, &pkt->tag, sizeof(*pkt)); return handshake_failure(vio); } } else { vio->ver.major = pkt->major; vio->ver.minor = pkt->minor; vio->hs_state = VIO_HS_GOTVERS; } switch (vio->dev_class) { case VDEV_NETWORK: case VDEV_DISK: if (send_attr(vio) < 0) return handshake_failure(vio); break; default: break; } return 0; }
static int process_rdx_nack(struct vio_driver_state *vio, struct vio_rdx *pkt) { viodbg(HS, "GOT RDX NACK\n"); return handshake_failure(vio); }
static int process_dreg_info(struct vio_driver_state *vio, struct vio_dring_register *pkt) { struct vio_dring_state *dr; int i, len; viodbg(HS, "GOT DRING_REG INFO ident[%llx] " "ndesc[%u] dsz[%u] opt[0x%x] ncookies[%u]\n", (unsigned long long) pkt->dring_ident, pkt->num_descr, pkt->descr_size, pkt->options, pkt->num_cookies); if (!(vio->dr_state & VIO_DR_STATE_RXREQ)) goto send_nack; if (vio->dr_state & VIO_DR_STATE_RXREG) goto send_nack; BUG_ON(vio->desc_buf); vio->desc_buf = kzalloc(pkt->descr_size, GFP_ATOMIC); if (!vio->desc_buf) goto send_nack; vio->desc_buf_len = pkt->descr_size; dr = &vio->drings[VIO_DRIVER_RX_RING]; dr->num_entries = pkt->num_descr; dr->entry_size = pkt->descr_size; dr->ncookies = pkt->num_cookies; for (i = 0; i < dr->ncookies; i++) { dr->cookies[i] = pkt->cookies[i]; viodbg(HS, "DRING COOKIE(%d) [%016llx:%016llx]\n", i, (unsigned long long) pkt->cookies[i].cookie_addr, (unsigned long long) pkt->cookies[i].cookie_size); } pkt->tag.stype = VIO_SUBTYPE_ACK; pkt->dring_ident = ++dr->ident; viodbg(HS, "SEND DRING_REG ACK ident[%llx]\n", (unsigned long long) pkt->dring_ident); len = (sizeof(*pkt) + (dr->ncookies * sizeof(struct ldc_trans_cookie))); if (send_ctrl(vio, &pkt->tag, len) < 0) goto send_nack; vio->dr_state |= VIO_DR_STATE_RXREG; return 0; send_nack: pkt->tag.stype = VIO_SUBTYPE_NACK; viodbg(HS, "SEND DRING_REG NACK\n"); (void) send_ctrl(vio, &pkt->tag, sizeof(*pkt)); return handshake_failure(vio); }