seq_file не работает должным образом после следующего возврата NULL - PullRequest
1 голос
/ 06 марта 2020

Что не так с этим кодом (наверное, относительно совместимости с различными версиями ядра)?

#define BUFFER_SIZE 2048
#define BUFFER_STEP 128

static char buffer[BUFFER_SIZE] = { 0 }; // this is filled with repeating letters a-z in module init

static void* seqf_ex_start (struct seq_file* m, loff_t* pos)
{
    if (*pos >= BUFFER_SIZE) {
        return NULL;
    }

    return buffer + *pos;
}

static void* seqf_ex_next (struct seq_file* m, void* v, loff_t* pos)
{
    *pos += BUFFER_STEP;

    if (*pos >= BUFFER_SIZE) {
        return NULL;
    }

    return buffer + *pos;
}

static int seqf_ex_show (struct seq_file* m, void* v)
{
    seq_printf(m, "%.*s\n", BUFFER_STEP, (char*)v);

    return 0;
}

При запуске модуля ядра в qemuarm (ядро v5.0.19) я получаю правильный вывод :

$ cat /proc/seqf-ex | awk '{ print "line" OFS NR ":" OFS length($0) }'
line 1: 128
line 2: 128
line 3: 128
line 4: 128
line 5: 128
line 6: 128
line 7: 128
line 8: 128
line 9: 128
line 10: 128
line 11: 128
line 12: 128
line 13: 128
line 14: 128
line 15: 128
line 16: 128

Но при работе на ядре v4.15.0 я получаю это:

$ cat /proc/seqf-ex | awk '{ print "line" OFS NR ":" OFS length($0) }'
line 1: 128
line 2: 128
line 3: 128
line 4: 128
line 5: 128
line 6: 128
line 7: 128
line 8: 128
line 9: 128
line 10: 128
line 11: 128
line 12: 128
line 13: 128
line 14: 128
line 15: 128
line 16: 128
line 17: 127
line 18: 126
line 19: 125
line 20: 124
line 21: 123
line 22: 122
line 23: 121
line 24: 120
line 25: 119
line 26: 118
line 27: 117
line 28: 116
line 29: 115
line 30: 114
line 31: 113
line 32: 112
line 33: 111
line 34: 110
line 35: 109
line 36: 108
line 37: 107
line 38: 106
line 39: 105
line 40: 104
line 41: 103
line 42: 102
line 43: 101
line 44: 100
line 45: 99
line 46: 98
line 47: 97
line 48: 96
line 49: 95
line 50: 94
line 51: 93
line 52: 92
line 53: 91
line 54: 90
line 55: 89
line 56: 88
line 57: 87
line 58: 86
line 59: 85
line 60: 84
line 61: 83
line 62: 82
line 63: 81
line 64: 80
line 65: 79
line 66: 78
line 67: 77
line 68: 76
line 69: 75
line 70: 74
line 71: 73
line 72: 72
line 73: 71
line 74: 70
line 75: 69
line 76: 68
line 77: 67
line 78: 66
line 79: 65
line 80: 64
line 81: 63
line 82: 62
line 83: 61
line 84: 60
line 85: 59
line 86: 58
line 87: 57
line 88: 56
line 89: 55
line 90: 54
line 91: 53
line 92: 52
line 93: 51
line 94: 50
line 95: 49
line 96: 48
line 97: 47
line 98: 46
line 99: 45
line 100: 44
line 101: 43
line 102: 42
line 103: 41
line 104: 40
line 105: 39
line 106: 38
line 107: 37
line 108: 36
line 109: 35
line 110: 34
line 111: 33
line 112: 32
line 113: 31
line 114: 30
line 115: 29
line 116: 28
line 117: 27
line 118: 26
line 119: 25
line 120: 24
line 121: 23
line 122: 22
line 123: 21
line 124: 20
line 125: 19
line 126: 18
line 127: 17
line 128: 16
line 129: 15
line 130: 14
line 131: 13
line 132: 12
line 133: 11
line 134: 10
line 135: 9
line 136: 8
line 137: 7
line 138: 6
line 139: 5
line 140: 4
line 141: 3
line 142: 2
line 143: 1

Кажется, что функция seqf_start вызывается повторно после того, как seqf_next возвращает NULL. Почему это происходит?

1 Ответ

1 голос
/ 07 марта 2020

Существует некоторая несовместимость между Linux версиями ядра 4.x и 5.x об обработке *pos аргумента, переданного seq_file функциям обратного вызова.

Linux ядру 5.x устанавливает только *pos в 0 и оставляет другие модификации для обратных вызовов seq_file (функции .start и .next в struct seq_operations).

Однако в Linux в ядре 4. x *pos может быть увеличен на 1 вне ваших обратных вызовов . Это делается, если последний .show вызовет точно количество байтов, необходимых для выполнения запроса read системного вызова.

Поскольку ваш код готов только к *pos значениям, которые являются делится на BUFFER_STEP, он работает некорректно, когда *pos увеличивается вне ваших функций, поэтому он делится на BUFFER_STEP больше.


Я не знаю, поведение Linux 4.x корректен, но если вы хотите, чтобы ваш код работал с таким поведением, вам нужно подготовиться ко всем значениям *pos, а не только к значениям, созданным вашим кодом.

Простое исправление вашего код будет интерпретироваться *pos как индекс чанка вместо байтовый индекс :

static void* seqf_ex_start (struct seq_file* m, loff_t* pos)
{
    if (*pos >= (BUFFER_SIZE/BUFFER_STEP)) {
        return NULL;
    }

    return buffer + (*pos * BUFFER_STEP);
}

static void* seqf_ex_next (struct seq_file* m, void* v, loff_t* pos)
{
    *pos += 1;

    if (*pos >= (BUFFER_SIZE/BUFFER_STEP)) {
        return NULL;
    }

    return buffer + (*pos * BUFFER_STEP);
}

static int seqf_ex_show (struct seq_file* m, void* v)
{
    seq_printf(m, "%.*s\n", BUFFER_STEP, (char*)v);

    return 0;
}

Таким образом, ваш код будет работать с all значения из *pos. (Те, которые слишком высоки, будут отсечены чеками if (*pos >= (BUFFER_SIZE/BUFFER_STEP))). И приращение *pos на 1, выполняемое вне вашего кода, эквивалентно вызову вашего .next обратного вызова.

...