CAM(4)
π CAM(4) β the Common Access Method Storage subsystem, explained by your favorite unix bestie
Okay so picture this: it's CAM(4), the Common Access Method Storage subsystem, and honestly? She's the main character of your storage stack and nobody gives her her flowers. Let me put you on. π«Ά
β¨ SYNOPSIS β¨
Like, here's the whole vibe in kernel-config form β copy it exactly, don't freestyle:
device scbus
device ada
device cd
device ch
device da
device pass
device pt
device sa
options CAMDEBUG
options CAM_DEBUG_BUS=-1
options CAM_DEBUG_TARGET=-1
options CAM_DEBUG_LUN=-1
options CAM_DEBUG_COMPILE=CAM_DEBUG_INFO|CAM_DEBUG_CDB|CAM_DEBUG_PROBE
options CAM_DEBUG_FLAGS=CAM_DEBUG_INFO|CAM_DEBUG_CDB
options CAM_MAX_HIGHPOWER=4
options SCSI_NO_SENSE_STRINGS
options SCSI_NO_OP_STRINGS
options SCSI_DELAY=8000
πΈ DESCRIPTION πΈ
So the CAM subsystem is this uniform, modular lil system for implementing drivers that control all your SCSI, ATA, NVMe, and MMC / SD devices β and it also lets you use all different kinds of SCSI, ATA, NVMe, and MMC / SD host adapters through host adapter drivers. Iconic, flexible, no cap. π
When the system probes buses, it attaches any devices it finds to the appropriate drivers. And the pass(4) driver β if it's configured in the kernel β will attach to all the devices. Every single one. She's nosy and we love her for it. π
π KERNEL CONFIGURATION π
There's a whole lineup of generic kernel configuration options for CAM. Let's meet the girls:
CAM_BOOT_DELAYβ extra time to wait after the static parts of the kernel have run, so there's room to discover additional devices that might take a while to show up β think USB attached storage being slow to connect. She's patient. She waits. π°οΈCAM_IOSCHED_DYNAMICβ turns on dynamic decisions in the I/O scheduler, based on hints and on how the storage devices are currently performing. Adaptive queen. β¨CAM_IO_STATSβ enables collection of statistics for periph devices. Numbers! Data! We love to see it. πCAM_TEST_FAILUREβ enables the ability to simulate I/O failures. Yes, on purpose. For science. π§ͺCAMDEBUGβ this one compiles in all the CAM debuggingprintfcode. But lowkey? Including it by itself will not actually cause any debugging information to be printed out. See below for the details, bestie. πCAM_MAX_HIGHPOWER=4β sets the maximum allowable number of concurrent "high power" commands. A "high power" command is one that takes more electrical power than most to complete β classic example: the SCSISTART UNITcommand. Starting a disk often takes significantly more electrical power than normal operation. This option is literally you telling the system how many concurrent high power commands may be outstanding without overloading the power supply on his computer. Respect the wattage. β‘SCSI_NO_SENSE_STRINGSβ this one eliminates the text descriptions of each SCSI Additional Sense Code and Additional Sense Code Qualifier pair. Since that's a fairly large text database, killing it reduces kernel size somewhat. It's primarily necessary for boot floppies and other low disk space or low memory space environments. In most cases, though, this should be enabled, since it speeds the interpretation of SCSI error messages. And the page is very clear: do not let the "kernel bloat" zealots get to you β leave the sense descriptions in your kernel! π€πSCSI_NO_OP_STRINGSβ disables the text descriptions of each SCSI opcode. This option, like the sense string option above, is primarily useful for environments like a boot floppy where kernel size is critical. Enabling this for normal use is not recommended, since it slows debugging of SCSI problems. So like... don't. πSCSI_DELAY=8000β this is the SCSI "bus settle delay." Important: in CAM it's specified in milliseconds, not seconds like the old SCSI layer used to do. (Glow-up, honestly.) When the kernel boots, it sends a bus reset to each SCSI bus, telling each device to reset itself to a default set of transfer negotiations and other settings. Most SCSI devices need some amount of time to recover from a bus reset. Newer disks may need as little as100ms, while old, slow devices may need way longer. IfSCSI_DELAYisn't specified, it defaults to 2 seconds. The minimum allowable value is"100", i.e. 100ms. And one special case: ifSCSI_DELAYis set to0, that's taken to mean the "lowest possible value" β in which caseSCSI_DELAYgets reset to 100ms. π¦
π Device wiring, or: how units get their assigned seats
All devices and buses support dynamic allocation, so you don't need to configure some upper number of devices and controllers β device da will suffice for any number of disk drivers. Slay, scalability. π
The devices are either wired so they appear as a particular device unit, or counted so that they appear as the next available unused unit.
Units get wired down by setting kernel environment hints. This is usually done either interactively from loader(8), or automatically via the /boot/device.hints file. The basic syntax is:
hint.device.unit.property="value"
Individual CAM bus numbers can be wired down to specific controllers with a config line similar to this:
hint.scbus.0.at="mpr1"
That assigns CAM bus number 0 to the mpr1 driver instance. And for controllers supporting more than one bus, a particular bus can be assigned like this:
hint.scbus.0.at="ahci1"
hint.scbus.0.bus="1"
Which assigns CAM bus 0 to the bus 1 instance on ahci1. Peripheral drivers can be wired to a specific bus, target, and lun as so:
hint.da.0.at="scbus0"
hint.da.0.target="0"
hint.da.0.lun="0"
That assigns da0 to target 0, unit (lun) 0 of scbus0. Omitting the target or unit hints tells CAM to treat them as wildcards and use the first respective counted instances. And you can combine all these examples together so a peripheral device gets wired to any particular controller, bus, target, and/or unit instance. So many knobs, so little time. ποΈ
π§ This also works with nvme(4) drives, obviously
hint.nvme.4.at="pci7:0:0"
hint.scbus.10.at="nvme4"
hint.nda.10.at="scbus10"
hint.nda.10.target="1"
hint.nda.10.lun="12"
hint.nda.11.at="scbus10"
hint.nda.11.target="1"
hint.nda.11.lun="2"
So this assigns the NVMe card at PCI bus 7 slot 0 function 1 to scbus10. Two things to remember forever: the target for nda(4) devices is always 1, and the unit is the namespace identifier from the drive. The namespace id 1 is exported as nda10, and namespace id 2 is exported as nda11. iykyk. π§
π Serial-number wiring, the glow-up
For devices that provide a serial number, units may be wired to that serial number without regard to where the drive is attached:
hint.nda.3.sn="CY0AN07101120B12P"
hint.da.44.sn="143282400011"
hint.ada.2.sn="A065D591"
Which wires nda3, da44, and ada2 to the drives with those specified serial numbers. And the best part? You don't need to specify an at line when serial numbers are used. Match made in heaven. π
π₯ ADAPTERS π₯
The system allows common device drivers to work through many different types of adapters. The adapters take requests from the upper layers and do all the IO between the SCSI, ATA, NVMe, or MMC / SD bus and the system. The maximum size of a transfer is governed by the adapter β most adapters can transfer 1MB in a single operation, however many can transfer larger amounts. Big transfer energy. π
β TARGET MODE β
Some adapters support target mode, in which the system is capable of operating as a device, responding to operations initiated by another system. Target mode is supported for some adapters, but it's not yet complete for this version of the CAM SCSI subsystem. So like... she's still cooking. Don't expect the full meal yet. π«Ά
ποΈ ARCHITECTURE ποΈ
So like, CAM is the social glue of your system, fr fr. It connects the upper layers to your storage devices. The PERIPH devices take storage requests from GEOM and other upper layers and translate them into protocol requests. Then XPT (that's the transport layer, bestie) dispatches those protocol requests to a SIM driver. A SIM driver takes those protocol requests and translates them into hardware commands that the host adapter actually understands, so it can transfer the protocol requests β and any data β over to the storage device. And the CCB? That's the messenger carrying all these requests around. π
π CAM
The Common Access Method was a standard defined in the 1990s for talking to disk drives. FreeBSD is one of the few operating systems that fully implements this model β main character energy, honestly. π
The interface between the different parts of CAM is the CCB (or CAM Control Block). Every single CCB has a standard header, which holds the type of request plus dispatch info, and then a command-specific portion. A CAM Periph generates requests. The XPT layer dispatches them to the appropriate SIM. Some CCBs get sent straight to the SIM for immediate processing, while others get queued up and complete when the I/O finishes. A SIM takes CCBs and translates them into hardware-specific commands β pushing the SCSI CDB or other protocol control block out to the peripheral, plus setting up the DMA for the associated data. π¦
π Periph Devices
A periph driver knows how to translate standard requests into protocol messages that a SIM can deliver to hardware. These requests can come from any upper layer source, but mostly they roll in via GEOM as a bio request. They can also come in directly from character device requests for tapes and pass-through commands.
Disk devices β direct access (da) in CAM β are one type of peripheral. They show themselves to the kernel as a device ending in "da". Each protocol gets its own unique device name:
da(4)β a SCSI or SAS device, or devices that accept SCSI CDBs for I/O.ada(4)β an ATA or SATA device.nda(4)β an NVME device.sdda(4)β an SD or MMC block storage device.
Tape devices are called serial access (sa(4)) in CAM. They interface to the system via a character device and give you ioctl(2) control for tape drives.
The pass(4) device passes CCB requests from userland straight through to the SIM. You use it to send commands to a device that aren't read, write, trim or flush. The camcontrol(8) command uses this device. π₯
β¨ XPT drivers
The transport driver connects the periph to the SIM. It is not configured separately. It's also the one responsible for device discovery for those SIM drivers that don't enumerate themselves. lowkey doing all the work tbh.
πΈ SIM driver
SIM used to stand for SCSI Interface Module. Now it's just SIM, because it understands protocols other than SCSI. There are two types of SIM drivers: virtual and physical. Physical SIMs are typically called host bus adapters (HBA), but not universally. Virtual SIM drivers are for communicating with network or virtual machine hosts. π
π FILES π
see other CAM device entries. (yes that's it, go look at the others bestie, they got the tea β)
π©Ί DIAGNOSTICS π©Ί
You can use an XPT_DEBUG CCB to enable various amounts of tracing info on any specific bus/device, picked from the list of options compiled into the kernel. There are currently seven debugging flags that may be compiled in and used:
CAM_DEBUG_INFOβ enables general informational printfs for the device or devices in question.CAM_DEBUG_TRACEβ enables function-level command flow tracing, i.e. kernel printfs will happen at the entrance and exit of various functions.CAM_DEBUG_SUBTRACEβ enables debugging output internal to various functions.CAM_DEBUG_CDBβ causes the kernel to print out all ATA and SCSI commands sent to a particular device or devices.CAM_DEBUG_XPTβ enables command scheduler tracing.CAM_DEBUG_PERIPHβ enables peripheral drivers messages.CAM_DEBUG_PROBEβ enables devices probe process tracing.
Some of these flags, most notablyCAM_DEBUG_TRACEandCAM_DEBUG_SUBTRACE, will produce kernel printfs in EXTREME numbers. like, embarrassingly many. π you have been warned bestie.
Users can enable debugging from their kernel config file by using the following kernel config options:
CAMDEBUGβ builds all possible CAM debugging into the kernel.CAM_DEBUG_COMPILEβ specifies support for which of the debugging flags described above should be built into the kernel. Flags may be ORed together if the user wishes to see printfs for multiple debugging levels.CAM_DEBUG_FLAGSβ sets the various debugging flags from a kernel config file.CAM_DEBUG_BUSβ specify a bus to debug. To debug all buses, set this to-1.CAM_DEBUG_TARGETβ specify a target to debug. To debug all targets, set this to-1.CAM_DEBUG_LUNβ specify a lun to debug. To debug all luns, set this to-1.
Users may also enable debugging on the fly by using the camcontrol(8) utility, if the wanted options are built into the kernel. See camcontrol(8) for details. π§
π SEE ALSO π
Commands:
Libraries:
Periph Drivers:
ada(4), da(4), nda(4), pass(4), sa(4)
SIM Devices:
aac(4), aacraid(4), ahc(4), ahci(4), ata(4), aw_mmc(4), ciss(4), hv_storvsc(4), isci(4), iscsi(4), isp(4), mpr(4), mps(4), mpt(4), mrsas(4), mvs(4), nvme(4), pms(4), pvscsi(4), sdhci(4), smartpqi(4), sym(4), tws(4), umass(4), virtio_scsi(4)
Deprecated or Poorly Supported SIM Devices:
ahd(4), amr(4), arcmsr(4), esp(4), hpt27xx(4), hptiop(4), hptmv(4), hptnr(4), iir(4), mfi(4), sbp(4), twa(4)
that last list is giving "we don't talk about them" energy π still loved tho.
π HISTORY π
The CAM SCSI subsystem first appeared in FreeBSD 3.0. The CAM ATA support was added in FreeBSD 8.0. glow-up timeline, honestly. β¨
π«Ά AUTHORS π«Ά
The CAM SCSI subsystem was written by Justin Gibbs and Kenneth Merry. The CAM ATA support was added by Alexander Motin <mav@FreeBSD.org>. The CAM NVMe support was added by Warner Losh <imp@FreeBSD.org>. absolute icons, all of them. π
and that's a wrap bestie!! π this page came from the Debian edition, dated December 11, 2023, and it's the CAM(4) page π you're literally a storage expert now, go slay β¨
π¦ where this page comes from
- freebsd 15.1: base 15.1
- Copyright (c) 1996 Julian Elischer <julian@FreeBSD.org>. All rights reserved
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms