xref: /titanic_44/usr/src/uts/common/fs/smbsrv/smb_net.c (revision 1529f529004c61fcfd0d95ab79b0f257d6ad4451)
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 
26 #include <sys/types.h>
27 #include <sys/param.h>
28 #include <sys/cpuvar.h>
29 #include <sys/ddi.h>
30 #include <sys/sunddi.h>
31 #include <sys/time.h>
32 #include <sys/varargs.h>
33 #include <sys/modctl.h>
34 #include <sys/pathname.h>
35 #include <sys/fs/snode.h>
36 #include <sys/fs/dv_node.h>
37 #include <sys/vnode.h>
38 #undef mem_free /* XXX Remove this after we convert everything to kmem_alloc */
39 
40 #include <smbsrv/smb_vops.h>
41 #include <smbsrv/smb.h>
42 #include <smbsrv/smb_kproto.h>
43 #include <smbsrv/smb_kstat.h>
44 
45 static	kmem_cache_t	*smb_txr_cache = NULL;
46 
47 /*
48  * smb_net_init
49  *
50  *	This function initializes the resources necessary to access the
51  *	network. It assumes it won't be called simultaneously by multiple
52  *	threads.
53  *
54  * Return Value
55  *
56  *	0	Initialization successful
57  *	ENOMEM	Initialization failed
58  */
59 int
60 smb_net_init(void)
61 {
62 	int	rc = 0;
63 
64 	ASSERT(smb_txr_cache == NULL);
65 
66 	smb_txr_cache = kmem_cache_create(SMBSRV_KSTAT_TXRCACHE,
67 	    sizeof (smb_txreq_t), 8, NULL, NULL, NULL, NULL, NULL, 0);
68 	if (smb_txr_cache == NULL)
69 		rc = ENOMEM;
70 	return (rc);
71 }
72 
73 /*
74  * smb_net_fini
75  *
76  *	This function releases the resources allocated by smb_net_init(). It
77  *	assumes it won't be called simultaneously by multiple threads.
78  *	This function can safely be called even if smb_net_init() hasn't been
79  *	called previously.
80  *
81  * Return Value
82  *
83  *	None
84  */
85 void
86 smb_net_fini(void)
87 {
88 	if (smb_txr_cache) {
89 		kmem_cache_destroy(smb_txr_cache);
90 		smb_txr_cache = NULL;
91 	}
92 }
93 
94 /*
95  * SMB Network Socket API
96  *
97  * smb_socreate:	Creates an socket based on domain/type.
98  * smb_soshutdown:	Disconnect a socket created with smb_socreate
99  * smb_sodestroy:	Release resources associated with a socket
100  * smb_sosend:		Send the contents of a buffer on a socket
101  * smb_sorecv:		Receive data into a buffer from a socket
102  * smb_iov_sosend:	Send the contents of an iovec on a socket
103  * smb_iov_sorecv:	Receive data into an iovec from a socket
104  */
105 
106 struct sonode *
107 smb_socreate(int domain, int type, int protocol)
108 {
109 	vnode_t		*dvp		= NULL;
110 	vnode_t		*vp		= NULL;
111 	struct snode	*csp		= NULL;
112 	int		err		= 0;
113 	major_t		maj;
114 
115 	if ((vp = solookup(domain, type, protocol, NULL, &err)) == NULL) {
116 
117 		/*
118 		 * solookup calls sogetvp if the vp is not found in the cache.
119 		 * Since the call to sogetvp is hardwired to use USERSPACE
120 		 * and declared static we'll do the work here instead.
121 		 */
122 		err = lookupname(type == SOCK_STREAM ? "/dev/tcp" : "/dev/udp",
123 		    UIO_SYSSPACE, FOLLOW, NULLVPP, &vp);
124 		if (err)
125 			return (NULL);
126 
127 		/* Check that it is the correct vnode */
128 		if (vp->v_type != VCHR) {
129 			VN_RELE(vp);
130 			return (NULL);
131 		}
132 
133 		csp = VTOS(VTOS(vp)->s_commonvp);
134 		if (!(csp->s_flag & SDIPSET)) {
135 			char    *pathname = kmem_alloc(MAXPATHLEN, KM_SLEEP);
136 			err = ddi_dev_pathname(vp->v_rdev, S_IFCHR,
137 			    pathname);
138 			if (err == 0) {
139 				err = devfs_lookupname(pathname, NULLVPP,
140 				    &dvp);
141 			}
142 			VN_RELE(vp);
143 			kmem_free(pathname, MAXPATHLEN);
144 			if (err != 0) {
145 				return (NULL);
146 			}
147 			vp = dvp;
148 		}
149 
150 		maj = getmajor(vp->v_rdev);
151 		if (!STREAMSTAB(maj)) {
152 			VN_RELE(vp);
153 			return (NULL);
154 		}
155 	}
156 
157 	return (socreate(vp, domain, type, protocol, SOV_DEFAULT, NULL, &err));
158 }
159 
160 /*
161  * smb_soshutdown will disconnect the socket and prevent subsequent PDU
162  * reception and transmission.  The sonode still exists but its state
163  * gets modified to indicate it is no longer connected.  Calls to
164  * smb_sorecv/smb_iov_sorecv will return so smb_soshutdown can be used
165  * regain control of a thread stuck in smb_sorecv.
166  */
167 void
168 smb_soshutdown(struct sonode *so)
169 {
170 	(void) soshutdown(so, SHUT_RDWR);
171 }
172 
173 /*
174  * smb_sodestroy releases all resources associated with a socket previously
175  * created with smb_socreate.  The socket must be shutdown using smb_soshutdown
176  * before the socket is destroyed with smb_sodestroy, otherwise undefined
177  * behavior will result.
178  */
179 void
180 smb_sodestroy(struct sonode *so)
181 {
182 	vnode_t *vp = SOTOV(so);
183 
184 	(void) VOP_CLOSE(vp, 0, 1, 0, kcred, NULL);
185 	VN_RELE(vp);
186 }
187 
188 int
189 smb_sorecv(struct sonode *so, void *msg, size_t len)
190 {
191 	iovec_t iov;
192 	int err;
193 
194 	ASSERT(so != NULL);
195 	ASSERT(len != 0);
196 
197 	/*
198 	 * Fill in iovec and receive data
199 	 */
200 	iov.iov_base = msg;
201 	iov.iov_len = len;
202 
203 	if ((err = smb_iov_sorecv(so, &iov, 1, len)) != 0) {
204 		return (err);
205 	}
206 
207 	/* Successful receive */
208 	return (0);
209 }
210 
211 /*
212  * smb_iov_sorecv - Receives an iovec from a connection
213  *
214  * This function gets the data asked for from the socket.  It will return
215  * only when all the requested data has been retrieved or if an error
216  * occurs.
217  *
218  * Returns 0 for success, the socket errno value if sorecvmsg fails, and
219  * -1 if sorecvmsg returns success but uio_resid != 0
220  */
221 int
222 smb_iov_sorecv(struct sonode *so, iovec_t *iop, int iovlen, size_t total_len)
223 {
224 	struct msghdr		msg;
225 	struct uio		uio;
226 	int			error;
227 
228 	ASSERT(iop != NULL);
229 
230 	/* Initialization of the message header. */
231 	bzero(&msg, sizeof (msg));
232 	msg.msg_iov	= iop;
233 	msg.msg_flags	= MSG_WAITALL;
234 	msg.msg_iovlen	= iovlen;
235 
236 	/* Initialization of the uio structure. */
237 	bzero(&uio, sizeof (uio));
238 	uio.uio_iov	= iop;
239 	uio.uio_iovcnt	= iovlen;
240 	uio.uio_segflg	= UIO_SYSSPACE;
241 	uio.uio_resid	= total_len;
242 
243 	if ((error = sorecvmsg(so, &msg, &uio)) == 0) {
244 		/* Received data */
245 		if (uio.uio_resid == 0) {
246 			/* All requested data received.  Success */
247 			return (0);
248 		} else {
249 			/* Not all data was sent.  Failure */
250 			return (-1);
251 		}
252 	}
253 
254 	/* Receive failed */
255 	return (error);
256 }
257 
258 /*
259  * smb_net_txl_constructor
260  *
261  *	Transmit list constructor
262  */
263 void
264 smb_net_txl_constructor(smb_txlst_t *txl)
265 {
266 	ASSERT(txl->tl_magic != SMB_TXLST_MAGIC);
267 
268 	mutex_init(&txl->tl_mutex, NULL, MUTEX_DEFAULT, NULL);
269 	list_create(&txl->tl_list, sizeof (smb_txreq_t),
270 	    offsetof(smb_txreq_t, tr_lnd));
271 	txl->tl_active = B_FALSE;
272 	txl->tl_magic = SMB_TXLST_MAGIC;
273 }
274 
275 /*
276  * smb_net_txl_destructor
277  *
278  *	Transmit list destructor
279  */
280 void
281 smb_net_txl_destructor(smb_txlst_t *txl)
282 {
283 	ASSERT(txl->tl_magic == SMB_TXLST_MAGIC);
284 
285 	txl->tl_magic = 0;
286 	list_destroy(&txl->tl_list);
287 	mutex_destroy(&txl->tl_mutex);
288 }
289 
290 /*
291  * smb_net_txr_alloc
292  *
293  *	Transmit buffer allocator
294  */
295 smb_txreq_t *
296 smb_net_txr_alloc(void)
297 {
298 	smb_txreq_t	*txr;
299 
300 	txr = kmem_cache_alloc(smb_txr_cache, KM_SLEEP);
301 	txr->tr_len = 0;
302 	bzero(&txr->tr_lnd, sizeof (txr->tr_lnd));
303 	txr->tr_magic = SMB_TXREQ_MAGIC;
304 	return (txr);
305 }
306 
307 /*
308  * smb_net_txr_free
309  *
310  *	Transmit buffer deallocator
311  */
312 void
313 smb_net_txr_free(smb_txreq_t *txr)
314 {
315 	ASSERT(txr->tr_magic == SMB_TXREQ_MAGIC);
316 	ASSERT(!list_link_active(&txr->tr_lnd));
317 
318 	txr->tr_magic = 0;
319 	kmem_cache_free(smb_txr_cache, txr);
320 }
321 
322 /*
323  * smb_net_txr_send
324  *
325  *	This routine puts the transmit buffer passed in on the wire. If another
326  *	thread is already draining the transmit list, the transmit buffer is
327  *	queued and the routine returns immediately.
328  */
329 int
330 smb_net_txr_send(struct sonode *so, smb_txlst_t *txl, smb_txreq_t *txr)
331 {
332 	list_t		local;
333 	int		rc = 0;
334 	iovec_t		iov;
335 	struct msghdr	msg;
336 	struct uio	uio;
337 
338 	ASSERT(txl->tl_magic == SMB_TXLST_MAGIC);
339 
340 	mutex_enter(&txl->tl_mutex);
341 	list_insert_tail(&txl->tl_list, txr);
342 	if (txl->tl_active) {
343 		mutex_exit(&txl->tl_mutex);
344 		return (0);
345 	}
346 	txl->tl_active = B_TRUE;
347 
348 	list_create(&local, sizeof (smb_txreq_t),
349 	    offsetof(smb_txreq_t, tr_lnd));
350 
351 	while (!list_is_empty(&txl->tl_list)) {
352 		list_move_tail(&local, &txl->tl_list);
353 		mutex_exit(&txl->tl_mutex);
354 		while ((txr = list_head(&local)) != NULL) {
355 			ASSERT(txr->tr_magic == SMB_TXREQ_MAGIC);
356 			list_remove(&local, txr);
357 
358 			iov.iov_base = (void *)txr->tr_buf;
359 			iov.iov_len = txr->tr_len;
360 
361 			bzero(&msg, sizeof (msg));
362 			msg.msg_iov	= &iov;
363 			msg.msg_flags	= MSG_WAITALL;
364 			msg.msg_iovlen	= 1;
365 
366 			bzero(&uio, sizeof (uio));
367 			uio.uio_iov	= &iov;
368 			uio.uio_iovcnt	= 1;
369 			uio.uio_segflg	= UIO_SYSSPACE;
370 			uio.uio_resid	= txr->tr_len;
371 
372 			rc = sosendmsg(so, &msg, &uio);
373 
374 			smb_net_txr_free(txr);
375 
376 			if ((rc == 0) && (uio.uio_resid == 0))
377 				continue;
378 
379 			if (rc == 0)
380 				rc = -1;
381 
382 			while ((txr = list_head(&local)) != NULL) {
383 				ASSERT(txr->tr_magic == SMB_TXREQ_MAGIC);
384 				list_remove(&local, txr);
385 				smb_net_txr_free(txr);
386 			}
387 			break;
388 		}
389 		mutex_enter(&txl->tl_mutex);
390 		if (rc == 0)
391 			continue;
392 
393 		while ((txr = list_head(&txl->tl_list)) != NULL) {
394 			ASSERT(txr->tr_magic == SMB_TXREQ_MAGIC);
395 			list_remove(&txl->tl_list, txr);
396 			smb_net_txr_free(txr);
397 		}
398 		break;
399 	}
400 	txl->tl_active = B_FALSE;
401 	mutex_exit(&txl->tl_mutex);
402 	return (rc);
403 }
404