Phát triển mã hạt nhân cho GKI

Hình ảnh hạt nhân chung (GKI) làm giảm sự phân mảnh hạt nhân bằng cách căn chỉnh chặt chẽ với hạt nhân Linux ngược dòng. Tuy nhiên, có những lý do chính đáng khiến một số bản vá không thể được chấp nhận ngược dòng và có những lịch trình sản phẩm phải được đáp ứng, vì vậy một số bản vá được duy trì trong nguồn Hạt nhân chung của Android (ACK) mà GKI được xây dựng từ đó.

Các nhà phát triển phải gửi các thay đổi mã ngược dòng bằng cách sử dụng Danh sách gửi thư hạt nhân Linux (LKML) làm lựa chọn đầu tiên và chỉ gửi các thay đổi mã tới nhánh ACK android-mainline khi có lý do chính đáng khiến việc ngược dòng không khả thi. Ví dụ về các lý do chính đáng và cách xử lý chúng được liệt kê như sau.

  • Bản vá đã được gửi tới LKML nhưng không được chấp nhận kịp thời để phát hành sản phẩm. Để xử lý bản vá này:

    • Cung cấp bằng chứng cho thấy bản vá đã được gửi tới LKML và nhận được nhận xét về bản vá hoặc thời gian ước tính mà bản vá sẽ được gửi ngược dòng.
    • Quyết định một quá trình hành động để đưa bản vá vào ACK, phê duyệt bản vá ngược dòng và sau đó đưa nó ra khỏi ACK khi phiên bản ngược dòng cuối cùng được hợp nhất thành ACK.
  • Bản vá xác định EXPORT_SYMBOLS_GPL() cho mô-đun nhà cung cấp nhưng không thể gửi ngược dòng vì không có mô-đun trong cây nào sử dụng ký hiệu đó. Để xử lý bản vá này, hãy cung cấp thông tin chi tiết về lý do tại sao không thể gửi mô-đun ngược dòng của bạn và các giải pháp thay thế mà bạn đã cân nhắc trước khi đưa ra yêu cầu này.

  • Bản vá không đủ chung cho thượng nguồn và không có thời gian để cấu trúc lại nó trước khi phát hành sản phẩm. Để xử lý bản vá này, hãy cung cấp thời gian ước tính để bản vá được tái cấu trúc được gửi ngược dòng (bản vá sẽ không được chấp nhận trong ACK nếu không có kế hoạch gửi bản vá được tái cấu trúc ngược dòng để xem xét).

  • Bản vá không thể được chấp nhận bởi thượng nguồn vì... <chèn lý do vào đây> . Để xử lý bản vá này, hãy liên hệ với nhóm nhân Android và làm việc với chúng tôi về các tùy chọn để cấu trúc lại bản vá để có thể gửi bản vá đó đi xem xét và chấp nhận ngược dòng.

Có rất nhiều lời biện minh tiềm năng hơn. Khi bạn gửi lỗi hoặc bản vá của mình, hãy đưa ra lời giải thích hợp lý và mong đợi một số lần lặp lại và thảo luận. Chúng tôi nhận thấy rằng ACK sẽ có một số bản vá, đặc biệt là trong giai đoạn đầu của GKI trong khi mọi người đang học cách làm việc ngược dòng nhưng không thể nới lỏng lịch trình sản phẩm để làm như vậy. Dự kiến ​​các yêu cầu ngược dòng sẽ trở nên nghiêm ngặt hơn theo thời gian.

Yêu cầu bản vá

Các bản vá phải tuân theo các tiêu chuẩn mã hóa nhân Linux được mô tả trong cây nguồn Linux , cho dù chúng được gửi ngược dòng hay tới ACK. Tập lệnh scripts/checkpatch.pl được chạy như một phần của thử nghiệm gửi trước Gerrit, vì vậy hãy chạy nó trước để đảm bảo nó vượt qua. Để chạy tập lệnh checkpatch có cùng cấu hình với thử nghiệm gửi trước, hãy sử dụng //build/kernel/static_analysis:checkpatch_presubmit . Để biết chi tiết, xem build/kernel/kleaf/docs/checkpatch.md .

Bản vá lỗi ACK

Các bản vá được gửi tới ACK phải tuân theo các tiêu chuẩn mã hóa nhân Linux và các nguyên tắc đóng góp . Bạn phải bao gồm thẻ Change-Id trong thông báo cam kết; nếu bạn gửi bản vá tới nhiều nhánh (ví dụ: android-mainlineandroid12-5.4 ), bạn phải sử dụng cùng một Change-Id cho tất cả các phiên bản của bản vá.

Trước tiên hãy gửi các bản vá tới LKML để xem xét ngược dòng. Nếu bản vá là:

  • Được chấp nhận ngược dòng, nó được tự động hợp nhất thành android-mainline .
  • Không được chấp nhận ngược dòng, hãy gửi nó tới android-mainline kèm theo tham chiếu đến lần gửi ngược dòng hoặc giải thích lý do tại sao nó không được gửi tới LKML.

Sau khi một bản vá được chấp nhận ngược dòng hoặc trong android-mainline , bản vá đó có thể được chuyển ngược sang ACK dựa trên LTS thích hợp (chẳng hạn như android12-5.4android11-5.4 cho các bản vá sửa lỗi mã dành riêng cho Android). Việc gửi tới android-mainline cho phép thử nghiệm với các ứng cử viên phát hành ngược dòng mới và đảm bảo rằng bản vá nằm trong ACK dựa trên LTS tiếp theo. Các trường hợp ngoại lệ bao gồm các trường hợp trong đó bản vá ngược dòng được nhập vào android12-5.4 (vì bản vá có thể đã có trong android-mainline ).

Các bản vá ngược dòng

Như được chỉ định trong hướng dẫn đóng góp , các bản vá ngược dòng dành cho hạt nhân ACK thuộc các nhóm sau (được liệt kê theo thứ tự khả năng được chấp nhận).

  • UPSTREAM: - Các bản vá được chọn từ 'android-mainline` có thể được chấp nhận vào ACK nếu có trường hợp sử dụng hợp lý.
  • BACKPORT: - Các bản vá từ thượng nguồn không rõ ràng và cần sửa đổi cũng có khả năng được chấp nhận nếu có trường hợp sử dụng hợp lý.
  • FROMGIT: - Các bản vá được chọn từ một nhánh bảo trì để chuẩn bị gửi lên dòng chính của Linux có thể được chấp nhận nếu có thời hạn sắp tới. Những điều này phải được chứng minh cả về nội dung và tiến độ.
  • FROMLIST: - Các bản vá đã được gửi tới LKML nhưng chưa được chấp nhận vào nhánh bảo trì dường như không được chấp nhận, trừ khi lý do biện minh đủ thuyết phục để bản vá đó sẽ được chấp nhận cho dù nó có xuất hiện trong Linux ngược dòng hay không (chúng tôi giả định rằng nó sẽ không). Phải có vấn đề liên quan đến các bản vá FROMLIST để tạo điều kiện thảo luận với nhóm nhân Android.

Các bản vá dành riêng cho Android

Nếu bạn không thể thực hiện các thay đổi bắt buộc ngược dòng, bạn có thể thử gửi trực tiếp các bản vá lỗi ngoài cây tới ACK. Việc gửi các bản vá ngoài cây yêu cầu bạn tạo ra một sự cố trong CNTT trích dẫn bản vá và lý do tại sao không thể gửi bản vá ngược dòng (xem danh sách trước đó để biết ví dụ). Tuy nhiên, có một số trường hợp không thể gửi mã ngược dòng. Những trường hợp này được đề cập như sau và phải tuân theo nguyên tắc đóng góp cho các bản vá dành riêng cho Android và được gắn thẻ tiền tố ANDROID: trong chủ đề.

Những thay đổi đối với gki_defconfig

Tất cả các thay đổi CONFIG đối với gki_defconfig phải được áp dụng cho cả phiên bản arm64 và x86 trừ khi CONFIG dành riêng cho kiến ​​trúc. Để yêu cầu thay đổi cài đặt CONFIG , hãy tạo một vấn đề trong bộ phận CNTT để thảo luận về thay đổi. Mọi thay đổi CONFIG ảnh hưởng đến Giao diện mô-đun hạt nhân (KMI) sau khi bị đóng băng đều bị từ chối. Trong trường hợp đối tác yêu cầu cài đặt xung đột cho một cấu hình, chúng tôi sẽ giải quyết xung đột thông qua thảo luận về các lỗi liên quan.

Mã không tồn tại ở thượng nguồn

Không thể gửi các sửa đổi đối với mã dành riêng cho Android. Ví dụ: mặc dù trình điều khiển liên kết được duy trì ngược dòng, nhưng các sửa đổi đối với các tính năng kế thừa ưu tiên của trình điều khiển liên kết không thể được gửi ngược dòng vì chúng dành riêng cho Android. Hãy trình bày rõ ràng lỗi của bạn và vá lỗi tại sao mã không thể được gửi ngược dòng. Nếu có thể, hãy chia các bản vá thành các phần có thể gửi ngược dòng và các phần dành riêng cho Android không thể gửi ngược dòng để giảm thiểu số lượng mã ngoài cây được duy trì trong ACK.

Những thay đổi khác trong danh mục này là các bản cập nhật cho tệp biểu diễn KMI, danh sách ký hiệu KMI, gki_defconfig , tập lệnh hoặc cấu hình bản dựng hoặc các tập lệnh khác không tồn tại ở thượng nguồn.

Các mô-đun ngoài cây

Linux ngược dòng tích cực không khuyến khích hỗ trợ xây dựng các mô-đun ngoài cây. Đây là một quan điểm hợp lý vì các nhà bảo trì Linux không đảm bảo về khả năng tương thích nhị phân hoặc nguồn trong nhân và không muốn hỗ trợ mã không có trong cây. Tuy nhiên, GKI đưa ra đảm bảo ABI cho các mô-đun của nhà cung cấp, đảm bảo rằng giao diện KMI ổn định trong suốt vòng đời được hỗ trợ của hạt nhân. Do đó, có một loại thay đổi để hỗ trợ các mô-đun của nhà cung cấp được ACK chấp nhận nhưng không được chấp nhận ở thượng nguồn.

Ví dụ: hãy xem xét một bản vá bổ sung macro EXPORT_SYMBOL_GPL() trong đó các mô-đun sử dụng tính năng xuất không có trong cây nguồn. Mặc dù bạn phải cố gắng yêu cầu ngược dòng EXPORT_SYMBOL_GPL() và cung cấp mô-đun sử dụng ký hiệu mới được xuất, nhưng nếu có lý do chính đáng giải thích tại sao mô-đun không được gửi ngược dòng, bạn có thể gửi bản vá tới ACK thay thế. Bạn cần đưa ra lý do giải thích tại sao mô-đun không thể ngược dòng trong vấn đề này. (Đừng yêu cầu biến thể không phải GPL, EXPORT_SYMBOL() .)

Cấu hình ẩn

Một số mô-đun trong cây tự động chọn các cấu hình ẩn không thể chỉ định trong gki_defconfig . Ví dụ: CONFIG_SND_SOC_TOPOLOGY được chọn tự động khi CONFIG_SND_SOC_SOF=y được định cấu hình. Để phù hợp với việc xây dựng mô-đun ngoài cây, GKI bao gồm một cơ chế cho phép cấu hình ẩn.

Để bật cấu hình ẩn, hãy thêm câu lệnh select trong init/Kconfig.gki để nó được chọn tự động dựa trên cấu hình kernel CONFIG_GKI_HACKS_TO_FIX , được bật trong gki_defconfig . Chỉ sử dụng cơ chế này cho các cấu hình ẩn; nếu cấu hình không bị ẩn thì nó phải được chỉ định rõ ràng trong gki_defconfig hoặc dưới dạng phần phụ thuộc.

Thống đốc có thể tải

Đối với các khung công tác hạt nhân (chẳng hạn như cpufreq ) hỗ trợ bộ điều chỉnh có thể tải, bạn có thể ghi đè bộ điều chỉnh mặc định (chẳng hạn như bộ điều chỉnh schedutil của cpufreq . Đối với các khung (chẳng hạn như khung nhiệt) không hỗ trợ bộ điều chỉnh hoặc trình điều khiển có thể tải nhưng vẫn yêu cầu một triển khai dành riêng cho nhà cung cấp, tạo ra sự cố trong CNTT và tham khảo ý kiến ​​của nhóm nhân Android .

Chúng tôi sẽ làm việc với bạn và những người bảo trì thượng nguồn để bổ sung sự hỗ trợ cần thiết.

Móc nhà cung cấp

Trong các bản phát hành trước đây, bạn có thể thêm trực tiếp các sửa đổi dành riêng cho nhà cung cấp vào nhân lõi. Điều này là không thể với GKI 2.0 vì mã dành riêng cho sản phẩm phải được triển khai trong các mô-đun và sẽ không được chấp nhận trong các nhân lõi ngược dòng hoặc trong ACK. Để kích hoạt các tính năng giá trị gia tăng mà các đối tác dựa vào với tác động tối thiểu đến mã hạt nhân lõi, GKI chấp nhận các hook của nhà cung cấp cho phép gọi mô-đun từ mã hạt nhân lõi. Ngoài ra, cấu trúc dữ liệu chính có thể được đệm bằng các trường dữ liệu của nhà cung cấp có sẵn để lưu trữ dữ liệu dành riêng cho nhà cung cấp nhằm triển khai các tính năng này.

Móc nhà cung cấp có hai biến thể (bình thường và hạn chế) dựa trên các điểm theo dõi (không phải sự kiện theo dõi) mà mô-đun của nhà cung cấp có thể đính kèm vào. Ví dụ: thay vì thêm hàm sched_exit() mới để thực hiện tính toán khi thoát tác vụ, nhà cung cấp có thể thêm một hook trong do_exit() mà mô-đun nhà cung cấp có thể đính kèm để xử lý. Một ví dụ triển khai bao gồm các móc nối nhà cung cấp sau đây.

  • Móc nhà cung cấp thông thường sử dụng DECLARE_HOOK() để tạo hàm tracepoint với tên trace_ name trong đó name là mã định danh duy nhất cho dấu vết. Theo quy ước, tên hook của nhà cung cấp thông thường bắt đầu bằng android_vh , vì vậy tên của hook sched_exit() sẽ là android_vh_sched_exit .
  • Cần có móc nhà cung cấp bị hạn chế trong các trường hợp như móc lập lịch trong đó chức năng đính kèm phải được gọi ngay cả khi CPU ngoại tuyến hoặc yêu cầu ngữ cảnh không nguyên tử. Không thể tách rời các móc nhà cung cấp bị hạn chế, do đó, các mô-đun gắn vào móc bị hạn chế không bao giờ có thể dỡ bỏ. Tên hook nhà cung cấp bị hạn chế bắt đầu bằng android_rvh .

Để thêm thông tin liên quan đến nhà cung cấp, hãy gửi sự cố trong bộ phận CNTT và gửi bản vá (như với tất cả các bản vá dành riêng cho Android, sự cố phải tồn tại và bạn phải đưa ra lý do giải thích). Hỗ trợ cho các hook của nhà cung cấp chỉ có trong ACK, vì vậy đừng gửi các bản vá này lên Linux ngược dòng.

Thêm trường nhà cung cấp vào cấu trúc

Bạn có thể liên kết dữ liệu nhà cung cấp với các cấu trúc dữ liệu chính bằng cách thêm các trường android_vendor_data bằng cách sử dụng macro ANDROID_VENDOR_DATA() . Ví dụ: để hỗ trợ các tính năng giá trị gia tăng, hãy nối các trường vào cấu trúc như trong mẫu mã sau.

Để tránh xung đột tiềm ẩn giữa các trường mà nhà cung cấp cần và các trường mà OEM cần, OEM không bao giờ được sử dụng các trường được khai báo bằng macro ANDROID_VENDOR_DATA() . Thay vào đó, OEM phải sử dụng ANDROID_OEM_DATA() để khai báo các trường 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 */
}

Xác định móc nhà cung cấp

Thêm các móc nối của nhà cung cấp vào mã hạt nhân dưới dạng điểm theo dõi bằng cách khai báo chúng bằng DECLARE_HOOK() hoặc DECLARE_RESTRICTED_HOOK() rồi thêm chúng vào mã dưới dạng điểm theo dõi. Ví dụ: để thêm trace_android_vh_sched_exit() vào hàm kernel do_exit() hiện có:

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

Hàm trace_android_vh_sched_exit() ban đầu chỉ kiểm tra xem có thứ gì được đính kèm hay không. Tuy nhiên, nếu mô-đun nhà cung cấp đăng ký trình xử lý bằng cách sử dụng register_trace_android_vh_sched_exit() thì hàm đã đăng ký sẽ được gọi. Trình xử lý phải nhận thức được bối cảnh liên quan đến khóa được giữ, trạng thái RCS và các yếu tố khác. Móc phải được xác định trong tệp tiêu đề trong thư mục include/trace/hooks .

Ví dụ: đoạn mã sau đưa ra một khai báo có thể có cho trace_android_vh_sched_exit() trong tệp 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>

Để khởi tạo các giao diện cần thiết cho hook nhà cung cấp, hãy thêm tệp tiêu đề có khai báo hook vào drivers/android/vendor_hooks.c và xuất các ký hiệu. Ví dụ: đoạn mã sau hoàn thành việc khai báo hook 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);

LƯU Ý : Cấu trúc dữ liệu được sử dụng trong khai báo hook cần phải được xác định đầy đủ để đảm bảo tính ổn định của ABI. Nếu không, sẽ không an toàn khi hủy đăng ký các con trỏ mờ hoặc sử dụng cấu trúc trong các ngữ cảnh có kích thước. Phần include cung cấp định nghĩa đầy đủ về các cấu trúc dữ liệu như vậy phải nằm trong phần #ifndef __GENKSYMS__ của drivers/android/vendor_hooks.c . Các tệp tiêu đề trong include/trace/hooks không được bao gồm tệp tiêu đề kernel với các định nghĩa loại để tránh những thay đổi CRC làm hỏng KMI. Thay vào đó hãy khai báo các loại.

Gắn vào móc của nhà cung cấp

Để sử dụng móc nhà cung cấp, mô-đun nhà cung cấp cần đăng ký trình xử lý cho móc (thường được thực hiện trong quá trình khởi tạo mô-đun). Ví dụ: đoạn mã sau hiển thị trình xử lý mô-đun foo.ko cho 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);
    ...
}

Tính năng hạt nhân cốt lõi

Nếu không có kỹ thuật nào trước đó cho phép bạn triển khai một tính năng từ một mô-đun thì bạn phải thêm tính năng đó dưới dạng sửa đổi dành riêng cho Android vào nhân lõi. Tạo một vấn đề trong trình theo dõi vấn đề (IT) để bắt đầu cuộc trò chuyện.

Giao diện lập trình ứng dụng người dùng (UAPI)

  • Các tệp tiêu đề UAPI. Các thay đổi đối với tệp tiêu đề UAPI phải xảy ra ngược dòng trừ khi các thay đổi đó đối với giao diện dành riêng cho Android. Sử dụng các tệp tiêu đề dành riêng cho nhà cung cấp để xác định giao diện giữa các mô-đun của nhà cung cấp và mã không gian người dùng của nhà cung cấp.
  • các nút sysfs. Không thêm các nút sysfs mới vào hạt nhân GKI (những bổ sung như vậy chỉ hợp lệ trong các mô-đun của nhà cung cấp). Các nút sysfs được sử dụng bởi các thư viện SoC và thiết bị bất khả tri cũng như mã Java bao gồm khung Android chỉ có thể được thay đổi theo những cách tương thích và phải được thay đổi ngược dòng nếu chúng không phải là các nút sysfs dành riêng cho Android. Bạn có thể tạo các nút sysfs dành riêng cho nhà cung cấp để không gian người dùng của nhà cung cấp sử dụng. Theo mặc định, quyền truy cập vào các nút sysfs theo không gian người dùng bị từ chối khi sử dụng SELinux. Tùy thuộc vào nhà cung cấp để thêm nhãn SELinux thích hợp để cho phép phần mềm của nhà cung cấp được ủy quyền truy cập.
  • Các nút gỡ lỗiFS. Các mô-đun của nhà cung cấp có thể xác định các nút trong debugfs chỉ để gỡ lỗi (vì debugfs không được gắn trong quá trình hoạt động bình thường của thiết bị).