1. 编译
- host
cd OCTEON-SDK/components/driver make
- core
cd OCTEON-SDK/applications/pci-core-app/base make
- test
cd OCTEON-SDK-2.3.0/components/driver/host/test make
2. 用户态使用
2.1. load
cd /home/yingjie/repos/OCTEON-SDK-2.3.0/target/bin
OCTEON_REMOTE_DEBUG=1 oct-pci-boot u-boot-octeon_nic10e_66.bin
OCTEON_REMOTE_DEBUG=1 oct-pci-load 0 ../../components/driver/bin/cvmcs.strip
OCTEON_REMOTE_DEBUG=1 oct-pci-bootcmd "bootoct 0 coremask=f"
cd /home/yingjie/repos/OCTEON-SDK-2.3.0/components/driver/bin
insmod octeon_drv.ko
mknod /dev/octeon_device c 127 0
./oct_req 0 -ubsy
对应代码OCTEON-SDK/components/driver/host/test/oct_req.c
[root@cvmx bin]# ./oct_req 0 -ubsy
Octeon Test utility version: PCI BASE RELEASE 2.3.0 build 84
Starting operation in silent mode with
Octeon id: 0
response order = UNORDERED (1)
response mode = BLOCKING (0)
dma mode = DIRECT (0)
Max in bufs = 1
Max out bufs = 1
Inbuf size: 1024 Outbuf size: 1024
Shared memory id is 851990
--- Press Ctrl-C to stop the test ---
Main thread pid: 20804
Thread (index: 0 pid: 20805) starting execution
Request Failed with status 4: 0x105d4a--C-
Test Thread 0 (pid: 20805)stopping now....
Main thread stopping...
Test completed: 19770 requests sent.
Verification: 19769 passed 0 failed
Tested with Input buffers [buffer count/requests sent]
[ 1/19770 ]
Tested with Output buffers [buffer count/requests sent]
[ 1/19770 ]
Max data sent: 1024 bytes
Max data received: 1024 bytes
3. 代码梳理
3.1. 共享内存里面保存的结构体
struct test {
volatile int ok_to_send;
pid_t main_pid;
OCTEON_RESPONSE_ORDER resp_order;
OCTEON_RESPONSE_MODE resp_mode;
OCTEON_DMA_MODE dma_mode;
//每个子进程都有一个test_stats
struct test_stats perthread[TEST_THREAD_COUNT];
struct test_stats total;
time_t start, end;
};
3.2. 每个进程一个的test_stats
struct test_stats {
void *sh_mem;
struct request_list *nbreqs;
pid_t pid;
volatile int running;
volatile int reqs_pending;
unsigned long incntfreq[MAX_INBUFS+1];
unsigned long outcntfreq[MAX_OUTBUFS+1];
unsigned long maxdatasent;
unsigned long maxdatareceived;
unsigned long request_count;
unsigned long verify_failed;
unsigned long verify_passed;
};
3.3. 处理SIGINT的handler
注意SIGINT会向前台进程组里面的所有进程发signal
所以这个函数里面会判断只有主进程才干活
在这里是置一个标志, 各个子进程会把当前的活干完, 才根据这个标志退出
void signal_handler(int x)
pid_t my_pid = getpid();
/* Just clear the ok_to_send flag. When the signal handler returns in
the main process, it will wait for the children to complete its
processing.
*/
if(t_main && (my_pid == t_main->main_pid) ) {
t_main->ok_to_send = 0;
return;
}
}
3.4. 主函数流程
main
octeon_initialize
//打开字符设备
oct_dev_handle = open("/dev/octeon_device", 0)
print_test_setup
//打印req的信息
//共享内存, 用于多进程
shmid = shmget(0, sizeof(struct test), IPC_CREAT | IPC_EXCL);
//attach共享内存, 得到地址
t_main = (struct test *)shmat(shmid, NULL, 0);
//安装signal处理SIGINT
prev_sig_handler = signal(signal_to_catch, signal_handler);
//起多线程
for TEST_THREAD_COUNT 个:
pid = fork()
//子进程
case 0:
oct_request_thread(q_no, count, i);
//fork失败
case -1:
//对已经成功fork的子进程kill, 发SIGINT
kill(t_main->perthread[i].pid, signal_to_catch);
//父进程
//记录子进程的pid
t_main->perthread[i].pid = pid
//开始
t_main->ok_to_send = 1;
t_main->main_pid = getpid();
//等待子进程完工
wait_for_thread_completion()
//在循环里sleep(1)等待所有子进程的t->perthread[i].running全为0
//为什么不用waitpid呢?
//统计并打印信息
add_thread_stats(t_main);
print_test_stats(t_main);
//detach共享内存
shmdt(t_main);
//恢复signal
signal(signal_to_catch, prev_sig_handler );
3.5. 重要结构体
components/driver/host/include/cavium_defs.h
//这个共用体的好处是不用强转地址了
/** Use this type to pass buffer address to the driver in ioctls. Use the
addr field to copy your buffer's address. */
typedef union {
uint64_t addr64;
uint8_t *addr;
} cavium_ptr_t;
//这里面MAX_BUFCNT是16
/**
Structure for passing input and output buffers in the request structure.
*/
typedef struct {
/** number of buffers */
uint32_t cnt;
uint32_t rsvd;
/** buffer pointers */
cavium_ptr_t ptr[MAX_BUFCNT];
/** their data sizes*/
uint32_t size[MAX_BUFCNT];
} octeon_buffer_t;
/** Information about the request sent to driver. This structure
* points to the input data buffer(s), to the output buffer(s) (if any)
* if a response is expected. It also keep information about the type of
* DMA, mode of operation (response order, mode etc).
* Several MACROS are defined to help access the fields.
*/
typedef struct {
/** The input buffers and their sizes. */
octeon_buffer_t inbuf;
/** The output buffer pointers and the size allocated at each pointer. */
octeon_buffer_t outbuf;
/** The instruction header to be sent with this request to Octeon. */
//和手册里面input ring里面DPI_INST_HDR一致
octeon_instr_ih_t ih;
/** The Input Request Header to be sent with the request to Octeon. */
octeon_instr_irh_t irh;
/** The extra headers (upto 4 64-bit words) for a 64-bytes instruction. */
uint64_t exhdr[4];
/** Information about the formatting to be done to each extra header. */
octeon_exhdr_info_t exhdr_info;
/** Additional information required for processing this request. Also
driver returns an id identifying the request and the current status
of the request in its fields.*/
union {
uint64_t addr64;
octeon_request_info_t *ptr;
} req_info;
} octeon_soft_request_t;
3.6. 这个测试程序的默认配置
//以下决定了往哪个dev上发
#define OCTEON_ID 0
#define REQ_IQ_NO 0
//一共用几个进程
#define TEST_THREAD_COUNT 1
//buffer数
#define MAX_INBUFS 1
#define MAX_OUTBUFS 1
//buffer大小
#define INBUF_SIZE (1 * 1024)
#define OUTBUF_SIZE (1 * 1024)
#define REQUEST_TIMEOUT 500
#define MAX_NB_REQUESTS 256
/*DMA可以是以下几种:
**OCTEON_DMA_DIRECT; OCTEON_DMA_GATHER;
**OCTEON_DMA_SCATTER; OCTEON_DMA_SCATTER_GATHER.
*/
OCTEON_DMA_MODE TEST_DMA_MODE = OCTEON_DMA_DIRECT;
//以下模式可以通过命令行传入
/*response的oder类型, 可以是:
**OCTEON_RESP_NORESPONSE; OCTEON_RESP_ORDERED; OCTEON_RESP_UNORDERED;
**用户态app不支持ORDERED模式
*/
OCTEON_RESPONSE_ORDER TEST_RESP_ORDER = OCTEON_RESP_NORESPONSE;
/*response的阻塞模式, 可以是:
**OCTEON_RESP_NON_BLOCKING; OCTEON_RESP_BLOCKING;
*/
OCTEON_RESPONSE_ORDER TEST_RESP_MODE = OCTEON_RESP_NON_BLOCKING;
3.7. 子进程
oct_request_thread(int q_no, int count, int tidx)
//注: google以后发现子进程应该会继承shmat的segment, 但这里为什么又自己attach一遍呢?
t = (struct test *)shmat(shmid, NULL, 0)
s = (struct test_stats *)&(t->perthread[tidx]);
s->sh_mem = t;
/*nbregs是个数组, 元素是request_list
struct request_list {
octeon_soft_request_t *sr;
int status;
uint32_t outsize;
uint32_t verify_size;
};
*/
//MAX_NB_REQUESTS是256
s->nbreqs = malloc(sizeof(struct request_list) * MAX_NB_REQUESTS)
//等着主进程发开始
do { sleep(1); } while(!t->ok_to_send);
time(&t1);
srandom(t1);
/*主体循环, 条件是上次发送成功&&t_main->ok_to_send = 1
**这个循环没有什么sleep操作, 全速运转
*/
/*response的order类型, 好像用户态不支持ORDERED
OCTEON_RESP_ORDERED=0,
OCTEON_RESP_UNORDERED=1,
OCTEON_RESP_NORESPONSE=2
response的block类型
OCTEON_RESP_BLOCKING=0,
OCTEON_RESP_NON_BLOCKING
*/
//所以req分几种:
//不需要response的应该最简单, 这里传入的tag是固定的0x101011
req_status = noresponse_request(q_no, tag, s, t->dma_mode);
//随机生成incnt outcnt insize outsize
generate_data_sizes()
//根据DMA mode, 创建no response, 非阻塞的请求
soft_req = create_soft_request()
//注意: 这里第二个malloc写错了!
//给以下两个结构malloc空间
octeon_soft_request_t *soft_req=malloc()
octeon_request_info_t *req_info=malloc()
//raw为0则其后面的位会被忽略
soft_req->ih.raw = 1;
soft_req->ih.qos = 0;
soft_req->ih.grp = 0;
soft_req->ih.rs = 0;
soft_req->ih.tagtype = 1;
soft_req->ih.tag = tag;
//DMA模式里面有gather则置1
soft_req->ih.gather = 1;
//irh是instruction response header
soft_req->irh.opcode = CVMCS_REQRESP_OP;
soft_req->irh.param = 0x10;
soft_req->irh.dport = 32;
//根据DMA置1
soft_req->irh.scatter = 1;
//这个req_info是给谁看的?
req_info->octeon_id = 0;
req_info->request_id = 0xff;
req_info->req_mask.dma_mode = dma_mode;
req_info->req_mask.resp_mode = resp_mode;
req_info->req_mask.resp_order = resp_order;
req_info->req_mask.iq_no = q_no;
req_info->timeout = REQUEST_TIMEOUT;
//这里要申请(malloc)真正的inbuf outbuf, 因为上面的结构体里面只有buffer指针和大小
set_buffers(soft_req, inbuf_cnt, outbuf_cnt)
//这里的status 3是什么意思?
SOFT_REQ_INFO(soft_req)->status = 3;
return soft_req;
req_status = send_request(oct_id, s, soft_req);
/*octeon*是用户态下的api, 基本上都是对ioctl的封装
**这部分代码在components/driver/host/api/octeon_user.c
*/
retval = octeon_send_request(oct_id, sr);
//raw模式下不支持ORDERED模式
//非raw模式不支持response
ioctl(oct_dev_handle, IOCTL_OCTEON_SEND_REQUEST, soft_req)
//发送成功则各种count++
//最后free这个soft_req
free_soft_request(soft_req);
/*上面详细看了noresponse的发送
**剩下是unordered阻塞, 这里采用轮询的策略, 调octeon_query_request()来查询
*/
req_status = unordered_blocking_request(q_no, tag, s, t->dma_mode);
//也是调这两个函数, 但阻塞在ioctl里面?
create_soft_request()
send_request()
//因为是阻塞方式, 现在就可以free这个soft_req了
free_soft_request(soft_req);
/*再剩下是unordered非阻塞
**这种情况下, 要看nbreqs[nbidx].status
**可以有三种:REQ_NONE REQ_PEND REQ_DONE
*/
switch nbreqs[nbidx].status: //这里的status是下面置的
//发出去的报文有回应, 但还没有处理
case REQ_PEND:
r = check_req_status(nbreqs[nbidx].sr);
//调用api
octeon_query_request()
/*如果此时r为OCTEON_REQUEST_PENDING
**说明respond还在queue里, 那本次不处理
*/
/*其他情况要么respond done, 要么出错了*/
if r == OCTEON_REQUEST_DONE
verify_output()
//释放buffer, 传入octeon_soft_request_t
free_soft_request(nbreqs[nbidx].sr);
//释放input buffer, output buffer和这个soft_req本身
nbreqs[nbidx].status = REQ_NONE;
case REQ_NONE:
/*上面看了阻塞式的带response的发送
**这个是非阻塞有response的发送, 入参多了一个参数struct request_list *nb
**还记得吗, struct request_list *nb是个数组, 里面有256项
**上面的case说的是已经发完了请求
**这里的case是说要开始发送
*/
req_status = unordered_nonblocking_request(q_no, tag,s,&nbreqs[nbidx], t->dma_mode);
//同样是
create_soft_request()
send_request()
//因为是非阻塞, 所以不能在这里free, 因此在这里做REQ_PEND标记, 在上面的case用
nb->status = REQ_PEND;
nb->outsize = outsize;
nb->verify_size = verify_size;
nb->sr = soft_req;
if(nbreqs[nbidx].status == REQ_PEND)
nbidx++;//简化, 实际会处理回环
//主循环结束,如果还有pending
if(s->reqs_pending)
wait_for_unordered_requests(s); //清理每个s->nbreqs[i]
free(s->nbreqs);
//dettach共享内存
shmdt(t);