كيف يمكنني استخدام spinlocks على إدخالات قائمة داخل kernel Linux؟

StackOverflow https://stackoverflow.com/questions/3610253

  •  25-09-2019
  •  | 
  •  

سؤال

أنا أقوم بتطوير تصحيح ل kernel Linux. لا بد لي من استخدام العديد من القوائم وعلي حمايةها من التعديل المتزامن على آلة متعددة النطاق. أحاول استخدام Spinlocks لهذا الهدف ، ولكن هناك شيء لا يمكنني فهمه. لا بد لي من قفل إدخالات القائمة (أنا أستخدم تنفيذ Linux الافتراضي للقوائم المرتبطة) ويمكن أن يحدث أن تقوم العملية بتزويد بإزالة عنصر واحد من القائمة أثناء نفس العنصر الذي تم قفله لأن بعض التعديل هو في الواقع يجري صنعها. إذا قمت بإدراج تدور داخل إدخال القائمة ، فما الذي يحدث إذا تمكنت العملية من إزالتها أثناء تدوير شخص ما عليه ؟؟ هل يجب أن أقفل القائمة بأكملها؟ أنا أبحث عن جزء من الكود يمكن أن يفسر كيفية التعامل مع هذا الموقف.

على سبيل المثال ، لا ينبغي أن يعمل هذا الرمز (انظر التعليق على السطر الأخير من الكود):

   struct lista{
    int c;
    spinlock_t lock;
    struct list_head;
}

spinlock_t list_lock;
struct lista lista;


//INSERT
struct lista* cursor;
struct lista* new = (struct lista*) kmalloc(sizeof(struct lista),GFP_KERNEL);

/*do something*/
spin_lock(&list_lock);     //Lock on the whole list
list_for_each_entry(cursor,&lista.list,list){
    if (cursor->c == something ){
        ...
        spin_unlock(&list_lock)  //unlock
        spin_lock(&cursor->lock) // Lock on list entry
        list_add(&new->list, &lista.list);
        spin_unlock(&cursor->lock)  // unlock of the list entry
        ...
    }
}


//REMOVAL
struct lista* cursor;

spin_lock(&list_lock);  
list_for_each_entry(cursor,&lista.list,list){
    if (cursor->c == something ){
        ...
        spin_unlock(&list_lock)  //unlock
        spin_lock(&cursor->lock) // Lock on list entry
        list_del(&cursor.list,&lista.list);
        spin_unlock(&cursor->lock)  // unlock of the list entry
        kfree(cursor);  //WHEN THE ENTRY IS FREED SOMEONE COULD HAVE TAKEN THE LOCK SINCE IT IS UNLOCKED
        ...
    }
}

هل يمكنك مساعدتي؟؟

هل كانت مفيدة؟

المحلول

لا تطلق list_lock حتى تنتهي من إزالة العنصر.

قد ينتهي بك الأمر مع الإجراء المحرج قليلاً من:

  1. الحصول على قفل القائمة (سيؤدي هذا إلى حظر المواضيع الواردة الأخرى)
  2. الحصول على قفل العنصر ، قفل عنصر الإصدار (هذا يضمن أن جميع المواضيع السابقة تتم)
  3. إزالة بند
  4. قفل قائمة الإصدار.

التباين: استخدم قفل القارئ للكتابة لقفل القائمة.

المواضيع التي تتطلع إلى تعديل عناصر القائمة تأخذ قفل القارئ ؛ هذا يسمح لخيوط متعددة بالعمل في القائمة بالتوازي.

المواضيع التي تتطلع إلى إزالة عناصر القائمة تأخذ قفل الكاتب ؛ هذا ينتظر أن يخرج جميع القراء وحظرهم حتى تصدره. في هذه الحالة ، لا يزال يتعين عليك الاحتفاظ بقفل القائمة حتى تنتهي من إزالة العنصر ،

وبهذه الطريقة يمكنك تجنب الخطوة 2 أعلاه. قد يبدو هذا أكثر وضوحًا من الناحية المفاهيمية ، حيث لا تحتاج إلى شرح القفل/الإصدار الذي لا معنى له.

نصائح أخرى

من المؤكد أنك لا ينبغي أن تستخدم spinlocks على الإطلاق ، ما لم يكن هناك وصول متزامن من سياق IRQ الصعب. استخدم Mutexes بدلاً من ذلك.

الخيار الأسهل في قائمتك هو قفل القائمة بأكملها أثناء العمل عليها. لا تقلق بشأن أقفال لكل عنصر ما لم تجد أن هناك خلافًا كافيًا على قفل القائمة الذي تحتاجه (وفي هذه الحالة ، ربما تريد أن تنظر في استخدام RCU بدلاً من ذلك ، على أي حال).

لا يحتاج رأس قائمتك إلى أن يكون struct lista, ، يجب أن يكون مجرد struct list_head. لاحظ أنك تستمر في استخدامها &lista.list, ، يجب أن يكون مجرد أ list_head تسمى "قائمة" أو شيء من هذا القبيل. انظر على سبيل المثال الرمز في drivers/pci/msi.c, ، لاحظ أن dev->msi_list ما هو الا list_head, ، ليس أ struct msi_desc.

لا يمكنك إسقاط قفل القائمة بأمان ثم الاستيلاء على قفل المؤشر. من الممكن أنه بعد إسقاط قفل القائمة ولكن قبل أن تحصل على قفل المؤشر ، جاء شخص آخر وحرر مؤشرك. يمكنك التوفيق بين الأقفال ، لكن هذا جداً من السهل أن تخطئ.

من المؤكد أنك بالتأكيد تريد قفلًا واحدًا ، وللأغنية بأكملها ، ولا توجد أقفال لكل عنصر. ويجب أن يكون قفل القائمة mutex ما لم تكن بحاجة إلى معالجة القائمة من سياق المقاطعة.

إذا كنت لا تتعامل مع الأجهزة و / أو الأقسام الحرجة من kernel حيث يكون قفل الدوران أمرًا ضروريًا (لأنه يعطل الاستباق والمقاطعة (بناءً على الطلب)) ، فإن y 2 استخدم أقفال الدوران التي ستغلق بشكل غير ضروري من الاستمتاع والمقاطعات . استخدام Semaphore أو Mutex الذي أيضًا في القائمة وليس القائمة يبدو أفضل حلًا.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top