| // Package nvme provides methods and data structures for issuing commands to |
| // device speaking the NVMe protocol. |
| // This package is written against the NVMe Specification Revision 1.3 and |
| // all references to figures or other parts of the spec refer to this version. |
| package nvme |
| |
| import ( |
| "errors" |
| "fmt" |
| "os" |
| "syscall" |
| "time" |
| ) |
| |
| // Device is a handle for a NVMe device. |
| type Device struct { |
| fd syscall.Conn |
| } |
| |
| // NewFromFd creates a new NVMe device handle from a system handle. |
| func NewFromFd(fd syscall.Conn) (*Device, error) { |
| d := &Device{fd: fd} |
| // There is no good way to validate that a file descriptor indeed points to |
| // a NVMe device. For future compatibility let this return an error so that |
| // code is already prepared to handle it. |
| return d, nil |
| } |
| |
| // Open opens a new NVMe device handle from a device path (like /dev/nvme0). |
| func Open(path string) (*Device, error) { |
| f, err := os.Open(path) |
| if err != nil { |
| return nil, fmt.Errorf("unable to open path: %w", err) |
| } |
| return NewFromFd(f) |
| } |
| |
| // Close closes the NVMe device handle. It returns an error if the handle was |
| // not created by Open. Please close the handle passed to NewFromFd yourself |
| // in that case. |
| func (d *Device) Close() error { |
| if f, ok := d.fd.(*os.File); ok { |
| return f.Close() |
| } else { |
| return errors.New("unable to close device not opened via Open, please close it yourself") |
| } |
| } |
| |
| const ( |
| // GlobalNamespace is the namespace ID for operations not on a specific |
| // namespace. |
| GlobalNamespace = 0xffffffff |
| ) |
| |
| // Command represents a generic NVMe command. Only use this if the command |
| // you need is not already wrapped by this library. |
| type Command struct { |
| Opcode uint8 |
| Flags uint8 |
| NamespaceID uint32 |
| CDW2, CDW3 uint32 |
| Metadata []byte |
| Data []byte |
| CDW10, CDW11, CDW12, CDW13, CDW14, CDW15 uint32 |
| Timeout time.Duration |
| } |
| |
| func (d *Device) GetLogPage(ns uint32, logPageIdentifier uint8, logSpecificField uint8, logPageOffset uint64, pageBuf []byte) error { |
| numberOfDwords := len(pageBuf) / 4 |
| return d.RawCommand(&Command{ |
| Opcode: 0x02, |
| NamespaceID: ns, |
| Data: pageBuf, |
| CDW10: uint32(logPageIdentifier) | uint32(logSpecificField&0xF)<<8 | uint32(numberOfDwords)<<16, // TODO: RAE |
| CDW11: uint32(numberOfDwords >> 16 & 0xffff), |
| CDW12: uint32(logPageOffset & 0xffffffff), |
| CDW13: uint32(logPageOffset >> 32), |
| }) |
| } |