xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_net.c (revision 45818ee124adeaaf947698996b4f4c722afc6d1f)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  *
25  * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
26  */
27 
28 #include <sys/types.h>
29 #include <sys/param.h>
30 #include <sys/ddi.h>
31 #include <sys/sunddi.h>
32 #include <sys/time.h>
33 #include <sys/varargs.h>
34 #include <sys/modctl.h>
35 #include <sys/pathname.h>
36 #include <sys/vnode.h>
37 #include <sys/socket.h>
38 #include <sys/ksocket.h>
39 #undef mem_free /* XXX Remove this after we convert everything to kmem_alloc */
40 
41 #include <smbsrv/smb_vops.h>
42 #include <smbsrv/smb.h>
43 #include <smbsrv/smb_kproto.h>
44 #include <smbsrv/smb_kstat.h>
45 
46 static	kmem_cache_t	*smb_txr_cache = NULL;
47 
48 /*
49  * smb_net_init
50  *
51  *	This function initializes the resources necessary to access the
52  *	network. It assumes it won't be called simultaneously by multiple
53  *	threads.
54  *
55  * Return Value
56  *
57  *	0	Initialization successful
58  *	ENOMEM	Initialization failed
59  */
60 void
61 smb_net_init(void)
62 {
63 
64 	if (smb_txr_cache != NULL)
65 		return;
66 
67 	smb_txr_cache = kmem_cache_create(SMBSRV_KSTAT_TXRCACHE,
68 	    sizeof (smb_txreq_t), 8, NULL, NULL, NULL, NULL, NULL, 0);
69 }
70 
71 /*
72  * smb_net_fini
73  *
74  *	This function releases the resources allocated by smb_net_init(). It
75  *	assumes it won't be called simultaneously by multiple threads.
76  *	This function can safely be called even if smb_net_init() hasn't been
77  *	called previously.
78  *
79  * Return Value
80  *
81  *	None
82  */
83 void
84 smb_net_fini(void)
85 {
86 	if (smb_txr_cache) {
87 		kmem_cache_destroy(smb_txr_cache);
88 		smb_txr_cache = NULL;
89 	}
90 }
91 
92 /*
93  * SMB Network Socket API
94  *
95  * smb_socreate:	Creates an socket based on domain/type.
96  * smb_soshutdown:	Disconnect a socket created with smb_socreate
97  * smb_sodestroy:	Release resources associated with a socket
98  * smb_sosend:		Send the contents of a buffer on a socket
99  * smb_sorecv:		Receive data into a buffer from a socket
100  * smb_iov_sosend:	Send the contents of an iovec on a socket
101  * smb_iov_sorecv:	Receive data into an iovec from a socket
102  */
103 
104 ksocket_t
105 smb_socreate(int domain, int type, int protocol)
106 {
107 	ksocket_t	sock;
108 	int		err = 0;
109 
110 	err = ksocket_socket(&sock, domain, type, protocol, KSOCKET_SLEEP,
111 	    CRED());
112 
113 	if (err != 0)
114 		return (NULL);
115 	else
116 		return (sock);
117 }
118 
119 /*
120  * smb_soshutdown will disconnect the socket and prevent subsequent PDU
121  * reception and transmission.  The sonode still exists but its state
122  * gets modified to indicate it is no longer connected.  Calls to
123  * smb_sorecv/smb_iov_sorecv will return so smb_soshutdown can be used
124  * regain control of a thread stuck in smb_sorecv.
125  */
126 void
127 smb_soshutdown(ksocket_t so)
128 {
129 	(void) ksocket_shutdown(so, SHUT_RDWR, CRED());
130 }
131 
132 /*
133  * smb_sodestroy releases all resources associated with a socket previously
134  * created with smb_socreate.  The socket must be shutdown using smb_soshutdown
135  * before the socket is destroyed with smb_sodestroy, otherwise undefined
136  * behavior will result.
137  */
138 void
139 smb_sodestroy(ksocket_t so)
140 {
141 	(void) ksocket_close(so, CRED());
142 }
143 
144 int
145 smb_sorecv(ksocket_t so, void *msg, size_t len)
146 {
147 	size_t recvd;
148 	int err;
149 
150 	ASSERT(so != NULL);
151 	ASSERT(len != 0);
152 
153 	if ((err = ksocket_recv(so, msg, len, MSG_WAITALL, &recvd,
154 	    CRED())) != 0) {
155 		return (err);
156 	}
157 
158 	/* Successful receive */
159 	return ((recvd == len) ? 0 : -1);
160 }
161 
162 /*
163  * smb_net_txl_constructor
164  *
165  *	Transmit list constructor
166  */
167 void
168 smb_net_txl_constructor(smb_txlst_t *txl)
169 {
170 	ASSERT(txl->tl_magic != SMB_TXLST_MAGIC);
171 
172 	mutex_init(&txl->tl_mutex, NULL, MUTEX_DEFAULT, NULL);
173 	list_create(&txl->tl_list, sizeof (smb_txreq_t),
174 	    offsetof(smb_txreq_t, tr_lnd));
175 	txl->tl_active = B_FALSE;
176 	txl->tl_magic = SMB_TXLST_MAGIC;
177 }
178 
179 /*
180  * smb_net_txl_destructor
181  *
182  *	Transmit list destructor
183  */
184 void
185 smb_net_txl_destructor(smb_txlst_t *txl)
186 {
187 	ASSERT(txl->tl_magic == SMB_TXLST_MAGIC);
188 
189 	txl->tl_magic = 0;
190 	list_destroy(&txl->tl_list);
191 	mutex_destroy(&txl->tl_mutex);
192 }
193 
194 /*
195  * smb_net_txr_alloc
196  *
197  *	Transmit buffer allocator
198  */
199 smb_txreq_t *
200 smb_net_txr_alloc(void)
201 {
202 	smb_txreq_t	*txr;
203 
204 	txr = kmem_cache_alloc(smb_txr_cache, KM_SLEEP);
205 	txr->tr_len = 0;
206 	bzero(&txr->tr_lnd, sizeof (txr->tr_lnd));
207 	txr->tr_magic = SMB_TXREQ_MAGIC;
208 	return (txr);
209 }
210 
211 /*
212  * smb_net_txr_free
213  *
214  *	Transmit buffer deallocator
215  */
216 void
217 smb_net_txr_free(smb_txreq_t *txr)
218 {
219 	ASSERT(txr->tr_magic == SMB_TXREQ_MAGIC);
220 	ASSERT(!list_link_active(&txr->tr_lnd));
221 
222 	txr->tr_magic = 0;
223 	kmem_cache_free(smb_txr_cache, txr);
224 }
225 
226 /*
227  * smb_net_txr_send
228  *
229  *	This routine puts the transmit buffer passed in on the wire. If another
230  *	thread is already draining the transmit list, the transmit buffer is
231  *	queued and the routine returns immediately.
232  */
233 int
234 smb_net_txr_send(ksocket_t so, smb_txlst_t *txl, smb_txreq_t *txr)
235 {
236 	list_t		local;
237 	int		rc = 0;
238 	size_t		sent = 0;
239 	size_t		len;
240 
241 	ASSERT(txl->tl_magic == SMB_TXLST_MAGIC);
242 
243 	mutex_enter(&txl->tl_mutex);
244 	list_insert_tail(&txl->tl_list, txr);
245 	if (txl->tl_active) {
246 		mutex_exit(&txl->tl_mutex);
247 		return (0);
248 	}
249 	txl->tl_active = B_TRUE;
250 
251 	list_create(&local, sizeof (smb_txreq_t),
252 	    offsetof(smb_txreq_t, tr_lnd));
253 
254 	while (!list_is_empty(&txl->tl_list)) {
255 		list_move_tail(&local, &txl->tl_list);
256 		mutex_exit(&txl->tl_mutex);
257 		while ((txr = list_head(&local)) != NULL) {
258 			ASSERT(txr->tr_magic == SMB_TXREQ_MAGIC);
259 			list_remove(&local, txr);
260 
261 			len = txr->tr_len;
262 			rc = ksocket_send(so, txr->tr_buf, txr->tr_len,
263 			    MSG_WAITALL, &sent, CRED());
264 			smb_net_txr_free(txr);
265 			if ((rc == 0) && (sent == len))
266 				continue;
267 
268 			if (rc == 0)
269 				rc = -1;
270 
271 			while ((txr = list_head(&local)) != NULL) {
272 				ASSERT(txr->tr_magic == SMB_TXREQ_MAGIC);
273 				list_remove(&local, txr);
274 				smb_net_txr_free(txr);
275 			}
276 			break;
277 		}
278 		mutex_enter(&txl->tl_mutex);
279 		if (rc == 0)
280 			continue;
281 
282 		while ((txr = list_head(&txl->tl_list)) != NULL) {
283 			ASSERT(txr->tr_magic == SMB_TXREQ_MAGIC);
284 			list_remove(&txl->tl_list, txr);
285 			smb_net_txr_free(txr);
286 		}
287 		break;
288 	}
289 	txl->tl_active = B_FALSE;
290 	mutex_exit(&txl->tl_mutex);
291 	return (rc);
292 }
293