Thanks alot.
Here’s a description of what I’m doing in a bit more detail.
The platform is a basic off-the-shelf 1ghz intel machine with pci 32/66MHz.
The Host Bus Adapter I’m using is a Qlogic ISP2300 series fibrechannel pcix card with 2GBit optical.
Communication with the device is like so:
1.) A SCSI command is submitted into the top-half of the i/o stack and translated into a request structure on a ring queue. (The ring queue physical address has been provided to the Qlogic HBA during initialization time and each q entry is 64-bytes…)
1a.) Part of the ‘translation’ process is to format the scatter-gather list data maintained on the ‘private’ i/o request structure onto the shared ring-q. In theory there may be a lot of these but in practice it’s never more than 10 and in my unit test case it’s only 1 since the buffer is physically contiguous.
2.) Update the in-pointer index on the Qlogic chip by writing to a register.
3.) The Qlogic firmware detects the change, reads in the queue entries from the shared ring-q.
4.) From here it’s automated.
The Qlogic HBA is a bus-master and sets up a DMA data transfer using the scatter-gather entries provided in the ring-q record and handles all the fibrechannel protocol details to carry out the i/o request with the scsi target.
5.) An interrupt pulse is received from the Qlogic HBA when the command is completed which executes a callback specified in the ‘private’ i/o structure to notify the client that the i/o is completed.
So as you can see, if you specify a data tx length of say 1meg (or even 640K) with a single s/g descriptor, the amount of cpu time required to get the Qlogic chip to start working on the command is very small compared to the amount of time required for the actual data transfer to complete on the 2Gig wire. (That’s what I meant when I said that the cpu was loping along; it was just idle waiting for the big, relatively slow data transfers to complete between the FC-SCSI initiator and target).
Shared Memory Approach
Ok, I see how this works now… Thanks for the explanation; and doesn’t look like the way to go for what I’m trying to do. (Though I’ve learned quite a bit more about what memory mapping means to QNX, which is always a good thing! 8^) )
Custom API Approach
Wow! I have to admit I was really surpised to see how fast QNX can send and receive messages between processes; even with multiple threads in each one! So following Tim’s suggestion, I modified the current resource manager with message_attach() and then use these structures to carry the information describing the i/o to be executed:
typedef struct fcdev_io_rqst_s {
//
// The routing header.
//
io_msg_t hdr;
//
// The target the i/o is headed for.
//
uint32_t target_handle;
//
// The description of the i/o operation.
// Bitwise OR them together to change the behavior of the i/o
// message processing.
//
uint32_t flags;
#define PI_FCDEV_IO_FLAG_RD (UINT32_C(1) << 0)
#define PI_FCDEV_IO_FLAG_WR (UINT32_C(1) << 1)
#define PI_FCDEV_IO_FLAG_ASYNC (UINT32_C(1) << 2)
#define PI_FCDEV_IO_FLAG_10CDB (UINT32_C(1) << 3)
//
// The physical address in memory where the i/o should
// be conducted to/from.
//
uint64_t paddr;
//
// The i/o request lba address.
//
uint64_t lba;
//
// The number of 512-byte blocks you want to transfer.
//
uint32_t blk_cnt;
//
// Caller's private data pointer.
// (This will be copied to the reply message...)
//
uint32_t caller_key;
}fcdev_io_rqst_st;
//*******************************************
// Reply message to the above i/o request.
//*******************************************
typedef struct fcdev_io_reply_s {
//
// The routine header.
//
io_msg_t hdr;
//
// Response codes.
//
uint32_t io_submit; // Should be EOK if the i/o stack accepted it.
//
// SCSI Response Codes. (See pds_scsi.h for the details...)
//
uint8_t scsi_status; // Should be SCSI_STATUS_GOOD
uint8_t skey; // Only valid if 'scsi_status' is not GOOD.
uint8_t asc; // Additional sense code.
uint8_t ascq; // Additional sense code qualifier.
//
// Caller's private data pointer.
// (Copied from the above io_rqst message...)
//
uint32_t caller_key;
}fcdev_io_reply_st;
Using these messages and just programming values onto them in the client, then ‘faking’ completion in the resource manager just for the sake of seeing how many commands/replies the cpu could execute, I measured a transaction rate just over 300,000 transaction per second.
Considering the Qlogic h/w can only handle and absolute maximum 40,000/Sec that’s more than enough message bandwidth!
Current Experiment and a Question:
1.) Create an api that ‘feels’ like read()/write().
My plan is to use these message structs above and just create a library that will allow synchronous i/o (where I leave the client blocked until the i/o completes…) and async i/o (where I perform the reply from the callback and just return _RESMGR_NOREPLY from the message handler I attached). That’s the correct way to respond if you want to perform the reply outside the message handler, right?
I was just going wrap the MsgSend() calls within some xxx_read(), xxx_write() function so the test engineers can write their test code in such a way that makes them cozy.
2.) Question: Can I get ‘path-specific’ data delivered to the message_attach() function?
What I really liked about the traditional read()/write() approach was that I could associate target-specific information in an extended ocb. This way I get the pre-computed target-handle the Qlogic layer can use directly (just a 32-bit key…) in each read()/write() iofunc entrypoint execution which saves a lot of time.
But the message_attach() just appears to let me associate a void* that will accompany the handler function. What I really need is a way to associate path-specific information with every message that gets sent to that fd after it’s been open()'d. Is that possible? (If I could get at the ocb I’d be set!)
The other possibility would be to use the ctp->rcvid as a key to lookup the target information.
There are never more than 127 targets max and the number of open()'s would be <16 (or could be driven lower if necessary…) Does that sound like a reasonable approach if the answer to the above question is ‘No’?
Thanks again for your patience and taking the time to answer my questions, I certainly appreciate it!
Also, Tim thanks for the book title; going to pick that up asap!
regards,
-barry