DF-3043 / df3043.c
/* DF-3043 - nm_bioqlen leak kills async read-ahead mount-wide. * usage: df3043 <mnt> * phase1: read seq (1MiB) fully, report wall time (async baseline) * phase2: poison: 300 preads of 'big' at (maxfile-8192) - legal reads whose * read-ahead blocks EFBIG early-out client-side, leaking nm_bioqlen * phase3: read seq2 (1MiB) fully, report wall time (should be ~blockcount*delay * if async is dead => reproduced) */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <fcntl.h> #include <unistd.h> #include <time.h> #include <sys/types.h> static double now(void){struct timespec ts;clock_gettime(CLOCK_MONOTONIC,&ts);return ts.tv_sec+ts.tv_nsec/1e9;} static double readall(const char*p){ char b[32768]; int fd=open(p,O_RDONLY); if(fd<0){perror(p);exit(1);} ssize_t s; double t0=now(); long tot=0; while((s=read(fd,b,sizeof b))>0) tot+=s; double t1=now(); close(fd); printf(" read %s: %ld bytes in %.3f s\n",p,tot,t1-t0); return t1-t0; } int main(int argc,char**argv){ char p[512]; if(argc<2){fprintf(stderr,"usage: df3043 mnt\n");return 2;} printf("phase1: baseline async sequential read (seq)\n"); double base=readall((snprintf(p,sizeof p,"%s/seq",argv[1]),p)); printf("phase2: poison nm_bioqlen via 'big' preads at maxfile-8192\n"); snprintf(p,sizeof p,"%s/big",argv[1]); int fd=open(p,O_RDONLY); if(fd<0){perror("big");return 1;} char b[4096]; int efbig=0,ok=0; for(int i=0;i<300;i++){ errno=0; ssize_t r=pread(fd,b,sizeof b,0x7FFFFFFFULL-8192); if(r<0){ if(errno== EFBIG||errno==0) efbig++; } else ok++; } close(fd); printf(" poison preads: ok=%d err=%d\n",ok,efbig); printf("phase3: sequential read of never-read seq2 (read-ahead should be dead)\n"); double after=readall((snprintf(p,sizeof p,"%s/seq2",argv[1]),p)); printf("TIMES: base=%.3f after=%.3f ratio=%.2fx\n",base,after,base>0?after/base:0); return 0; } |