summaryrefslogtreecommitdiff
path: root/usr/src/lib/libpcp/common/libpcp.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libpcp/common/libpcp.c')
-rw-r--r--usr/src/lib/libpcp/common/libpcp.c300
1 files changed, 276 insertions, 24 deletions
diff --git a/usr/src/lib/libpcp/common/libpcp.c b/usr/src/lib/libpcp/common/libpcp.c
index 9d32387ff0..6c9eb09263 100644
--- a/usr/src/lib/libpcp/common/libpcp.c
+++ b/usr/src/lib/libpcp/common/libpcp.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -48,6 +47,8 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/glvc.h>
+#include <sys/vldc.h>
+#include <sys/ldc.h>
#include <netinet/in.h>
#include "libpcp.h"
@@ -81,6 +82,11 @@ static int check_magic_byte_presence(int byte_cnt, uint8_t *byte_val,
static uint16_t checksum(uint16_t *addr, int32_t count);
static int pcp_cleanup(int channel_fd);
+static int vldc_read(int fd, uint8_t *bufp, int size);
+static int vldc_write(int fd, uint8_t *bufp, int size);
+static int pcp_update_read_area(int byte_cnt);
+static int pcp_vldc_frame_error_handle(void);
+
/*
* local channel (glvc) file descriptor set by pcp_send_recv()
*/
@@ -156,6 +162,19 @@ static struct sigaction glvc_act;
/* To restore old SIGALRM signal handler */
static struct sigaction old_act;
+/*
+ * Variables to support vldc based streaming transport
+ */
+typedef enum {
+ GLVC_NON_STREAM,
+ VLDC_STREAMING
+} xport_t;
+
+static int xport_type = GLVC_NON_STREAM;
+#define CHANNEL_DEV "channel-devices"
+
+#define VLDC_MTU_SIZE (2048)
+
static void
glvc_timeout_handler(void)
{
@@ -178,6 +197,7 @@ pcp_init(char *channel_name)
if (channel_name == NULL)
return (PCPL_INVALID_ARGS);
+
/*
* Open virtual channel name.
*/
@@ -186,12 +206,33 @@ pcp_init(char *channel_name)
}
/*
- * Get the Channel MTU size
+ * Check if the channel-name points to a vldc node
+ * or a glvc node
*/
+ if (strstr(channel_name, CHANNEL_DEV) != NULL) {
+ vldc_opt_op_t op;
+
+ xport_type = VLDC_STREAMING;
+ mtu_size = VLDC_MTU_SIZE;
+
+ op.op_sel = VLDC_OP_SET;
+ op.opt_sel = VLDC_OPT_MODE;
+ op.opt_val = LDC_MODE_STREAM;
+ if (ioctl(channel_fd, VLDC_IOCTL_OPT_OP, &op) != 0) {
+ (void) close(channel_fd);
+ return (PCPL_GLVC_ERROR);
+ }
+ } else {
+ xport_type = GLVC_NON_STREAM;
+ /*
+ * Get the Channel MTU size
+ */
- if (pcp_get_prop(channel_fd, GLVC_XPORT_OPT_MTU_SZ, &mtu_size) != 0) {
- (void) close(channel_fd);
- return (PCPL_GLVC_ERROR);
+ if (pcp_get_prop(channel_fd, GLVC_XPORT_OPT_MTU_SZ,
+ &mtu_size) != 0) {
+ (void) close(channel_fd);
+ return (PCPL_GLVC_ERROR);
+ }
}
/*
@@ -233,7 +274,8 @@ pcp_close(int channel_fd)
{
if (channel_fd >= 0) {
- (void) pcp_cleanup(channel_fd);
+ if (xport_type == GLVC_NON_STREAM)
+ (void) pcp_cleanup(channel_fd);
(void) close(channel_fd);
} else {
return (-1);
@@ -631,7 +673,6 @@ pcp_peek(uint8_t *buf, int bytes_cnt)
(void) memcpy(buf, peek_area, m);
return (m);
-
}
/*
@@ -648,13 +689,19 @@ pcp_write(uint8_t *buf, int byte_cnt)
return (PCPL_INVALID_ARGS);
}
- (void) alarm(glvc_timeout);
+ if (xport_type == GLVC_NON_STREAM) {
+ (void) alarm(glvc_timeout);
- if ((ret = write(chnl_fd, buf, byte_cnt)) < 0) {
+ if ((ret = write(chnl_fd, buf, byte_cnt)) < 0) {
+ (void) alarm(0);
+ return (ret);
+ }
(void) alarm(0);
- return (ret);
+ } else {
+ if ((ret = vldc_write(chnl_fd, buf, byte_cnt)) <= 0) {
+ return (ret);
+ }
}
- (void) alarm(0);
return (ret);
}
@@ -718,17 +765,28 @@ pcp_read(uint8_t *buf, int byte_cnt)
* do a peek to see how much data is available and read complete data.
*/
- if ((m = pcp_peek(read_tail, mtu_size)) < 0) {
- return (m);
- }
+ if (xport_type == GLVC_NON_STREAM) {
+ if ((m = pcp_peek(read_tail, mtu_size)) < 0) {
+ return (m);
+ }
+
+ (void) alarm(glvc_timeout);
+ if ((ret = read(chnl_fd, read_tail, m)) < 0) {
+ (void) alarm(0);
+ return (ret);
+ }
- (void) alarm(glvc_timeout);
- if ((ret = read(chnl_fd, read_tail, m)) < 0) {
(void) alarm(0);
- return (ret);
+ } else {
+ /*
+ * Read the extra number of bytes
+ */
+ m = byte_cnt - (read_tail - read_head);
+ if ((ret = vldc_read(chnl_fd,
+ read_tail, m)) <= 0) {
+ return (ret);
+ }
}
-
- (void) alarm(0);
read_tail += ret;
/*
@@ -743,6 +801,69 @@ pcp_read(uint8_t *buf, int byte_cnt)
}
/*
+ * Issue read from the driver until byet_cnt number
+ * of bytes are present in read buffer. Do not
+ * move the read head.
+ */
+static int
+pcp_update_read_area(int byte_cnt)
+{
+ int ret;
+ int n, i;
+
+ if (byte_cnt < 0 || byte_cnt > mtu_size) {
+ return (PCPL_INVALID_ARGS);
+ }
+
+ /*
+ * initialization of local read buffer
+ * from which the stream read requests are serviced.
+ */
+ if (read_area == NULL) {
+ read_area = (uint8_t *)umem_zalloc(READ_AREA_SIZE,
+ UMEM_DEFAULT);
+ if (read_area == NULL) {
+ return (PCPL_MALLOC_FAIL);
+ }
+ read_head = read_area;
+ read_tail = read_area;
+ }
+
+ /*
+ * if we already have sufficient data in the buffer,
+ * just return
+ */
+ if (byte_cnt <= (read_tail - read_head)) {
+ return (byte_cnt);
+ }
+
+ /*
+ * if the request is not satisfied from the buffered data, then move the
+ * remaining data to front of the buffer and read new data.
+ */
+ for (i = 0; i < (read_tail - read_head); ++i) {
+ read_area[i] = read_head[i];
+ }
+ read_head = read_area;
+ read_tail = read_head + i;
+
+ n = byte_cnt - (read_tail - read_head);
+
+ if ((ret = vldc_read(chnl_fd,
+ read_tail, n)) <= 0) {
+ return (ret);
+ }
+ read_tail += ret;
+
+ /*
+ * Return the number of bytes we could read
+ */
+ n = MIN(byte_cnt, (read_tail - read_head));
+
+ return (n);
+}
+
+/*
* This function is slight different from pcp_peek. The peek requests are first
* serviced from local read buffer, if data is available. If the peek request
* is not serviceble from local read buffer, then the data is peeked from
@@ -798,7 +919,6 @@ pcp_peek_read(uint8_t *buf, int byte_cnt)
if ((m = pcp_peek(peek_read_tail, mtu_size)) < 0) {
return (m);
}
-
peek_read_tail += m;
/*
@@ -874,7 +994,12 @@ pcp_recv_resp_msg_hdr(pcp_resp_msg_hdr_t *resp_hdr)
* (magic seq) or if an error happens while reading data from
* channel.
*/
- if ((ret = pcp_frame_error_handle()) != 0)
+ if (xport_type == GLVC_NON_STREAM)
+ ret = pcp_frame_error_handle();
+ else
+ ret = pcp_vldc_frame_error_handle();
+
+ if (ret != 0)
return (PCPL_FRAME_ERROR);
/* read magic number first */
@@ -1059,6 +1184,55 @@ pcp_frame_error_handle(void)
}
/*
+ * This function handles channel framing errors. It waits until proper
+ * frame with starting sequence as magic numder (0xAFBCAFA0)
+ * is arrived. It removes unexpected data (before the magic number sequence)
+ * on the channel. It returns when proper magic number sequence is seen
+ * or when any failure happens while reading/peeking the channel.
+ */
+static int
+pcp_vldc_frame_error_handle(void)
+{
+ uint8_t magic_num_buf[4];
+ uint32_t net_magic_num; /* magic byte in network byte order */
+ uint32_t host_magic_num = PCP_MAGIC_NUM;
+ int found_magic = 0;
+
+ net_magic_num = htonl(host_magic_num);
+ (void) memcpy(magic_num_buf, (uint8_t *)&net_magic_num, 4);
+
+ /*
+ * For vldc, we need to read whatever data is available and
+ * advance the read pointer one byte at a time until we get
+ * the magic word. When this function is invoked, we do not
+ * have any byte in the read buffer.
+ */
+
+ /*
+ * Keep reading until we find the matching magic number
+ */
+ while (!found_magic) {
+ while ((read_tail - read_head) < sizeof (host_magic_num)) {
+ if (pcp_update_read_area(sizeof (host_magic_num)) < 0)
+ return (-1);
+ }
+
+ /*
+ * We should have at least 4 bytes in read buffer. Check
+ * if the magic number can be matched
+ */
+ if (memcmp(read_head, magic_num_buf,
+ sizeof (host_magic_num))) {
+ read_head += 1;
+ } else {
+ found_magic = 1;
+ }
+ }
+
+ return (0);
+}
+
+/*
* checks whether certain byte sequence is present in the data stream.
*/
static int
@@ -1188,3 +1362,81 @@ pcp_cleanup(int channel_fd)
umem_free(buf, mtu_size);
return (ret);
}
+
+static int
+vldc_write(int fd, uint8_t *bufp, int size)
+{
+ int res;
+ int left = size;
+ pollfd_t pollfd;
+
+ pollfd.events = POLLOUT;
+ pollfd.revents = 0;
+ pollfd.fd = fd;
+
+ /*
+ * Poll for the vldc channel to be ready
+ */
+ if (poll(&pollfd, 1, glvc_timeout * MILLISEC) <= 0) {
+ return (-1);
+ }
+
+ do {
+ if ((res = write(fd, bufp, left)) <= 0) {
+ if (errno != EWOULDBLOCK) {
+ return (res);
+ }
+ } else {
+ bufp += res;
+ left -= res;
+ }
+ } while (left > 0);
+
+ /*
+ * Return number of bytes actually written
+ */
+ return (size - left);
+}
+
+/*
+ * Keep reading until we get the specified number of bytes
+ */
+static int
+vldc_read(int fd, uint8_t *bufp, int size)
+{
+ int res;
+ int left = size;
+
+ struct pollfd fds[1];
+
+ fds[0].events = POLLIN | POLLPRI;
+ fds[0].revents = 0;
+ fds[0].fd = fd;
+
+ if (poll(fds, 1, glvc_timeout * MILLISEC) <= 0) {
+ return (-1);
+ }
+
+ while (left > 0) {
+ res = read(fd, bufp, left);
+ /* return on error or short read */
+ if ((res == 0) || ((res < 0) &&
+ (errno == EAGAIN))) {
+ /* poll until the read is unblocked */
+ if ((poll(fds, 1, glvc_timeout * MILLISEC)) < 0)
+ return (-1);
+
+ continue;
+ } else
+ if (res < 0) {
+ /* unrecoverable error */
+
+ return (-1);
+ } else {
+ bufp += res;
+ left -= res;
+ }
+ }
+
+ return (size - left);
+}