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