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  }