xref: /titanic_41/usr/src/uts/common/fs/sockfs/nl7clogd.c (revision 2dd2efa5a06a9befe46075cf41e16f57533c9f98)
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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <sys/sysmacros.h>
29 #include <sys/callb.h>
30 #include <sys/fcntl.h>
31 #include <sys/filio.h>
32 #include <sys/pathname.h>
33 #include <sys/cpuvar.h>
34 #include <sys/promif.h>
35 #include <fs/sockfs/nl7c.h>
36 #include <fs/sockfs/nl7curi.h>
37 
38 #include <inet/nca/ncadoorhdr.h>
39 #include <inet/nca/ncalogd.h>
40 
41 extern boolean_t	nl7c_logd_enabled;
42 extern boolean_t	nl7c_logd_started;
43 extern boolean_t	nl7c_logd_cycle;
44 
45 extern void		nl7clogd_startup(void);
46 
47 extern boolean_t	nl7c_http_log(uri_desc_t *, uri_desc_t *,
48 			    nca_request_log_t *, char **, char **, uint32_t *);
49 
50 static void		logit_flush(void *);
51 
52 /*
53  * NL7C reuses the NCA logging scheme, the directory "/var/nca" contains
54  * the symlink "current" to 1 of up to 16 NCA BLF logging files, by default
55  * a single logging file "log", optionally paths of up to 16 log files can
56  * be specified via ncalogd.conf(4), note that these log files need not be
57  * in the "/var/nca" directory.
58  *
59  * NL7C reuses the NCA logging APIs defined in <inet/nca/ncalogd.h>, at
60  * some future date (when NCA is deprecated or improvements are needed)
61  * these need to be moved into NL7C.
62  *
63  * NL7C implements logging differently in 2 ways, 1st the initialization
64  * is handled completely in the kernel by NL7C when it's enabled vs NCA
65  * when the kmod was loaded, 2nd a simple worker thread with a FIFO queue
66  * is used to process log_buf_t's instead of a squeue_t (this is done as
67  * squeue_t's are private to NCA and IP at some future date we may us an
68  * IP squeue_t):
69  *
70  *	logd_t - used by various functions to manage a singly linked
71  * 	grounded list of log_buf_t's and it's worker thread.
72  */
73 
74 typedef struct logd_s {
75 	log_buf_t	*head;
76 	log_buf_t	*tail;
77 	kthread_t	*worker;
78 	kcondvar_t	wait;
79 	kmutex_t	lock;
80 } logd_t;
81 
82 /*
83  * In-kernel logging:
84  *
85  *	nl7c_logbuf_max - tunable for the number of preallocated next
86  *	log_buf_t(s) for use by log_buf_alloc(), note if the value is
87  *	0 (the default) then max_cpus worth will be allocated.
88  *
89  *	logd - global logd_t used to post log_buf_t's too.
90  *
91  *	log - global current log_buf_t that logit() logs too.
92  *
93  *	logv[] - vector of available next logbuf(s) such that when
94  *	logbuf is filled another can be used while being processed by
95  *	the logger() and kmem_cache_alloc() of a replacement is done.
96  *
97  *	logvcnt - count of logv[] vector element(s) and the index
98  *	plus 1 of the next logbuf.
99  *
100  *	log_buf_kmc - the kmem_cache to alloc/free log_buf_t's from/to.
101  *
102  *	fio - the global nca_fio_t used to manage file i/o to a logfile.
103  *
104  *	dir - path to the directory where the current logfile symlink
105  *	is created and the default directory for logfile(s).
106  *
107  *	symlink - name of the logfile symlink.
108  *
109  *	symlink_path - path to the logfile symlink.
110  *
111  *	log_lock - the kmutex_t used to guarantee atomic access of
112  * 	all of the above.
113  *
114  *	flush_tid - logit_flush() timeout id.
115  *
116  *	LOGBUFV_ALLOC() - macro used to add log_buf_t(s) to logv[].
117  */
118 
119 int			nl7c_logbuf_max = 0;
120 static logd_t		logd;
121 static log_buf_t	*log = NULL;
122 static log_buf_t	**logv = NULL;
123 static int		logvcnt = 0;
124 static kmem_cache_t	*log_buf_kmc;
125 static nca_fio_t	fio;
126 static caddr_t		dir = "/var/nca/";
127 static caddr_t		symlink = "current";
128 static caddr_t		symlink_dir = "/var/nca";
129 static caddr_t		symlink_path = "/var/nca/current";
130 
131 static kmutex_t		log_lock;
132 
133 static timeout_id_t	flush_tid;
134 
135 #define	LOGBUFV_ALLOC(kmflag) {						\
136 	log_buf_t	*_p;						\
137 									\
138 	ASSERT(mutex_owned(&log_lock));					\
139 	while (logvcnt < nl7c_logbuf_max) {				\
140 		/*CONSTCOND*/						\
141 		if (kmflag == KM_SLEEP)					\
142 			mutex_exit(&log_lock);				\
143 		_p = kmem_cache_alloc(log_buf_kmc, kmflag);		\
144 		/*CONSTCOND*/						\
145 		if (kmflag == KM_SLEEP) {				\
146 			mutex_enter(&log_lock);				\
147 			if (logvcnt == nl7c_logbuf_max) {		\
148 				mutex_exit(&log_lock);			\
149 				kmem_cache_free(log_buf_kmc, _p);	\
150 				mutex_enter(&log_lock);			\
151 				break;					\
152 			}						\
153 		} else {						\
154 			if (_p == NULL) {				\
155 				break;					\
156 			}						\
157 		}							\
158 		logv[logvcnt++] = _p;					\
159 	}								\
160 }
161 
162 /*
163  * Exports for inet/nca/ncaddi.c:
164  */
165 
166 nca_fio_t		*nl7c_logd_fio = &fio;
167 
168 static void
169 log_buf_alloc(int kmflag)
170 {
171 	nca_log_buf_hdr_t	*hdr;
172 	static	ulong_t		seq = 0;
173 
174 	ASSERT(mutex_owned(&log_lock));
175 
176 	if (logvcnt == 0) {
177 		/*
178 		 * No logv[] to use for the new log global logbuf,
179 		 * try to allocate one or more before giving up.
180 		 */
181 		LOGBUFV_ALLOC(kmflag);
182 		if (logvcnt == 0) {
183 			/* No joy, just give up. */
184 			log = NULL;
185 			return;
186 		}
187 	}
188 	log = logv[--logvcnt];
189 
190 	log->size = NCA_DEFAULT_LOG_BUF_SIZE;
191 	log->cur_pos = sizeof (*hdr);
192 
193 	hdr = (nca_log_buf_hdr_t *)&log->buffer;
194 	hdr->nca_loghdr.nca_version = NCA_LOG_VERSION1;
195 	hdr->nca_loghdr.nca_op = log_op;
196 	hdr->nca_logstats.n_log_size = NCA_DEFAULT_LOG_BUF_SIZE - sizeof (*hdr);
197 	hdr->nca_logstats.n_log_recs = 0;
198 	hdr->nca_logstats.n_log_upcall = seq++;
199 
200 	/* Try to allocate for at least the one we just used */
201 	LOGBUFV_ALLOC(kmflag);
202 }
203 
204 static void
205 logd_off()
206 {
207 	;
208 }
209 
210 static void
211 logd_log_write(kmutex_t *lock, log_buf_t *lbp)
212 {
213 	nca_log_buf_hdr_t *hdr = (nca_log_buf_hdr_t *)lbp->buffer;
214 	nca_log_stat_t	*sts = &hdr->nca_logstats;
215 	int		size = sts->n_log_size + sizeof (*hdr);
216 	vnode_t		*vp;
217 	uio_t		uio;
218 	iovec_t		iov;
219 	int		ret;
220 	boolean_t	noretry = B_FALSE;
221 	vattr_t		attr;
222 
223 	if (size & (DEV_BSIZE - 1)) {
224 		/*
225 		 * Not appropriately sized for directio(),
226 		 * add some filler so it is.
227 		 */
228 		sts->n_log_size += DEV_BSIZE - (size & (DEV_BSIZE - 1));
229 		size = sts->n_log_size + sizeof (*hdr);
230 	}
231 retry:
232 	if (nca_fio_offset(&fio) + size <= nca_fio_size(&fio)) {
233 		/*
234 		 * Room in the current log file so write the logbuf out,
235 		 * exit the logd lock while doing the i/o as to not block
236 		 * queuing.
237 		 */
238 		mutex_exit(lock);
239 
240 		vp = nca_fio_vp(&fio);
241 		(void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, NULL);
242 		iov.iov_base = lbp->buffer;
243 		iov.iov_len = size;
244 		uio.uio_iov = &iov;
245 		uio.uio_iovcnt = 1;
246 		uio.uio_segflg = UIO_SYSSPACE;
247 		uio.uio_fmode = 0;
248 		uio.uio_loffset = (u_offset_t)nca_fio_offset(&fio);
249 		uio.uio_llimit = curproc->p_fsz_ctl;
250 		uio.uio_resid = size;
251 		ret = VOP_WRITE(vp, &uio, 0, kcred, NULL);
252 		VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL);
253 		if (ret != 0) {
254 			if (ret == EFBIG) {
255 				/*
256 				 * Out of space for this file,
257 				 * retry with the next.
258 				 */
259 				nca_fio_size(&fio) = nca_fio_offset(&fio);
260 				if (noretry) {
261 					nl7c_logd_enabled = B_FALSE;
262 					goto done;
263 				} else
264 					goto next;
265 			}
266 		}
267 		nca_fio_offset(&fio) = uio.uio_loffset;
268 
269 		mutex_enter(lock);
270 		goto done;
271 	}
272 
273 	/*
274 	 * Current logfile doesn't have sufficient space
275 	 * so move on to next file (if any).
276 	 */
277 next:
278 	mutex_exit(lock);
279 	/* Close current file */
280 	ret = VOP_CLOSE(nca_fio_vp(&fio), FCREAT|FWRITE|FAPPEND|FTRUNC,
281 			1, (offset_t)0, kcred, NULL);
282 	nca_fio_vp(&fio) = NULL;
283 	if (ret) {
284 		cmn_err(CE_WARN, "nl7c_logd: close of %s failed (error %d)",
285 		    nca_fio_name(&fio), ret);
286 		nl7c_logd_enabled = B_FALSE;
287 		logd_off();
288 		return;
289 	}
290 
291 	/* Go to next file */
292 	nca_fio_ix(&fio)++;
293 	if (nca_fio_ix(&fio) == nca_fio_cnt(&fio)) {
294 		/*
295 		 * We have reached the last file. If cycling
296 		 * is not on, disable logging and bailout.
297 		 */
298 		if (nl7c_logd_cycle) {
299 			/* Start from the first file */
300 			nca_fio_ix(&fio) = 0;
301 		} else {
302 			nca_fio_ix(&fio)--;
303 			nl7c_logd_enabled = B_FALSE;
304 			logd_off();
305 			return;
306 		}
307 	}
308 
309 	/* Open the next log file */
310 	ret = vn_open(nca_fio_name(&fio), UIO_SYSSPACE, FCREAT|FWRITE|FTRUNC,
311 			0600, &nca_fio_vp(&fio), 0, 0);
312 	if (ret) {
313 		cmn_err(CE_WARN, "nl7c_logd: vn_open of %s failed (error %d)",
314 			nca_fio_name(&fio), ret);
315 		nl7c_logd_enabled = B_FALSE;
316 		logd_off();
317 		return;
318 	}
319 
320 	/* Turn on directio */
321 	(void) VOP_IOCTL(nca_fio_vp(&fio), _FIODIRECTIO,
322 			DIRECTIO_ON, 0, kcred, NULL, NULL);
323 
324 	/* Start writing from the begining of the file */
325 	nca_fio_offset(&fio) = 0;
326 
327 	/* Remove the current symlink */
328 	(void) VOP_REMOVE(nca_fio_dvp(&fio), symlink, kcred, NULL, 0);
329 
330 	attr.va_mask = AT_MODE | AT_TYPE;
331 	attr.va_mode = 0777;
332 	attr.va_type = VLNK;
333 
334 	/* Create symlink to the new log file */
335 	ret = VOP_SYMLINK(nca_fio_dvp(&fio), symlink,
336 			&attr, nca_fio_name(&fio), kcred, NULL, 0);
337 	if (ret) {
338 		cmn_err(CE_WARN, "nl7c_logd: symlink of %s to %s failed",
339 			symlink, nca_fio_name(&fio));
340 		nl7c_logd_enabled = B_FALSE;
341 		logd_off();
342 		return;
343 	}
344 	mutex_enter(lock);
345 	goto retry;
346 
347 done:
348 	if (logvcnt < nl7c_logbuf_max) {
349 		/* May need to allocate some logbuf(s) for logv[] */
350 		mutex_enter(&log_lock);
351 		if (logvcnt < nl7c_logbuf_max) {
352 			/*
353 			 * After acquiring the lock still need logbuf(s),
354 			 * if the global logbuf pointer is NULL then call
355 			 * log_buf_alloc() as it will fill up logbugv[]
356 			 * and initialize a new logbuf else fill up just
357 			 * the logv[] here.
358 			 */
359 			if (log == NULL) {
360 				log_buf_alloc(KM_SLEEP);
361 			} else {
362 				/*LINTED*/
363 				LOGBUFV_ALLOC(KM_SLEEP);
364 			}
365 		}
366 		mutex_exit(&log_lock);
367 	}
368 }
369 
370 static void
371 logd_worker(logd_t *logdp)
372 {
373 	log_buf_t	*lbp;
374 	kmutex_t	*lock = &logdp->lock;
375 	kcondvar_t	*wait = &logdp->wait;
376 	callb_cpr_t	cprinfo;
377 
378 	CALLB_CPR_INIT(&cprinfo, lock, callb_generic_cpr, "nl7c");
379 	mutex_enter(lock);
380 
381 	for (;;) {
382 		/* Wait for something to do */
383 		while ((lbp = logdp->head) == NULL) {
384 			CALLB_CPR_SAFE_BEGIN(&cprinfo);
385 			cv_wait(wait, lock);
386 			CALLB_CPR_SAFE_END(&cprinfo, lock);
387 		}
388 		if ((logdp->head = lbp->next) == NULL)
389 			logdp->tail = NULL;
390 		/* Got a logbuf to write out */
391 		if (nl7c_logd_enabled)
392 			logd_log_write(lock, lbp);
393 		kmem_cache_free(log_buf_kmc, lbp);
394 	}
395 }
396 
397 boolean_t
398 nl7c_logd_init(int fsz, caddr_t *fnv)
399 {
400 	vnode_t	*dvp;
401 	vnode_t	*svp;
402 	vnode_t	*vp;
403 	int	ret;
404 	caddr_t	*fnp;
405 	vattr_t	attr;
406 	uio_t	uio;
407 	iovec_t	iov;
408 	char	fbuf[TYPICALMAXPATHLEN + 1];
409 
410 	/*
411 	 * Initialize the global logfio.
412 	 */
413 	nca_fio_cnt(&fio) = 0;
414 	nca_fio_ix(&fio) = 0;
415 	fnp = fnv;
416 	while (*fnp != NULL) {
417 		nca_fio_cnt(&fio)++;
418 		nca_fio_name(&fio) = *fnp;
419 		nca_fio_size(&fio) = fsz;
420 		nca_fio_offset(&fio) = 0;
421 		nca_fio_file(&fio) = nca_fio_ix(&fio);
422 		nca_fio_vp(&fio) = NULL;
423 
424 		if (++fnp == &fnv[NCA_FIOV_SZ])
425 			break;
426 
427 		nca_fio_ix(&fio)++;
428 	}
429 	/*
430 	 * See if we can start logging from where we left off last time,
431 	 * first check if the symlink exists.
432 	 */
433 	dvp = NULL;
434 	ret = lookupname(symlink_path, UIO_SYSSPACE, NO_FOLLOW, &dvp, &svp);
435 	if (ret || dvp == NULL || svp == NULL) {
436 		if (dvp == NULL) {
437 			/* No NCA symlink directory, create one */
438 			attr.va_mask = AT_MODE | AT_TYPE;
439 			attr.va_mode = 0755;
440 			attr.va_type = VDIR;
441 			ret = vn_create(symlink_dir, UIO_SYSSPACE, &attr,
442 			    EXCL, 0, &dvp, CRMKDIR, 0, 0);
443 			if (ret) {
444 				cmn_err(CE_WARN, "nl7c_logd_init: create"
445 				    " symlink dir of %s failed(%d).",
446 				    symlink_dir, ret);
447 				goto error;
448 			}
449 		}
450 		nca_fio_dvp(&fio) = dvp;
451 		/* No symlink so don't know were to start from */
452 		goto fresh_start;
453 	}
454 	/* Save the symlink dir vnode */
455 	nca_fio_dvp(&fio) = dvp;
456 
457 	/* Check if the file pointed by the symlink exists */
458 	ret = lookupname(symlink_path, UIO_SYSSPACE, FOLLOW, NULLVPP, &vp);
459 	if (ret || vp == NULL)
460 		goto fresh_start;
461 	VN_RELE(vp);
462 
463 	/* Read the symlink and find it in fnv[], else fresh start */
464 	iov.iov_len = TYPICALMAXPATHLEN;
465 	iov.iov_base = fbuf;
466 	uio.uio_iov = &iov;
467 	uio.uio_iovcnt = 1;
468 	uio.uio_resid = iov.iov_len;
469 	uio.uio_segflg = UIO_SYSSPACE;
470 	uio.uio_loffset = 0;
471 	uio.uio_fmode = 0;
472 	ret = VOP_READLINK(svp, &uio, kcred, NULL);
473 	if (ret) {
474 		(void) VOP_REMOVE(dvp, symlink, kcred, NULL, 0);
475 		goto fresh_start;
476 	}
477 
478 	/* Null terminate the buf */
479 	fbuf[TYPICALMAXPATHLEN - (int)uio.uio_resid] = '\0';
480 	fnp = fnv;
481 	nca_fio_ix(&fio) = 0;
482 	while (*fnp != NULL) {
483 		if (strcmp(*fnp, fbuf) == 0)
484 			break;
485 		if (++fnp == &fnv[NCA_FIOV_SZ])
486 			goto fresh_start;
487 		nca_fio_ix(&fio)++;
488 	}
489 	if (*fnp == NULL)
490 		goto fresh_start;
491 
492 	/* Start writing to the end of the file */
493 	ret = vn_open(*fnp, UIO_SYSSPACE,
494 	    FCREAT|FWRITE|FAPPEND, 0600, &vp, 0, 0);
495 	if (ret) {
496 		cmn_err(CE_WARN, "nl7c_logd_init: vn_open of "
497 		    "%s failed (error %d)", *fnp, ret);
498 		goto error;
499 	}
500 	nca_fio_vp(&fio) = vp;
501 	(void) VOP_IOCTL(vp, _FIODIRECTIO, DIRECTIO_ON, 0, kcred, NULL, NULL);
502 	attr.va_mask = AT_SIZE;
503 	ret = VOP_GETATTR(nca_fio_vp(&fio), &attr, 0, NULL, NULL);
504 	if (ret) {
505 		cmn_err(CE_WARN, "nl7c_logd_init: getattr of %s failed", *fnp);
506 		goto error;
507 	}
508 	nca_fio_offset(&fio) = (off64_t)attr.va_size;
509 
510 	goto finish;
511 
512 fresh_start:
513 	/*
514 	 * Here if no previous logging environment found or if the previous
515 	 * logging environment isn't usable or isn't consistent with the new
516 	 * fnv[]. Remove the existing symlink (if any) then create the new
517 	 * symlink to point to the first logfile.
518 	 */
519 	nca_fio_ix(&fio) = 0;
520 	attr.va_mask = AT_MODE | AT_TYPE;
521 	attr.va_mode = 0777;
522 	attr.va_type = VLNK;
523 	(void) VOP_REMOVE(dvp, symlink, kcred, NULL, 0);
524 	ret = VOP_SYMLINK(dvp, symlink, &attr, nca_fio_name(&fio), kcred, NULL,
525 	    0);
526 	if (ret) {
527 		cmn_err(CE_WARN, "nl7c_logd_init: symlink of %s to %s failed",
528 		    symlink_path, nca_fio_name(&fio));
529 		goto error;
530 	}
531 	ret = vn_open(nca_fio_name(&fio), UIO_SYSSPACE,
532 	    FCREAT|FWRITE|FTRUNC, 0600, &nca_fio_vp(&fio), 0, 0);
533 	if (ret) {
534 		cmn_err(CE_WARN, "nl7c_logd_init: vn_open of "
535 		    "%s failed (error %d)", nca_fio_name(&fio), ret);
536 		goto error;
537 	}
538 
539 	/* Turn on directio */
540 	(void) VOP_IOCTL(nca_fio_vp(&fio), _FIODIRECTIO,
541 			DIRECTIO_ON, 0, kcred, NULL, NULL);
542 
543 finish:
544 	log_buf_kmc = kmem_cache_create("NL7C_log_buf_kmc", sizeof (log_buf_t),
545 		0, NULL, NULL, NULL, NULL, NULL, 0);
546 
547 	mutex_init(&log_lock, NULL, MUTEX_DEFAULT, NULL);
548 	mutex_enter(&log_lock);
549 
550 	if (nl7c_logbuf_max == 0)
551 		nl7c_logbuf_max = max_ncpus;
552 	logv = kmem_alloc(nl7c_logbuf_max * sizeof (*logv), KM_SLEEP);
553 	for (logvcnt = 0; logvcnt < nl7c_logbuf_max; logvcnt++) {
554 		logv[logvcnt] = kmem_cache_alloc(log_buf_kmc, KM_SLEEP);
555 	}
556 
557 	log_buf_alloc(KM_SLEEP);
558 
559 	mutex_init(&logd.lock, NULL, MUTEX_DEFAULT, NULL);
560 	cv_init(&logd.wait, NULL, CV_DEFAULT, NULL);
561 	logd.head = NULL;
562 	logd.tail = NULL;
563 	logd.worker = thread_create(NULL, 0, logd_worker, &logd,
564 	    0, &p0, TS_RUN, maxclsyspri);
565 
566 	mutex_exit(&log_lock);
567 
568 	/* Last, start logger timeout flush */
569 	logit_flush(NULL);
570 
571 	return (B_TRUE);
572 
573 	/*
574 	 * Error of some sort, free any resources in reverse order.
575 	 */
576 error:
577 	nca_fio_ix(&fio) = 0;
578 	while (nca_fio_ix(&fio) < nca_fio_cnt(&fio)) {
579 		char *name = nca_fio_name(&fio);
580 
581 		if ((vp = nca_fio_vp(&fio)) != NULL)
582 			VN_RELE(vp);
583 		kmem_free(name, (strlen(name) + 1));
584 		nca_fio_ix(&fio)++;
585 	}
586 	nca_fio_cnt(&fio) = 0;
587 
588 	if (svp)
589 		VN_RELE(svp);
590 
591 	if (dvp)
592 		VN_RELE(dvp);
593 
594 	return (B_FALSE);
595 }
596 
597 /*ARGSUSED*/
598 static void
599 logit_flush(void *arg)
600 {
601 	static log_buf_t *lastlbp = NULL;
602 	static int	lastpos;
603 	log_buf_t	*lbp = log;
604 
605 	flush_tid = 0;
606 
607 	mutex_enter(&log_lock);
608 	if (log == NULL) {
609 		/* No global logbuf ? Nothing to flush. */
610 		goto out;
611 	}
612 	if (lbp != NULL && lbp->cur_pos > (sizeof (nca_log_buf_hdr_t)) &&
613 		lastlbp == lbp && lastpos == lbp->cur_pos) {
614 		/*
615 		 * We have a logbuf and it has log data and it's the
616 		 * same logbuf and pos as last time and after lock
617 		 * still true, so flush.
618 		 */
619 		nca_log_stat_t	*sp;
620 
621 		sp = &(((nca_log_buf_hdr_t *)lbp)->nca_logstats);
622 		sp->n_log_size = lbp->cur_pos;
623 
624 		/* Link new logbuf onto end of logd and wake logd up */
625 		mutex_enter(&logd.lock);
626 		log->next = NULL;
627 		if (logd.tail == NULL)
628 			logd.head = log;
629 		else
630 			logd.tail->next = log;
631 		logd.tail = log;
632 		cv_signal(&logd.wait);
633 
634 		mutex_exit(&logd.lock);
635 
636 		log_buf_alloc(KM_NOSLEEP);
637 	}
638 
639 	if ((lastlbp = lbp) != NULL)
640 		lastpos = lbp->cur_pos;
641 
642 	mutex_exit(&log_lock);
643 out:
644 	/* Check again in 1 second */
645 	flush_tid = timeout(&logit_flush, NULL, hz);
646 }
647 
648 void
649 nl7c_logd_log(uri_desc_t *quri, uri_desc_t *suri, time_t rtime, ipaddr_t faddr)
650 {
651 	nca_request_log_t *req;
652 	char		*wp;
653 	char		*pep;
654 	int		sz;
655 	uint32_t	off = 0;
656 	int		kmflag = servicing_interrupt() ? KM_NOSLEEP : KM_SLEEP;
657 
658 	if (! nl7c_logd_enabled)
659 		return;
660 
661 	if (! nl7c_logd_started) {
662 		/* Startup logging */
663 		nl7clogd_startup();
664 	}
665 	mutex_enter(&log_lock);
666 again:
667 	if (log == NULL) {
668 		/* No global logbuf, try to allocate one before giving up. */
669 		log_buf_alloc(kmflag);
670 		if (log == NULL) {
671 			/* No joy, just give up. */
672 			mutex_exit(&log_lock);
673 			return;
674 		}
675 	}
676 	/*
677 	 * Get a pointer to an aligned write position, a pointer to past
678 	 * the end of the logbuf, and a pointer to the request header.
679 	 *
680 	 * As the request header is filled in field by field addtional
681 	 * storage is allcated following the request header.
682 	 *
683 	 * If at any point an allocation from the logbuf overflows (i.e.
684 	 * resulting in a pointer > pep) the current request logging is
685 	 * aborted, the current logbuf is posted for write, a new logbuf
686 	 * is allocated, and start all over.
687 	 */
688 	pep = &((char *)log)[log->size];
689 	wp = (log->buffer + log->cur_pos);
690 	wp = NCA_LOG_ALIGN(wp);
691 	req = (nca_request_log_t *)wp;
692 	if ((wp + sizeof (*req)) >= pep) goto full;
693 	bzero(wp, sizeof (*req));
694 	wp += sizeof (*req);
695 
696 	sz = MIN((quri->path.ep - quri->path.cp), MAX_URL_LEN);
697 	if ((wp + sz + 1) >= pep) goto full;
698 	bcopy(quri->path.cp, wp, sz);
699 	wp += sz;
700 	*wp++ = 0;
701 	sz++;
702 	req->request_url_len = sz;
703 	req->request_url = off;
704 	off += sz;
705 
706 	/*
707 	 * Set response length now as the scheme log function will
708 	 * subtract out any header length as we want the entity body
709 	 * length returned for the response_len.
710 	 */
711 	req->response_len = (uint_t)suri->resplen;
712 
713 	/* Call scheme log */
714 	if (nl7c_http_log(quri, suri, req, &wp, &pep, &off)) goto full;
715 
716 	/* Update logbuf */
717 	log->cur_pos = (wp - log->buffer);
718 
719 	req->response_status = HS_OK;
720 
721 	req->start_process_time = (time32_t)rtime;
722 	req->end_process_time = (time32_t)gethrestime_sec();
723 
724 	req->remote_host = faddr;
725 
726 	((nca_log_buf_hdr_t *)log)->nca_logstats.n_log_recs++;
727 	mutex_exit(&log_lock);
728 	return;
729 
730 full:
731 	/*
732 	 * The logbuf is full, zero fill from current
733 	 * write pointer through the end of the buf.
734 	 */
735 	wp = (log->buffer + log->cur_pos);
736 	sz = pep - wp;
737 	bzero(wp, sz);
738 	/*
739 	 * Link new logbuf onto end of logd and wake logd up.
740 	 */
741 	mutex_enter(&logd.lock);
742 	log->next = NULL;
743 	if (logd.tail == NULL)
744 		logd.head = log;
745 	else
746 		logd.tail->next = log;
747 	logd.tail = log;
748 	cv_signal(&logd.wait);
749 	mutex_exit(&logd.lock);
750 	/*
751 	 * Try to allocate a new global logbuf.
752 	 */
753 	log_buf_alloc(kmflag);
754 
755 	goto again;
756 }
757