summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authormeem <none@none>2008-05-08 16:56:26 -0700
committermeem <none@none>2008-05-08 16:56:26 -0700
commitca9327a6de44d69ddab3668cc1e143ce781387a3 (patch)
treee8f80a575694596c82ba7326781eca8437ecc661 /usr/src
parent7975540170826a816320fa0cf07ba06a66c4252d (diff)
downloadillumos-gate-ca9327a6de44d69ddab3668cc1e143ce781387a3.tar.gz
PSARC/2008/265 STREAMS _I_CMD and pfiles TLI support
4960773 pfiles should show info for TLI endpoints
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/streams.c55
-rw-r--r--usr/src/cmd/ptools/pfiles/pfiles.c78
-rw-r--r--usr/src/uts/common/inet/tcp/tcp.c226
-rw-r--r--usr/src/uts/common/inet/udp/udp.c251
-rw-r--r--usr/src/uts/common/io/dedump.c17
-rw-r--r--usr/src/uts/common/os/streamio.c190
-rw-r--r--usr/src/uts/common/sys/stream.h14
-rw-r--r--usr/src/uts/common/sys/stropts.h37
-rw-r--r--usr/src/uts/common/sys/strsubr.h3
9 files changed, 629 insertions, 242 deletions
diff --git a/usr/src/cmd/mdb/common/modules/genunix/streams.c b/usr/src/cmd/mdb/common/modules/genunix/streams.c
index 56d633157d..c000bc4ecc 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/streams.c
+++ b/usr/src/cmd/mdb/common/modules/genunix/streams.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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -152,7 +151,7 @@ static const struct str_flags stdf[] = {
{ SF(0x00020000), "unused" },
{ SF(0x00040000), "unused" },
{ SF(STRTOSTOP), "block background writes" },
- { SF(0x00100000), "unused" },
+ { SF(STRCMDWAIT), "someone is doing an _I_CMD" },
{ SF(0x00200000), "unused" },
{ SF(STRMOUNT), "stream is mounted" },
{ SF(STRNOTATMARK), "Not at mark (when empty read q)" },
@@ -211,6 +210,7 @@ static const strtypes_t mbt[] = {
{ "M_STARTI", M_STARTI, "restart reception after stop" },
{ "M_PCEVENT", M_PCEVENT, "Obsoleted: do not use" },
{ "M_UNHANGUP", M_UNHANGUP, "line reconnect" },
+ { "M_CMD", M_CMD, "out-of-band ioctl command" },
{ NULL, 0, NULL }
};
@@ -1255,17 +1255,17 @@ mblk_prt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
quiet = TRUE;
if (mdb_getopts(argc, argv,
- 'v', MDB_OPT_SETBITS, TRUE, &verbose,
- 'q', MDB_OPT_SETBITS, TRUE, &quiet,
- 'f', MDB_OPT_STR, &flag,
- 'F', MDB_OPT_STR, &not_flag,
- 't', MDB_OPT_STR, &typ,
- 'T', MDB_OPT_STR, &not_typ,
- 'l', MDB_OPT_UINT64, &len,
- 'L', MDB_OPT_UINT64, &llen,
- 'G', MDB_OPT_UINT64, &glen,
- 'b', MDB_OPT_UINT64, &blen,
- 'd', MDB_OPT_UINTPTR, &dbaddr,
+ 'v', MDB_OPT_SETBITS, TRUE, &verbose,
+ 'q', MDB_OPT_SETBITS, TRUE, &quiet,
+ 'f', MDB_OPT_STR, &flag,
+ 'F', MDB_OPT_STR, &not_flag,
+ 't', MDB_OPT_STR, &typ,
+ 'T', MDB_OPT_STR, &not_typ,
+ 'l', MDB_OPT_UINT64, &len,
+ 'L', MDB_OPT_UINT64, &llen,
+ 'G', MDB_OPT_UINT64, &glen,
+ 'b', MDB_OPT_UINT64, &blen,
+ 'd', MDB_OPT_UINTPTR, &dbaddr,
NULL) != argc)
return (DCMD_USAGE);
@@ -1328,16 +1328,17 @@ mblk_prt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
db_type = dblk.db_type;
/* M_DATA is 0, so tmask has special value 0xff for it */
- if ((tmask != 0) &&
- (((tmask == M_DATA_T) && (db_type != M_DATA)) ||
- ((tmask != M_DATA_T) && (db_type != tmask))))
- return (DCMD_OK);
-
+ if (tmask != 0) {
+ if ((tmask == M_DATA_T && db_type != M_DATA) ||
+ (tmask != M_DATA_T && db_type != tmask))
+ return (DCMD_OK);
+ }
- if ((not_tmask != 0) &&
- (((not_tmask == M_DATA_T) && (db_type == M_DATA)) ||
- (db_type == not_tmask)))
- return (DCMD_OK);
+ if (not_tmask != 0) {
+ if ((not_tmask == M_DATA_T && db_type == M_DATA) ||
+ (db_type == not_tmask))
+ return (DCMD_OK);
+ }
if (dbaddr != 0 && (uintptr_t)mblk.b_datap != dbaddr)
return (DCMD_OK);
@@ -1577,9 +1578,9 @@ queue_help(void)
" q2wrq: given a queue addr print write queue pointer\n"
" q2otherq: given a queue addr print other queue pointer\n"
" q2syncq: given a queue addr print syncq pointer"
- " (::help syncq)\n"
+ " (::help syncq)\n"
" q2stream: given a queue addr print its stream pointer\n"
- "\t\t(see ::help stream and ::help stdata)\n\n"
+ "\t\t(see ::help stream and ::help stdata)\n\n"
"To walk q_next pointer of the queue use\n"
" queue_addr::walk qnext\n");
}
diff --git a/usr/src/cmd/ptools/pfiles/pfiles.c b/usr/src/cmd/ptools/pfiles/pfiles.c
index 891a370c7d..e23c9fd756 100644
--- a/usr/src/cmd/ptools/pfiles/pfiles.c
+++ b/usr/src/cmd/ptools/pfiles/pfiles.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -33,22 +33,20 @@
#include <ctype.h>
#include <string.h>
#include <signal.h>
-#include <errno.h>
#include <dirent.h>
#include <limits.h>
#include <door.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
-#include <sys/mman.h>
#include <sys/mkdev.h>
+#include <sys/stropts.h>
+#include <sys/timod.h>
#include <sys/un.h>
-#include <netdb.h>
#include <libproc.h>
#include <netinet/in.h>
#include <netinet/udp.h>
#include <arpa/inet.h>
-#include <netdb.h>
#define copyflock(dst, src) \
(dst).l_type = (src).l_type; \
@@ -66,6 +64,7 @@ static boolean_t nflag = B_FALSE;
static void intr(int);
static void dofcntl(struct ps_prochandle *, int, int, int);
static void dosocket(struct ps_prochandle *, int);
+static void dotli(struct ps_prochandle *, int);
static void show_files(struct ps_prochandle *);
static void show_fileflags(int);
static void show_door(struct ps_prochandle *, int);
@@ -196,6 +195,7 @@ show_files(struct ps_prochandle *Pr)
{
DIR *dirp;
struct dirent *dentp;
+ const char *dev;
char pname[100];
char fname[PATH_MAX];
struct stat64 statb;
@@ -305,13 +305,34 @@ show_files(struct ps_prochandle *Pr)
(void) sprintf(pname, "/proc/%d/path/%d", (int)pid, fd);
- if ((ret = readlink(pname, fname, PATH_MAX - 1)) > 0) {
- fname[ret] = '\0';
- (void) printf(" %s\n", fname);
+ if ((ret = readlink(pname, fname, PATH_MAX - 1)) <= 0)
+ continue;
+
+ fname[ret] = '\0';
+
+ if ((statb.st_mode & S_IFMT) == S_IFCHR &&
+ (dev = strrchr(fname, ':')) != NULL) {
+ /*
+ * There's no elegant way to determine if a
+ * character device supports TLI, so we lame
+ * out and just check a hardcoded list of
+ * known TLI devices.
+ */
+ int i;
+ const char *tlidevs[] =
+ { "tcp", "tcp6", "udp", "udp6", NULL };
+
+ dev++; /* skip past the `:' */
+ for (i = 0; tlidevs[i] != NULL; i++) {
+ if (strcmp(dev, tlidevs[i]) == 0) {
+ dotli(Pr, fd);
+ break;
+ }
+ }
}
+ (void) printf(" %s\n", fname);
}
}
-
(void) closedir(dirp);
}
@@ -469,13 +490,15 @@ show_door(struct ps_prochandle *Pr, int fd)
(void) printf("pid %d", (int)door_info.di_target);
}
+/*
+ * Print out the socket address pointed to by `sa'. `len' is only
+ * needed for AF_UNIX sockets.
+ */
static void
show_sockaddr(const char *str, struct sockaddr *sa, socklen_t len)
{
- /* LINTED pointer assignment */
- struct sockaddr_in *so_in = (struct sockaddr_in *)sa;
- /* LINTED pointer assignment */
- struct sockaddr_in6 *so_in6 = (struct sockaddr_in6 *)sa;
+ struct sockaddr_in *so_in = (struct sockaddr_in *)(void *)sa;
+ struct sockaddr_in6 *so_in6 = (struct sockaddr_in6 *)(void *)sa;
struct sockaddr_un *so_un = (struct sockaddr_un *)sa;
char abuf[INET6_ADDRSTRLEN];
const char *p;
@@ -484,14 +507,12 @@ show_sockaddr(const char *str, struct sockaddr *sa, socklen_t len)
default:
return;
case AF_INET:
- (void) printf("\t%s: AF_INET %s port: %u\n",
- str,
+ (void) printf("\t%s: AF_INET %s port: %u\n", str,
inet_ntop(AF_INET, &so_in->sin_addr, abuf, sizeof (abuf)),
ntohs(so_in->sin_port));
return;
case AF_INET6:
- (void) printf("\t%s: AF_INET6 %s port: %u\n",
- str,
+ (void) printf("\t%s: AF_INET6 %s port: %u\n", str,
inet_ntop(AF_INET6, &so_in6->sin6_addr,
abuf, sizeof (abuf)),
ntohs(so_in->sin_port));
@@ -500,7 +521,7 @@ show_sockaddr(const char *str, struct sockaddr *sa, socklen_t len)
if (len >= sizeof (so_un->sun_family)) {
/* Null terminate */
len -= sizeof (so_un->sun_family);
- so_un->sun_path[len] = NULL;
+ so_un->sun_path[len] = '\0';
(void) printf("\t%s: AF_UNIX %s\n",
str, so_un->sun_path);
}
@@ -632,8 +653,7 @@ dosocket(struct ps_prochandle *Pr, int fd)
int type, tlen;
tlen = sizeof (type);
- if (pr_getsockopt(Pr, fd, SOL_SOCKET, SO_TYPE, &type, &tlen)
- == 0)
+ if (pr_getsockopt(Pr, fd, SOL_SOCKET, SO_TYPE, &type, &tlen) == 0)
show_socktype((uint_t)type);
show_sockopts(Pr, fd);
@@ -646,3 +666,21 @@ dosocket(struct ps_prochandle *Pr, int fd)
if (pr_getpeername(Pr, fd, sa, &len) == 0)
show_sockaddr("peername", sa, len);
}
+
+/* the file is a TLI endpoint */
+static void
+dotli(struct ps_prochandle *Pr, int fd)
+{
+ struct strcmd strcmd;
+
+ strcmd.sc_len = STRCMDBUFSIZE;
+ strcmd.sc_timeout = 5;
+
+ strcmd.sc_cmd = TI_GETMYNAME;
+ if (pr_ioctl(Pr, fd, _I_CMD, &strcmd, sizeof (strcmd)) == 0)
+ show_sockaddr("sockname", (void *)&strcmd.sc_buf, 0);
+
+ strcmd.sc_cmd = TI_GETPEERNAME;
+ if (pr_ioctl(Pr, fd, _I_CMD, &strcmd, sizeof (strcmd)) == 0)
+ show_sockaddr("peername", (void *)&strcmd.sc_buf, 0);
+}
diff --git a/usr/src/uts/common/inet/tcp/tcp.c b/usr/src/uts/common/inet/tcp/tcp.c
index 12b781c0bc..9f443745c3 100644
--- a/usr/src/uts/common/inet/tcp/tcp.c
+++ b/usr/src/uts/common/inet/tcp/tcp.c
@@ -18493,6 +18493,119 @@ no_more_eagers:
}
}
+static int
+tcp_getmyname(tcp_t *tcp, struct sockaddr *sa, uint_t *salenp)
+{
+ sin_t *sin = (sin_t *)sa;
+ sin6_t *sin6 = (sin6_t *)sa;
+
+ switch (tcp->tcp_family) {
+ case AF_INET:
+ ASSERT(tcp->tcp_ipversion == IPV4_VERSION);
+
+ if (*salenp < sizeof (sin_t))
+ return (EINVAL);
+
+ *sin = sin_null;
+ sin->sin_family = AF_INET;
+ sin->sin_port = tcp->tcp_lport;
+ sin->sin_addr.s_addr = tcp->tcp_ipha->ipha_src;
+ break;
+
+ case AF_INET6:
+ if (*salenp < sizeof (sin6_t))
+ return (EINVAL);
+
+ *sin6 = sin6_null;
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = tcp->tcp_lport;
+ if (tcp->tcp_ipversion == IPV4_VERSION) {
+ IN6_IPADDR_TO_V4MAPPED(tcp->tcp_ipha->ipha_src,
+ &sin6->sin6_addr);
+ } else {
+ sin6->sin6_addr = tcp->tcp_ip6h->ip6_src;
+ }
+ break;
+ }
+
+ return (0);
+}
+
+static int
+tcp_getpeername(tcp_t *tcp, struct sockaddr *sa, uint_t *salenp)
+{
+ sin_t *sin = (sin_t *)sa;
+ sin6_t *sin6 = (sin6_t *)sa;
+
+ if (tcp->tcp_state < TCPS_SYN_RCVD)
+ return (ENOTCONN);
+
+ switch (tcp->tcp_family) {
+ case AF_INET:
+ ASSERT(tcp->tcp_ipversion == IPV4_VERSION);
+
+ if (*salenp < sizeof (sin_t))
+ return (EINVAL);
+
+ *sin = sin_null;
+ sin->sin_family = AF_INET;
+ sin->sin_port = tcp->tcp_fport;
+ IN6_V4MAPPED_TO_IPADDR(&tcp->tcp_remote_v6,
+ sin->sin_addr.s_addr);
+ break;
+
+ case AF_INET6:
+ if (*salenp < sizeof (sin6_t))
+ return (EINVAL);
+
+ *sin6 = sin6_null;
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = tcp->tcp_fport;
+ sin6->sin6_addr = tcp->tcp_remote_v6;
+ if (tcp->tcp_ipversion == IPV6_VERSION) {
+ sin6->sin6_flowinfo = tcp->tcp_ip6h->ip6_vcf &
+ ~IPV6_VERS_AND_FLOW_MASK;
+ }
+ break;
+ }
+
+ return (0);
+}
+
+/*
+ * Handle special out-of-band ioctl requests (see PSARC/2008/265).
+ */
+static void
+tcp_wput_cmdblk(queue_t *q, mblk_t *mp)
+{
+ void *data;
+ mblk_t *datamp = mp->b_cont;
+ tcp_t *tcp = Q_TO_TCP(q);
+ cmdblk_t *cmdp = (cmdblk_t *)mp->b_rptr;
+
+ if (datamp == NULL || MBLKL(datamp) < cmdp->cb_len) {
+ cmdp->cb_error = EPROTO;
+ qreply(q, mp);
+ return;
+ }
+
+ data = datamp->b_rptr;
+
+ switch (cmdp->cb_cmd) {
+ case TI_GETPEERNAME:
+ cmdp->cb_error = tcp_getpeername(tcp, data, &cmdp->cb_len);
+ break;
+ case TI_GETMYNAME:
+ cmdp->cb_error = tcp_getmyname(tcp, data, &cmdp->cb_len);
+ break;
+ default:
+ cmdp->cb_error = EINVAL;
+ break;
+ }
+
+ qreply(q, mp);
+}
+
void
tcp_wput(queue_t *q, mblk_t *mp)
{
@@ -18525,6 +18638,11 @@ tcp_wput(queue_t *q, mblk_t *mp)
(*tcp_squeue_wput_proc)(connp->conn_sqp, mp,
tcp_output, connp, SQTAG_TCP_OUTPUT);
return;
+
+ case M_CMD:
+ tcp_wput_cmdblk(q, mp);
+ return;
+
case M_PROTO:
case M_PCPROTO:
/*
@@ -18573,14 +18691,6 @@ tcp_wput(queue_t *q, mblk_t *mp)
tcp_ioctl_abort_conn(q, mp);
return;
case TI_GETPEERNAME:
- if (tcp->tcp_state < TCPS_SYN_RCVD) {
- iocp->ioc_error = ENOTCONN;
- iocp->ioc_count = 0;
- mp->b_datap->db_type = M_IOCACK;
- qreply(q, mp);
- return;
- }
- /* FALLTHRU */
case TI_GETMYNAME:
mi_copyin(q, mp, NULL,
SIZEOF_STRUCT(strbuf, iocp->ioc_flag));
@@ -21897,16 +22007,14 @@ static void
tcp_wput_iocdata(tcp_t *tcp, mblk_t *mp)
{
mblk_t *mp1;
+ struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
STRUCT_HANDLE(strbuf, sb);
- uint16_t port;
- queue_t *q = tcp->tcp_wq;
- in6_addr_t v6addr;
- ipaddr_t v4addr;
- uint32_t flowinfo = 0;
- int addrlen;
+ queue_t *q = tcp->tcp_wq;
+ int error;
+ uint_t addrlen;
/* Make sure it is one of ours. */
- switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) {
+ switch (iocp->ioc_cmd) {
case TI_GETMYNAME:
case TI_GETPEERNAME:
break;
@@ -21937,93 +22045,35 @@ tcp_wput_iocdata(tcp_t *tcp, mblk_t *mp)
return;
}
- STRUCT_SET_HANDLE(sb, ((struct iocblk *)mp->b_rptr)->ioc_flag,
- (void *)mp1->b_rptr);
+ STRUCT_SET_HANDLE(sb, iocp->ioc_flag, (void *)mp1->b_rptr);
addrlen = tcp->tcp_family == AF_INET ? sizeof (sin_t) : sizeof (sin6_t);
-
if (STRUCT_FGET(sb, maxlen) < addrlen) {
mi_copy_done(q, mp, EINVAL);
return;
}
- switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) {
+
+ mp1 = mi_copyout_alloc(q, mp, STRUCT_FGETP(sb, buf), addrlen, B_TRUE);
+ if (mp1 == NULL)
+ return;
+
+ switch (iocp->ioc_cmd) {
case TI_GETMYNAME:
- if (tcp->tcp_family == AF_INET) {
- if (tcp->tcp_ipversion == IPV4_VERSION) {
- v4addr = tcp->tcp_ipha->ipha_src;
- } else {
- /* can't return an address in this case */
- v4addr = 0;
- }
- } else {
- /* tcp->tcp_family == AF_INET6 */
- if (tcp->tcp_ipversion == IPV4_VERSION) {
- IN6_IPADDR_TO_V4MAPPED(tcp->tcp_ipha->ipha_src,
- &v6addr);
- } else {
- v6addr = tcp->tcp_ip6h->ip6_src;
- }
- }
- port = tcp->tcp_lport;
+ error = tcp_getmyname(tcp, (void *)mp1->b_rptr, &addrlen);
break;
case TI_GETPEERNAME:
- if (tcp->tcp_family == AF_INET) {
- if (tcp->tcp_ipversion == IPV4_VERSION) {
- IN6_V4MAPPED_TO_IPADDR(&tcp->tcp_remote_v6,
- v4addr);
- } else {
- /* can't return an address in this case */
- v4addr = 0;
- }
- } else {
- /* tcp->tcp_family == AF_INET6) */
- v6addr = tcp->tcp_remote_v6;
- if (tcp->tcp_ipversion == IPV6_VERSION) {
- /*
- * No flowinfo if tcp->tcp_ipversion is v4.
- *
- * flowinfo was already initialized to zero
- * where it was declared above, so only
- * set it if ipversion is v6.
- */
- flowinfo = tcp->tcp_ip6h->ip6_vcf &
- ~IPV6_VERS_AND_FLOW_MASK;
- }
- }
- port = tcp->tcp_fport;
+ error = tcp_getpeername(tcp, (void *)mp1->b_rptr, &addrlen);
break;
- default:
- mi_copy_done(q, mp, EPROTO);
- return;
}
- mp1 = mi_copyout_alloc(q, mp, STRUCT_FGETP(sb, buf), addrlen, B_TRUE);
- if (!mp1)
- return;
- if (tcp->tcp_family == AF_INET) {
- sin_t *sin;
-
- STRUCT_FSET(sb, len, (int)sizeof (sin_t));
- sin = (sin_t *)mp1->b_rptr;
- mp1->b_wptr = (uchar_t *)&sin[1];
- *sin = sin_null;
- sin->sin_family = AF_INET;
- sin->sin_addr.s_addr = v4addr;
- sin->sin_port = port;
+ if (error != 0) {
+ mi_copy_done(q, mp, error);
} else {
- /* tcp->tcp_family == AF_INET6 */
- sin6_t *sin6;
+ mp1->b_wptr += addrlen;
+ STRUCT_FSET(sb, len, addrlen);
- STRUCT_FSET(sb, len, (int)sizeof (sin6_t));
- sin6 = (sin6_t *)mp1->b_rptr;
- mp1->b_wptr = (uchar_t *)&sin6[1];
- *sin6 = sin6_null;
- sin6->sin6_family = AF_INET6;
- sin6->sin6_flowinfo = flowinfo;
- sin6->sin6_addr = v6addr;
- sin6->sin6_port = port;
+ /* Copy out the address */
+ mi_copyout(q, mp);
}
- /* Copy out the address */
- mi_copyout(q, mp);
}
/*
diff --git a/usr/src/uts/common/inet/udp/udp.c b/usr/src/uts/common/inet/udp/udp.c
index d4d99f187d..2282373476 100644
--- a/usr/src/uts/common/inet/udp/udp.c
+++ b/usr/src/uts/common/inet/udp/udp.c
@@ -7273,6 +7273,142 @@ done:
return (mp);
}
+
+static int
+udp_getpeername(udp_t *udp, struct sockaddr *sa, uint_t *salenp)
+{
+ sin_t *sin = (sin_t *)sa;
+ sin6_t *sin6 = (sin6_t *)sa;
+
+ ASSERT(RW_LOCK_HELD(&udp->udp_rwlock));
+
+ if (udp->udp_state != TS_DATA_XFER)
+ return (ENOTCONN);
+
+ switch (udp->udp_family) {
+ case AF_INET:
+ ASSERT(udp->udp_ipversion == IPV4_VERSION);
+
+ if (*salenp < sizeof (sin_t))
+ return (EINVAL);
+
+ *salenp = sizeof (sin_t);
+ *sin = sin_null;
+ sin->sin_family = AF_INET;
+ sin->sin_port = udp->udp_dstport;
+ sin->sin_addr.s_addr = V4_PART_OF_V6(udp->udp_v6dst);
+ break;
+
+ case AF_INET6:
+ if (*salenp < sizeof (sin6_t))
+ return (EINVAL);
+
+ *salenp = sizeof (sin6_t);
+ *sin6 = sin6_null;
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = udp->udp_dstport;
+ sin6->sin6_addr = udp->udp_v6dst;
+ sin6->sin6_flowinfo = udp->udp_flowinfo;
+ break;
+ }
+
+ return (0);
+}
+
+static int
+udp_getmyname(udp_t *udp, struct sockaddr *sa, uint_t *salenp)
+{
+ sin_t *sin = (sin_t *)sa;
+ sin6_t *sin6 = (sin6_t *)sa;
+
+ ASSERT(RW_LOCK_HELD(&udp->udp_rwlock));
+
+ switch (udp->udp_family) {
+ case AF_INET:
+ ASSERT(udp->udp_ipversion == IPV4_VERSION);
+
+ if (*salenp < sizeof (sin_t))
+ return (EINVAL);
+
+ *salenp = sizeof (sin_t);
+ *sin = sin_null;
+ sin->sin_family = AF_INET;
+ sin->sin_port = udp->udp_port;
+
+ /*
+ * If udp_v6src is unspecified, we might be bound to broadcast
+ * / multicast. Use udp_bound_v6src as local address instead
+ * (that could also still be unspecified).
+ */
+ if (!IN6_IS_ADDR_V4MAPPED_ANY(&udp->udp_v6src) &&
+ !IN6_IS_ADDR_UNSPECIFIED(&udp->udp_v6src)) {
+ sin->sin_addr.s_addr = V4_PART_OF_V6(udp->udp_v6src);
+ } else {
+ sin->sin_addr.s_addr =
+ V4_PART_OF_V6(udp->udp_bound_v6src);
+ }
+ break;
+
+ case AF_INET6:
+ if (*salenp < sizeof (sin6_t))
+ return (EINVAL);
+
+ *salenp = sizeof (sin6_t);
+ *sin6 = sin6_null;
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = udp->udp_port;
+ sin6->sin6_flowinfo = udp->udp_flowinfo;
+
+ /*
+ * If udp_v6src is unspecified, we might be bound to broadcast
+ * / multicast. Use udp_bound_v6src as local address instead
+ * (that could also still be unspecified).
+ */
+ if (!IN6_IS_ADDR_UNSPECIFIED(&udp->udp_v6src))
+ sin6->sin6_addr = udp->udp_v6src;
+ else
+ sin6->sin6_addr = udp->udp_bound_v6src;
+ break;
+ }
+
+ return (0);
+}
+
+/*
+ * Handle special out-of-band ioctl requests (see PSARC/2008/265).
+ */
+static void
+udp_wput_cmdblk(queue_t *q, mblk_t *mp)
+{
+ void *data;
+ mblk_t *datamp = mp->b_cont;
+ udp_t *udp = Q_TO_UDP(q);
+ cmdblk_t *cmdp = (cmdblk_t *)mp->b_rptr;
+
+ if (datamp == NULL || MBLKL(datamp) < cmdp->cb_len) {
+ cmdp->cb_error = EPROTO;
+ qreply(q, mp);
+ return;
+ }
+ data = datamp->b_rptr;
+
+ rw_enter(&udp->udp_rwlock, RW_READER);
+ switch (cmdp->cb_cmd) {
+ case TI_GETPEERNAME:
+ cmdp->cb_error = udp_getpeername(udp, data, &cmdp->cb_len);
+ break;
+ case TI_GETMYNAME:
+ cmdp->cb_error = udp_getmyname(udp, data, &cmdp->cb_len);
+ break;
+ default:
+ cmdp->cb_error = EINVAL;
+ break;
+ }
+ rw_exit(&udp->udp_rwlock);
+
+ qreply(q, mp);
+}
+
static void
udp_wput_other(queue_t *q, mblk_t *mp)
{
@@ -7293,6 +7429,10 @@ udp_wput_other(queue_t *q, mblk_t *mp)
cr = DB_CREDDEF(mp, connp->conn_cred);
switch (db->db_type) {
+ case M_CMD:
+ udp_wput_cmdblk(q, mp);
+ return;
+
case M_PROTO:
case M_PCPROTO:
if (mp->b_wptr - rptr < sizeof (t_scalar_t)) {
@@ -7492,16 +7632,14 @@ static void
udp_wput_iocdata(queue_t *q, mblk_t *mp)
{
mblk_t *mp1;
+ struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
STRUCT_HANDLE(strbuf, sb);
- uint16_t port;
- in6_addr_t v6addr;
- ipaddr_t v4addr;
- uint32_t flowinfo = 0;
- int addrlen;
- udp_t *udp = Q_TO_UDP(q);
+ udp_t *udp = Q_TO_UDP(q);
+ int error;
+ uint_t addrlen;
/* Make sure it is one of ours. */
- switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) {
+ switch (iocp->ioc_cmd) {
case TI_GETMYNAME:
case TI_GETPEERNAME:
break;
@@ -7544,102 +7682,39 @@ udp_wput_iocdata(queue_t *q, mblk_t *mp)
* and TI_GETPEERNAME. Next we copyout the requested
* address and then we'll copyout the strbuf.
*/
- STRUCT_SET_HANDLE(sb, ((struct iocblk *)mp->b_rptr)->ioc_flag,
- (void *)mp1->b_rptr);
- if (udp->udp_family == AF_INET)
- addrlen = sizeof (sin_t);
- else
- addrlen = sizeof (sin6_t);
-
+ STRUCT_SET_HANDLE(sb, iocp->ioc_flag, (void *)mp1->b_rptr);
+ addrlen = udp->udp_family == AF_INET ? sizeof (sin_t) : sizeof (sin6_t);
if (STRUCT_FGET(sb, maxlen) < addrlen) {
mi_copy_done(q, mp, EINVAL);
return;
}
- switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) {
+
+ mp1 = mi_copyout_alloc(q, mp, STRUCT_FGETP(sb, buf), addrlen, B_TRUE);
+ if (mp1 == NULL)
+ return;
+
+ rw_enter(&udp->udp_rwlock, RW_READER);
+ switch (iocp->ioc_cmd) {
case TI_GETMYNAME:
- if (udp->udp_family == AF_INET) {
- ASSERT(udp->udp_ipversion == IPV4_VERSION);
- if (!IN6_IS_ADDR_V4MAPPED_ANY(&udp->udp_v6src) &&
- !IN6_IS_ADDR_UNSPECIFIED(&udp->udp_v6src)) {
- v4addr = V4_PART_OF_V6(udp->udp_v6src);
- } else {
- /*
- * INADDR_ANY
- * udp_v6src is not set, we might be bound to
- * broadcast/multicast. Use udp_bound_v6src as
- * local address instead (that could
- * also still be INADDR_ANY)
- */
- v4addr = V4_PART_OF_V6(udp->udp_bound_v6src);
- }
- } else {
- /* udp->udp_family == AF_INET6 */
- if (!IN6_IS_ADDR_UNSPECIFIED(&udp->udp_v6src)) {
- v6addr = udp->udp_v6src;
- } else {
- /*
- * UNSPECIFIED
- * udp_v6src is not set, we might be bound to
- * broadcast/multicast. Use udp_bound_v6src as
- * local address instead (that could
- * also still be UNSPECIFIED)
- */
- v6addr = udp->udp_bound_v6src;
- }
- }
- port = udp->udp_port;
+ error = udp_getmyname(udp, (void *)mp1->b_rptr, &addrlen);
break;
case TI_GETPEERNAME:
- if (udp->udp_state != TS_DATA_XFER) {
- mi_copy_done(q, mp, ENOTCONN);
- return;
- }
- if (udp->udp_family == AF_INET) {
- ASSERT(udp->udp_ipversion == IPV4_VERSION);
- v4addr = V4_PART_OF_V6(udp->udp_v6dst);
- } else {
- /* udp->udp_family == AF_INET6) */
- v6addr = udp->udp_v6dst;
- flowinfo = udp->udp_flowinfo;
- }
- port = udp->udp_dstport;
+ error = udp_getpeername(udp, (void *)mp1->b_rptr, &addrlen);
break;
- default:
- mi_copy_done(q, mp, EPROTO);
- return;
}
- mp1 = mi_copyout_alloc(q, mp, STRUCT_FGETP(sb, buf), addrlen, B_TRUE);
- if (!mp1)
- return;
-
- if (udp->udp_family == AF_INET) {
- sin_t *sin;
+ rw_exit(&udp->udp_rwlock);
- STRUCT_FSET(sb, len, (int)sizeof (sin_t));
- sin = (sin_t *)mp1->b_rptr;
- mp1->b_wptr = (uchar_t *)&sin[1];
- *sin = sin_null;
- sin->sin_family = AF_INET;
- sin->sin_addr.s_addr = v4addr;
- sin->sin_port = port;
+ if (error != 0) {
+ mi_copy_done(q, mp, error);
} else {
- /* udp->udp_family == AF_INET6 */
- sin6_t *sin6;
+ mp1->b_wptr += addrlen;
+ STRUCT_FSET(sb, len, addrlen);
- STRUCT_FSET(sb, len, (int)sizeof (sin6_t));
- sin6 = (sin6_t *)mp1->b_rptr;
- mp1->b_wptr = (uchar_t *)&sin6[1];
- *sin6 = sin6_null;
- sin6->sin6_family = AF_INET6;
- sin6->sin6_flowinfo = flowinfo;
- sin6->sin6_addr = v6addr;
- sin6->sin6_port = port;
+ /* Copy out the address */
+ mi_copyout(q, mp);
}
- /* Copy out the address */
- mi_copyout(q, mp);
}
-
static int
udp_unitdata_opt_process(queue_t *q, mblk_t *mp, int *errorp,
udpattrs_t *udpattrs)
diff --git a/usr/src/uts/common/io/dedump.c b/usr/src/uts/common/io/dedump.c
index 16c52c500d..4c68621cf4 100644
--- a/usr/src/uts/common/io/dedump.c
+++ b/usr/src/uts/common/io/dedump.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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -125,6 +124,15 @@ dedump_ssize(mblk_t *mp)
}
static void
+dedump_cmdblk(mblk_t *mp)
+{
+ struct cmdblk *cbp = (struct cmdblk *)mp->b_rptr;
+
+ (void) printf("%s cmd %x cred %p len %u error %d\n", hdr, cbp->cb_cmd,
+ (void *)cbp->cb_cr, cbp->cb_len, cbp->cb_error);
+}
+
+static void
dedump_iocblk(mblk_t *mp)
{
struct iocblk *ic = (struct iocblk *)mp->b_rptr;
@@ -205,6 +213,7 @@ static msgfmt_t msgfmt[256] = {
{ M_STARTI, "M_STARTI ", dedump_raw },
{ M_PCEVENT, "M_PCEVENT ", dedump_raw },
{ M_UNHANGUP, "M_UNHANGUP", dedump_raw },
+ { M_CMD, "M_CMD ", dedump_cmdblk },
};
/*ARGSUSED1*/
diff --git a/usr/src/uts/common/os/streamio.c b/usr/src/uts/common/os/streamio.c
index d80fa67f56..3fcbf8634b 100644
--- a/usr/src/uts/common/os/streamio.c
+++ b/usr/src/uts/common/os/streamio.c
@@ -143,6 +143,7 @@ static uint32_t ioc_id;
static void putback(struct stdata *, queue_t *, mblk_t *, int);
static void strcleanall(struct vnode *);
static int strwsrv(queue_t *);
+static int strdocmd(struct stdata *, struct strcmd *, cred_t *);
/*
* qinit and module_info structures for stream head read and write queues
@@ -391,6 +392,7 @@ ckreturn:
stp->sd_wroff = 0;
stp->sd_tail = 0;
stp->sd_iocblk = NULL;
+ stp->sd_cmdblk = NULL;
stp->sd_pushcnt = 0;
stp->sd_qn_minpsz = 0;
stp->sd_qn_maxpsz = INFPSZ - 1; /* used to check for initialization */
@@ -811,6 +813,8 @@ strclose(struct vnode *vp, int flag, cred_t *crp)
vp->v_stream = NULL;
mutex_exit(&vp->v_lock);
mutex_enter(&stp->sd_lock);
+ freemsg(stp->sd_cmdblk);
+ stp->sd_cmdblk = NULL;
stp->sd_flag &= ~STRCLOSE;
cv_broadcast(&stp->sd_monitor);
mutex_exit(&stp->sd_lock);
@@ -2124,6 +2128,24 @@ strrput_nondata(queue_t *q, mblk_t *bp)
freemsg(bp);
return (0);
+ case M_CMD:
+ if (MBLKL(bp) != sizeof (cmdblk_t)) {
+ freemsg(bp);
+ return (0);
+ }
+
+ mutex_enter(&stp->sd_lock);
+ if (stp->sd_flag & STRCMDWAIT) {
+ ASSERT(stp->sd_cmdblk == NULL);
+ stp->sd_cmdblk = bp;
+ cv_broadcast(&stp->sd_monitor);
+ mutex_exit(&stp->sd_lock);
+ } else {
+ mutex_exit(&stp->sd_lock);
+ freemsg(bp);
+ }
+ return (0);
+
case M_FLUSH:
/*
* Flush queues. The indication of which queues to flush
@@ -3180,6 +3202,7 @@ strioctl(struct vnode *vp, int cmd, intptr_t arg, int flag, int copyflag,
cred_t *crp, int *rvalp)
{
struct stdata *stp;
+ struct strcmd *scp;
struct strioctl strioc;
struct uio uio;
struct iovec iov;
@@ -3552,6 +3575,39 @@ strioctl(struct vnode *vp, int cmd, intptr_t arg, int flag, int copyflag,
}
return (error);
+ case _I_CMD:
+ /*
+ * Like I_STR, but without using M_IOC* messages and without
+ * copyins/copyouts beyond the passed-in argument.
+ */
+ if (stp->sd_flag & STRHUP)
+ return (ENXIO);
+
+ if ((scp = kmem_alloc(sizeof (strcmd_t), KM_NOSLEEP)) == NULL)
+ return (ENOMEM);
+
+ if (copyin((void *)arg, scp, sizeof (strcmd_t))) {
+ kmem_free(scp, sizeof (strcmd_t));
+ return (EFAULT);
+ }
+
+ access = job_control_type(scp->sc_cmd);
+ mutex_enter(&stp->sd_lock);
+ if (access != -1 && (error = i_straccess(stp, access)) != 0) {
+ mutex_exit(&stp->sd_lock);
+ kmem_free(scp, sizeof (strcmd_t));
+ return (error);
+ }
+ mutex_exit(&stp->sd_lock);
+
+ *rvalp = 0;
+ if ((error = strdocmd(stp, scp, crp)) == 0) {
+ if (copyout(scp, (void *)arg, sizeof (strcmd_t)))
+ error = EFAULT;
+ }
+ kmem_free(scp, sizeof (strcmd_t));
+ return (error);
+
case I_NREAD:
/*
* Return number of bytes of data in first message
@@ -6321,6 +6377,140 @@ waitioc:
}
/*
+ * Send an M_CMD message downstream and wait for a reply. This is a ptools
+ * special used to retrieve information from modules/drivers a stream without
+ * being subjected to flow control or interfering with pending messages on the
+ * stream (e.g. an ioctl in flight).
+ */
+int
+strdocmd(struct stdata *stp, struct strcmd *scp, cred_t *crp)
+{
+ mblk_t *mp;
+ struct cmdblk *cmdp;
+ int error = 0;
+ int errs = STRHUP|STRDERR|STWRERR|STPLEX;
+ clock_t rval, timeout = STRTIMOUT;
+
+ if (scp->sc_len < 0 || scp->sc_len > sizeof (scp->sc_buf) ||
+ scp->sc_timeout < -1)
+ return (EINVAL);
+
+ if (scp->sc_timeout > 0)
+ timeout = scp->sc_timeout * MILLISEC;
+
+ if ((mp = allocb_cred(sizeof (struct cmdblk), crp)) == NULL)
+ return (ENOMEM);
+
+ crhold(crp);
+
+ cmdp = (struct cmdblk *)mp->b_wptr;
+ cmdp->cb_cr = crp;
+ cmdp->cb_cmd = scp->sc_cmd;
+ cmdp->cb_len = scp->sc_len;
+ cmdp->cb_error = 0;
+ mp->b_wptr += sizeof (struct cmdblk);
+
+ DB_TYPE(mp) = M_CMD;
+ DB_CPID(mp) = curproc->p_pid;
+
+ /*
+ * Copy in the payload.
+ */
+ if (cmdp->cb_len > 0) {
+ mp->b_cont = allocb_cred(sizeof (scp->sc_buf), crp);
+ if (mp->b_cont == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+
+ /* cb_len comes from sc_len, which has already been checked */
+ ASSERT(cmdp->cb_len <= sizeof (scp->sc_buf));
+ (void) bcopy(scp->sc_buf, mp->b_cont->b_wptr, cmdp->cb_len);
+ mp->b_cont->b_wptr += cmdp->cb_len;
+ DB_CPID(mp->b_cont) = curproc->p_pid;
+ }
+
+ /*
+ * Since this mechanism is strictly for ptools, and since only one
+ * process can be grabbed at a time, we simply fail if there's
+ * currently an operation pending.
+ */
+ mutex_enter(&stp->sd_lock);
+ if (stp->sd_flag & STRCMDWAIT) {
+ mutex_exit(&stp->sd_lock);
+ error = EBUSY;
+ goto out;
+ }
+ stp->sd_flag |= STRCMDWAIT;
+ ASSERT(stp->sd_cmdblk == NULL);
+ mutex_exit(&stp->sd_lock);
+
+ putnext(stp->sd_wrq, mp);
+ mp = NULL;
+
+ /*
+ * Timed wait for acknowledgment. If the reply has already arrived,
+ * don't sleep. If awakened from the sleep, fail only if the reply
+ * has not arrived by then. Otherwise, process the reply.
+ */
+ mutex_enter(&stp->sd_lock);
+ while (stp->sd_cmdblk == NULL) {
+ if (stp->sd_flag & errs) {
+ if ((error = strgeterr(stp, errs, 0)) != 0)
+ goto waitout;
+ }
+
+ rval = str_cv_wait(&stp->sd_monitor, &stp->sd_lock, timeout, 0);
+ if (stp->sd_cmdblk != NULL)
+ break;
+
+ if (rval <= 0) {
+ error = (rval == 0) ? EINTR : ETIME;
+ goto waitout;
+ }
+ }
+
+ /*
+ * We received a reply.
+ */
+ mp = stp->sd_cmdblk;
+ stp->sd_cmdblk = NULL;
+ ASSERT(mp != NULL && DB_TYPE(mp) == M_CMD);
+ ASSERT(stp->sd_flag & STRCMDWAIT);
+ stp->sd_flag &= ~STRCMDWAIT;
+ mutex_exit(&stp->sd_lock);
+
+ cmdp = (struct cmdblk *)mp->b_rptr;
+ if ((error = cmdp->cb_error) != 0)
+ goto out;
+
+ /*
+ * Data may have been returned in the reply (cb_len > 0).
+ * If so, copy it out to the user's buffer.
+ */
+ if (cmdp->cb_len > 0) {
+ if (mp->b_cont == NULL || MBLKL(mp->b_cont) < cmdp->cb_len) {
+ error = EPROTO;
+ goto out;
+ }
+
+ cmdp->cb_len = MIN(cmdp->cb_len, sizeof (scp->sc_buf));
+ (void) bcopy(mp->b_cont->b_rptr, scp->sc_buf, cmdp->cb_len);
+ }
+ scp->sc_len = cmdp->cb_len;
+out:
+ freemsg(mp);
+ crfree(crp);
+ return (error);
+waitout:
+ ASSERT(stp->sd_cmdblk == NULL);
+ stp->sd_flag &= ~STRCMDWAIT;
+ mutex_exit(&stp->sd_lock);
+ crfree(crp);
+ return (error);
+}
+
+/*
* For the SunOS keyboard driver.
* Return the next available "ioctl" sequence number.
* Exported, so that streams modules can send "ioctl" messages
diff --git a/usr/src/uts/common/sys/stream.h b/usr/src/uts/common/sys/stream.h
index 7142a1f19d..3eca2fefdf 100644
--- a/usr/src/uts/common/sys/stream.h
+++ b/usr/src/uts/common/sys/stream.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -469,6 +469,7 @@ typedef struct bcache {
#define M_STARTI 0x90 /* restart reception after stop */
#define M_PCEVENT 0x91 /* Obsoleted: do not use */
#define M_UNHANGUP 0x92 /* line reconnect, sigh */
+#define M_CMD 0x93 /* out-of-band ioctl command */
/*
* Queue message class definitions.
@@ -588,7 +589,6 @@ union ioctypes {
* Options structure for M_SETOPTS message. This is sent upstream
* by a module or driver to set stream head options.
*/
-
struct stroptions {
uint_t so_flags; /* options to set */
short so_readopt; /* read option */
@@ -664,6 +664,16 @@ typedef struct infod {
#define INFOD_COUNT 0x08 /* return count of mblk(s) */
#define INFOD_COPYOUT 0x10 /* copyout any M_DATA mblk(s) */
+/*
+ * Structure used by _I_CMD mechanism, similar in spirit to iocblk.
+ */
+typedef struct cmdblk {
+ int cb_cmd; /* ioctl command type */
+ cred_t *cb_cr; /* full credentials */
+ uint_t cb_len; /* payload size */
+ int cb_error; /* error code */
+} cmdblk_t;
+
#endif /* _KERNEL */
/*
diff --git a/usr/src/uts/common/sys/stropts.h b/usr/src/uts/common/sys/stropts.h
index 3d5cc9617c..354de4b186 100644
--- a/usr/src/uts/common/sys/stropts.h
+++ b/usr/src/uts/common/sys/stropts.h
@@ -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.
@@ -24,7 +23,7 @@
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -278,14 +277,12 @@ extern "C" {
* IOCTLs (STR|050) - (STR|055) are available for use.
*/
-#define _I_MUXID2FD (STR|056) /* Private, get a fd from a muxid */
-#define _I_INSERT (STR|057) /* Private, insert a module */
-#define _I_REMOVE (STR|060) /* Private, remove a module */
-
-#define _I_GETPEERCRED (STR|061) /* Private, get peer cred */
-
-/* Private Layered Driver ioctl's */
-#define _I_PLINK_LH (STR|062)
+#define _I_MUXID2FD (STR|056) /* Private: get a fd from a muxid */
+#define _I_INSERT (STR|057) /* Private: insert a module */
+#define _I_REMOVE (STR|060) /* Private: remove a module */
+#define _I_GETPEERCRED (STR|061) /* Private: get peer cred */
+#define _I_PLINK_LH (STR|062) /* Private: Layered Driver ioctl */
+#define _I_CMD (STR|063) /* Private: send ioctl via M_CMD */
/*
* User level ioctl format for ioctls that go downstream (I_STR)
@@ -316,6 +313,22 @@ struct strioctl32 {
#define INFTIM _INFTIM
#endif
+#if !defined(__XOPEN_OR_POSIX) || defined(__EXTENSIONS__)
+/*
+ * For _I_CMD: similar to strioctl, but with included buffer (to avoid copyin/
+ * copyout from another address space). NOTE: the size of this structure must
+ * be less than libproc.h`MAXARGL for pr_ioctl() to handle it.
+ */
+#define STRCMDBUFSIZE 2048
+typedef struct strcmd {
+ int sc_cmd; /* ioctl command */
+ int sc_timeout; /* timeout value (in seconds) */
+ int sc_len; /* length of data */
+ int sc_pad;
+ char sc_buf[STRCMDBUFSIZE]; /* data buffer */
+} strcmd_t;
+#endif
+
/*
* Stream buffer structure for putmsg and getmsg system calls
*/
diff --git a/usr/src/uts/common/sys/strsubr.h b/usr/src/uts/common/sys/strsubr.h
index 6be0519425..df489c3dff 100644
--- a/usr/src/uts/common/sys/strsubr.h
+++ b/usr/src/uts/common/sys/strsubr.h
@@ -244,6 +244,7 @@ typedef struct stdata {
kcondvar_t sd_zcopy_wait;
uint_t sd_copyflag; /* copy-related flags */
zoneid_t sd_anchorzone; /* Allow removal from same zone only */
+ struct msgb *sd_cmdblk; /* reply from _I_CMD */
} stdata_t;
/*
@@ -278,7 +279,7 @@ typedef struct stdata {
/* 0x00020000 unused */
/* 0x00040000 unused */
#define STRTOSTOP 0x00080000 /* block background writes */
- /* 0x00100000 unused */
+#define STRCMDWAIT 0x00100000 /* someone is doing an _I_CMD */
/* 0x00200000 unused */
#define STRMOUNT 0x00400000 /* stream is mounted */
#define STRNOTATMARK 0x00800000 /* Not at mark (when empty read q) */