1 /*
2  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
3  */
4 
5 /*
6  * This file contains code imported from the OFED rds source file info.c
7  * Oracle elects to have and use the contents of info.c under and governed
8  * by the OpenIB.org BSD license (see below for full license text). However,
9  * the following notice accompanied the original version of this file:
10  */
11 
12 /*
13  * Copyright (c) 2006 Oracle.  All rights reserved.
14  *
15  * This software is available to you under a choice of one of two
16  * licenses.  You may choose to be licensed under the terms of the GNU
17  * General Public License (GPL) Version 2, available from the file
18  * COPYING in the main directory of this source tree, or the
19  * OpenIB.org BSD license below:
20  *
21  *     Redistribution and use in source and binary forms, with or
22  *     without modification, are permitted provided that the following
23  *     conditions are met:
24  *
25  *      - Redistributions of source code must retain the above
26  *        copyright notice, this list of conditions and the following
27  *        disclaimer.
28  *
29  *      - Redistributions in binary form must reproduce the above
30  *        copyright notice, this list of conditions and the following
31  *        disclaimer in the documentation and/or other materials
32  *        provided with the distribution.
33  *
34  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
35  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
36  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
37  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
38  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
39  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
40  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
41  * SOFTWARE.
42  *
43  */
44 #include <sys/rds.h>
45 
46 #include <sys/ib/clients/rdsv3/rdsv3.h>
47 #include <sys/ib/clients/rdsv3/rdsv3_debug.h>
48 
49 /*
50  * This file implements a getsockopt() call which copies a set of fixed
51  * sized structs into a user-specified buffer as a means of providing
52  * read-only information about RDS.
53  *
54  * For a given information source there are a given number of fixed sized
55  * structs at a given time.  The structs are only copied if the user-specified
56  * buffer is big enough.  The destination pages that make up the buffer
57  * are pinned for the duration of the copy.
58  *
59  * This gives us the following benefits:
60  *
61  * - simple implementation, no copy "position" across multiple calls
62  * - consistent snapshot of an info source
63  * - atomic copy works well with whatever locking info source has
64  * - one portable tool to get rds info across implementations
65  * - long-lived tool can get info without allocating
66  *
67  * at the following costs:
68  *
69  * - info source copy must be pinned, may be "large"
70  */
71 
72 static kmutex_t rdsv3_info_lock;
73 static rdsv3_info_func rdsv3_info_funcs[RDS_INFO_LAST - RDS_INFO_FIRST + 1];
74 
75 void
rdsv3_info_register_func(int optname,rdsv3_info_func func)76 rdsv3_info_register_func(int optname, rdsv3_info_func func)
77 {
78 	int offset = optname - RDS_INFO_FIRST;
79 
80 	ASSERT(optname >= RDS_INFO_FIRST && optname <= RDS_INFO_LAST);
81 
82 	mutex_enter(&rdsv3_info_lock);
83 	ASSERT(!rdsv3_info_funcs[offset]);
84 	rdsv3_info_funcs[offset] = func;
85 	mutex_exit(&rdsv3_info_lock);
86 }
87 
88 /* ARGSUSED */
89 void
rdsv3_info_deregister_func(int optname,rdsv3_info_func func)90 rdsv3_info_deregister_func(int optname, rdsv3_info_func func)
91 {
92 	int offset = optname - RDS_INFO_FIRST;
93 
94 	ASSERT(optname >= RDS_INFO_FIRST && optname <= RDS_INFO_LAST);
95 
96 	mutex_enter(&rdsv3_info_lock);
97 	rdsv3_info_funcs[offset] = NULL;
98 	mutex_exit(&rdsv3_info_lock);
99 }
100 
101 /*
102  * @optval points to the userspace buffer that the information snapshot
103  * will be copied into.
104  *
105  * This function returns -errno if there is a failure, particularly -ENOSPC
106  * if the given userspace buffer was not large enough to fit the snapshot.
107  * On success it returns the positive number of bytes of each array element
108  * in the snapshot.
109  */
110 int
rdsv3_info_ioctl(struct rsock * sock,int optname,char * optval,int32_t * rvalp)111 rdsv3_info_ioctl(struct rsock *sock, int optname, char *optval,
112     int32_t *rvalp)
113 {
114 	struct rdsv3_info_iterator iter;
115 	struct rdsv3_info_lengths lens;
116 	rdsv3_info_func func;
117 	struct rds_info_arg arg;
118 	uint32_t ulen = 0, klen;
119 
120 	func = rdsv3_info_funcs[optname - RDS_INFO_FIRST];
121 	if (func == NULL) {
122 		RDSV3_DPRINTF2("rdsv3_info_ioctl",
123 		    "No Info Function, optname: %d", optname);
124 		return (ENOPROTOOPT);
125 	}
126 
127 	if (optval == NULL) {
128 		RDSV3_DPRINTF2("rdsv3_info_ioctl", "optval is NULL");
129 		return (EINVAL);
130 	}
131 	if (ddi_copyin(optval, &arg, sizeof (struct rds_info_arg), 0) != 0) {
132 		RDSV3_DPRINTF2("rdsv3_info_ioctl",
133 		    "ddi_copyin for address: 0x%p failed", optval);
134 		return (EFAULT);
135 	}
136 
137 	RDSV3_DPRINTF4("rdsv3_info_ioctl",
138 	    "optname: %d lenp: %llx datap: %llx", optname, arg.lenp, arg.datap);
139 
140 	if (arg.lenp == (uintptr_t)NULL) {
141 		RDSV3_DPRINTF2("rdsv3_info_ioctl", "arg.lenp is NULL");
142 		return (EFAULT);
143 	}
144 
145 	if (ddi_copyin((void *)(uintptr_t)arg.lenp, &ulen,
146 	    sizeof (uint32_t), 0) != 0) {
147 		RDSV3_DPRINTF2("rdsv3_info_ioctl",
148 		    "ddi_copyin for address, lenp: 0x%p failed", arg.lenp);
149 		return (EFAULT);
150 	}
151 
152 	RDSV3_DPRINTF3("rdsv3_info_ioctl", "optname: %d len: %d datap: %p",
153 	    optname, ulen, arg.datap);
154 
155 	bzero(&iter, sizeof (struct rdsv3_info_iterator));
156 	/* a 0 len call is just trying to probe its length */
157 	if (ulen == 0) {
158 		iter.addr = NULL;
159 	} else if (arg.datap == (uintptr_t)NULL) {
160 		RDSV3_DPRINTF2("rdsv3_info_ioctl",
161 		    "arg.datap is NULL, ulen set to: %d", ulen);
162 		return (EINVAL);
163 	} else {
164 		iter.addr = (char *)(uintptr_t)arg.datap;
165 	}
166 	iter.offset = 0;
167 
168 	bzero(&lens, sizeof (struct rdsv3_info_lengths));
169 	func(sock, ulen, &iter, &lens);
170 
171 	klen = lens.nr * lens.each;
172 
173 	if (ddi_copyout(&klen, (void *)(uintptr_t)arg.lenp,
174 	    sizeof (uint32_t), 0) != 0) {
175 		RDSV3_DPRINTF2("rdsv3_info_ioctl",
176 		    "ddi_copyout(%p %p) failed", &klen, arg.lenp);
177 		return (EFAULT);
178 	}
179 
180 	RDSV3_DPRINTF3("rdsv3_info_ioctl",
181 	    "optname: %d ulen: %d klen: %d each: %d", optname, ulen, klen,
182 	    lens.each);
183 
184 	if (ulen < klen) {
185 		return (ENOSPC);
186 	}
187 
188 	RDSV3_DPRINTF4("rdsv3_info_ioctl", "Return optname: %d", optname);
189 
190 	*rvalp = lens.each;
191 	return (0);
192 }
193