2015-11-20 17 views
8

Tôi đang làm việc với thẻ nối tiếp tốc độ cao để truyền dữ liệu tốc độ cao từ nguồn bên ngoài vào hộp Linux với thẻ PCIe. Thẻ PCIe đi kèm với một số trình điều khiển bên thứ 3 sử dụng dma_alloc_coherent để cấp phát bộ đệm dma để nhận dữ liệu. Tuy nhiên, do hạn chế của Linux, cách tiếp cận này giới hạn việc truyền dữ liệu tới 4MB. Tôi đã đọc và thử nhiều phương pháp để phân bổ một bộ đệm DMA lớn và không thể có được một để làm việc.Large PCIe DMA Linux x86-64

Hệ thống này có 32 GB bộ nhớ và đang chạy Red Hat với phiên bản hạt nhân là 3.10 và tôi muốn tạo 4GB bộ nhớ khả dụng cho DMA liền kề. Tôi biết phương pháp ưa thích là phân tán/thu thập, nhưng điều này là không thể trong tình huống của tôi vì có một chip phần cứng dịch giao thức nối tiếp thành một DMA ngoài tầm kiểm soát của tôi, điều duy nhất tôi có thể kiểm soát là thêm địa chỉ đến (ví dụ, địa chỉ 0 như được thấy từ hệ thống bên ngoài có thể được ánh xạ tới địa chỉ 0x700000000 trên bus cục bộ).

Vì đây là máy thử nghiệm một lần, tôi nghĩ cách tiếp cận nhanh nhất/dễ nhất là sử dụng tham số cấu hình khởi động mem = 28GB. Tôi có điều này làm việc tốt, nhưng bước tiếp theo để truy cập bộ nhớ đó từ không gian ảo là nơi tôi đang gặp vấn đề. Dưới đây là mã của tôi ngưng tụ để các thành phần có liên quan:

Trong mô-đun hạt nhân:

size_t len = 0x100000000ULL; // 4GB 
size_t phys = 0x700000000ULL; // 28GB 
size_t virt = ioremap_nocache(phys, len); // address not usable via direct reference 
size_t bus = (size_t)virt_to_bus((void*)virt); // this should be the same as phys for x86-64, shouldn't it? 

// OLD WAY 
/*size_t len = 0x400000; // 4MB 
size_t bus; 
size_t virt = dma_alloc_coherent(devHandle, len, &bus, GFP_ATOMIC); 
size_t phys = (size_t)virt_to_phys((void*)virt);*/ 

Trong ứng dụng:

// Attempt to make a usable virtual pointer 
u32 pSize = sysconf(_SC_PAGESIZE); 
void* mapAddr = mmap(0, len+(phys%pSize), PROT_READ|PROT_WRITE, MAP_SHARED, devHandle, phys-(phys%pSize)); 
virt = (size_t)mapAddr + (phys%pSize); 

// do DMA to 0x700000000 bus address 

printf("Value %x\n", *((u32*)virt)); // this is returning zero 

Một điều thú vị là trước khi làm tất cả điều này, vật lý địa chỉ trả về từ dma_alloc_coherent lớn hơn số lượng RAM trên hệ thống (0x83d000000). Tôi nghĩ rằng trong x86 RAM sẽ luôn luôn là địa chỉ thấp nhất và do đó tôi mong đợi một địa chỉ ít hơn 32GB.

Mọi trợ giúp sẽ được đánh giá cao.

+1

Err ... '0x770000000ULL' là 29,75 GB, chứ không phải 28 ... Hãy thử' 0x700000000' để thay thế. –

+0

Dope, lỗi toán học ngu ngốc. Vẫn không nên quan trọng vì khu vực đó vẫn là RAM hợp lệ. Tôi đã không đi đến một trường hợp thử nghiệm 4GB và vẫn chỉ sử dụng 4MB. Sẽ cập nhật câu hỏi. – LINEMAN78

+0

Tôi có một hệ thống bộ nhớ 32GB tiện dụng. Bạn có thể đăng một mã vạch tuyệt đối nhưng tệp nguồn mô-đun hạt nhân hoàn chỉnh, cũng như một chương trình usermode tối thiểu tuyệt đối để kiểm tra không? Ngoài ra, tại sao bạn gắn thẻ với [C++] khi hạt nhân Linux là độc quyền [c] và đoạn mã usermode bạn hiển thị sử dụng các API C độc quyền? –

Trả lời

0

Thay vì hạn chế dung lượng bộ nhớ hệ thống qua mem, hãy thử sử dụng CMA: https://lwn.net/Articles/486301/

Sử dụng đối số dòng lệnh hạt nhân CMA cho phép bạn đặt số tiền nhất định của bộ nhớ cho các hoạt động DMA mà là đảm bảo được liền kề nhau. Hạt nhân sẽ cho phép các quy trình không phải DMA truy cập vào bộ nhớ đó, nhưng ngay sau khi một hoạt động DMA cần bộ nhớ đó, các quy trình không DMA sẽ bị loại bỏ. Vì vậy, tôi khuyên bạn không nên thay đổi thông số mem của mình, nhưng thêm cma=4G vào cmdline của bạn. dma_alloc_coherent sẽ tự động kéo từ không gian dành riêng đó, nhưng bạn có thể bật gỡ lỗi CMA trong cấu hình hạt nhân để đảm bảo.

Các vấn đề liên quan