2012-09-26 13 views
15

मुझे हाल ही में एक कस्टम लिनक्स कर्नेल (2.6.31.5, x86) ड्राइवर में एक समस्या का सामना करना पड़ा जहां copy_to_user समय-समय पर उपयोगकर्ता बाइट को किसी बाइट की प्रतिलिपि नहीं बनायेगा। यह इसे पारित बाइट्स की गिनती वापस कर देगा, जो दर्शाता है कि उसने कुछ भी कॉपी नहीं किया था। कोड निरीक्षण के बाद हमने पाया कि कोड copy_to_user को कॉल करते समय इंटरप्ट को अक्षम कर रहा था जो इसके अनुबंध का उल्लंघन करता है। इसे ठीक करने के बाद, समस्या उत्पन्न हो गई। चूंकि यह मुद्दा इतनी बार हुआ, मुझे यह साबित करने की ज़रूरत है कि बाधाओं को अक्षम करने से समस्या उत्पन्न हुई।क्या होता है जब एक mov निर्देश x86 पर अक्षम इंटरप्ट के साथ पेज गलती का कारण बनता है?

यदि आप आर्क/x86/lib/usercopy_32.c प्रतिनिधि से नीचे कोड स्निपेट देखते हैं; movsl शब्दों को सीएक्स में गिनती से उपयोगकर्ता स्थान पर कॉपी करता है। बाहर निकलने पर सीएक्स के साथ आकार अपडेट किया गया है। यदि movsl सही ढंग से निष्पादित करता है तो सीएक्स 0 होगा। क्योंकि सीएक्स शून्य नहीं है, movs? copy_to_user और मनाए गए व्यवहार की परिभाषा को फिट करने के लिए निर्देशों को निष्पादित नहीं किया जाना चाहिए था।

/* Generic arbitrary sized copy. */ 
#define __copy_user(to, from, size)     \ 
do {         \ 
    int __d0, __d1, __d2;      \ 
    __asm__ __volatile__(      \ 
     " cmp $7,%0\n"     \ 
     " jbe 1f\n"     \ 
     " movl %1,%0\n"     \ 
     " negl %0\n"     \ 
     " andl $7,%0\n"     \ 
     " subl %0,%3\n"     \ 
     "4: rep; movsb\n"     \ 
     " movl %3,%0\n"     \ 
     " shrl $2,%0\n"     \ 
     " andl $3,%3\n"     \ 
     " .align 2,0x90\n"    \ 
     "0: rep; movsl\n"     \ 
     " movl %3,%0\n"     \ 
     "1: rep; movsb\n"     \ 
     "2:\n"       \ 
     ".section .fixup,\"ax\"\n"    \ 
     "5: addl %3,%0\n"     \ 
     " jmp 2b\n"     \ 
     "3: lea 0(%3,%0,4),%0\n"    \ 
     " jmp 2b\n"     \ 
     ".previous\n"      \ 
     ".section __ex_table,\"a\"\n"    \ 
     " .align 4\n"     \ 
     " .long 4b,5b\n"     \ 
     " .long 0b,3b\n"     \ 
     " .long 1b,2b\n"     \ 
     ".previous"      \ 
     : "=&c"(size), "=&D" (__d0), "=&S" (__d1), "=r"(__d2) \ 
     : "3"(size), "0"(size), "1"(to), "2"(from)  \ 
     : "memory");      \ 
} while (0) 

2 विचारों है कि मैं कर रहे हैं:

  1. जब बीच में आता है अक्षम हैं, पृष्ठ दोष नहीं होती है और तो प्रतिनिधि; movs? कुछ भी किए बिना छोड़ दिया गया है। वापसी मूल्य तब सीएक्स होगा, या उपयोगकर्ता स्पेस की प्रतिलिपि नहीं की गई राशि, क्योंकि परिभाषा निर्दिष्ट करती है और व्यवहार देखा जाता है।
  2. पेज गलती होती है, लेकिन लिनक्स इसे संसाधित नहीं कर सकता क्योंकि इंटरप्ट अक्षम हैं, इसलिए पेज गलती हैंडलर निर्देश छोड़ देता है, हालांकि मुझे नहीं पता कि पेज गलती हैंडलर यह कैसे करेगा। फिर, इस मामले में सीएक्स अनमोडिफाइड रहेगा और वापसी मूल्य सही होगा।

क्या कोई मुझे इंटेल मैनुअल में सेक्शन में इंगित कर सकता है जो इस व्यवहार को निर्दिष्ट करता है, या मुझे किसी भी अतिरिक्त लिनक्स स्रोत को इंगित करता है जो उपयोगी हो सकता है?

+0

आप उल्लेख करते हैं कि "कोड इंटरप्ट अक्षम कर रहा था"। क्या आप विस्तारित कर सकते हैं कि कौन सा इंटरप्ट करता है और कैसे? ... – TheCodeArtist

+0

@TheCodeArtist: write_lock_bh(); आयोजित किया गया था, जो मेरी समझ से सॉफ्टवेयर इंटरप्ट अक्षम करता है। – Edward

+0

@TheCodeArtist: धन्यवाद! आपकी टिप्पणी ने मुझे write_lock_bh() में बहुत अधिक बारीकी से देखा, मुझे रास्ता दिखा रहा है! – Edward

उत्तर

7

मैं इस सवाल का जवाब मिल गया है। मेरा # 2 सुझाव सही था और मेरे चेहरे के सामने तंत्र सही था। पृष्ठ गलती होती है, लेकिन fixup_exception तंत्र का उपयोग अपवाद/जारी रखने के तंत्र के लिए किया जाता है। इस अनुभाग में अपवाद संचालक मेज पर प्रविष्टियों को जोड़ता है:

".section __ex_table,\"a\"\n"    \ 
    " .align 4\n"     \ 
    " .long 4b,5b\n"     \ 
    " .long 0b,3b\n"     \ 
    " .long 1b,6b\n"     \ 
    ".previous"      \ 

यह कहते हैं: आईपी पते पहली प्रविष्टि है और एक अपवाद एक गलती हैंडलर में सामना करना पड़ा है, तो दूसरे का पता करने के लिए आईपी पते की स्थापना की और जारी है।

तो यदि अपवाद "4:" पर होता है, तो "5:" पर जाएं। यदि अपवाद "0:" पर होता है तो "3:" पर जाएं और यदि अपवाद "1:" पर "6:" पर होता है।() चाप में/86/mm/fault.c

गायब टुकड़ा do_page_fault में है:

/* 
* If we're in an interrupt, have no user context or are running 
* in an atomic region then we must not take the fault: 
*/ 
if (unlikely(in_atomic() || !mm)) { 
    bad_area_nosemaphore(regs, error_code, address); 
    return; 
} 

in_atomic सच लौट आए क्योंकि हम एक write_lock_bh() ताला में हैं! bad_area_nosemaphore अंततः फिक्सअप करता है।

यदि कोई पेज_फॉल्ट होता है (जो काम करने की जगह की अवधारणा के कारण असंभव था) तो फ़ंक्शन कॉल विफल हो जाएगी और __copy_user मैक्रो से बाहर निकल जाएगा, बिना सेट किए गए बाइट्स आकार के सेट पर हैं क्योंकि प्रीम्प्शन अक्षम किया गया था।

4

पृष्ठ दोष मास्क-सक्षम इंटरप्ट नहीं हैं। वास्तव में, वे तकनीकी रूप से बाधा नहीं डाल रहे हैं - बल्कि अपवाद हैं, हालांकि मैं मानता हूं कि अंतर अधिक अर्थपूर्ण है।

आपके copy_to_user विफल होने के कारण जब आप इसे निष्क्रिय इंटरप्ट के साथ परमाणु संदर्भ में बुलाते हैं तो यह है कि कोड के लिए इसकी स्पष्ट जांच है।

देखें http://lxr.free-electrons.com/source/arch/x86/lib/usercopy_32.c#L575

+0

आपके उत्तर के लिए धन्यवाद। कॉल ज्यादातर समय काम किया। यह केवल बहुत ही कम विफल रहा। अगर यह परमाणु संदर्भ के कारण था, तो मैं हमेशा यह विफल होने की उम्मीद करता हूं। वैसे भी उस स्थिति को पेंटियम पर निष्पादित नहीं किया जाना चाहिए। , [लिनस के अनुसार] (http://answers.softpicks.net/answers/topic/-BUG-__copy_to_user_inatomic-broken-on-non-Pentium-machines-2056019-1.htm) boot_cpu_data.wp_works_ok चाहिए == 0 चालू 386 से अधिक सब कुछ। – Edward

संबंधित मुद्दे