تطوير كود Kernel لـ GKI

تعمل صورة النواة العامة (GKI) على تقليل تجزئة النواة من خلال مواءمتها بشكل وثيق مع نواة Linux الأولية. ومع ذلك، هناك أسباب وجيهة لعدم قبول بعض التصحيحات في المراحل الأولية، وهناك جداول زمنية للمنتج يجب الوفاء بها، لذلك يتم الاحتفاظ ببعض التصحيحات في مصادر Android Common Kernel (ACK) التي تم إنشاء GKI منها.

يجب على المطورين إرسال تغييرات التعليمات البرمجية إلى المنبع باستخدام القائمة البريدية لـ Linux Kernel (LKML) كخيار أول، وإرسال تغييرات التعليمات البرمجية إلى فرع ACK android-mainline فقط عندما يكون هناك سبب قوي لعدم جدوى المنبع. يتم سرد أمثلة على الأسباب الصحيحة وكيفية التعامل معها على النحو التالي.

  • تم إرسال التصحيح إلى LKML، ولكن لم يتم قبوله في الوقت المناسب لإصدار المنتج. للتعامل مع هذا التصحيح:

    • قم بتقديم دليل على أن التصحيح قد تم إرساله إلى LKML والتعليقات المستلمة بشأن التصحيح، أو الوقت المقدر الذي سيتم خلاله إرسال التصحيح إلى المنبع.
    • حدد مسار العمل لوضع التصحيح في ACK، والحصول على الموافقة عليه من المنبع، ثم إخراجه من ACK عندما يتم دمج الإصدار النهائي من المنبع في ACK.
  • يحدد التصحيح EXPORT_SYMBOLS_GPL() لوحدة البائع، ولكن لا يمكن إرساله إلى المنبع لأنه لا توجد وحدات داخل الشجرة تستهلك هذا الرمز. للتعامل مع هذا التصحيح، قم بتقديم تفاصيل حول سبب عدم إمكانية إرسال الوحدة الخاصة بك إلى المنبع والبدائل التي أخذتها في الاعتبار قبل تقديم هذا الطلب.

  • التصحيح ليس عامًا بما يكفي للإصدار الأولي ولا يوجد وقت لإعادة هيكلته قبل إصدار المنتج. للتعامل مع هذا التصحيح، قم بتوفير الوقت المقدر الذي سيتم خلاله إرسال التصحيح المُعاد تصنيعه إلى المنبع (لن يتم قبول التصحيح في ACK بدون خطة لإرسال تصحيح معاد تصنيعه للمراجعة).

  • لا يمكن قبول التصحيح بواسطة المنبع لأن... <أدخل السبب هنا> . للتعامل مع هذا التصحيح، تواصل مع فريق Android kernel واعمل معنا على خيارات إعادة بناء التصحيح بحيث يمكن إرساله للمراجعة وقبوله في المراحل الأولية.

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

متطلبات التصحيح

يجب أن تتوافق التصحيحات مع معايير ترميز Linux kernel الموضحة في شجرة مصدر Linux ، سواء تم إرسالها إلى المنبع أو إلى ACK. يتم تشغيل البرنامج النصي scripts/checkpatch.pl كجزء من اختبار الإرسال المسبق لـ Gerrit، لذا قم بتشغيله مسبقًا للتأكد من اجتيازه. لتشغيل البرنامج النصي checkpatch بنفس التكوين مثل اختبار الإرسال المسبق، استخدم //build/kernel/static_analysis:checkpatch_presubmit . للحصول على التفاصيل، راجع build/kernel/kleaf/docs/checkpatch.md .

بقع ACK

يجب أن تتوافق التصحيحات المقدمة إلى ACK مع معايير ترميز Linux kernel وإرشادات المساهمة . يجب عليك تضمين علامة Change-Id في رسالة الالتزام؛ إذا قمت بإرسال التصحيح إلى فروع متعددة (على سبيل المثال، android-mainline و android12-5.4 )، فيجب عليك استخدام نفس Change-Id لجميع مثيلات التصحيح.

قم بإرسال التصحيحات إلى LKML أولاً لإجراء مراجعة أولية. إذا كان التصحيح:

  • تم قبوله في المنبع، ويتم دمجه تلقائيًا في android-mainline .
  • لم يتم قبوله من المنبع، قم بإرساله إلى android-mainline مع الإشارة إلى الإرسال الأولي أو شرح سبب عدم إرساله إلى LKML.

بعد قبول التصحيح إما في المنبع أو في android-mainline ، يمكن إعادته إلى ACK المناسب المستند إلى LTS (مثل android12-5.4 و android11-5.4 للتصحيحات التي تعمل على إصلاح التعليمات البرمجية الخاصة بنظام Android). يؤدي الإرسال إلى android-mainline إلى تمكين الاختبار مع المرشحين الجدد للإصدار الأولي ويضمن وجود التصحيح في ACK التالي المستند إلى LTS. تتضمن الاستثناءات الحالات التي يتم فيها نقل التصحيح الأولي إلى android12-5.4 (لأن التصحيح من المحتمل أن يكون موجودًا بالفعل في android-mainline ).

بقع المنبع

كما هو محدد في إرشادات المساهمة ، تقع التصحيحات الأولية المخصصة لنواة ACK ضمن المجموعات التالية (مدرجة حسب احتمالية قبولها).

  • UPSTREAM: - من المحتمل أن يتم قبول التصحيحات المنتقاة من "android-mainline" في ACK إذا كانت هناك حالة استخدام معقولة.
  • BACKPORT: - من المحتمل أيضًا أن يتم قبول التصحيحات من المنبع التي لا يتم انتقاءها بشكل نظيف وتحتاج إلى تعديل إذا كانت هناك حالة استخدام معقولة.
  • FROMGIT: - قد يتم قبول التصحيحات المنتقاة من فرع المشرف استعدادًا لتقديمها إلى خط Linux الرئيسي إذا كان هناك موعد نهائي قادم. ويجب تبريرها من حيث المحتوى والجدول الزمني.
  • FROMLIST: - التصحيحات التي تم إرسالها إلى LKML ولكن لم يتم قبولها في فرع الصيانة حتى الآن من غير المرجح أن يتم قبولها، ما لم يكن المبرر مقنعًا بدرجة كافية بحيث يتم قبول التصحيح سواء وصل إلى Linux الأساسي أم لا (نحن نفترض أنه لن يحدث). يجب أن تكون هناك مشكلة مرتبطة بتصحيحات FROMLIST لتسهيل المناقشة مع فريق Android kernel.

التصحيحات الخاصة بنظام Android

إذا لم تتمكن من إجراء التغييرات المطلوبة في المنبع، فيمكنك محاولة إرسال تصحيحات خارج الشجرة إلى ACK مباشرةً. يتطلب إرسال التصحيحات خارج الشجرة إنشاء مشكلة في قسم تكنولوجيا المعلومات تشير إلى التصحيح والسبب المنطقي وراء عدم إمكانية إرسال التصحيح إلى المنبع (راجع القائمة السابقة للحصول على أمثلة). ومع ذلك، هناك بعض الحالات التي لا يمكن فيها إرسال الكود إلى المنبع. يتم تناول هذه الحالات على النحو التالي ويجب أن تتبع إرشادات المساهمة الخاصة بالتصحيحات الخاصة بنظام Android وأن يتم وضع علامة عليها ببادئة ANDROID: في الموضوع.

التغييرات على gki_defconfig

يجب تطبيق جميع تغييرات CONFIG على gki_defconfig على الإصدارين Arm64 وx86 ما لم يكن CONFIG خاصًا بالبنية. لطلب تغيير إعداد CONFIG ، قم بإنشاء مشكلة في قسم تكنولوجيا المعلومات لمناقشة التغيير. يتم رفض أي تغيير CONFIG يؤثر على واجهة وحدة Kernel (KMI) بعد تجميدها. في الحالات التي يطلب فيها الشركاء إعدادات متضاربة لتكوين واحد، نقوم بحل التعارضات من خلال مناقشة الأخطاء ذات الصلة.

رمز غير موجود في المنبع

لا يمكن إرسال التعديلات على التعليمات البرمجية الخاصة بنظام Android مسبقًا. على سبيل المثال، على الرغم من الحفاظ على برنامج تشغيل الموثق في المنبع، لا يمكن إرسال التعديلات على ميزات الوراثة ذات الأولوية لبرنامج تشغيل الموثق في المنبع لأنها خاصة بنظام Android. كن واضحًا في الخطأ الخاص بك وقم بتصحيح سبب عدم إمكانية إرسال الرمز إلى المنبع. إذا كان ذلك ممكنًا، قم بتقسيم التصحيحات إلى أجزاء يمكن إرسالها إلى المنبع وأجزاء خاصة بنظام Android لا يمكن إرسالها إلى المنبع لتقليل مقدار التعليمات البرمجية خارج الشجرة التي يتم الاحتفاظ بها في ACK.

التغييرات الأخرى في هذه الفئة هي تحديثات لملفات تمثيل KMI، أو قوائم رموز KMI، أو gki_defconfig ، أو إنشاء البرامج النصية أو التكوين، أو البرامج النصية الأخرى غير الموجودة في المنبع.

وحدات خارج الشجرة

لا يشجع Upstream Linux بشكل فعال دعم إنشاء وحدات خارج الشجرة. يعد هذا موقفًا معقولًا نظرًا لأن مشرفي Linux لا يقدمون ضمانات بشأن المصدر داخل النواة أو التوافق الثنائي ولا يريدون دعم التعليمات البرمجية غير الموجودة في الشجرة. ومع ذلك، فإن GKI تقدم ضمانات ABI لوحدات البائع، مما يضمن استقرار واجهات KMI طوال العمر المدعوم للنواة. لذلك، هناك فئة من التغييرات لدعم وحدات البائع المقبولة لـ ACK ولكنها غير مقبولة للمنبع.

على سبيل المثال، فكر في التصحيح الذي يضيف وحدات ماكرو EXPORT_SYMBOL_GPL() حيث لا تكون الوحدات النمطية التي تستخدم التصدير في الشجرة المصدر. بينما يجب عليك محاولة طلب EXPORT_SYMBOL_GPL() ‎ في المنبع وتوفير وحدة تستخدم الرمز الذي تم تصديره حديثًا، إذا كان هناك مبرر صالح لعدم إرسال الوحدة في المنبع، فيمكنك إرسال التصحيح إلى ACK بدلاً من ذلك. يجب عليك تضمين مبرر سبب عدم إمكانية نقل الوحدة إلى المصدر في المشكلة. (لا تطلب متغيرًا غير تابع لـ GPL، EXPORT_SYMBOL() .)

التكوينات المخفية

تقوم بعض الوحدات داخل الشجرة تلقائيًا بتحديد التكوينات المخفية التي لا يمكن تحديدها في gki_defconfig . على سبيل المثال، يتم تحديد CONFIG_SND_SOC_TOPOLOGY تلقائيًا عند تكوين CONFIG_SND_SOC_SOF=y . لاستيعاب بناء الوحدات خارج الشجرة، تتضمن GKI آلية لتمكين التكوينات المخفية.

لتمكين التكوين المخفي، قم بإضافة عبارة select في init/Kconfig.gki بحيث يتم تحديده تلقائيًا بناءً على تكوين kernel CONFIG_GKI_HACKS_TO_FIX ، والذي تم تمكينه في gki_defconfig . استخدم هذه الآلية فقط للتكوينات المخفية؛ إذا لم يكن التكوين مخفيًا، فيجب تحديده في gki_defconfig إما بشكل صريح أو كتبعية.

حكام قابلة للتحميل

بالنسبة لأطر عمل kernel (مثل cpufreq ) التي تدعم أدوات التحكم القابلة للتحميل، يمكنك تجاوز أدوات التحكم الافتراضية (مثل أدوات التحكم schedutil لـ cpufreq . بالنسبة لأطر العمل (مثل الإطار الحراري) التي لا تدعم أدوات التحكم أو برامج التشغيل القابلة للتحميل ولكنها لا تزال تتطلب التنفيذ الخاص بالمورد، وإنشاء مشكلة في تكنولوجيا المعلومات والتشاور مع فريق Android kernel .

سنعمل معك ومع المشرفين على المنبع لإضافة الدعم اللازم.

خطاف البائع

في الإصدارات السابقة، كان بإمكانك إضافة تعديلات خاصة بالمورد مباشرة إلى النواة الأساسية. هذا غير ممكن مع GKI 2.0 لأنه يجب تنفيذ التعليمات البرمجية الخاصة بالمنتج في وحدات ولن يتم قبولها في النواة الأساسية المنبع أو في ACK. لتمكين ميزات القيمة المضافة التي يعتمد عليها الشركاء مع الحد الأدنى من التأثير على كود kernel الأساسي، يقبل GKI خطافات البائع التي تسمح باستدعاء الوحدات من كود kernel الأساسي. بالإضافة إلى ذلك، يمكن تعبئة هياكل البيانات الرئيسية بحقول بيانات البائع المتوفرة لتخزين البيانات الخاصة بالمورد لتنفيذ هذه الميزات.

تأتي خطافات البائع في نوعين مختلفين (عادي ومقيد) يعتمدان على نقاط التتبع (وليس أحداث التتبع) التي يمكن أن ترتبط بها وحدات البائع. على سبيل المثال، بدلاً من إضافة وظيفة sched_exit() جديدة لإجراء المحاسبة عند الخروج من المهمة، يمكن للبائعين إضافة خطاف في do_exit() يمكن لوحدة البائع إرفاقه للمعالجة. يتضمن تطبيق المثال خطافات البائع التالية.

  • تستخدم خطافات البائع العادية DECLARE_HOOK() لإنشاء دالة تتبع بالاسم trace_ name حيث يكون name هو المعرف الفريد للتتبع. وفقًا للاتفاقية، تبدأ أسماء ربط الموردين العادية بـ android_vh ، لذا فإن اسم خطاف sched_exit() سيكون android_vh_sched_exit .
  • هناك حاجة إلى خطوط ربط البائع المقيدة لحالات مثل خطافات الجدولة حيث يجب استدعاء الوظيفة المرفقة حتى إذا كانت وحدة المعالجة المركزية غير متصلة بالإنترنت أو تتطلب سياقًا غير ذري. لا يمكن فصل خطافات البائع المقيدة، لذلك لا يمكن أبدًا إلغاء تحميل الوحدات المرتبطة بخطاف مقيد. تبدأ أسماء ربط البائعين المقيدة بـ android_rvh .

لإضافة رابط بائع، قم بتقديم مشكلة إلى قسم تكنولوجيا المعلومات وإرسال تصحيحات (كما هو الحال مع جميع التصحيحات الخاصة بنظام Android، يجب أن تكون هناك مشكلة ويجب عليك تقديم مبرر). يتوفر دعم خطافات البائع فقط في ACK، لذا لا ترسل هذه التصحيحات إلى Linux الأساسي.

إضافة حقول البائع إلى الهياكل

يمكنك ربط بيانات البائع بهياكل البيانات الرئيسية عن طريق إضافة حقول android_vendor_data باستخدام وحدات الماكرو ANDROID_VENDOR_DATA() . على سبيل المثال، لدعم ميزات القيمة المضافة، قم بإلحاق الحقول بالبنيات كما هو موضح في نموذج التعليمات البرمجية التالي.

لتجنب التعارضات المحتملة بين الحقول التي يحتاجها البائعون والحقول التي يحتاجها مصنعو المعدات الأصلية، يجب على مصنعي المعدات الأصلية ألا يستخدموا أبدًا الحقول المعلنة باستخدام وحدات ماكرو ANDROID_VENDOR_DATA() . بدلاً من ذلك، يجب على مصنعي المعدات الأصلية استخدام ANDROID_OEM_DATA() لإعلان حقول android_oem_data .

#include <linux/android_vendor.h>
...
struct important_kernel_data {
  [all the standard fields];
  /* Create vendor data for use by hook implementations. The
   * size of vendor data is based on vendor input. Vendor data
   * can be defined as single u64 fields like the following that
   * declares a single u64 field named "android_vendor_data1" :
   */
  ANDROID_VENDOR_DATA(1);

  /*
   * ...or an array can be declared. The following is equivalent to
   * u64 android_vendor_data2[20]:
   */
  ANDROID_VENDOR_DATA_ARRAY(2, 20);

  /*
   * SoC vendors must not use fields declared for OEMs and
   * OEMs must not use fields declared for SoC vendors.
   */
  ANDROID_OEM_DATA(1);

  /* no further fields */
}

تحديد خطافات البائع

قم بإضافة خطافات البائع إلى كود kernel كنقاط تتبع عن طريق الإعلان عنها باستخدام DECLARE_HOOK() أو DECLARE_RESTRICTED_HOOK() ثم إضافتها إلى الكود كنقطة تتبع. على سبيل المثال، لإضافة trace_android_vh_sched_exit() إلى وظيفة kernel الموجودة do_exit() :

#include <trace/hooks/exit.h>
void do_exit(long code)
{
    struct task_struct *tsk = current;
    ...
    trace_android_vh_sched_exit(tsk);
    ...
}

تتحقق الدالة trace_android_vh_sched_exit() في البداية فقط من وجود شيء مرفق. ومع ذلك، إذا قامت وحدة البائع بتسجيل معالج باستخدام register_trace_android_vh_sched_exit() ، فسيتم استدعاء الوظيفة المسجلة. يجب أن يكون المعالج على علم بالسياق فيما يتعلق بالأقفال المعلقة وحالة RCS وعوامل أخرى. يجب تعريف الخطاف في ملف رأس في دليل include/trace/hooks .

على سبيل المثال، التعليمة البرمجية التالية تعطي تعريفًا محتملاً لـ trace_android_vh_sched_exit() في الملف include/trace/hooks/exit.h .

/* SPDX-License-Identifier: GPL-2.0 */
#undef TRACE_SYSTEM
#define TRACE_SYSTEM sched
#define TRACE_INCLUDE_PATH trace/hooks

#if !defined(_TRACE_HOOK_SCHED_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_HOOK_SCHED_H
#include <trace/hooks/vendor_hooks.h>
/*
 * Following tracepoints are not exported in tracefs and provide a
 * mechanism for vendor modules to hook and extend functionality
 */

struct task_struct;

DECLARE_HOOK(android_vh_sched_exit,
             TP_PROTO(struct task_struct *p),
             TP_ARGS(p));

#endif /* _TRACE_HOOK_SCHED_H */

/* This part must be outside protection */
#include <trace/define_trace.h>

لإنشاء مثيل للواجهات المطلوبة لربط البائع، قم بإضافة ملف الرأس مع إعلان الربط إلى drivers/android/vendor_hooks.c وقم بتصدير الرموز. على سبيل المثال، التعليمة البرمجية التالية تكمل تعريف الخطاف android_vh_sched_exit() .

#ifndef __GENKSYMS__
/* struct task_struct */
#include <linux/sched.h>
#endif

#define CREATE_TRACE_POINTS
#include <trace/hooks/vendor_hooks.h>
#include <trace/hooks/exit.h>
/*
 * Export tracepoints that act as a bare tracehook (i.e. have no trace
 * event associated with them) to allow external modules to probe
 * them.
 */
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_sched_exit);

ملاحظة : يجب تعريف بنيات البيانات المستخدمة ضمن تعريف الخطاف بشكل كامل لضمان استقرار ABI. وإلا فإنه من غير الآمن إلغاء الإشارة إلى المؤشرات غير الشفافة أو استخدام البنية في سياقات ذات حجم. يجب أن يتم وضع التضمين الذي يوفر التعريف الكامل لهياكل البيانات هذه داخل قسم #ifndef __GENKSYMS__ في drivers/android/vendor_hooks.c . يجب ألا تتضمن ملفات الرأس الموجودة في include/trace/hooks ملف رأس kernel مع تعريفات النوع لتجنب تغييرات CRC التي تؤدي إلى تعطيل KMI. بدلا من ذلك قم بإعلان الأنواع.

نعلق على السنانير البائع

لاستخدام خطافات البائع، تحتاج وحدة البائع إلى تسجيل معالج للخطاف (يتم ذلك عادةً أثناء تهيئة الوحدة). على سبيل المثال، يُظهر التعليمة البرمجية التالية معالج الوحدة foo.ko لـ trace_android_vh_sched_exit() .

#include <trace/hooks/sched.h>
...
static void foo_sched_exit_handler(void *data, struct task_struct *p)
{
    foo_do_exit_accounting(p);
}
...
static int foo_probe(..)
{
    ...
    rc = register_trace_android_vh_sched_exit(foo_sched_exit_handler, NULL);
    ...
}

ميزات النواة الأساسية

إذا لم تمكنك أي من التقنيات السابقة من تنفيذ ميزة من وحدة، فيجب عليك إضافة الميزة كتعديل خاص بنظام Android إلى النواة الأساسية. أنشئ مشكلة في أداة تعقب المشكلات (IT) لبدء المحادثة.

واجهة برمجة تطبيقات المستخدم (UAPI)

  • ملفات رأس UAPI. يجب أن تحدث التغييرات في ملفات رأس UAPI في المراحل الأولية ما لم تكن التغييرات على واجهات خاصة بنظام Android. استخدم ملفات الرأس الخاصة بالمورد لتحديد الواجهات بين وحدات البائع ورمز مساحة مستخدم البائع.
  • العقد sysfs. لا تقم بإضافة عقد sysfs جديدة إلى نواة GKI (مثل هذه الإضافات صالحة فقط في وحدات البائع). لا يمكن تغيير عقد sysfs التي تستخدمها مكتبات SoC والمكتبات الحيادية للجهاز ورمز Java الذي يشتمل على إطار عمل Android إلا بطرق متوافقة ويجب تغييرها في المنبع إذا لم تكن عقد sysfs خاصة بنظام Android. يمكنك إنشاء عقد sysfs خاصة بالمورد لاستخدامها بواسطة مساحة مستخدم البائع. بشكل افتراضي، يتم رفض الوصول إلى عقد sysfs بواسطة مساحة المستخدم باستخدام SELinux. الأمر متروك للمورد لإضافة ملصقات SELinux المناسبة للسماح بالوصول عن طريق برنامج البائع المعتمد.
  • العقد DebugFS. يمكن لوحدات البائع تحديد العقد في debugfs لتصحيح الأخطاء فقط (حيث لا يتم تركيب debugfs أثناء التشغيل العادي للجهاز).