github.com/iceber/iouring-go@v0.0.0-20230403020409-002cfd2e2a90/examples/nvme-id-ctrl/main.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "os" 6 "time" 7 "unsafe" 8 9 "github.com/iceber/iouring-go" 10 iouring_syscall "github.com/iceber/iouring-go/syscall" 11 ) 12 13 const ( 14 nvmeOpCodeAdminIdentify = uint8(0x06) 15 16 nvmeIdentifyCnsCtrl = uint32(0x01) 17 18 entries uint = 64 19 20 nrBits = 8 21 typeBits = 8 22 sizeBits = 14 23 24 nrShift = 0 25 typeShift = nrShift + nrBits 26 sizeShift = typeShift + typeBits 27 dirShift = sizeShift + sizeBits 28 29 iocRead = uint64(2) 30 iocWrite = uint64(1) 31 iocWrRd = iocWrite | iocRead 32 33 iocInOut = iocWrRd << dirShift 34 35 iocNVMeType = 'N' << typeShift 36 37 uringCmdAdmin = iocInOut | iocNVMeType | (0x82 << nrShift) | uint64(unsafe.Sizeof(AdminCmd{})<<sizeShift) 38 ) 39 40 type PassthruCmd struct { 41 OpCode uint8 42 Flags uint8 43 _ uint16 // Reserved 44 NSId uint32 45 CDW2 uint32 46 CDW3 uint32 47 Meta uintptr 48 Data uintptr 49 MetaLength uint32 50 DataLength uint32 51 CDW10 uint32 52 CDW11 uint32 53 CDW12 uint32 54 CDW13 uint32 55 CDW14 uint32 56 CDW15 uint32 57 } 58 59 type AdminCmd struct { 60 PassthruCmd 61 62 TimeoutMSec uint32 63 Result uint32 64 } 65 66 func PrepNVMeIdCtrl(fd int, b []byte) iouring.PrepRequest { 67 // id-ctrl command don't use the cntId, nsid and nvmSetId 68 const ( 69 cntId uint32 = 0 70 nsid uint32 = 0 71 nvmSetId uint32 = 0 72 ) 73 74 return func(sqe iouring_syscall.SubmissionQueueEntry, userData *iouring.UserData) { 75 userData.SetRequestInfo(b) 76 userData.SetRequestCallback(func(result iouring.Result) error { 77 if err := result.Err(); err != nil { 78 fmt.Printf("nvme admin passthru failed: %v", err) 79 } 80 81 buf := result.GetRequestInfo().([]byte) 82 fmt.Printf("VID: %x%x\n", buf[1], buf[0]) 83 fmt.Printf("Controller Model Number: %s\n", buf[4:44]) 84 85 return nil 86 }) 87 88 // nvme command passthru will not use the user data pointer because nvme driver uses 89 // nvme command structure same with IOCtl, at the last 80byte of SQE128 entry, and 90 // also use the data pointer and length field in the NVMe command structure. 91 sqe.PrepOperation( 92 iouring_syscall.IORING_OP_URING_CMD, 93 int32(fd), 94 0, 95 0, 96 uringCmdAdmin, 97 ) 98 99 nvmeCmd := sqe.CMD(AdminCmd{}).(*AdminCmd) 100 nvmeCmd.OpCode = nvmeOpCodeAdminIdentify 101 nvmeCmd.NSId = nsid 102 nvmeCmd.CDW10 = cntId<<16 | nvmeIdentifyCnsCtrl 103 nvmeCmd.CDW11 = nvmSetId 104 105 nvmeCmd.Data, nvmeCmd.DataLength = uintptr(unsafe.Pointer(&b[0])), uint32(len(b)) 106 } 107 } 108 109 func main() { 110 if len(os.Args) != 2 { 111 fmt.Printf("Usage: %s nvme_chr_device\n", os.Args[0]) 112 return 113 } 114 115 // nvme passthru command use only SQE128 and CQE32 flags 116 iour, err := iouring.New(entries, iouring.WithSQE128(), iouring.WithCQE32()) 117 if err != nil { 118 fmt.Printf("new IOURing error: %v", err) 119 return 120 } 121 defer func() { _ = iour.Close() }() 122 123 dev, err := os.Open(os.Args[1]) 124 if err != nil { 125 fmt.Printf("Open device file failed: %v\n", err) 126 return 127 } 128 defer func() { _ = dev.Close() }() 129 130 ch := make(chan iouring.Result) 131 prepRequest := PrepNVMeIdCtrl(int(dev.Fd()), make([]byte, 4096)) 132 133 now := time.Now() 134 if _, err = iour.SubmitRequest(prepRequest, ch); err != nil { 135 fmt.Printf("request error: %v\n", err) 136 return 137 } 138 139 fmt.Printf("waiting response callback\n") 140 141 select { 142 case result := <-ch: 143 _ = result.Callback() 144 fmt.Printf("nvme id-ctrl successful: %v\n", time.Now().Sub(now)) 145 } 146 }