eBPF/BCCతో హై సెఫ్ లాటెన్సీ నుండి కెర్నల్ ప్యాచ్ వరకు

eBPF/BCCతో హై సెఫ్ లాటెన్సీ నుండి కెర్నల్ ప్యాచ్ వరకు

Linux కెర్నల్ మరియు అప్లికేషన్‌లను డీబగ్గింగ్ చేయడానికి పెద్ద సంఖ్యలో సాధనాలను కలిగి ఉంది. వాటిలో ఎక్కువ భాగం అప్లికేషన్ పనితీరుపై ప్రతికూల ప్రభావాన్ని చూపుతాయి మరియు ఉత్పత్తిలో ఉపయోగించబడవు.

కొన్ని సంవత్సరాల క్రితం ఉంది మరొక సాధనం అభివృద్ధి చేయబడింది - eBPF. ఇది కెర్నల్ మరియు వినియోగదారు అప్లికేషన్‌లను తక్కువ ఓవర్‌హెడ్‌తో మరియు ప్రోగ్రామ్‌లను పునర్నిర్మించాల్సిన అవసరం లేకుండా మరియు థర్డ్-పార్టీ మాడ్యూల్‌లను కెర్నల్‌లోకి లోడ్ చేయడాన్ని సాధ్యం చేస్తుంది.

eBPFని ఉపయోగించే అనేక అప్లికేషన్ యుటిలిటీలు ఇప్పటికే ఉన్నాయి మరియు ఈ కథనంలో లైబ్రరీ ఆధారంగా మీ స్వంత ప్రొఫైలింగ్ యుటిలిటీని ఎలా వ్రాయాలో చూద్దాం. పైథాన్BCC. కథనం వాస్తవ సంఘటనల ఆధారంగా రూపొందించబడింది. నిర్దిష్ట పరిస్థితుల్లో ఇప్పటికే ఉన్న యుటిలిటీలను ఎలా ఉపయోగించవచ్చో చూపడానికి మేము సమస్య నుండి పరిష్కారానికి వెళ్తాము.

Ceph నెమ్మదిగా ఉంది

Ceph క్లస్టర్‌కి కొత్త హోస్ట్ జోడించబడింది. దానికి కొంత డేటాను మైగ్రేట్ చేసిన తర్వాత, దాని ద్వారా వ్రాత అభ్యర్థనలను ప్రాసెస్ చేసే వేగం ఇతర సర్వర్‌ల కంటే చాలా తక్కువగా ఉందని మేము గమనించాము.

eBPF/BCCతో హై సెఫ్ లాటెన్సీ నుండి కెర్నల్ ప్యాచ్ వరకు
ఇతర ప్లాట్‌ఫారమ్‌ల వలె కాకుండా, ఈ హోస్ట్ bcache మరియు కొత్త linux 4.15 కెర్నల్‌ను ఉపయోగించింది. ఈ కాన్ఫిగరేషన్ యొక్క హోస్ట్‌ను ఇక్కడ ఉపయోగించడం ఇదే మొదటిసారి. మరియు ఆ సమయంలో సమస్య యొక్క మూలం సిద్ధాంతపరంగా ఏదైనా కావచ్చునని స్పష్టమైంది.

హోస్ట్‌ను విచారిస్తోంది

ceph-osd ప్రక్రియలో ఏమి జరుగుతుందో చూడటం ద్వారా ప్రారంభిద్దాం. దీని కోసం మేము ఉపయోగిస్తాము perf и ఫ్లేమ్స్కోప్ (దీని గురించి మీరు మరింత చదవగలరు ఇక్కడ):

eBPF/BCCతో హై సెఫ్ లాటెన్సీ నుండి కెర్నల్ ప్యాచ్ వరకు
చిత్రం ఫంక్షన్ అని మాకు చెబుతుంది fdatasync() ఫంక్షన్లకు రిక్వెస్ట్ పంపడానికి చాలా సమయం వెచ్చించారు generic_make_request(). దీనర్థం, సమస్యలకు కారణం osd డెమోన్ వెలుపల ఎక్కడో ఉంటుంది. ఇది కెర్నల్ లేదా డిస్క్‌లు కావచ్చు. iostat అవుట్‌పుట్ bcache డిస్క్‌ల ద్వారా అభ్యర్థనలను ప్రాసెస్ చేయడంలో అధిక జాప్యాన్ని చూపింది.

హోస్ట్‌ని తనిఖీ చేస్తున్నప్పుడు, systemd-udevd డెమోన్ పెద్ద మొత్తంలో CPU సమయాన్ని వినియోగిస్తున్నట్లు మేము కనుగొన్నాము - అనేక కోర్లలో దాదాపు 20%. ఇది వింత ప్రవర్తన, కాబట్టి మీరు ఎందుకు కనుగొనాలి. Systemd-udevd ueventsతో పని చేస్తుంది కాబట్టి, మేము వాటిని చూడాలని నిర్ణయించుకున్నాము udevadm మానిటర్. సిస్టమ్‌లోని ప్రతి బ్లాక్ పరికరానికి పెద్ద సంఖ్యలో మార్పు ఈవెంట్‌లు సృష్టించబడినట్లు ఇది మారుతుంది. ఇది చాలా అసాధారణమైనది, కాబట్టి ఈ ఈవెంట్‌లన్నింటిని ఏది ఉత్పత్తి చేస్తుందో మనం చూడాలి.

BCC టూల్‌కిట్‌ని ఉపయోగించడం

మేము ఇప్పటికే కనుగొన్నట్లుగా, కెర్నల్ (మరియు సిస్టమ్ కాల్‌లోని సెఫ్ డెమోన్) చాలా సమయాన్ని వెచ్చిస్తుంది generic_make_request(). ఈ ఫంక్షన్ యొక్క వేగాన్ని కొలవడానికి ప్రయత్నిద్దాం. IN బిసిసి ఇప్పటికే అద్భుతమైన యుటిలిటీ ఉంది - క్రియాత్మకత. మేము డెమోన్‌ను దాని PID ద్వారా అవుట్‌పుట్‌ల మధ్య 1 సెకను విరామంతో ట్రేస్ చేస్తాము మరియు ఫలితాన్ని మిల్లీసెకన్లలో అవుట్‌పుట్ చేస్తాము.

eBPF/BCCతో హై సెఫ్ లాటెన్సీ నుండి కెర్నల్ ప్యాచ్ వరకు
ఈ ఫీచర్ సాధారణంగా త్వరగా పని చేస్తుంది. డివైజ్ డ్రైవర్ క్యూకు అభ్యర్థనను పంపడమే ఇది చేస్తుంది.

Bcache వాస్తవానికి మూడు డిస్క్‌లను కలిగి ఉండే సంక్లిష్ట పరికరం:

  • బ్యాకింగ్ పరికరం (కాష్డ్ డిస్క్), ఈ సందర్భంలో ఇది నెమ్మదిగా HDD;
  • కాషింగ్ పరికరం (కాషింగ్ డిస్క్), ఇక్కడ ఇది NVMe పరికరం యొక్క ఒక విభజన;
  • అప్లికేషన్ రన్ అయ్యే bcache వర్చువల్ పరికరం.

అభ్యర్థన ప్రసారం నెమ్మదిగా జరుగుతుందని మాకు తెలుసు, అయితే ఈ పరికరాల్లో దేనికి? మేము దీనితో కొంచెం తర్వాత వ్యవహరిస్తాము.

ఈవెంట్‌లు సమస్యలను కలిగించే అవకాశం ఉందని ఇప్పుడు మనకు తెలుసు. వారి తరానికి సరిగ్గా కారణమేమిటో కనుగొనడం అంత సులభం కాదు. ఇది క్రమానుగతంగా ప్రారంభించబడే ఒక రకమైన సాఫ్ట్‌వేర్ అని అనుకుందాం. స్క్రిప్ట్‌ని ఉపయోగించి సిస్టమ్‌లో ఎలాంటి సాఫ్ట్‌వేర్ రన్ అవుతుందో చూద్దాం execsnoop అదే నుండి BCC యుటిలిటీ కిట్. దాన్ని రన్ చేసి అవుట్‌పుట్‌ని ఫైల్‌కి పంపుదాం.

ఉదాహరణకు ఇలా:

/usr/share/bcc/tools/execsnoop  | tee ./execdump

మేము ఇక్కడ execsnoop యొక్క పూర్తి అవుట్‌పుట్‌ను చూపము, కానీ మాకు ఆసక్తి ఉన్న ఒక లైన్ ఇలా ఉంది:

sh 1764905 5802 0 sudo arcconf getconfig 1 AD | grep Temperature | awk -F '[:/]' '{print $2}' | sed 's/^ ([0-9]*) C.*/1/'

మూడవ నిలువు వరుస ప్రక్రియ యొక్క PPID (పేరెంట్ PID). PID 5802తో ప్రక్రియ మా పర్యవేక్షణ వ్యవస్థ యొక్క థ్రెడ్‌లలో ఒకటిగా మారింది. పర్యవేక్షణ వ్యవస్థ యొక్క కాన్ఫిగరేషన్‌ను తనిఖీ చేస్తున్నప్పుడు, తప్పు పారామితులు కనుగొనబడ్డాయి. HBA అడాప్టర్ యొక్క ఉష్ణోగ్రత ప్రతి 30 సెకన్లకు తీసుకోబడుతుంది, ఇది అవసరమైన దానికంటే చాలా తరచుగా ఉంటుంది. చెక్ విరామాన్ని పెద్దదానికి మార్చిన తర్వాత, ఇతర హోస్ట్‌లతో పోల్చితే ఈ హోస్ట్‌లోని అభ్యర్థన ప్రాసెసింగ్ జాప్యం ఇకపై ప్రత్యేకంగా లేదని మేము కనుగొన్నాము.

కానీ bcache పరికరం ఎందుకు నెమ్మదిగా ఉందో ఇప్పటికీ అస్పష్టంగా ఉంది. మేము ఒకే విధమైన కాన్ఫిగరేషన్‌తో టెస్ట్ ప్లాట్‌ఫారమ్‌ను సిద్ధం చేసాము మరియు bcacheలో fioని అమలు చేయడం ద్వారా సమస్యను పునరుత్పత్తి చేయడానికి ప్రయత్నించాము, uevents రూపొందించడానికి udevadm ట్రిగ్గర్‌ను క్రమానుగతంగా అమలు చేస్తాము.

BCC-ఆధారిత సాధనాలను వ్రాయడం

నెమ్మదిగా కాల్‌లను కనుగొనడానికి మరియు ప్రదర్శించడానికి సరళమైన యుటిలిటీని వ్రాయడానికి ప్రయత్నిద్దాం generic_make_request(). ఈ ఫంక్షన్ పిలిచిన డ్రైవ్ పేరుపై కూడా మాకు ఆసక్తి ఉంది.

ప్రణాళిక సులభం:

  • నమోదు చేసుకోండి kprobegeneric_make_request():
    • మేము డిస్క్ పేరును మెమరీలో సేవ్ చేస్తాము, ఫంక్షన్ ఆర్గ్యుమెంట్ ద్వారా యాక్సెస్ చేయవచ్చు;
    • మేము టైమ్‌స్టాంప్‌ను సేవ్ చేస్తాము.

  • నమోదు చేసుకోండి kretprobe నుండి తిరిగి రావడానికి generic_make_request():
    • మేము ప్రస్తుత సమయముద్రను పొందుతాము;
    • మేము సేవ్ చేసిన టైమ్‌స్టాంప్ కోసం చూస్తాము మరియు దానిని ప్రస్తుత దానితో పోల్చాము;
    • ఫలితం పేర్కొన్నదాని కంటే ఎక్కువగా ఉంటే, మేము సేవ్ చేసిన డిస్క్ పేరును కనుగొని టెర్మినల్‌లో ప్రదర్శిస్తాము.

Kprobes и kretprobes ఫ్లైలో ఫంక్షన్ కోడ్‌ని మార్చడానికి బ్రేక్‌పాయింట్ మెకానిజంను ఉపయోగించండి. మీరు చదవగలరు డాక్యుమెంటేషన్ и మంచిది ఈ అంశంపై వ్యాసం. మీరు వివిధ యుటిలిటీల కోడ్‌ను పరిశీలిస్తే బిసిసి, అప్పుడు అవి ఒకే విధమైన నిర్మాణాన్ని కలిగి ఉన్నాయని మీరు చూడవచ్చు. కాబట్టి ఈ కథనంలో మేము స్క్రిప్ట్ ఆర్గ్యుమెంట్‌లను అన్వయించడాన్ని దాటవేసి, BPF ప్రోగ్రామ్‌కు వెళ్తాము.

పైథాన్ స్క్రిప్ట్‌లోని eBPF టెక్స్ట్ ఇలా కనిపిస్తుంది:

bpf_text = “”” # Here will be the bpf program code “””

ఫంక్షన్ల మధ్య డేటాను మార్పిడి చేయడానికి, eBPF ప్రోగ్రామ్‌లు ఉపయోగిస్తాయి హాష్ పట్టికలు. అలాగే చేస్తాం. మేము ప్రాసెస్ PIDని కీగా ఉపయోగిస్తాము మరియు నిర్మాణాన్ని విలువగా నిర్వచిస్తాము:

struct data_t {
	u64 pid;
	u64 ts;
	char comm[TASK_COMM_LEN];
	u64 lat;
	char disk[DISK_NAME_LEN];
};

BPF_HASH(p, u64, struct data_t);
BPF_PERF_OUTPUT(events);

ఇక్కడ మేము అనే హాష్ పట్టికను నమోదు చేస్తాము p, కీ రకంతో u64 మరియు రకం విలువ struct data_t. మా BPF ప్రోగ్రామ్ సందర్భంలో పట్టిక అందుబాటులో ఉంటుంది. BPF_PERF_OUTPUT మాక్రో అనే మరొక పట్టికను నమోదు చేస్తుంది ఈవెంట్స్, ఇది కోసం ఉపయోగించబడుతుంది సమాచార ప్రసారం వినియోగదారు స్థలంలోకి.

ఫంక్షన్‌కు కాల్ చేయడం మరియు దాని నుండి తిరిగి రావడం లేదా వివిధ ఫంక్షన్‌లకు కాల్‌ల మధ్య ఆలస్యాలను కొలిచేటప్పుడు, అందుకున్న డేటా తప్పనిసరిగా ఒకే సందర్భానికి చెందినదని మీరు పరిగణనలోకి తీసుకోవాలి. మరో మాటలో చెప్పాలంటే, మీరు ఫంక్షన్ల సమాంతర ప్రయోగ గురించి గుర్తుంచుకోవాలి. ఒక ప్రక్రియ సందర్భంలో ఫంక్షన్‌కు కాల్ చేయడం మరియు మరొక ప్రక్రియ సందర్భంలో ఆ ఫంక్షన్ నుండి తిరిగి రావడం మధ్య జాప్యాన్ని కొలవగల సామర్థ్యం మాకు ఉంది, కానీ ఇది నిరుపయోగంగా ఉంటుంది. ఇక్కడ ఒక మంచి ఉదాహరణ ఉంటుంది బయోలేటెన్సీ యుటిలిటీ, ఇక్కడ హాష్ టేబుల్ కీ పాయింటర్‌కి సెట్ చేయబడింది నిర్మాణ అభ్యర్థన, ఇది ఒక డిస్క్ అభ్యర్థనను ప్రతిబింబిస్తుంది.

తరువాత, అధ్యయనంలో ఉన్న ఫంక్షన్‌ని పిలిచినప్పుడు అమలు చేయబడే కోడ్‌ను మనం వ్రాయాలి:

void start(struct pt_regs *ctx, struct bio *bio) {
	u64 pid = bpf_get_current_pid_tgid();
	struct data_t data = {};
	u64 ts = bpf_ktime_get_ns();
	data.pid = pid;
	data.ts = ts;
	bpf_probe_read_str(&data.disk, sizeof(data.disk), (void*)bio->bi_disk->disk_name);
	p.update(&pid, &data);
}

ఇక్కడ కాల్ చేసిన ఫంక్షన్ యొక్క మొదటి ఆర్గ్యుమెంట్ రెండవ ఆర్గ్యుమెంట్‌గా భర్తీ చేయబడుతుంది generic_make_request(). దీని తర్వాత, మేము పని చేస్తున్న సందర్భంలో ప్రక్రియ యొక్క PIDని మరియు నానోసెకన్లలో ప్రస్తుత టైమ్‌స్టాంప్‌ను పొందుతాము. మేము అన్నింటినీ తాజాగా ఎంచుకున్న వాటిలో వ్రాస్తాము డేటా_t డేటాను రూపొందించండి. మేము నిర్మాణం నుండి డిస్క్ పేరు పొందుతాము బయో, ఇది కాల్ చేసినప్పుడు ఆమోదించబడింది generic_make_request(), మరియు దానిని అదే నిర్మాణంలో సేవ్ చేయండి సమాచారం. చివరి దశ ముందుగా పేర్కొన్న హాష్ పట్టికకు ఎంట్రీని జోడించడం.

నుండి తిరిగి వచ్చినప్పుడు కింది ఫంక్షన్ కాల్ చేయబడుతుంది generic_make_request():

void stop(struct pt_regs *ctx) {
    u64 pid = bpf_get_current_pid_tgid();
    u64 ts = bpf_ktime_get_ns();
    struct data_t* data = p.lookup(&pid);
    if (data != 0 && data->ts > 0) {
        bpf_get_current_comm(&data->comm, sizeof(data->comm));
        data->lat = (ts - data->ts)/1000;
        if (data->lat > MIN_US) {
            FACTOR
            data->pid >>= 32;
            events.perf_submit(ctx, data, sizeof(struct data_t));
        }
        p.delete(&pid);
    }
}

ఈ ఫంక్షన్ మునుపటి మాదిరిగానే ఉంటుంది: మేము ప్రాసెస్ మరియు టైమ్‌స్టాంప్ యొక్క PIDని కనుగొంటాము, కానీ కొత్త డేటా నిర్మాణం కోసం మెమరీని కేటాయించము. బదులుగా, మేము కీ == ప్రస్తుత PIDని ఉపయోగించి ఇప్పటికే ఉన్న నిర్మాణం కోసం హాష్ పట్టికను శోధిస్తాము. నిర్మాణం కనుగొనబడితే, మేము నడుస్తున్న ప్రక్రియ పేరును కనుగొని దానికి జోడిస్తాము.

థ్రెడ్ GIDని పొందడానికి మనం ఇక్కడ ఉపయోగించే బైనరీ షిఫ్ట్ అవసరం. ఆ. మేము పని చేస్తున్న సందర్భంలో థ్రెడ్‌ను ప్రారంభించిన ప్రధాన ప్రక్రియ యొక్క PID. మేము పిలిచే ఫంక్షన్ bpf_get_current_pid_tgid() థ్రెడ్ యొక్క GID మరియు దాని PID రెండింటినీ ఒకే 64-బిట్ విలువలో అందిస్తుంది.

టెర్మినల్‌కు అవుట్‌పుట్ చేస్తున్నప్పుడు, మాకు ప్రస్తుతం స్ట్రీమ్‌పై ఆసక్తి లేదు, కానీ ప్రధాన ప్రక్రియపై మాకు ఆసక్తి ఉంది. ఇచ్చిన థ్రెషోల్డ్‌తో ఫలిత ఆలస్యాన్ని పోల్చిన తర్వాత, మేము మా నిర్మాణాన్ని పాస్ చేస్తాము సమాచారం పట్టిక ద్వారా వినియోగదారు స్థలంలోకి ఈవెంట్స్, దాని తర్వాత మేము ఎంట్రీని తొలగిస్తాము p.

ఈ కోడ్‌ని లోడ్ చేసే పైథాన్ స్క్రిప్ట్‌లో, మేము MIN_US మరియు FACTORని ఆలస్యం థ్రెషోల్డ్‌లు మరియు టైమ్ యూనిట్‌లతో భర్తీ చేయాలి, వీటిని మేము ఆర్గ్యుమెంట్‌ల ద్వారా పంపుతాము:

bpf_text = bpf_text.replace('MIN_US',str(min_usec))
if args.milliseconds:
	bpf_text = bpf_text.replace('FACTOR','data->lat /= 1000;')
	label = "msec"
else:
	bpf_text = bpf_text.replace('FACTOR','')
	label = "usec"

ఇప్పుడు మనం BPF ప్రోగ్రామ్‌ను సిద్ధం చేయాలి BPF మాక్రో మరియు నమూనాలను నమోదు చేయండి:

b = BPF(text=bpf_text)
b.attach_kprobe(event="generic_make_request",fn_name="start")
b.attach_kretprobe(event="generic_make_request",fn_name="stop")

మేము కూడా నిర్ణయించవలసి ఉంటుంది struct data_t మా స్క్రిప్ట్‌లో, లేకుంటే మనం ఏమీ చదవలేము:

TASK_COMM_LEN = 16	# linux/sched.h
DISK_NAME_LEN = 32	# linux/genhd.h
class Data(ct.Structure):
	_fields_ = [("pid", ct.c_ulonglong),
            	("ts", ct.c_ulonglong),
            	("comm", ct.c_char * TASK_COMM_LEN),
            	("lat", ct.c_ulonglong),
            	("disk",ct.c_char * DISK_NAME_LEN)]

టెర్మినల్‌కు డేటాను అవుట్‌పుట్ చేయడం చివరి దశ:

def print_event(cpu, data, size):
    global start
    event = ct.cast(data, ct.POINTER(Data)).contents
    if start == 0:
        start = event.ts
    time_s = (float(event.ts - start)) / 1000000000
    print("%-18.9f %-16s %-6d   %-1s %s   %s" % (time_s, event.comm, event.pid, event.lat, label, event.disk))

b["events"].open_perf_buffer(print_event)
# format output
start = 0
while 1:
    try:
        b.perf_buffer_poll()
    except KeyboardInterrupt:
        exit()

స్క్రిప్ట్ కూడా అందుబాటులో ఉంది GITHub. fio నడుస్తున్న టెస్ట్ ప్లాట్‌ఫారమ్‌లో దీన్ని అమలు చేయడానికి ప్రయత్నిద్దాం, bcacheకి వ్రాయండి మరియు udevadm మానిటర్‌కు కాల్ చేయండి:

eBPF/BCCతో హై సెఫ్ లాటెన్సీ నుండి కెర్నల్ ప్యాచ్ వరకు
చివరగా! ఇప్పుడు మనం స్టాలింగ్ bcache పరికరం లాగా కనిపించేది నిజానికి స్టాలింగ్ కాల్ అని చూస్తాము generic_make_request() కాష్ చేసిన డిస్క్ కోసం.

కెర్నల్‌లోకి తవ్వండి

అభ్యర్థన ప్రసార సమయంలో సరిగ్గా వేగాన్ని తగ్గించడం ఏమిటి? అభ్యర్థన అకౌంటింగ్ ప్రారంభానికి ముందే ఆలస్యం జరుగుతుందని మేము చూస్తాము, అనగా. దానిపై (/proc/diskstats లేదా iostat) గణాంకాల యొక్క తదుపరి అవుట్‌పుట్ కోసం నిర్దిష్ట అభ్యర్థనను లెక్కించడం ఇంకా ప్రారంభించబడలేదు. సమస్యను పునరుత్పత్తి చేస్తున్నప్పుడు iostatని అమలు చేయడం ద్వారా దీన్ని సులభంగా ధృవీకరించవచ్చు లేదా BCC స్క్రిప్ట్ బయోలేటెన్సీ, ఇది అభ్యర్థన అకౌంటింగ్ ప్రారంభం మరియు ముగింపుపై ఆధారపడి ఉంటుంది. కాష్ చేయబడిన డిస్క్‌కి అభ్యర్థనల కోసం ఈ యుటిలిటీలు ఏవీ సమస్యలను చూపవు.

ఫంక్షన్ చూస్తే generic_make_request(), అభ్యర్థన అకౌంటింగ్ ప్రారంభించే ముందు, మరో రెండు విధులు పిలవబడతాయని మేము చూస్తాము. ప్రధమ - generic_make_request_checks(), డిస్క్ సెట్టింగ్‌లకు సంబంధించి అభ్యర్థన యొక్క చట్టబద్ధతపై తనిఖీలను నిర్వహిస్తుంది. రెండవ - blk_queue_enter(), ఇందులో ఆసక్తికరమైన సవాలు ఉంది వెయిట్_ఈవెంట్_ఇంటరప్టిబుల్():

ret = wait_event_interruptible(q->mq_freeze_wq,
	(atomic_read(&q->mq_freeze_depth) == 0 &&
	(preempt || !blk_queue_preempt_only(q))) ||
	blk_queue_dying(q));

అందులో, క్యూ స్తంభింపజేయడానికి కెర్నల్ వేచి ఉంటుంది. జాప్యాన్ని కొలుద్దాం blk_queue_enter():

~# /usr/share/bcc/tools/funclatency  blk_queue_enter -i 1 -m               	 
Tracing 1 functions for "blk_queue_enter"... Hit Ctrl-C to end.

 	msecs           	: count 	distribution
     	0 -> 1      	: 341  	|****************************************|

 	msecs           	: count 	distribution
     	0 -> 1      	: 316  	|****************************************|

 	msecs           	: count 	distribution
     	0 -> 1      	: 255  	|****************************************|
     	2 -> 3      	: 0    	|                                    	|
     	4 -> 7      	: 0    	|                                    	|
     	8 -> 15     	: 1    	|                                    	|

మేము ఒక పరిష్కారానికి దగ్గరగా ఉన్నట్లు కనిపిస్తోంది. క్యూను స్తంభింపజేయడానికి/అన్‌ఫ్రీజ్ చేయడానికి ఉపయోగించే విధులు blk_mq_freeze_queue и blk_mq_unfreeze_queue. అభ్యర్థన క్యూ సెట్టింగ్‌లను మార్చాల్సిన అవసరం వచ్చినప్పుడు అవి ఉపయోగించబడతాయి, ఇవి ఈ క్యూలోని అభ్యర్థనలకు ప్రమాదకరమైనవి. కాల్ చేస్తున్నప్పుడు blk_mq_freeze_queue() ఫంక్షన్ blk_freeze_queue_start() కౌంటర్ పెరిగింది q->mq_freeze_depth. దీని తరువాత, క్యూ ఖాళీ అయ్యే వరకు కెర్నల్ వేచి ఉంటుంది blk_mq_freeze_queue_wait().

ఈ క్యూను క్లియర్ చేయడానికి పట్టే సమయం డిస్క్ లేటెన్సీకి సమానం, ఎందుకంటే క్యూలో ఉన్న అన్ని ఆపరేషన్‌లు పూర్తయ్యే వరకు కెర్నల్ వేచి ఉంది. క్యూ ఖాళీ అయిన తర్వాత, సెట్టింగ్‌ల మార్పులు వర్తింపజేయబడతాయి. దాని తర్వాత అంటారు blk_mq_unfreeze_queue(), కౌంటర్ తగ్గించడం ఫ్రీజ్_డెప్త్.

ఇప్పుడు పరిస్థితిని సరిదిద్దడానికి మాకు తగినంత తెలుసు. udevadm ట్రిగ్గర్ ఆదేశం బ్లాక్ పరికరం కోసం సెట్టింగ్‌లను వర్తింపజేస్తుంది. ఈ సెట్టింగ్‌లు udev నియమాలలో వివరించబడ్డాయి. ఏ సెట్టింగ్‌లు క్యూను స్తంభింపజేస్తున్నాయో వాటిని sysfs ద్వారా మార్చడానికి ప్రయత్నించడం ద్వారా లేదా కెర్నల్ సోర్స్ కోడ్‌ని చూడటం ద్వారా మనం కనుగొనవచ్చు. మేము BCC యుటిలిటీని కూడా ప్రయత్నించవచ్చు ట్రేస్, ఇది టెర్మినల్‌కు ప్రతి కాల్ కోసం కెర్నల్ మరియు యూజర్‌స్పేస్ స్టాక్ ట్రేస్‌లను అవుట్‌పుట్ చేస్తుంది blk_freeze_queueఉదాహరణకు:

~# /usr/share/bcc/tools/trace blk_freeze_queue -K -U
PID 	TID 	COMM        	FUNC        	 
3809642 3809642 systemd-udevd   blk_freeze_queue
    	blk_freeze_queue+0x1 [kernel]
    	elevator_switch+0x29 [kernel]
    	elv_iosched_store+0x197 [kernel]
    	queue_attr_store+0x5c [kernel]
    	sysfs_kf_write+0x3c [kernel]
    	kernfs_fop_write+0x125 [kernel]
    	__vfs_write+0x1b [kernel]
    	vfs_write+0xb8 [kernel]
    	sys_write+0x55 [kernel]
    	do_syscall_64+0x73 [kernel]
    	entry_SYSCALL_64_after_hwframe+0x3d [kernel]
    	__write_nocancel+0x7 [libc-2.23.so]
    	[unknown]

3809631 3809631 systemd-udevd   blk_freeze_queue
    	blk_freeze_queue+0x1 [kernel]
    	queue_requests_store+0xb6 [kernel]
    	queue_attr_store+0x5c [kernel]
    	sysfs_kf_write+0x3c [kernel]
    	kernfs_fop_write+0x125 [kernel]
    	__vfs_write+0x1b [kernel]
    	vfs_write+0xb8 [kernel]
    	sys_write+0x55 [kernel]
    	do_syscall_64+0x73 [kernel]
    	entry_SYSCALL_64_after_hwframe+0x3d [kernel]
    	__write_nocancel+0x7 [libc-2.23.so]
    	[unknown]

Udev నియమాలు చాలా అరుదుగా మారుతాయి మరియు సాధారణంగా ఇది నియంత్రిత పద్ధతిలో జరుగుతుంది. కాబట్టి ఇప్పటికే సెట్ చేసిన విలువలను కూడా వర్తింపజేయడం వల్ల అభ్యర్థనను అప్లికేషన్ నుండి డిస్క్‌కి బదిలీ చేయడంలో ఆలస్యం పెరుగుతుందని మేము చూస్తాము. వాస్తవానికి, డిస్క్ కాన్ఫిగరేషన్‌లో మార్పులు లేనప్పుడు udev ఈవెంట్‌లను రూపొందించడం (ఉదాహరణకు, పరికరం మౌంట్ చేయబడలేదు/డిస్‌కనెక్ట్ చేయబడలేదు) మంచి పద్ధతి కాదు. అయినప్పటికీ, మేము కెర్నల్ అనవసరమైన పనిని చేయకుండా సహాయం చేయవచ్చు మరియు అది అవసరం లేకుంటే అభ్యర్థన క్యూను స్తంభింపజేయవచ్చు. మూడు చిన్నది కట్టుబడి పరిస్థితిని సరిదిద్దండి.

ముగింపు

eBPF చాలా సౌకర్యవంతమైన మరియు శక్తివంతమైన సాధనం. వ్యాసంలో మేము ఒక ఆచరణాత్మక కేసును చూశాము మరియు ఏమి చేయవచ్చో దానిలో ఒక చిన్న భాగాన్ని ప్రదర్శించాము. BCC యుటిలిటీలను అభివృద్ధి చేయడంలో మీకు ఆసక్తి ఉంటే, దాన్ని పరిశీలించడం విలువైనదే అధికారిక ట్యుటోరియల్, ఇది ప్రాథమికాలను బాగా వివరిస్తుంది.

eBPF ఆధారంగా ఇతర ఆసక్తికరమైన డీబగ్గింగ్ మరియు ప్రొఫైలింగ్ సాధనాలు ఉన్నాయి. వారిలో వొకరు - bpftrace, ఇది awk-వంటి భాషలో శక్తివంతమైన వన్-లైనర్లు మరియు చిన్న ప్రోగ్రామ్‌లను వ్రాయడానికి మిమ్మల్ని అనుమతిస్తుంది. మరొకటి - ebpf_exporter, తక్కువ-స్థాయి, అధిక-రిజల్యూషన్ కొలమానాలను నేరుగా మీ ప్రోమేథియస్ సర్వర్‌లో సేకరించడానికి మిమ్మల్ని అనుమతిస్తుంది, తర్వాత అందమైన విజువలైజేషన్‌లు మరియు హెచ్చరికలను కూడా పొందగల సామర్థ్యం.

మూలం: www.habr.com

ఒక వ్యాఖ్యను జోడించండి