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 1994-2002 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 /*
30 * Methods of the cfsd_maptbl classes.
31 */
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <stddef.h>
36 #include <string.h>
37 #include <synch.h>
38 #include <unistd.h>
39 #include <fcntl.h>
40 #include <errno.h>
41 #include <sys/utsname.h>
42 #include <sys/vfs.h>
43 #include <sys/cred.h>
44 #include <sys/param.h>
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 #include <sys/mman.h>
48 #include <sys/fs/cachefs_fs.h>
49 #include <sys/fs/cachefs_dlog.h>
50 #include <mdbug/mdbug.h>
51 #include "cfsd.h"
52 #include "cfsd_logfile.h"
53
54 /*
55 * cfsd_logfile_create
56 *
57 * Description:
58 * Arguments:
59 * Returns:
60 * Preconditions:
61 */
62 cfsd_logfile_object_t *
cfsd_logfile_create(void)63 cfsd_logfile_create(void)
64 {
65 cfsd_logfile_object_t *logfile_object_p;
66
67 dbug_enter("cfsd_logfile_create");
68
69 logfile_object_p = cfsd_calloc(sizeof (cfsd_logfile_object_t));
70 logfile_object_p->i_fid = -1;
71 logfile_object_p->i_map_entry.i_pa = NULL;
72 logfile_object_p->i_map_entry.i_paoff = 0;
73 logfile_object_p->i_map_entry.i_paend = 0;
74 logfile_object_p->i_map_entry.i_palen = 0;
75 logfile_object_p->i_map_offset.i_pa = NULL;
76 logfile_object_p->i_map_offset.i_paoff = 0;
77 logfile_object_p->i_map_offset.i_paend = 0;
78 logfile_object_p->i_map_offset.i_palen = 0;
79 logfile_object_p->i_cur_offset = 0;
80 logfile_object_p->i_cur_entry = NULL;
81 dbug_leave("cfsd_logfile_create");
82 return (logfile_object_p);
83 }
84
85 /*
86 * cfsd_logfile_destroy
87 *
88 * Description:
89 * Arguments:
90 * Returns:
91 * Preconditions:
92 */
93 void
cfsd_logfile_destroy(cfsd_logfile_object_t * logfile_object_p)94 cfsd_logfile_destroy(cfsd_logfile_object_t *logfile_object_p)
95 {
96 dbug_enter("cfsd_logfile_destroy");
97 logfile_sync(logfile_object_p);
98 logfile_teardown(logfile_object_p);
99 cfsd_free(logfile_object_p);
100 dbug_leave("cfsd_logfile_destroy");
101 }
102
103 /*
104 * logfile_domap
105 *
106 * Description:
107 * Maps in the specified section of the file.
108 * Arguments:
109 * off The offset to map in. Must be i_pagesize aligned.
110 * map 0 means use map_entry, 1 means use map_offset
111 * Returns:
112 * Returns 0 for success or an errno value on failure.
113 * Preconditions:
114 */
115 int
logfile_domap(cfsd_logfile_object_t * logfile_object_p,off_t off,int map)116 logfile_domap(cfsd_logfile_object_t *logfile_object_p, off_t off, int map)
117 {
118 int xx;
119 int len;
120 mmap_info_t *mmp;
121
122 dbug_enter("logfile_domap");
123 dbug_precond(logfile_object_p->i_fid >= 0);
124
125 len = logfile_object_p->i_maplen;
126 mmp = (map == 0) ?
127 &logfile_object_p->i_map_entry :
128 &logfile_object_p->i_map_offset;
129
130 logfile_object_p->i_stat_mapmove++;
131
132 /* destroy old mapping if it exists */
133 if (mmp->i_pa) {
134 /* determine how far we have to move the map */
135 logfile_object_p->i_stat_mapdist += abs(mmp->i_paoff - off);
136
137 /* remove the map */
138 xx = munmap(mmp->i_pa, mmp->i_palen);
139 if (xx == -1) {
140 xx = errno;
141 dbug_print(("error", "Could not unmap %s, %d, %p, %d",
142 logfile_object_p->i_name, xx, mmp->i_pa,
143 mmp->i_palen));
144 }
145 mmp->i_pa = NULL;
146 mmp->i_palen = 0;
147 mmp->i_paoff = 0;
148 mmp->i_paend = 0;
149 }
150
151 /* do the mapping */
152 mmp->i_pa = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED,
153 logfile_object_p->i_fid, off);
154 if (mmp->i_pa == MAP_FAILED) {
155 xx = errno;
156 dbug_print(("error",
157 "Could not map %s, error %d, off %d, len %d",
158 logfile_object_p->i_name, xx, off, len));
159 mmp->i_pa = NULL;
160 dbug_leave("logfile_domap");
161 return (xx);
162 }
163
164 mmp->i_palen = len;
165 mmp->i_paoff = off;
166 mmp->i_paend = off + len - 1;
167 dbug_leave("logfile_domap");
168 return (0);
169 }
170
171 /*
172 * logfile_getaddr
173 *
174 * Description:
175 * Returns an address of a particular offset in the file.
176 * The size of the item to map is i_maxmap
177 * This routine assumes that if we have to remap that i_maxmap
178 * will fit inside the default mapping size.
179 * Arguments:
180 * start offset in the file to map
181 * map 0 means use map_entry, 1 means use map_offset
182 * Returns:
183 * Returns NULL for a failure with the mapping file.
184 * Preconditions:
185 */
186 caddr_t
logfile_getaddr(cfsd_logfile_object_t * logfile_object_p,off_t start,int map)187 logfile_getaddr(cfsd_logfile_object_t *logfile_object_p, off_t start, int map)
188 {
189 mmap_info_t *mmp;
190 caddr_t pa;
191 off_t end;
192
193 dbug_enter("logfile_getaddr");
194
195 mmp = (map == 0) ?
196 &logfile_object_p->i_map_entry :
197 &logfile_object_p->i_map_offset;
198
199 /* determine the end of the item */
200 end = start + logfile_object_p->i_maxmap - 1;
201
202 /* map the entry in if necessary */
203 if ((start < mmp->i_paoff) || (mmp->i_paend < end)) {
204 if (logfile_domap(logfile_object_p,
205 start & logfile_object_p->i_pagemask, map)) {
206 dbug_leave("logfile_getaddr");
207 return (NULL);
208 }
209 dbug_assert((mmp->i_paoff <= start) && (end <= mmp->i_paend));
210 }
211
212 /* make an address and return it */
213 pa = mmp->i_pa + (start - mmp->i_paoff);
214 dbug_leave("logfile_getaddr");
215 return (pa);
216 }
217
218 /*
219 * logfile_setup
220 *
221 * Description:
222 * Sets up to use the specified file.
223 * Call this routine before using any of the other routines.
224 * Arguments:
225 * filename file to use
226 * maxmap max amount needed after a map
227 * Returns:
228 * Returns 0 for success or an errno value.
229 * Preconditions:
230 * precond(filename)
231 */
232 int
logfile_setup(cfsd_logfile_object_t * logfile_object_p,const char * filename,int maxmap)233 logfile_setup(cfsd_logfile_object_t *logfile_object_p,
234 const char *filename, int maxmap)
235 {
236 int xx;
237 struct stat sinfo;
238 long *versionp;
239
240 dbug_enter("logfile_setup");
241 dbug_precond(filename);
242
243 /* clean up from a previous setup */
244 logfile_teardown(logfile_object_p);
245
246 strlcpy(logfile_object_p->i_name, filename,
247 sizeof (logfile_object_p->i_name));
248 dbug_print(("info", "filename %s", logfile_object_p->i_name));
249 logfile_object_p->i_maxmap = maxmap;
250
251 /* get the page info */
252 logfile_object_p->i_pagesize = PAGESIZE;
253 logfile_object_p->i_pagemask = PAGEMASK;
254 logfile_object_p->i_maplen = logfile_object_p->i_pagesize * 100;
255
256 /* open the file */
257 logfile_object_p->i_fid = open(logfile_object_p->i_name,
258 O_RDWR | O_NONBLOCK);
259 if (logfile_object_p->i_fid == -1) {
260 xx = errno;
261 dbug_print(("error", "Could not open %s, %d",
262 logfile_object_p->i_name, xx));
263 dbug_leave("logfile_setup");
264 return (xx);
265 }
266
267 /* get the size and type of file */
268 xx = fstat(logfile_object_p->i_fid, &sinfo);
269 if (xx) {
270 xx = errno;
271 if (xx == ENOENT) {
272 dbug_print(("info", "No log file to roll"));
273 } else {
274 dbug_print(("error", "Could not stat %s, %d",
275 logfile_object_p->i_name, xx));
276 }
277 dbug_leave("logfile_setup");
278 return (xx);
279 }
280 logfile_object_p->i_size = sinfo.st_size;
281
282 /* sanity check, better be a regular file */
283 if (!S_ISREG(sinfo.st_mode)) {
284 xx = ENOTSUP;
285 dbug_print(("error", "%s Not a regular file.",
286 logfile_object_p->i_name));
287 dbug_leave("logfile_setup");
288 return (xx);
289 }
290
291 /* better not be too small */
292 if (logfile_object_p->i_size < LOGFILE_ENTRY_START) {
293 dbug_print(("error", "File %s is too small %d.",
294 logfile_object_p->i_name, logfile_object_p->i_size));
295 dbug_leave("logfile_setup");
296 return (EINVAL);
297 }
298
299 /* initialize statistic gathering */
300 logfile_object_p->i_stat_mapmove = 0;
301 logfile_object_p->i_stat_mapdist = 0;
302
303 /* check the version number */
304 versionp = (long *)logfile_getaddr(logfile_object_p, 0, 1);
305 if (versionp == NULL) {
306 dbug_leave("logfile_setup");
307 return (EIO);
308 }
309 if (*versionp != CFS_DLOG_VERSION) {
310 dbug_print(("error", "Log file version mismatch %d != %d",
311 *versionp, CFS_DLOG_VERSION));
312 dbug_leave("logfile_setup");
313 return (EINVAL);
314 }
315
316 /* return success */
317 dbug_leave("logfile_setup");
318 return (0);
319 }
320
321 /*
322 * logfile_teardown
323 *
324 * Description:
325 * Uninitializes the object.
326 * Call logfile_setup before using this object again.
327 * Arguments:
328 * Returns:
329 * Preconditions:
330 */
331 void
logfile_teardown(cfsd_logfile_object_t * logfile_object_p)332 logfile_teardown(cfsd_logfile_object_t *logfile_object_p)
333 {
334 int xx;
335
336 dbug_enter("logfile_teardown");
337
338 if (logfile_object_p->i_map_entry.i_pa) {
339 xx = munmap(logfile_object_p->i_map_entry.i_pa,
340 logfile_object_p->i_map_entry.i_palen);
341 if (xx == -1) {
342 xx = errno;
343 dbug_print(("error", "Could not unmap %s, %d, %p, %d",
344 logfile_object_p->i_name, xx,
345 logfile_object_p->i_map_entry.i_pa,
346 logfile_object_p->i_map_entry.i_palen));
347 }
348 logfile_object_p->i_map_entry.i_pa = NULL;
349 }
350 logfile_object_p->i_map_entry.i_paoff = 0;
351 logfile_object_p->i_map_entry.i_paend = 0;
352 logfile_object_p->i_map_entry.i_palen = 0;
353
354 if (logfile_object_p->i_map_offset.i_pa) {
355 xx = munmap(logfile_object_p->i_map_offset.i_pa,
356 logfile_object_p->i_map_offset.i_palen);
357 if (xx == -1) {
358 xx = errno;
359 dbug_print(("error", "Could not unmap %s, %d, %p, %d",
360 logfile_object_p->i_name, xx,
361 logfile_object_p->i_map_offset.i_pa,
362 logfile_object_p->i_map_offset.i_palen));
363 }
364 logfile_object_p->i_map_offset.i_pa = NULL;
365 }
366 logfile_object_p->i_map_offset.i_paoff = 0;
367 logfile_object_p->i_map_offset.i_paend = 0;
368 logfile_object_p->i_map_offset.i_palen = 0;
369
370 if (logfile_object_p->i_fid != -1) {
371 if (close(logfile_object_p->i_fid))
372 dbug_print(("error", "Could not close %s, %d",
373 logfile_object_p->i_name, errno));
374 logfile_object_p->i_fid = -1;
375 }
376 logfile_object_p->i_cur_offset = 0;
377 logfile_object_p->i_cur_entry = NULL;
378 dbug_leave("logfile_teardown");
379 }
380
381 /*
382 * logfile_entry
383 *
384 * Description:
385 * Sets addrp to the address of the log entry at offset
386 * The mapping remains in effect until:
387 * a) this routine is called again
388 * b) logfile_teardown is called
389 * c) this object is destroyed
390 * Arguments:
391 * offset offset to start of entry
392 * entpp place to store address
393 * Returns:
394 * Returns 0 for success, 1 for EOF, -1 if a fatal error occurs.
395 * Preconditions:
396 * precond(addrp)
397 */
398 int
logfile_entry(cfsd_logfile_object_t * logfile_object_p,off_t offset,cfs_dlog_entry_t ** entpp)399 logfile_entry(cfsd_logfile_object_t *logfile_object_p,
400 off_t offset,
401 cfs_dlog_entry_t **entpp)
402 {
403 cfs_dlog_entry_t *entp;
404
405 dbug_enter("logfile_entry");
406 dbug_precond(entpp);
407 dbug_precond(offset >= sizeof (long));
408
409
410 logfile_object_p->i_stat_nextcnt++;
411
412 /* check for eof */
413 if (offset >= logfile_object_p->i_size) {
414 dbug_leave("logfile_entry");
415 return (1);
416 }
417 dbug_assert((offset & 3) == 0);
418
419 /* get the address of the entry */
420 entp = (cfs_dlog_entry_t *)logfile_getaddr(logfile_object_p, offset, 0);
421 if (entp == NULL) {
422 dbug_leave("logfile_entry");
423 return (-1);
424 }
425 /* sanity check, record should be alligned */
426 if (entp->dl_len & 3) {
427 dbug_print(("error",
428 "Record at offset %d length is not alligned %d",
429 offset, entp->dl_len));
430 dbug_leave("logfile_entry");
431 return (-1);
432 }
433
434 /* sanity check record should a reasonable size */
435 if ((entp->dl_len < CFS_DLOG_ENTRY_MINSIZE) ||
436 (entp->dl_len > CFS_DLOG_ENTRY_MAXSIZE)) {
437 dbug_print(("error",
438 "Record at offset %d has an invalid size %d", offset,
439 entp->dl_len));
440 dbug_leave("logfile_entry");
441 return (-1);
442 }
443
444 /* preserve offset and pointer */
445 logfile_object_p->i_cur_offset = offset;
446 logfile_object_p->i_cur_entry = entp;
447
448 /* return success */
449 *entpp = entp;
450 dbug_leave("logfile_entry");
451 return (0);
452 }
453
454 /*
455 * logfile_offset
456 *
457 * Description:
458 * Sets addrp to the address of the specified offset.
459 * The mapping remains in effect until:
460 * a) this routine is called again
461 * b) logfile_teardown is called
462 * c) this object is destroyed
463 * Arguments:
464 * offset offset into file, must be 0 <= offset < i_size
465 * addrp returns mapped address
466 * Returns:
467 * Returns 0 for success, -1 if a fatal error occurs.
468 * Preconditions:
469 * precond(addrp)
470 */
471 int
logfile_offset(cfsd_logfile_object_t * logfile_object_p,off_t offset,caddr_t * addrp)472 logfile_offset(cfsd_logfile_object_t *logfile_object_p,
473 off_t offset,
474 caddr_t *addrp)
475 {
476 caddr_t pa;
477
478 dbug_enter("logfile_offset");
479 dbug_precond(addrp);
480 dbug_precond((0 <= offset) && (offset < logfile_object_p->i_size));
481
482 logfile_object_p->i_stat_offcnt++;
483
484 /* get the address for the offset */
485 pa = logfile_getaddr(logfile_object_p, offset, 1);
486 if (pa == NULL) {
487 dbug_leave("logfile_offset");
488 return (-1);
489 }
490 /* return success */
491 *addrp = pa;
492 dbug_leave("logfile_offset");
493 return (0);
494 }
495
496 /*
497 * logfile_sync
498 *
499 * Description:
500 * Performs an fsync on the log file.
501 * Arguments:
502 * Returns:
503 * Returns 0 for success or an errno value on failure.
504 * Preconditions:
505 */
506 int
logfile_sync(cfsd_logfile_object_t * logfile_object_p)507 logfile_sync(cfsd_logfile_object_t *logfile_object_p)
508 {
509 int xx;
510
511 dbug_enter("logfile_sync");
512
513 if (logfile_object_p->i_fid == -1) {
514 dbug_leave("logfile_sync");
515 return (0);
516 }
517 xx = fsync(logfile_object_p->i_fid);
518 if (xx) {
519 xx = errno;
520 dbug_print(("error", "fsync failed %d", xx));
521 }
522 dbug_leave("logfile_sync");
523 return (xx);
524 }
525
526 /*
527 * logfile_dumpstats
528 *
529 * Description:
530 * Prints out various stats about the hashing.
531 * Arguments:
532 * Returns:
533 * Preconditions:
534 */
535 void
logfile_dumpstats(cfsd_logfile_object_t * logfile_object_p)536 logfile_dumpstats(cfsd_logfile_object_t *logfile_object_p)
537 {
538 int xx;
539 double dd;
540
541 dbug_enter("logfile_dumpstats");
542
543 dbug_print(("dump", "Request - next %d",
544 logfile_object_p->i_stat_nextcnt));
545 dbug_print(("dump", "Request - offset %d",
546 logfile_object_p->i_stat_offcnt));
547 dbug_print(("dump", "Map Moves %d", logfile_object_p->i_stat_mapmove));
548 dbug_print(("dump", "Mapping Size %d", logfile_object_p->i_maplen));
549 dbug_print(("dump", "Item Size %d", logfile_object_p->i_maxmap));
550 dbug_print(("dump", "File Size %d", logfile_object_p->i_size));
551 if (logfile_object_p->i_stat_mapmove == 0) {
552 dbug_leave("logfile_dumpstats");
553 return;
554 }
555
556 dd = (double)logfile_object_p->i_stat_mapmove /
557 (logfile_object_p->i_stat_nextcnt +
558 logfile_object_p->i_stat_offcnt);
559 dbug_print(("dump", "Mmap moves per Request %.2f", dd));
560
561 xx = logfile_object_p->i_stat_mapdist /
562 logfile_object_p->i_stat_mapmove;
563 dbug_print(("dump", "Average distance per mmap moves %d", xx));
564 dbug_leave("logfile_dumpstats");
565 }
566