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
41extern boolean_t	nl7c_logd_enabled;
42extern boolean_t	nl7c_logd_started;
43extern boolean_t	nl7c_logd_cycle;
44
45extern void		nl7clogd_startup(void);
46
47extern boolean_t	nl7c_http_log(uri_desc_t *, uri_desc_t *,
48			    nca_request_log_t *, char **, char **, uint32_t *);
49
50static 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
74typedef 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
119int			nl7c_logbuf_max = 0;
120static logd_t		logd;
121static log_buf_t	*log = NULL;
122static log_buf_t	**logv = NULL;
123static int		logvcnt = 0;
124static kmem_cache_t	*log_buf_kmc;
125static nca_fio_t	fio;
126static caddr_t		dir = "/var/nca/";
127static caddr_t		symlink = "current";
128static caddr_t		symlink_dir = "/var/nca";
129static caddr_t		symlink_path = "/var/nca/current";
130
131static kmutex_t		log_lock;
132
133static 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
166nca_fio_t		*nl7c_logd_fio = &fio;
167
168static void
169log_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
204static void
205logd_off()
206{
207	;
208}
209
210static void
211logd_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	}
231retry:
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	 */
277next:
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
347done:
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
370static void
371logd_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
397boolean_t
398nl7c_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
512fresh_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
543finish:
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	 */
576error:
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*/
598static void
599logit_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);
643out:
644	/* Check again in 1 second */
645	flush_tid = timeout(&logit_flush, NULL, hz);
646}
647
648void
649nl7c_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);
666again:
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
730full:
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