43fdf27470
Since we have to be able to handle MD updates, having an in-tree set of data structures representing the MD objects actually makes things more painful. The MD itself is easy to parse, and we can implement the existing interfaces using direct parsing of the MD binary image. The MD is now reference counted, so accesses have to now take the form: handle = mdesc_grab(); ... operations on MD ... mdesc_release(handle); The only remaining issue are cases where code holds on to references to MD property values. mdesc_get_property() returns a direct pointer to the property value, most cases just pull in the information they need and discard the pointer, but there are few that use the pointer directly over a long lifetime. Those will be fixed up in a subsequent changeset. A preliminary handler for MD update events from domain services is there, it is rudimentry but it works and handles all of the reference counting. It does not check the generation number of the MDs, and it does not generate a "add/delete" list for notification to interesting parties about MD changes but that will be forthcoming. Signed-off-by: David S. Miller <davem@davemloft.net>
793 lines
17 KiB
C
793 lines
17 KiB
C
/* viohs.c: LDOM Virtual I/O handshake helper layer.
|
|
*
|
|
* Copyright (C) 2007 David S. Miller <davem@davemloft.net>
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/string.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/slab.h>
|
|
|
|
#include <asm/ldc.h>
|
|
#include <asm/vio.h>
|
|
|
|
int vio_ldc_send(struct vio_driver_state *vio, void *data, int len)
|
|
{
|
|
int err, limit = 1000;
|
|
|
|
err = -EINVAL;
|
|
while (limit-- > 0) {
|
|
err = ldc_write(vio->lp, data, len);
|
|
if (!err || (err != -EAGAIN))
|
|
break;
|
|
udelay(1);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL(vio_ldc_send);
|
|
|
|
static int send_ctrl(struct vio_driver_state *vio,
|
|
struct vio_msg_tag *tag, int len)
|
|
{
|
|
tag->sid = vio_send_sid(vio);
|
|
return vio_ldc_send(vio, tag, len);
|
|
}
|
|
|
|
static void init_tag(struct vio_msg_tag *tag, u8 type, u8 stype, u16 stype_env)
|
|
{
|
|
tag->type = type;
|
|
tag->stype = stype;
|
|
tag->stype_env = stype_env;
|
|
}
|
|
|
|
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 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;
|
|
}
|
|
|
|
void vio_link_state_change(struct vio_driver_state *vio, int event)
|
|
{
|
|
if (event == LDC_EVENT_UP) {
|
|
vio->hs_state = VIO_HS_INVALID;
|
|
|
|
switch (vio->dev_class) {
|
|
case VDEV_NETWORK:
|
|
case VDEV_NETWORK_SWITCH:
|
|
vio->dr_state = (VIO_DR_STATE_TXREQ |
|
|
VIO_DR_STATE_RXREQ);
|
|
break;
|
|
|
|
case VDEV_DISK:
|
|
vio->dr_state = VIO_DR_STATE_TXREQ;
|
|
break;
|
|
case VDEV_DISK_SERVER:
|
|
vio->dr_state = VIO_DR_STATE_RXREQ;
|
|
break;
|
|
}
|
|
start_handshake(vio);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(vio_link_state_change);
|
|
|
|
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;
|
|
}
|
|
|
|
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 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 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 send_attr(struct vio_driver_state *vio)
|
|
{
|
|
return vio->ops->send_attr(vio);
|
|
}
|
|
|
|
static struct vio_version *find_by_major(struct vio_driver_state *vio,
|
|
u16 major)
|
|
{
|
|
struct vio_version *ret = NULL;
|
|
int i;
|
|
|
|
for (i = 0; i < vio->ver_table_entries; i++) {
|
|
struct vio_version *v = &vio->ver_table[i];
|
|
if (v->major <= major) {
|
|
ret = v;
|
|
break;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
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_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) ||
|
|
!(nver = find_by_major(vio, pkt->major)))
|
|
return handshake_failure(vio);
|
|
|
|
if (send_version(vio, nver->major, nver->minor) < 0)
|
|
return handshake_failure(vio);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int process_ver(struct vio_driver_state *vio, struct vio_ver_info *pkt)
|
|
{
|
|
switch (pkt->tag.stype) {
|
|
case VIO_SUBTYPE_INFO:
|
|
return process_ver_info(vio, pkt);
|
|
|
|
case VIO_SUBTYPE_ACK:
|
|
return process_ver_ack(vio, pkt);
|
|
|
|
case VIO_SUBTYPE_NACK:
|
|
return process_ver_nack(vio, pkt);
|
|
|
|
default:
|
|
return handshake_failure(vio);
|
|
};
|
|
}
|
|
|
|
static int process_attr(struct vio_driver_state *vio, void *pkt)
|
|
{
|
|
int err;
|
|
|
|
if (!(vio->hs_state & VIO_HS_GOTVERS))
|
|
return handshake_failure(vio);
|
|
|
|
err = vio->ops->handle_attr(vio, pkt);
|
|
if (err < 0) {
|
|
return handshake_failure(vio);
|
|
} else {
|
|
vio->hs_state |= VIO_HS_GOT_ATTR;
|
|
|
|
if ((vio->dr_state & VIO_DR_STATE_TXREQ) &&
|
|
!(vio->hs_state & VIO_HS_SENT_DREG)) {
|
|
if (send_dreg(vio) < 0)
|
|
return handshake_failure(vio);
|
|
|
|
vio->hs_state |= VIO_HS_SENT_DREG;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int all_drings_registered(struct vio_driver_state *vio)
|
|
{
|
|
int need_rx, need_tx;
|
|
|
|
need_rx = (vio->dr_state & VIO_DR_STATE_RXREQ);
|
|
need_tx = (vio->dr_state & VIO_DR_STATE_TXREQ);
|
|
|
|
if (need_rx &&
|
|
!(vio->dr_state & VIO_DR_STATE_RXREG))
|
|
return 0;
|
|
|
|
if (need_tx &&
|
|
!(vio->dr_state & VIO_DR_STATE_TXREG))
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
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;
|
|
|
|
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);
|
|
}
|
|
|
|
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 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 process_dreg(struct vio_driver_state *vio,
|
|
struct vio_dring_register *pkt)
|
|
{
|
|
if (!(vio->hs_state & VIO_HS_GOTVERS))
|
|
return handshake_failure(vio);
|
|
|
|
switch (pkt->tag.stype) {
|
|
case VIO_SUBTYPE_INFO:
|
|
return process_dreg_info(vio, pkt);
|
|
|
|
case VIO_SUBTYPE_ACK:
|
|
return process_dreg_ack(vio, pkt);
|
|
|
|
case VIO_SUBTYPE_NACK:
|
|
return process_dreg_nack(vio, pkt);
|
|
|
|
default:
|
|
return handshake_failure(vio);
|
|
}
|
|
}
|
|
|
|
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 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_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_rdx_nack(struct vio_driver_state *vio, struct vio_rdx *pkt)
|
|
{
|
|
viodbg(HS, "GOT RDX NACK\n");
|
|
|
|
return handshake_failure(vio);
|
|
}
|
|
|
|
static int process_rdx(struct vio_driver_state *vio, struct vio_rdx *pkt)
|
|
{
|
|
if (!all_drings_registered(vio))
|
|
handshake_failure(vio);
|
|
|
|
switch (pkt->tag.stype) {
|
|
case VIO_SUBTYPE_INFO:
|
|
return process_rdx_info(vio, pkt);
|
|
|
|
case VIO_SUBTYPE_ACK:
|
|
return process_rdx_ack(vio, pkt);
|
|
|
|
case VIO_SUBTYPE_NACK:
|
|
return process_rdx_nack(vio, pkt);
|
|
|
|
default:
|
|
return handshake_failure(vio);
|
|
}
|
|
}
|
|
|
|
int vio_control_pkt_engine(struct vio_driver_state *vio, void *pkt)
|
|
{
|
|
struct vio_msg_tag *tag = pkt;
|
|
u8 prev_state = vio->hs_state;
|
|
int err;
|
|
|
|
switch (tag->stype_env) {
|
|
case VIO_VER_INFO:
|
|
err = process_ver(vio, pkt);
|
|
break;
|
|
|
|
case VIO_ATTR_INFO:
|
|
err = process_attr(vio, pkt);
|
|
break;
|
|
|
|
case VIO_DRING_REG:
|
|
err = process_dreg(vio, pkt);
|
|
break;
|
|
|
|
case VIO_DRING_UNREG:
|
|
err = process_dunreg(vio, pkt);
|
|
break;
|
|
|
|
case VIO_RDX:
|
|
err = process_rdx(vio, pkt);
|
|
break;
|
|
|
|
default:
|
|
err = process_unknown(vio, pkt);
|
|
break;
|
|
}
|
|
if (!err &&
|
|
vio->hs_state != prev_state &&
|
|
(vio->hs_state & VIO_HS_COMPLETE))
|
|
vio->ops->handshake_complete(vio);
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL(vio_control_pkt_engine);
|
|
|
|
void vio_conn_reset(struct vio_driver_state *vio)
|
|
{
|
|
}
|
|
EXPORT_SYMBOL(vio_conn_reset);
|
|
|
|
/* 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;
|
|
}
|
|
EXPORT_SYMBOL(vio_validate_sid);
|
|
|
|
u32 vio_send_sid(struct vio_driver_state *vio)
|
|
{
|
|
switch (vio->dev_class) {
|
|
case VDEV_NETWORK:
|
|
case VDEV_NETWORK_SWITCH:
|
|
case VDEV_DISK:
|
|
default:
|
|
return vio->_local_sid;
|
|
|
|
case VDEV_DISK_SERVER:
|
|
return vio->_peer_sid;
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(vio_send_sid);
|
|
|
|
extern int vio_ldc_alloc(struct vio_driver_state *vio,
|
|
struct ldc_channel_config *base_cfg,
|
|
void *event_arg)
|
|
{
|
|
struct ldc_channel_config cfg = *base_cfg;
|
|
struct ldc_channel *lp;
|
|
|
|
cfg.tx_irq = vio->vdev->tx_irq;
|
|
cfg.rx_irq = vio->vdev->rx_irq;
|
|
|
|
lp = ldc_alloc(vio->vdev->channel_id, &cfg, event_arg);
|
|
if (IS_ERR(lp))
|
|
return PTR_ERR(lp);
|
|
|
|
vio->lp = lp;
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(vio_ldc_alloc);
|
|
|
|
void vio_ldc_free(struct vio_driver_state *vio)
|
|
{
|
|
ldc_free(vio->lp);
|
|
vio->lp = NULL;
|
|
|
|
kfree(vio->desc_buf);
|
|
vio->desc_buf = NULL;
|
|
vio->desc_buf_len = 0;
|
|
}
|
|
EXPORT_SYMBOL(vio_ldc_free);
|
|
|
|
void vio_port_up(struct vio_driver_state *vio)
|
|
{
|
|
unsigned long flags;
|
|
int err, state;
|
|
|
|
spin_lock_irqsave(&vio->lock, flags);
|
|
|
|
state = ldc_state(vio->lp);
|
|
|
|
err = 0;
|
|
if (state == LDC_STATE_INIT) {
|
|
err = ldc_bind(vio->lp, vio->name);
|
|
if (err)
|
|
printk(KERN_WARNING "%s: Port %lu bind failed, "
|
|
"err=%d\n",
|
|
vio->name, vio->vdev->channel_id, err);
|
|
}
|
|
|
|
if (!err) {
|
|
err = ldc_connect(vio->lp);
|
|
if (err)
|
|
printk(KERN_WARNING "%s: Port %lu connect failed, "
|
|
"err=%d\n",
|
|
vio->name, vio->vdev->channel_id, err);
|
|
}
|
|
if (err) {
|
|
unsigned long expires = jiffies + HZ;
|
|
|
|
expires = round_jiffies(expires);
|
|
mod_timer(&vio->timer, expires);
|
|
}
|
|
|
|
spin_unlock_irqrestore(&vio->lock, flags);
|
|
}
|
|
EXPORT_SYMBOL(vio_port_up);
|
|
|
|
static void vio_port_timer(unsigned long _arg)
|
|
{
|
|
struct vio_driver_state *vio = (struct vio_driver_state *) _arg;
|
|
|
|
vio_port_up(vio);
|
|
}
|
|
|
|
int vio_driver_init(struct vio_driver_state *vio, struct vio_dev *vdev,
|
|
u8 dev_class, struct vio_version *ver_table,
|
|
int ver_table_size, struct vio_driver_ops *ops,
|
|
char *name)
|
|
{
|
|
switch (dev_class) {
|
|
case VDEV_NETWORK:
|
|
case VDEV_NETWORK_SWITCH:
|
|
case VDEV_DISK:
|
|
case VDEV_DISK_SERVER:
|
|
break;
|
|
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!ops->send_attr ||
|
|
!ops->handle_attr ||
|
|
!ops->handshake_complete)
|
|
return -EINVAL;
|
|
|
|
if (!ver_table || ver_table_size < 0)
|
|
return -EINVAL;
|
|
|
|
if (!name)
|
|
return -EINVAL;
|
|
|
|
spin_lock_init(&vio->lock);
|
|
|
|
vio->name = name;
|
|
|
|
vio->dev_class = dev_class;
|
|
vio->vdev = vdev;
|
|
|
|
vio->ver_table = ver_table;
|
|
vio->ver_table_entries = ver_table_size;
|
|
|
|
vio->ops = ops;
|
|
|
|
setup_timer(&vio->timer, vio_port_timer, (unsigned long) vio);
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(vio_driver_init);
|