Monday, May 06, 2013

Process Virtual Memory

Each process maps either 2^32 or 2^44 bytes of memory (depending on whether the OS is running in 32 or 64-bit mode). This works out to 4GB or 16TB. Not all of this memory is allocated (used); the virtual memory is used as address space that can be mapped to actual memory resources.

Virtual memory structure for a process can be described as in this diagram:

Kernel

While this is part of the address space, it cannot be addressed directly by the process. It must be addressed via system calls.
Stack

Used by the program for variables and storage. It grows and shrinks in size depending on what routines are called and what their stack space requirements are. It is normally about 8MB in size.
Shared Libraries
Shared libraries are position independent so that they can be shared by all programs that want to use them. One common example is libc.so.
hole

This is the address space that is unallocated and unused. It does not tie up physical memory. For most processes, this is the largest portion of the virtual memory for the process.
heap

Used for some types of working storage. It is allocated by the malloc function.
BSS

Uninitialized variables. These are not part of the executable file and their initial value is set to zeros.
Data

Global variables, constants, static variables from the program.
Text

Set of instructions from the compiler-generated executable file.

The virtual memory map for a process can be displayed using the pmap command.

Additional information is available on the Processes page.

Friday, May 03, 2013

Solaris Processes

The process is one of the fundamental abstactions of Unix. Every object in Unix is represented as either a file or a process. (With the introduction of the /proc structure, there has been an effort to represent even processes as files.)

Processes are usually created with fork or a less resource intensive alternative such as fork1 or vfork. fork duplicates the entire process context, while fork1 only duplicates the context of the calling thread. This can be useful (for example), when exec will be called shortly.

Solaris, like other Unix systems, provides two modes of operation: user mode, and kernel (or system) mode. Kernel mode is a more privileged mode of operation. Processes can be executed in either mode, but user processes usually operate in user mode.

Per-process Virtual Memory

Each process has its own virtual memory space. References to real memory are provided through a process-specific set of address translation maps. The computer's Memory Management Unit (MMU) contains a set of registers that point to the current process's address translation maps. When the current process changes, the MMU must load the translation maps for the new process. This is called a context switch.

The MMU is only addressable in kernel mode, for obvious security reasons.

The kernel text and data structures are mapped in a portion of each process's virtual memory space. This area is called the kernel space (or system space).

In addition, each process contains these two important kernel-owned areas in virtual memory: u area and kernel stack. The u area contains information about the process such as information about open files, identification information and process registers. The kernel stack is provided on a per-process basis to allow the kernel to be re-entrant. (ie, several processes can be involved in the kernel, and may even be executing the same routine concurrently.) Each process's kernel stack keeps track of its function call sequence when executing in the kernel.

The kernel can access the memory maps for non-current processes by using temporary maps.

The kernel can operate in either process context or system (or interrupt) context. In process context, the kernel has access to the process's memory space (including u area and kernel stack). It can also block the current process while waiting for a resource. In kernel context, the kernel cannot access the address space, u area or kernel stack. Kernel context is used for handling certain system-wide issues such as device interrupt handling or process priority computation.

Additional information is available on the Process Virtual Memory page.

Process Context

Each process's context contains information about the process, including the following:

  • Hardware context:
    • Program counter: address of the next instruction.
    • Stack pointer: address of the last element on the stack.
    • Processor status word: information about system state, with bits devoted to things like execution modes, interrupt priority levels, overflow bits, carry bits, etc.
    • Memory management registers: Mapping of the address translation tables of the process.
    • Floating point unit registers.
  • User address space: program text, data, user stack, shared memory regions, etc.
  • Control information: u area, proc structure, kernel stack, address translation maps.
  • Credentials: user and group IDs (real and effective).
  • Environment variables: strings of the form variable= value.

During a context switch, the hardware context registers are stored in the Process Control Block in the u area.

The u area includes the following:

  • Process control block.
  • Pointer to the proc structure.
  • Real/effective UID/GID.
  • Information regarding current system call.
  • Signal handlers.
  • Memory management information (text, data, stack sizes).
  • Table of open file descriptors.
  • Pointers to the current directory vnode and the controlling terminal vnode.
  • CPU useage statistics.
  • Resource limitations (disk quotas, etc)
The proc structure includes the following:
  • Identification: process ID and session ID
  • Kernel address map location.
  • Current process state.
  • Pointers linking the process to a scheduler queue or sleep queue.
  • Pointers linking this process to lists of active, free or zombie processes.
  • Pointers keeping this structure in a hash queue based on PID.
  • Sleep channel (if the process is blocked).
  • Scheduling priority.
  • Signal handling information.
  • Memory management information.
  • Flags.
  • Information on the relationship of this process and other processes.

Kernel Services

The Solaris kernel may be seen as a bundle of kernel threads. It uses synchronization primitives to prevent priority inversion. These include mutexes, semaphores, condition variables and read/write locks.

The kernel provides service to processes in the following four ways:

  • System Calls: The kernel executes requests submitted by processes via system calls. The system call interface invokes a special trap instruction.
  • Hardware Exceptions: The kernel notifies a process that attempts several illegal activities such as dividing by zero or overflowing the user stack.
  • Hardware Interrupts: Devices use interrupts to notify the kernel of status changes (such as I/O completions).
  • Resource Management: The kernel manages resources via special processes such as the pagedaemon.

In addition, some system services (such as NFS service) are contained within the kernel in order to reduce overhead from context switching.

Threads

An application's parallelism is the degree of parallel execution acheived. In the real world, this is limited by the number of processors available in the hardware configuration. Concurrency is the maximum acheivable parallelism in a theoretical machine that has an unlimited number of processors. Threads are frequently used to increase an application's concurrency.

A thread represents a relatively independent set of instructions within a program. A thread is a control point within a process. It shares global resources within the context of the process (address space, open files, user credentials, quotas, etc). Threads also have private resources (program counter, stack, register context, etc).

The main benefit of threads (as compared to multiple processes) is that the context switches are much cheaper than those required to change current processes. Sun reports that a fork() takes 30 times as long as an unbound thread creation and 5 times as long as a bound thread creation.

Even within a single-processor environment, multiple threads are advantageous because one thread may be able to progress even though another thread is blocked while waiting for a resource.

Interprocess communication also takes considerably less time for threads than for processes, since global data can be shared instantly.

Kernel Threads

A kernel thread is the entity that is scheduled by the kernel. If no lightweight process is attached, it is also known as a system thread. It uses kernel text and global data, but has its own kernel stack, as well as a data structure to hold scheduling and syncronization information.

Kernel threads store the following in their data structure:

  • Copy of the kernel registers.
  • Priority and scheduling information.
  • Pointers to put the thread on the scheduler or wait queue.
  • Pointer to the stack.
  • Pointers to associated LWP and proc structures.
  • Pointers to maintain queues of threads in a process and threads in the system.
  • Information about the associated LWP (as appropriate).

Kernel threads can be independently scheduled on CPUs. Context switching between kernel threads is very fast because memory mappings do not have to be flushed.

Lightweight Processes

A lightweight process can be considered as the swappable portion of a kernel thread.

Another way to look at a lightweight process is to think of them as "virtual CPUs" which perform the processing for applications. Application threads are attached to available lightweight processes, which are attached to a a kernel thread, which is scheduled on the system's CPU dispatch queue.

LWPs can make system calls and can block while waiting for resources. All LWPs in a process share a common address space. IPC (interprocess communication) facilities exist for coordinating access to shared resources.

LWPs contain the following information in their data structure:

  • Saved values of user-level registers (if the LWP is not active)
  • System call arguments, results, error codes.
  • Signal handling information.
  • Data for resource useage and profiling.
  • Virtual time alarms.
  • User time/CPU usage.
  • Pointer to the associated kernel thread.
  • Pointer to the associated proc structure.

By default, one LWP is assigned to each process; additional LWPs are created if all the process's LWPs are sleeping and there are additional user threads that libthread can schedule. The programmer can specify that threads are bound to LWPs.

Lightweight process information for a process can be examined with ps -elcL.

User Threads

User threads are scheduled on their LWPs via a scheduler in libthread. This scheduler does implement priorities, but does not implement time slicing. If time slicing is desired, it must be programmed in.

Locking issues must also be carefully considered by the programmer in order to prevent several threads from blocking on a single resource.

User threads are also responsible for handling of SIGSEGV (segmentation violation) signals, since the kernel does not keep track of user thread stacks.

Each thread has the following characteristics:

  • Has its own stack.
  • Shares the process address space.
  • Executes independently (and perhaps concurrently with other threads).
  • Completely invisible from outside the process.
  • Cannot be controlled from the command line.
  • No system protection between threads in a process; the programmer is responsible for interactions.
  • Can share information between threads without IPC overhead.

Priorities

Higher numbered priorities are given precedence. The scheduling page contains additional information on how priorities are set.

Zombie Processes

When a process dies, it becomes a zombie process. Normally, the parent performs a wait() and cleans up the PID. Sometimes, the parent receives too many SIGCHLD signals at once, but can only handle one at a time. It is possible to resend the signal on behalf of the child via kill -18 PPID. Killing the parent or rebooting will also clean up zombies. The correct answer is to fix the buggy parent code that failed to perform the wait() properly.

Aside from their inherent sloppiness, the only problem with zombies is that they take up a place in the process table.

Kernel Tunables

The following kernel tunables are important when looking at processes:
  • maxusers: By default, this is set to 2 less than the number of Mb of physical memory, up to 1024. It can be set up to 2048 manually in the /etc/system file.
  • max_nprocs: Maximum number of processes that can be active simultaneously on the system. The default for this is (16 x maxusers) + 10. The minimum setting for this is 138, the maximum is 30,000.
  • maxuprc: The default setting for this is max_nprocs - 5. The minimum is 133, the maximum is . This is the numberof processes a single non-root user can create.
  • ndquot: This is the number of disk quota structures. The default for this is (maxusers x 10) + max_nprocs. The minimum is 213.
  • pt_cnt: Sets the number of System V ptys.
  • npty: Sets the number of BSD ptys. (Should be set to pt_cnt.)
  • sad_cnt: Sets the number of STREAMS addressable devices. (Should be set to 2 x pt_cnt.)
  • nautopush: Sets the number of STREAMS autopush entries. (Should be set to pt_cnt.)
  • ncsize: Sets DNLC size.
  • ufs_ninode: Sets inode cache size.

proc Commands

The proc tools are useful for tracing attributes of processes. These utilities include:
  • pflags: Prints the tracing flags, pending and held signals and other /proc status information for each LWP.
  • pcred: Prints credentials (ie, EUID/EGID, RUID/EGID, saved UID/GIDs).
  • pmap: Prints process address space map.
  • pldd: Lists dynamic libraries linked to the process.
  • psig: Lists signal actions.
  • pstack: Prints a stack trace for each LWP in the process.
  • pfiles: Reports fstat, fcntl information for all open files.
  • pwdx: Prints each process's working directory.
  • pstop: Stops process.
  • prun: Starts stopped process.
  • pwait: Wait for specified processes to terminate.
  • ptree: Prints process tree for process.
  • ptime: Times the command using microstate accounting; does not time children.

These commands can be run against a specific process, but most of them can also be run against all processes on the system. See the above- referenced man page for details.

Thursday, May 02, 2013

Basic NFS Troubleshooting

NFS Troubleshooting

Sun's web pages contain substantial information about NFS services; search for an NFS Administration Guide or NFS Server Performance and Tuning Guide for the version of Solaris you are running. The share_nfs man page contains specific information about export options.

If NFS is not working at all, try the following:

  • Make sure that the NFS server daemons are running. In particular, check for statd, lockd, nfsd and rarpd. If the daemons are not running, they can be started by running /etc/init.d/nfs.server start. See Daemons below for information on NFS-related daemons.
  • Check the /etc/dfs/dfstab and type shareall.
  • Use share or showmount -e to see which filesystems are currently exported, and to whom. showmount -a shows who the server believes is actually mounting which filesystems.
  • Make sure that your name service is translating the server and client hostnames correctly on both ends. Check the server logs to see if there are messages regarding failed or rejected mount attempts; check to make sure that the hostnames are correct in these messages.
  • Make sure that the /etc/net/*/hosts files on both ends report the correct hostnames. Reboot if these have to be edited.

If you are dealing with a performance issue, check

  • Network Issues
  • CPU Useage
  • Memory Levels
  • Disk I/O
  • Increase the number of nfsd threads in /etc/init.d/nfs.server if the problem is that requests are waiting for a turn. Note that this does increase memory useage by the kernel, so make sure that there is enough RAM in the server to handle the additional load.
  • Where possible, mount filesystem with the ro option to prevent additional, unnecessary attribute traffic.
  • If attribute caching does not make sense (for example, with a mail spool), mount the filesystem with the noac option. If nfsstat reports a high getattr level, actimeo may need to be increased (if the attributes do not change too often).
  • nfsstat reports on most NFS-related statistics. The nfsstat page includes information on tuning suggestions for different types of problems that can be revealed with nfsstat.

If these steps do not resolve the issue, structural changes may be required:

  • cachefs can be used to push some of the load from the NFS server onto the NFS clients. To be useful, cfsadmin should be used to increase maxfilesize for the cache to a value high enough to allow for the caching of commonly-used files. (The default value is 3 Mb.)

NFS Client

When a client makes a request to the NFS server, a file handle is returned. The file handle is a 32 byte structure which is interpreted by the NFS server. Commonly, the file handle includes a file system ID, inode number and the generation number of the inode. (The latter can be used to return a "stale file handle" error message if the inode has been freed and re-used between client file accesses.)

If a response is not received for a request, it is resent, but with an incremented xid (transmission ID). This can happen because of congestion on the network or the server, and can be observed with a snoop session between server and client.

The server handles retransmissions differently depending on whether the requests are idempotent (can be executed several times without ill effect) or nonidempotent (cannot be executed several times). Examples of these would include things like reads and getattrs versus writes, creates and removes. The system maintains a cache of nonidempotent requests so that appropriate replies can be returned.

Daemons

The following daemons play a critical role in NFS service:

  • biod: On the client end, handles asynchronous I/O for blocks of NFS files.
  • nfsd: Listens and responds to client NFS requests.
  • mountd: Handles mount requests.
  • lockd: Network lock manager.
  • statd: Network status manager.

Wednesday, May 01, 2013

Native Solaris Storage Multipathing with mpathadm

The “Solaris FC and Storage Multipathing Software” is included with the Solaris 10 license. It is enabled by default with the Solaris 10x86 installation, but is optional in the Sparc installlation.

Currently, the software supports multipathing for fibre channel connections using supported host bus adapters. It does not currently support multipathing for parallel SCSI devices or IP over FC.

For Sparc-based systems, multipathing support is enabled and disabled via the stmsboot -e and stmsboot -d commands. This command reboots the system to complete the process, so make sure that the right boot-device is included in the EEPROM settings before proceeding.

When multipathing is enabled, copies of the /etc/vfstab and /kernel/drv/fp.conf files are preserved to allow the changes to be backed out if necessary.

For x86-based systems, directly edit the fp.conf to change the value of mpxio-disable to “no.” (Disabling it will involve changing it to “yes.”) After the change, run a reconfiguration reboot.

To enable or disable multipathing on a per-port basis, the mpxio-disable parameter may be set on a port-specific line in the fp.conf. (Syntax guidance is included in the comments of the fp.conf file.)

mpathadm Commands
TaskCommand
List available multipathing support mpathadm list mpath-support
View properties for supported multipathing facilities. mpathadm show mpath-support facilityname
List initiator ports. mpathadm list initiator-port
Get initiator port properties. mpathadm show initiator-port portname
List available logical units. mpathadm list lu
Get logical unit properties. mpathadm show lu lunname
List logical units associated with a target port. mpatadm list lu -t portname
Configure auto-failback. mpathadm modify mpath-support -a off facilityname
Disable a path. mpathadm disable path -i initportname -t tgtportname -l luname

Adding Third Party Device Support

Support for many third-party devices is not contained in the default version of the configuration file at /kernel/drv/scsi_vhci.conf. The following shows the changes necessary to bring EMC Symmetrix support into the multipathing software:

# cat /kernel/drv/scsi_vhci.conf
#
# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
#pragma ident "@(#)scsi_vhci.conf 1.9 04/08/26 SMI"
#
name="scsi_vhci" class="root";
...
# symmetric-option = 0x1000000;
device-type-scsi-options-list =
"EMC SYMMETRIX 5671", "symmetric-option";

symmetric-option = 0x1000000;

Unfortunately, the syntax on the scsi_vhci.conf file is incredibly picky. The vendor name must be exactly eight characters long, even if you have to pad it with spaces. The product ID can be “up to” 16 characters long, but most recommendations I've seen say to pad the product ID with spaces as well.

Fortunately, the scsi_vhci.conf file contains helpful syntax comments.

Tuesday, April 30, 2013

kstat Structures

kstat structures are created by system components to contain information about component characteristics that need to be measured. Each component has its own format for kstat information. This information is queried by monitoring commands.

Much of the same information (and even configurable access to the kstat structures themselves) is available through the dtrace command.

The libkstat library contains C-language functions available to programs which need to access the kstat structure. The /dev/kstat pseudo-device is the driver for this access.

Prior to Solaris 10, the netstat -k provided a listing of several kstat structures. Starting with Solaris 8, the /bin/kstat command has allowed access to particular kstats. The rest of this page focuses on the /bin/kstat command.

A particular statistic man be specified by 4-tuples of the following sort:
module:instance:name:statistic
where each element may be either a shell glob or a Perl regular expression enclosed in '/' (forward-slash) characters. The -m, -i , -n and -s options may be used to match a particular object (or glob of objects).

At the end of a kstat command, numbers representing intervals and count may be added.

kstat -l
lists kstat names but not values. For a given kernel module, the command
kstat -l -m module-name
lists all available kstat measurements for that module.

Sunday, April 28, 2013

Basic Kernel Module Commands

  • modinfo prints out information on all currently loaded kernel modules.
  • modload loads kernel modules.
  • modunload unloads kernel modules.
  • forceload in the /etc/system file loads a module at boot time.

Saturday, April 27, 2013

Book Review: PANIC! UNIX System Crash Dump Analysis Handbook

Even almost 20 years later, this book is still the single best volume out there on how to use adb to examine crash dumps. Without this book, I don't believe I would have been able to write the adb section of my own book.

I'm sorry that the author never produced an updated version of this book. Even though some of the examples don't work any more, you can learn techniques and a mindset that can be translated to current operating systems.

For anyone interested in performing analysis of crash dumps (or of the kernel of a running system), this book should be on your shelf.

Friday, April 26, 2013

File Descriptors

A file descriptor is a handle created by a process when a file is opened. A new descriptor is created each time the file is opened. It is associated with a file object which includes information such as the mode in which the file was opened and the offset pointer where the next operation will begin. This information is called the context of the file.

File descriptors are retired when the file is closed or the process terminates. Opens always choose the lowest-numbered file descriptor available. Available file descriptors are allocated as follows (both parameters have a maximum setting of 65536 in current versions of Solaris 10):

  • rlim_fd_cur: Prior to Solaris 10, it was dangerous to set this value higher than 256 due to limitations with the stdio library. If programs require more than 256 file descriptors, they should use setrlimit directly.

  • rlim_fd_max: Prior to Solaris 10, it was dangerous to set this value higher than 1024 due to limitations with select. If programs require more than 1024 file descriptors, they should use setrlimit directly.

Thursday, April 25, 2013

Changing a hostname

The following steps are required to change a Sun system's hostname.
  • /etc/hosts.allow (to correct access permissions)
  • /etc/dfs/dfstab on this system's NFS servers (to allow proper mount access)
  • /etc/vfstab on this system's NFS clients (so they will point at the correct server)
  • kerberos configurations
  • ethers and hosts NIS maps
  • DNS information
  • Netgroup information
  • cron jobs should be reviewed.
  • Other hostname-specific scripts and configuration files.

Additional steps may be required in order to correct issues involving other systems.

Having said all that, the minumum number of changes required are:

  • /etc/nodename
  • /etc/inet/hosts (aka /etc/inet/ipnodes)
  • /etc/hostname.*
  • /etc/net/*/hosts (These are dynamically set at boot time in current Solaris 10 versions.)

Wednesday, April 24, 2013

Solaris 2.x Core Dump Analysis

Solaris 2.x Core Dump Analysis

If you are having trouble getting a core dump, see the savecore page.

Several useful pieces of information can be found by running strings on the vmcore.# file and piping it through more or grep: strings vmcore.# | more. In particular, this will tell you the architecture of the system and the OS level. The message buffer is also near the top of the strings output, and may include messages that had not been written to the console or log files yet. (Note that the message buffer is a ring buffer, so the messages may not be in cronological order.)

If the system panic'ed due to a bad trap, adb can determine the instruction that was running at the time of the crash.

crash can also provide useful information, including lock and kernel memory allocation. crash output is notably less cryptic than adb output.

Some netstat, nfsstat and arp commands are also available for crash dumps. After running the command on the corefiles (eg netstat -d unix.# vmcore.#), compare the output to that on the live system, since some of the options do not report the crash dump statistics.

ipcs can also be used with crash dumps in the following format: ipcs -a -C vmcore.# -N unix.# See the IPC page for information on these facilities.

Tuesday, April 23, 2013

savecore

For a system to produce a panic core dump, savecore must be enabled (by uncommenting and editing the savecore lines in /etc/init.d/sysetup in Solaris 2.6 and earlier, or by dumpadm and /etc/init.d/savecore in Solaris 7 and later). If the system is hung, it may be necessary to force a panic by using Stop-a and typing sync at the ok> prompt. (sync forces a write from memory to swap.)

There are several reasons why savecore may be unable to save a core following a panic. These include:


  • The primary swap area may not be big enough to hold the coredump. Solaris 7 and later versions are more careful about only preserving "interesting" parts of memory, but coredumps may still be 128 M or 35% of main memory, whichever is larger. In Solaris 7, the main dump device can be specified with dumpadm. Solaris 2.6 and earlier use the first swap device specified in the /etc/vfstab.

  • The dump area may be too large. Although some patches have been released to deal with this problem, dump partitions larger than 2G can cause problems with a core dump.

  • The dump area is not on a regular disk. If the swap area is managed by Veritas or Disk Suite, it is possible that this software may not be functional at the time of the dump. A regular disk partition should be specified as the primary dump area.

  • The dump area is not local. If the NFS client software is not functional at the time of the software, memory may not be able to dump to the swap area.

  • The dump device hardware is having problems. Use hardware diagnostics to check out the hardware in question.

  • The storage area for the corefiles is too small. By default, the storage area is in /var/crash. Either make sure that there is enough storage space there or change the storage area to a partition with more space.

  • The storage area hardware has problems. Use hardware diagnostics to check out the hardware in question.

  • savecore is not enabled after all.

After you have a corefile, it can be submitted to Sun for analysis. You can perform some initial analysis yourself, using either mdb or adb.

Monday, April 22, 2013

mdb and adb on a Live System

Solaris's debugger environment allows us access to extremely detailed views of exactly what is happening inside the operating system. This level of detail is well beyond what most system administrators want or need. But every now and again we trip over a problem that requires a microscopic view of the system state. Some links to debugging sessions are provided at the bottom of the page.

mdb was introduced in Solaris 8 as a replacement for the adb and code>crash commands. It extends the adb command set, so it makes sense to look at the basic functionality of the two utilities side by side. For practical purposes, only mdb should be used for systems running Solaris 8+. In Solaris 10, adb is implemented as a link to mdb in adb compatibility mode.

This page has been updated to deal specifically with mdb, with some usage notes for adb. There may be some additional differences between the two that have not been noted. In general, anything that can be done on adb can be done in the same way on mdb, but the converse is not true.

mdb can look at the live kernel with the command:
mdb -k

adb can be run to analyze a live system with the command:
adb -k -P adb: /dev/ksyms /dev/mem
To tweak the kernel, the command is:
adb -kw -P adb: /dev/ksyms /dev/mem
(Here the -P option sets a prompt. Since adb does not have one by default, this can help reduce confusion.)

To look at a kernel crash dump, specify the name list and image by running:
mdb -k unix.# vmcore.#

Kernel Debuggers

The associated kernel debuggers, kadb and kmdb also use much of the same syntax. The old kadb must be loaded at boot time. To do this, from the ok> prompt:
ok> boot kadb -d
kadb: (hit the "return" key)
kadb[0]: kmem_flags/W 01
kadb[0]: :c

(Loading kadb this way means that kadb will only be effective for this current boot session.)

Fortunately, kmdb no longer requires this level of fiddling. In Solaris 10, it can be loaded by running
mdb -K
from the console. (Invoking kmdb this way activates the session at the same time. Note that when the kmdb session is active, the system is stopped.) In order to unload kmdb, it is necessary to run
mdb -U
or quit out of the debugger session with
::quit -u

kmdb can be entered at boottime like kadb. To boot into kmdb, perform the following:
ok> boot kmdb -d
0> kmem_flags/W 01
0> :c

Loading kmdb or kadb at boot time means that the system must be rebooted to unload it. Note that a system that crashes with the kernel debugger loaded returns to the kernel debugger prompt rather than the ok> prompt (unless nopanicdebug has been set to 1, either via kmdb or the /etc/system).

We can also set and manipulate breakpoints within our environment to drop us to the kernel debugger prompt when specified actions take place.

If we have the debugger loaded and have not set a breakpoint, we can enter the kernel debugger environment by sending a break (on a serial console), Stop-A or L1-A (on Sparc systems with a Sun keyboard) or F1-A (for x86/x64 systems with a local keyboard).

Not all mdb macros are accessible to kmdb; use $M to see a list of available macros.

Modular Debugger

Besides being able to analyze a core dump in the same way as adb, mdb is a modular debugger which allows the end user to create custom tools and commands to do almost anything.

The modularity of mdb is its main strength. Modules can allow us to look at programs in a number of contexts, both live and post-mortem. These tools are located in loadable modules which can be accessed via dlopen(). These modules are called dmods and include both dcmds (commands) and walkers. ("Walker" commands allow mdb to change the target to a different part of the program structure.)

mdb also has a number of good interactive features, including command line history, editing and logging. In addition, there is a syntax-checking facility and a built-in output pager.

Target Properties

The target is the object under inspection by mdb. It may be a core file, a live kernel, a crash dump, a user process, a data file, or an ELF object file.

The following types of target properties are available to be read and/or written by mdb:

  • address spaces: Allows reading and writing data from the target's virtual address space.
  • symbol table: Allows access to the symbol tables (both static and dynamic) of the target's primary executable file.
  • external data: Read target's external data buffers.
  • load objects: Objects can be loaded within mdb
  • threads: Execution of threads can be controlled.

Dot

In mdb jargon, the current object is known as "dot" ("."). This represents an address in the memory space. Walkers are commands that allow us to shift our focus to another area of memory.

Process Analysis

We can look at a particular process by either running the command as an argument to mdb or by specifying the mdb -p PID command for the given process ID.

Command Syntax

A simple command has the following syntax:
[address] [,count] command
where a command is a verb followed by modifiers. Verbs include:

  • ?: Examine code or variables in executable object file
  • /: Examine value.
  • =: Value printed in different formats.
  • $<: Invoke miscellaneous commands, including macros.
  • >: Assign value to variable or register.
  • <: Read value from variable or register.

The address is usually provided by an expression. In mdb, an expression is one of the following:

  • An integer: May be specified in binary (0i), hexidecimal (0x), or decimal ( 0t).
  • 0tnatural-number.natural-number: Specifies a decimal floating-point number.
  • 'string-of-characters': Generates an integer by converting the characters to ASCII equivalents.
  • <identifier: The value of the indicated variable.
  • identifier: The value of the indicated symbol.
  • (expression): The value of the expression.
  • .: The value of the current location.
  • &: The value of the location most recently used to execute a dcmd.
  • +: The incremented value of the current location.
  • ^: The decremented value of the current location.

Registers

  • %g0-%g7: General registers.
    g0=zero
    g7=address of current thread.
  • %i0-%i7: Input registers.
    i6=Frame pointer (for tracing previous function through stack.)
  • %o0=%o7: Output registers.
    o6=Stack pointer (sp)
    o7=program counter (pc).
  • %l0-%l7: Local registers.

Variables

Variables are assigned using the or ::typeset dcmds. Variables may use non-reserved names consisting of sequences of letters, digits, underscores or periods. The value of a variable is a 64-bit unsigned integer.

The following variables are persistent:

  • 0: Most recent value printed by \ / ? or =
  • 9: Most recent count from $<
  • b: Virtual address of the base of the data section.
  • d: Size of the data section (bytes).
  • e: Virtual address of entry point.
  • hits: Number of times the event specifier has been matched.
  • m: Magic number of target's primary object file.
  • t: Size of text section (bytes).
  • thread: Current representative thread's identifier.

Symbols

In an expression context, a symbol is evaluated, usually to the virtual address associated with the symbol. The symbol table can be examined by the ::nm dcmd and the ::nm -P dcmd (for the private symbol table). Symbols can be scoped via the backtick character: object`file`name evaluates to the address of name from file in the object kernel module.

Headers

The header files give information about the structures we will examine with adb. These files are located in /usr/include/* and /usr/platform/arch_name/include/*

General Commands

The following are the most commonly-used general commands. A full list can be obtained from the mdb prompt using the ::dcmds dcmd.

In the listings below, commands of the following forms are part of adb compatibility mode:

  • :character
  • $character

The commands have been grouped together based on the ways in which they are most commonly used:

Control Commands
$< or $<< Replace input with a macro or source a macro.
$>filename or
::log filename
Log session to a file. If no filename, use default.
| Pipe. Allows simple commands to be joined.
! Shell escape. Acts as a pipe to a shell command. (Not available in kmdb.)
// Comment. Following words on the same line are ignored.
$M Show built-in macros (Kernel debugger only).
$Pstring Set prompt to string.
$Q
::quit
Quit. (From kmdb, use -u option to avoid exiting to ok> prompt.)
$W Re-open target in writable mode.
$p Change target context.
$w Control output page width.
:A Attach to a process or core file.
:R Release attachment.
:k Kill and release targets.
$v Print non-zero variables.
>
::typeset
Assign a variable.
::dcmds Print available commands.
::nm Print symbol table. (-P specifies a private symbol table. Manipulated with ::nmadd and ::nmdelete
::help dcmd Provide usage notes on a dcmd.
::typeset Manipulate variable.
::walk Walk data structure.
::walkers List available walkers.

Input & Output Commands
$<
$<<
Replace input with a macro or source a macro.
$>filename Log session to a file. If no filename, use default.
address/format-spec
/format-spec
Read the value in a memory address formatted as format-spec. If no address is provided, use dot.
address/W value Write the value in the four bytes starting with address. If no address is provided, use dot. v, w or Z may also be used instead of W to write 1, 2 or 8 bytes, respectively.
address=format-spec
=format-spec
Format immediate value of address or dot.
? Read/write primary object file.
@format-spec Read/write physical address as format-spec.
\format-spec Read/write physical address as format-spec.
The difference between / and = is subtle. For example, to find the address holding the value of the maxphys symbol in decimal, we would run:
maxphys=D
To find the value inside the above address, we would use / like:
maxphys/D

Format Specification
Note that the ::formats dcmd prints out a full list of supported formats.
D Display in signed decimal.
i Display as a disassembled instruction.
U Display in unsigned decimal.
X Display in signed hexidecimal.
0txyz Specifies xyz as a decimal value.

System Examination
cpu$<cpus Display cpu0.
cpun$<cpu Display cpu #n.
$<msgbuf Display message buffer, including all console messages up to panic.
<sp$<stacktrace Use the stack pointer address (sp) to display a detailed stack trace.
$r
::regs
Display general registers, including program counter and stack pointer.
::callout Print callout table.
::cpuinfo -v Information about activities of CPUs, including runqueue inhabitants.
::cpuregs
::cpuregs -c cpuid
Print CPU registers. kmdb only. Can specify a cpu.
::cpustack
::cpustack -c cpuid
Print CPU stack. kmdb only. Can specify a cpu.
::dnlc Print DNLC contents.
::ipcs Print SVR4 IPC information.
::kmalog Display kernel memory log and stack traces.
::kmastat Print current kernel memory allocations
::memstat Print current memory usage.
::nm Print symbol table. (-P specifies a private symbol table. Manipulated with ::nmadd and ::nmdelete
::ps List processes with associated threads and lwps
::ptree Print process tree.

Target Examination
$? Print status and registers.
$C Show call trace and arguments, saved frame pointer and saved program counter for each stack frame.
$X, $Y, $x, $y and ::fpregs Display floating point registers.
$c Display stack backtrace.
$e Print list of global symbols.
$f Print list of source files.
$l Print representative thread's lwp ID.
$m Print address space mappings.
$r
::regs
Display general registers, including program counter and stack pointer.
as::as2proc Convert as pointer to a proc_t pointer.
::devbindings devinfo nodes bound to device-name or major-num.
::devinfo Detailed devinfo of node.
::devinfo2driver Driver name for this node.
::devnames Print devnames array.
::devt Display dev_t's major & minor numbers.
::did2thread Kernel thread for this ID.
::dumpaddress Dump memory from address.
::findfalse Find potentially falsely shared structures.
::findleaks Search for potential kernel memory leaks.
::findlocks Find locks held by specified thread.
threadp::findstack Find kernel thread stack for associated thread.
::inode Display summary of inode_t.
::kmsqid Display message queue structure (kmsqid).
::ksemid Display a semaphore structure (ksemid).
::kshmid Display a shared memory structure (kshmid).
::pgrep pattern Find proc_t pointers matching the pattern.
0tPID::pid2proc Convert decimal PID to a proc_t pointer.
procp::ps Process information matching the associated proc_t.
::status Print summary of target status.
sobj::walk blocked Walk threads blocked on a particular synchronization object (sobj).
procp::walk thread Walk threads of associated process.
sobj::wchaninfo -v Blocked on condition variables for a particular synchronization object (sobj).
address::whatis Attempts to identify address contents.
vnode::whereopen Processes with vnode open.

Tracing, Watchpoints and Breakpoints
(Breakpoints for kernel debugger only.)
$b Show all breakpoints.
$i Print list of ignored signals.
:a Set a watchpoint.
:b Set a breakpoint.
:c or ::cont Continue target execution.
:d Delete a breakpoint.
:e Step over next instruction.
:i Step over next instruction.
:k Kill and release targets.
:p Set execute access watchpoints.
:r Run new target process.
:s Step target to next instruction.
:t Stop on delivery of specified signals.
:u Step out of current function.
:w Set write access watchpoint.
:z Delete all breakpoints.

General Debugging
$G Toggle C++ demangling.
$V Toggle disassembly mode.
$g Toggle C++ demangling.
address::dis Disassemble text starting at address.

Comparison Operators
== Logical equality.
!= Logical inequality.
& Bitwise AND.
| Bitwise OR.
^ Bitwise XOR.

Usage Examples

There are several usage examples available on the web. Here are a few:

Sunday, April 21, 2013

Troubleshooting Intermittent Problems

Intermittent problems are extremely difficult to troubleshoot. Any reproducible problem can be troubleshot, if for no other reason than that each individual component can be proven to not be the problem through experimentation. Problems that are not reproducible cannot be approached in the same way.

Problems present as intermittent for one of two reasons:

  1. We have not identified the real cause of the problem.
  2. The problem is being caused by failing or flaky hardware.

The first possibility should be addressed by going back to brainstorming hypotheses.

It may be helpful to bring a fresh perspective into the brainstorming session, either by bringing in different people, or by sleeping on the problem.

The second problem is tougher. There are hardware diagnostics tests that can be run to try to identify the failing piece of hardware.

The first thing to do is to perform general maintenance on the system. Re-seat memory chips, processors, expansion boards and hard drives.

Once general maintenance has been performed, test suites like SunVTS can perform stress-testing on a system to try to trigger the failure and identify the failing part.

It may be the case, however, that the costs associated with this level of troubleshooting are prohibitive. In this case, we may want to attempt to shotgun the problem.

Shotgunning is the practice of replacing potentially failing parts without having identified them as actually being flaky. In general, parts are replaced by price point, with the cheapest parts being replaced first.

Though we are likely to inadvertently replace working parts, the cost of the replacement may be cheaper than the costs of the alternatives (like the downtime cost associated with stress testing).

When parts are removed during shotgunning, it is important to discard them rather than keep them as spares. Any part you remove as part of a troubleshooting exercise is questionable. (After all, what if a power surge caused multiple parts to fail? Or what if there was a cascading failure?) It does not make sense to have questionable parts in inventory; such parts would be useless for troubleshooting, and putting questionable parts into service just generates additional downtime down the road.

This practice may violate your service contract if performed without the knowledge and consent of your service provider.

Regardless of the method used to deal with intermittent problems, it is essential to keep good records. Relationships between our problem and other events may only become clear when we look at patterns over time. We may only be confident that we have really resolved the problem if we can demonstrate that we've gone well beyond the usual re-occurrence frequency without the problem re-emerging.

Saturday, April 20, 2013

Troubleshooting Hard Drive Connectivity

Disk drive connectivity problems on a Solaris 2.x systems can be caused by software, hardware or PROM configuration problems.

Software Problems

New devices may require that the appropriate /dev and /devices files be created. This can be done through use of the drvconfig and disks commands, but it is usually done by performing a boot -r from the ok> prompt.

Once the system is back, the root user should be able to run format and see the disk listed as available. The disk can then be partitioned and labelled with format and have filesystems created with newfs or mkfs, as appropriate.

The presence of the appropriate /dev and /devices files can be verified by running the commands ls -lL /dev/dsk and ls -lL /dev/rdsk and making sure that they are block and character special files respectively, with major numbers depending on the driver used. (See the ls man page if you are not sure what this means.)

Files that can cause problems with hard drive connectivity include:

    /dev/dsk/c#t#d#s# or /dev/rdsk/c#t#d#s# and related /devices files
    /etc/name_to_major
    /etc/minor_perm

Problems with the /dev and /devices files can be corrected directly by removing the offending files and recreating them, either directly with mknod and ln -s or indirectly with drvconfig , disks or boot -r (as appropriate).

Hardware Problems

The most common sources of hard drive connectivity problems (once the device files are built) are loose cables and terminators. Check these first before proceeding.

The system SCSI buses can be probed at the ok> prompt. To set this up, perform the following:

    ok> setenv auto-boot? false
    ok> reset
    ok> probe-scsi-all

    (after output)
    ok> setenv auto-boot? true
    (if appropriate)

This will give a hardware mapping of all SCSI devices on the system. If the hard drive in question does not appear, you have either a hardware problem or a PROM search path problem. To check for the PROM search path problem, run the following:

ok> printenv

Look for the pcia-probe-list or sbus-probe-default parameters and make sure that they are set to the default for your system.

Some additional hardware diagnostics are available at the PROM monitor (ok>) prompt. Additional information may come from navigating the PROM device tree at the ok> prompt.

Friday, April 19, 2013

Sun CD ROM Troubleshooting

Many CD ROM problems are software rather than hardware problems. If your problem is hardware-related, you can look at our Hardware Diagnostics page. Our page on Hard Drive Connectivity may also be useful by analogy.

Some Sun patches resolve security holes in vold by commenting devices out of the /etc/rmmount.conf file. While this is effective, it also disables the devices that are commented out.

For vold to work with your CD ROM and floppy drives within CDE in the designed fashion, you should uncomment the following lines in your /etc/rmmount.conf:

ident hsfs ident_hsfs.so cdrom
ident ufs ident_ufs.so floppy cdrom pcmem
ident pcfs ident_pcfs.so floppy pcmem
action cdrom action_filemgr.so
action floppy action_filemgr.so

Note that this will allow mounted CDs and floppies to be mounted with SUID permissions. For systems where this is inappropriate, your /etc/rmmount.conf file should also contain the following:

mount * ufs -o nosuid
mount * hsfs -o nosuid

(Note that pcfs does not understand SUID, so pcfs does not pose a risk in the same way that hsfs and ufs do.)

The Solaris Volume Manager documentation contains substantial additional information about the operation of vold.

Thursday, April 18, 2013

ZFS Management

ZFS was first publicly released in the 6/2006 distribution of Solaris 10. Previous versions of Solaris 10 did not include ZFS.

ZFS is flexible, scalable and reliable. It is a POSIX-compliant filesystem with several important features:

No separate filesystem creation step is required. The mount of the filesystem is automatic and does not require vfstab maintenance. Mounts are controlled via the mountpoint attribute of each file system.

Pool Management

Members of a storage pool may either be hard drives or slices of at least 128MB in size.

To create a mirrored pool:
zpool create -f pool-name mirror c#t#d# c#t#d#
To check a pool's status, run:
zpool status -v pool-name
To list existing pools:
zpool list
To remove a pool and free its resources:
zpool destroy pool-name
A destroyed pool can sometimes be recovered as follows:
zpool import -D

Additional disks can be added to an existing pool. When this happens in a mirrored or RAID Z pool, the ZFS is resilvered to redistribute the data. To add storage to an existing mirrored pool:
zpool add -f pool-name mirror c#t#d# c#t#d#

Pools can be exported and imported to transfer them between hosts.
zpool export pool-name
zpool import pool-name
Without a specified pool, the import command lists available pools. zpool import

To clear a pool's error count, run:
zpool clear pool-name

Although virtual volumes (such as those from DiskSuite or VxVM) can be used as base devices, it is not recommended for performance reasons.

Filesystem Management

Similar filesystems should be grouped together in hierarchies to make management easier. Naming schemes should be thought out as well to make it easier to group administrative commands for similarly managed filesystems.

When a new pool is created, a new filesystem is mounted at /pool-name.

To create another filesystem:
zfs create pool-name/fs-name
To delete a filesystem:
zfs destroy filesystem-name

To rename a ZFS filesystem:
zfs rename old-name new-name

Properties are set via the zfs set command.
To turn on compression:
zfs set compression=on pool-name/filesystem-name
To share the filesystem via NFS:
zfs set sharenfs=on pool-name/fs-name
zfs set sharenfs="mount-options " pool-name/fs-name
Rather than editing the /etc/vfstab:
zfs set mountpoint= mountpoint-name pool-name/filesystem-name

Quotas are also set via the same command:
zfs set quota=#gigG pool-name/filesystem-name

RAID Levels

ZFS filesystems automatically stripe across all top-level disk devices. (Mirrors and RAID-Z devices are considered to be top-level devices.) It is not recommended that RAID types be mixed in a pool. (zpool tries to prevent this, but it can be forced with the -f flag.)

The following RAID levels are supported:

  • RAID-0 (striping)
  • RAID-1 (mirror)
  • RAID-Z (similar to RAID 5, but with variable-width stripes to avoid the RAID 5 write hole)
  • RAID-Z2

The zfs man page recommends 3-9 disks for RAID-Z pools.

Performance Monitoring

ZFS performance management is handled differently than with older generation file systems. In ZFS, I/Os are scheduled similarly to how jobs are scheduled on CPUs. The ZFS I/O scheduler tracks a priority and a deadline for each I/O. Within each deadline group, the I/Os are scheduled in order of logical block address.

Writes are assigned lower priorities than reads, which can help to avoid traffic jams where reads are unable to be serviced because they are queued behind writes. (If a read is issued for a write that is still underway, the read will be executed against the in-memory image and will not hit the hard drive.)

In addition to scheduling, ZFS attempts to intelligently prefetch information into memory. The algorithm tries to pick information that is likely to be needed. Any forward or backward linear access patterns are picked up and used to perform the prefetch.

The zpool iostat command can monitor performance on ZFS objects:

  • USED CAPACITY: Data currently stored
  • AVAILABLE CAPACITY: Space available
  • READ OPERATIONS: Number of operations
  • WRITE OPERATIONS: Number of operations
  • READ BANDWIDTH: Bandwidth of all read operations
  • WRITE BANDWIDTH: Bandwidth of all write operations

The health of an object can be monitored with
zpool status

Snapshots and Clones

To create a snapshot:
zfs snapshot pool-name/filesystem-name@ snapshot-name
To clone a snapshot:
zfs clone snapshot-name filesystem-name
To roll back to a snapshot:
zfs rollback pool-name/filesystem-name@snapshot-name

zfs send and zfs receive allow clones of filesystems to be sent to a development environment.

The difference between a snapshot and a clone is that a clone is a writable, mountable copy of the file system. This capability allows us to store multiple copies of mostly-shared data in a very space-efficient way.

Each snapshot is accessible through the .zfs/snapshot in the /pool-name directory. This can allow end users to recover their files without system administrator intervention.

Zones

If the filesystem is created in the global zone and added to the local zone via zonecfg, it may be assigned to more than one zone unless the mountpoint is set to legacy.
zfs set mountpoint=legacy pool-name/filesystem-name

To import a ZFS filesystem within a zone:
zonecfg -z zone-name

add fs
set dir=mount-point
set special=pool-name/filesystem-name
set type=zfs
end
verify
commit
exit

Administrative rights for a filesystem can be granted to a local zone:
zonecfg -z zone-name

add dataset
set name=pool-name/filesystem-name
end
commit exit

Data Protection

ZFS is a transactional file system. Data consistency is protected via Copy-On-Write (COW). For each write request, a copy is made of the specified block. All changes are made to the copy. When the write is complete, all pointers are changed to point to the new block.

Checksums are used to validate data during reads and writes. The checksum algorithm is user-selectable. Checksumming and data recovery is done at a filesystem level; it is not visible to applications. If a block becomes corrupted on a pool protected by mirroring or RAID, ZFS will identify the correct data value and fix the corrupted value.

Raid protections are also part of ZFS.

Scrubbing is an additional type of data protection available on ZFS. This is a mechanism that performs regular validation of all data. Manual scrubbing can be performed by:
zpool scrub pool-name
The results can be viewed via:
zpool status
Any issues should be cleared with:
zpool clear pool-name

The scrubbing operation walks through the pool metadata to read each copy of each block. Each copy is validated against its checksum and corrected if it has become corrupted.

Hardware Maintenance

To replace a hard drive with another device, run:
zpool replace pool-name old-disk new-disk

To offline a failing drive, run:
zpool offline pool-name disk-name
(A -t flag allows the disk to come back online after a reboot.)

Once the drive has been physically replaced, run the replace command against the device:
zpool replace pool-name device-name
After an offlined drive has been replaced, it can be brought back online:
zpool online pool-name disk-name

Firmware upgrades may cause the disk device ID to change. ZFS should be able to update the device ID automatically, assuming that the disk was not physically moved during the update. If necessary, the pool can be exported and re-imported to update the device IDs.

Troubleshooting ZFS

The three categories of errors experienced by ZFS are:

  • missing devices: Missing devices placed in a "faulted" state.
  • damaged devices: Caused by things like transient errors from the disk or controller, driver bugs or accidental overwrites (usually on misconfigured devices).
  • data corruption: Data damage to top-level devices; usually requires a restore. Since ZFS is transactional, this only happens as a result of driver bugs, hardware failure or filesystem misconfiguration.

It is important to check for all three categories of errors. One type of problem is often connected to a problem from a different family. Fixing a single problem is usually not sufficient.

Data integrity can be checked by running a manual scrubbing:
zpool scrub pool-name
zpool status -v pool-name
checks the status after the scrubbing is complete.

The status command also reports on recovery suggestions for any errors it finds. These are reported in the action section. To diagnose a problem, use the output of the status command and the fmd messages in /var/adm/messages.

The config section of the status section reports the state of each device. The state can be:

  • ONLINE: Normal
  • FAULTED: Missing, damaged, or mis-seated device
  • DEGRADED: Device being resilvered
  • UNAVAILABLE: Device cannot be opened
  • OFFLINE: Administrative action

The status command also reports READ, WRITE or CHKSUM errors.

To check if any problem pools exist, use
zpool status -x
This command only reports problem pools.

If a ZFS configuration becomes damaged, it can be fixed by running export and import.

Devices can fail for any of several reasons:

  • "Bit rot:" Corruption caused by random environmental effects.
  • Misdirected Reads/Writes: Firmware or hardware faults cause reads or writes to be addressed to the wrong part of the disk.
  • Administrative Error
  • Intermittent, Sporadic or Temporary Outages: Caused by flaky hardware or administrator error.
  • Device Offline: Usually caused by administrative action.

Once the problems have been fixed, transient errors should be cleared:
zpool clear pool-name

In the event of a panic-reboot loop caused by a ZFS software bug, the system can be instructed to boot without the ZFS filesystems:
boot -m milestone=none
When the system is up, remount / as rw and remove the file /etc/zfs/zpool.cache. The remainder of the boot can proceed with the
svcadm milestone all command. At that point import the good pools. The damaged pools may need to be re-initialized.

Scalability

The filesystem is 128-bit. 256 quadrillion zetabytes of information is addressable. Directories can have up to 256 trillion entries. No limit exists on the number of filesystems or files within a filesystem.

ZFS Recommendations

Because ZFS uses kernel addressable memory, we need to make sure to allow enough system resources to take advantage of its capabilities. We should run on a system with a 64-bit kernel, at least 1GB of physical memory, and adequate swap space.

While slices are supported for creating storage pools, their performance will not be adequate for production uses.

Mirrored configurations should be set up across multiple controllers where possible to maximize performance and redundancy.

Scrubbing should be scheduled on a regular basis to identify problems before they become serious.

When latency or other requirements are important, it makes sense to separate them onto different pools with distinct hard drives. For example, database log files should be on separate pools from the data files.

Root pools are not yet supported in the Solaris 10 6/2006 release, though they are anticipated in a future release. When they are used, it is best to put them on separate pools from the other filesystems.

On filesystems with many file creations and deletions, utilization should be kept under 80% to protect performance.

The recordsize parameter can be tuned on ZFS filesystems. When it is changed, it only affects new files. zfs set recordsize=size tuning can help where large files (like database files) are accessed via small, random reads and writes. The default is 128KB; it can be set to any power of two between 512B and 128KB. Where the database uses a fixed block or record size, the recordsize should be set to match. This should only be done for the filesystems actually containing heavily-used database files.

In general, recordsize should be reduced when iostat regularly shows a throughput near the maximum for the I/O channel. As with any tuning, make a minimal change to a working system, monitor it for long enough to understand the impact of the change, and repeat the process if the improvement was not good enough or reverse it if the effects were bad.

The ZFS Evil Tuning Guide contains a number of tuning methods that may or may not be appropriate to a particular installation. As the document suggests, these tuning mechanisms will have to be used carefully, since they are not appropriate to all installations.

For example, the Evil Tuning Guide provides instructions for:
  • Turning off file system checksums to reduce CPU usage. This is done on a per-file system basis:
    zfs set checksum=off filesystem

  • zfs set checksum='on | fletcher2 | fletcher4 | sha256' filesystem
  • Limiting the ARC size by setting
    set zfs:zfs_arc_max
    in /etc/system on 8/07 and later.
  • If the I/O includes multiple small reads, the file prefetch can be turned off by setting
    zfs:zfs_prefetch_disable
    on 8/07 and later.
  • If the I/O channel becomes saturated, the device level prefetch can be turned off with
    set zfs:zfs_vdev_cache_bshift = 13
    in /etc/system for 8/07 and later
  • I/O concurrency can be tuned by setting
    set zfs:zfs_vdev_max_pending = 10
    in /etc/system in 8/07 and later.
  • If storage with an NVRAM cache is used, cache flushes may be disabled with
    set zfs:zfs_nocacheflush = 1
    in /etc/system for 11/06 and later.
  • ZIL intent logging can be disabled. (WARNING: Don't do this.)
  • Metadata compression can be disabled. (Read this section of the Evil Tuning Guide first-- you probably do not need to do this.)

Sun Cluster Integration

ZFS can be used as a failover-only file system with Sun Cluster installations.

If it is deployed on disks also used by Sun Cluster, do not deploy it on any Sun Cluster quorum disks. (A ZFS-owned disk may be promoted to be a quorum disk on current Sun Cluster versions, but adding a disk to a ZFS pool may result in quorum keys being overwritten.)

ZFS Internals

Max Bruning wrote an excellent paper on how to examine the internals of a ZFS data structure. (Look for the article on the ZFS On-Disk Data Walk.) The structure is defined in ZFS On-Disk Specification.

Some key structures:

  • uberblock_t: The starting point when examining a ZFS file system. 128k array of 1k uberblock_t structures, starting at 0x20000 bytes within a vdev label. Defined in uts/common/fs/zfs/sys/uberblock_impl.h Only one uberblock is active at a time; the active uberblock can be found with
    zdb -uuu zpool-name
  • blkptr_t: Locates, describes, and verifies blocks on a disk. Defined in uts/common/fs/zfs/sys/spa.h.
  • dnode_phys_t: Describes an object. Defined by uts/common/fs/zfs/sys/dmu.h
  • objset_phys_t: Describes a group of objects. Defined by uts/common/fs/zfs/sys/dmu_objset.h
  • ZAP Objects: Blocks containing name/value pair attributes. ZAP stands for ZFS Attribute Processor. Defined by uts/common/fs/zfs/sys/zap_leaf.h
  • Bonus Buffer Objects:
    • dsl_dir_phys_t: Contained in a DSL directory dnode_phys_t; contains object ID for a DSL dataset dnode_phys_t
    • dsl_dataset_phys_t: Contained in a DSL dataset dnode_phys_t; contains a blkprt_t pointing indirectly at a second array of dnode_phys_t for objects within a ZFS file system.
    • znode_phys_t: In the bonus buffer of dnode_phys_t structures for files and directories; contains attributes of the file or directory. Similar to a UFS inode in a ZFS context.