Lorenz Brun | fba5da0 | 2022-12-15 11:20:47 +0000 | [diff] [blame] | 1 | // Package nvme provides methods and data structures for issuing commands to |
| 2 | // device speaking the NVMe protocol. |
| 3 | // This package is written against the NVMe Specification Revision 1.3 and |
| 4 | // all references to figures or other parts of the spec refer to this version. |
| 5 | package nvme |
| 6 | |
| 7 | import ( |
| 8 | "errors" |
| 9 | "fmt" |
| 10 | "os" |
| 11 | "syscall" |
| 12 | "time" |
| 13 | ) |
| 14 | |
| 15 | // Device is a handle for a NVMe device. |
| 16 | type Device struct { |
| 17 | fd syscall.Conn |
| 18 | } |
| 19 | |
| 20 | // NewFromFd creates a new NVMe device handle from a system handle. |
| 21 | func NewFromFd(fd syscall.Conn) (*Device, error) { |
| 22 | d := &Device{fd: fd} |
| 23 | // There is no good way to validate that a file descriptor indeed points to |
| 24 | // a NVMe device. For future compatibility let this return an error so that |
| 25 | // code is already prepared to handle it. |
| 26 | return d, nil |
| 27 | } |
| 28 | |
| 29 | // Open opens a new NVMe device handle from a device path (like /dev/nvme0). |
| 30 | func Open(path string) (*Device, error) { |
| 31 | f, err := os.Open(path) |
| 32 | if err != nil { |
| 33 | return nil, fmt.Errorf("unable to open path: %w", err) |
| 34 | } |
| 35 | return NewFromFd(f) |
| 36 | } |
| 37 | |
| 38 | // Close closes the NVMe device handle. It returns an error if the handle was |
| 39 | // not created by Open. Please close the handle passed to NewFromFd yourself |
| 40 | // in that case. |
| 41 | func (d *Device) Close() error { |
| 42 | if f, ok := d.fd.(*os.File); ok { |
| 43 | return f.Close() |
| 44 | } else { |
| 45 | return errors.New("unable to close device not opened via Open, please close it yourself") |
| 46 | } |
| 47 | } |
| 48 | |
| 49 | const ( |
| 50 | // GlobalNamespace is the namespace ID for operations not on a specific |
| 51 | // namespace. |
| 52 | GlobalNamespace = 0xffffffff |
| 53 | ) |
| 54 | |
| 55 | // Command represents a generic NVMe command. Only use this if the command |
| 56 | // you need is not already wrapped by this library. |
| 57 | type Command struct { |
| 58 | Opcode uint8 |
| 59 | Flags uint8 |
| 60 | NamespaceID uint32 |
| 61 | CDW2, CDW3 uint32 |
| 62 | Metadata []byte |
| 63 | Data []byte |
| 64 | CDW10, CDW11, CDW12, CDW13, CDW14, CDW15 uint32 |
| 65 | Timeout time.Duration |
| 66 | } |
| 67 | |
| 68 | func (d *Device) GetLogPage(ns uint32, logPageIdentifier uint8, logSpecificField uint8, logPageOffset uint64, pageBuf []byte) error { |
| 69 | numberOfDwords := len(pageBuf) / 4 |
| 70 | return d.RawCommand(&Command{ |
| 71 | Opcode: 0x02, |
| 72 | NamespaceID: ns, |
| 73 | Data: pageBuf, |
| 74 | CDW10: uint32(logPageIdentifier) | uint32(logSpecificField&0xF)<<8 | uint32(numberOfDwords)<<16, // TODO: RAE |
| 75 | CDW11: uint32(numberOfDwords >> 16 & 0xffff), |
| 76 | CDW12: uint32(logPageOffset & 0xffffffff), |
| 77 | CDW13: uint32(logPageOffset >> 32), |
| 78 | }) |
| 79 | } |