read() с жесткого диска с O_DIRECT завершается с ошибкой 22 (EINVAL, неверный аргумент)

Я хочу сделать основной read() с жесткого диска SATA /dev/sdd, write() похоже на работу. Также read() а также write() работает без O_DIRECT Флаг. Я читал, что он должен быть выровнен по размеру блока. Так что я использовал это, чтобы получить размер блока:

root$ blockdev --getsize /dev/sdd
488397168

root$ blockdev --getsize64 /dev/sdd
250059350016

root$ python -c "print 250059350016.0/488397168"
512.0

Как видите, у меня есть root. Жесткий диск подключен через карту SATA PCIe и lspci -vv показывает мне, что он использует основные ачи (drivers/ata/ahci.c) Водитель. Я работаю с ядром Linux 3.2.0 на 64-битной архитектуре Power.

Вот мой код:

#define _GNU_SOURCE

#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h> 
#include <unistd.h> 
#include <errno.h>

int main() {

    int r;
    char *buffer, *tmp_buffer;
    // alloc more than necesarry to align the real buffer
    tmp_buffer = malloc(2*512*sizeof(char)); 
    long align = (unsigned long)tmp_buffer%512;
    printf("tmp_buffer is at: %x \% 512 = %d\n",tmp_buffer,align);

    buffer = tmp_buffer+(512-align);
    printf("buffer is at: %x \% 512 = %d\n",buffer,(unsigned long)buffer%512);

    memset(buffer,0,sizeof(512));

    // OPEN
    int fd = open("/dev/sdd",O_DIRECT | O_RDWR | O_SYNC);
    if(fd!=3) printf("fd = %d\n",fd);

    // READ
    printf("try to read and then dump buffer:\n");

    r = read(fd,buffer,sizeof(512));
    if(r == -1) printf("Error: %s\n",strerror(errno));
    else {
        // DUMP BUFFER
        int i;
        for(i=0; i<sizeof(512); i++)
            printf("%c",buffer[i]);
    }
    printf("\n");
    return 0;
}

Выход:

tmp_buffer is at: 1cc80010 % 512 = 16
buffer is at: 1cc80200 % 512 = 0
try to read and then dump buffer:
Error: Invalid argument

редактировать: я обновил свой источник в соответствии с предложением Бретт Хейл. К сожалению, я все еще получаю ошибку. Мой способ узнать размер блока хорошо? Я сделал выравнивание правильно?

Большое спасибо за чтение,
Fabian

2 ответа

Решение

Прямая передача DMA обычно требует выравнивания буфера. От man:

Флаг O_DIRECT может накладывать ограничения выравнивания на длину и адрес буферов пространства пользователя и смещение файлов ввода / вывода.... В Linux 2.6 достаточно выравнивания по 512-байтовым границам.

Так char buffer[512]; может потребоваться выравнивание по 512-байтовому адресу.

Может быть невозможно достичь такого выравнивания в стеке, поэтому что-то вроде:

static char buffer[512] __attribute__ ((__aligned__ (512)));

может работать. Или, может быть, это выравнивание будет работать в стеке. В качестве альтернативы, если вы используете x86, вы можете использовать <mm_malloc.h> встроенные функции поддержки: _mm_malloc а также _mm_free,

Кстати, выравнивание не всегда должно быть кратно 512 байтам. это зависит от размера блока устройства. Вы должны найти его, используя ioctl с BLKSSZGET. Если чтение не выравнивается по этому значению при использовании O_DIRECT, чтение () завершится с ошибкой EINVAL.

Другие вопросы по тегам