Commit 3868772b authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'docs-5.0' of git://git.lwn.net/linux

Pull documentation update from Jonathan Corbet:
 "A fairly normal cycle for documentation stuff. We have a new document
  on perf security, more Italian translations, more improvements to the
  memory-management docs, improvements to the pathname lookup
  documentation, and the usual array of smaller fixes.

  As is often the case, there are a few reaches outside of
  Documentation/ to adjust kerneldoc comments"

* tag 'docs-5.0' of git://git.lwn.net/linux: (38 commits)
  docs: improve pathname-lookup document structure
  configfs: fix wrong name of struct in documentation
  docs/mm-api: link slab_common.c to "The Slab Cache" section
  slab: make kmem_cache_create{_usercopy} description proper kernel-doc
  doc:process: add links where missing
  docs/core-api: make mm-api.rst more structured
  x86, boot: documentation whitespace fixup
  Documentation: devres: note checking needs when converting
  doc:it: add some process/* translations
  doc:it: fixes in process/1.Intro
  Documentation: convert path-lookup from markdown to resturctured text
  Documentation/admin-guide: update admin-guide index.rst
  Documentation/admin-guide: introduce perf-security.rst file
  scripts/kernel-doc: Fix struct and struct field attribute processing
  Documentation: dev-tools: Fix typos in index.rst
  Correct gen_init_cpio tool's documentation
  Document /proc/pid PID reuse behavior
  Documentation: update path-lookup.md for parallel lookups
  Documentation: Use "while" instead of "whilst"
  dmaengine: Add mailing list address to the documentation
  ...
parents 6f9d71c9 942104a2
...@@ -31,14 +31,13 @@ ...@@ -31,14 +31,13 @@
#define YBLANK 38 #define YBLANK 38
#define XOFFSET 8 #define XOFFSET 8
#define XPULSE 144 #define XPULSE 144
#define YOFFSET (63+3) #define YOFFSET 3
#define YPULSE (63+6) #define YPULSE 6
#define DPI 72 #define DPI 72
#define VFREQ 60 /* Hz */ #define VFREQ 60 /* Hz */
#define TIMING_NAME "Linux XGA" #define TIMING_NAME "Linux XGA"
#define ESTABLISHED_TIMING2_BITS 0x08 /* Bit 3 -> 1024x768 @60 Hz */ #define ESTABLISHED_TIMING2_BITS 0x08 /* Bit 3 -> 1024x768 @60 Hz */
#define HSYNC_POL 0 #define HSYNC_POL 0
#define VSYNC_POL 0 #define VSYNC_POL 0
#define CRC 0x55
#include "edid.S" #include "edid.S"
...@@ -31,14 +31,13 @@ ...@@ -31,14 +31,13 @@
#define YBLANK 42 #define YBLANK 42
#define XOFFSET 48 #define XOFFSET 48
#define XPULSE 112 #define XPULSE 112
#define YOFFSET (63+1) #define YOFFSET 1
#define YPULSE (63+3) #define YPULSE 3
#define DPI 72 #define DPI 72
#define VFREQ 60 /* Hz */ #define VFREQ 60 /* Hz */
#define TIMING_NAME "Linux SXGA" #define TIMING_NAME "Linux SXGA"
/* No ESTABLISHED_TIMINGx_BITS */ /* No ESTABLISHED_TIMINGx_BITS */
#define HSYNC_POL 1 #define HSYNC_POL 1
#define VSYNC_POL 1 #define VSYNC_POL 1
#define CRC 0xa0
#include "edid.S" #include "edid.S"
...@@ -31,14 +31,13 @@ ...@@ -31,14 +31,13 @@
#define YBLANK 50 #define YBLANK 50
#define XOFFSET 64 #define XOFFSET 64
#define XPULSE 192 #define XPULSE 192
#define YOFFSET (63+1) #define YOFFSET 1
#define YPULSE (63+3) #define YPULSE 3
#define DPI 72 #define DPI 72
#define VFREQ 60 /* Hz */ #define VFREQ 60 /* Hz */
#define TIMING_NAME "Linux UXGA" #define TIMING_NAME "Linux UXGA"
/* No ESTABLISHED_TIMINGx_BITS */ /* No ESTABLISHED_TIMINGx_BITS */
#define HSYNC_POL 1 #define HSYNC_POL 1
#define VSYNC_POL 1 #define VSYNC_POL 1
#define CRC 0x9d
#include "edid.S" #include "edid.S"
...@@ -31,14 +31,13 @@ ...@@ -31,14 +31,13 @@
#define YBLANK 39 #define YBLANK 39
#define XOFFSET 104 #define XOFFSET 104
#define XPULSE 176 #define XPULSE 176
#define YOFFSET (63+3) #define YOFFSET 3
#define YPULSE (63+6) #define YPULSE 6
#define DPI 96 #define DPI 96
#define VFREQ 60 /* Hz */ #define VFREQ 60 /* Hz */
#define TIMING_NAME "Linux WSXGA" #define TIMING_NAME "Linux WSXGA"
/* No ESTABLISHED_TIMINGx_BITS */ /* No ESTABLISHED_TIMINGx_BITS */
#define HSYNC_POL 1 #define HSYNC_POL 1
#define VSYNC_POL 1 #define VSYNC_POL 1
#define CRC 0x26
#include "edid.S" #include "edid.S"
...@@ -31,14 +31,13 @@ ...@@ -31,14 +31,13 @@
#define YBLANK 45 #define YBLANK 45
#define XOFFSET 88 #define XOFFSET 88
#define XPULSE 44 #define XPULSE 44
#define YOFFSET (63+4) #define YOFFSET 4
#define YPULSE (63+5) #define YPULSE 5
#define DPI 96 #define DPI 96
#define VFREQ 60 /* Hz */ #define VFREQ 60 /* Hz */
#define TIMING_NAME "Linux FHD" #define TIMING_NAME "Linux FHD"
/* No ESTABLISHED_TIMINGx_BITS */ /* No ESTABLISHED_TIMINGx_BITS */
#define HSYNC_POL 1 #define HSYNC_POL 1
#define VSYNC_POL 1 #define VSYNC_POL 1
#define CRC 0x05
#include "edid.S" #include "edid.S"
...@@ -28,14 +28,13 @@ ...@@ -28,14 +28,13 @@
#define YBLANK 28 #define YBLANK 28
#define XOFFSET 40 #define XOFFSET 40
#define XPULSE 128 #define XPULSE 128
#define YOFFSET (63+1) #define YOFFSET 1
#define YPULSE (63+4) #define YPULSE 4
#define DPI 72 #define DPI 72
#define VFREQ 60 /* Hz */ #define VFREQ 60 /* Hz */
#define TIMING_NAME "Linux SVGA" #define TIMING_NAME "Linux SVGA"
#define ESTABLISHED_TIMING1_BITS 0x01 /* Bit 0: 800x600 @ 60Hz */ #define ESTABLISHED_TIMING1_BITS 0x01 /* Bit 0: 800x600 @ 60Hz */
#define HSYNC_POL 1 #define HSYNC_POL 1
#define VSYNC_POL 1 #define VSYNC_POL 1
#define CRC 0xc2
#include "edid.S" #include "edid.S"
...@@ -45,14 +45,5 @@ EDID: ...@@ -45,14 +45,5 @@ EDID:
#define YPIX vdisp #define YPIX vdisp
#define YBLANK vtotal-vdisp #define YBLANK vtotal-vdisp
#define YOFFSET (63+(vsyncstart-vdisp)) #define YOFFSET vsyncstart-vdisp
#define YPULSE (63+(vsyncend-vsyncstart)) #define YPULSE vsyncend-vsyncstart
The CRC value in the last line
#define CRC 0x55
also is a bit tricky. After a first version of the binary data set is
created, it must be checked with the "edid-decode" utility which will
most probably complain about a wrong CRC. Fortunately, the utility also
displays the correct CRC which must then be inserted into the source
file. After the make procedure is repeated, the EDID data set is ready
to be used.
...@@ -15,10 +15,21 @@ clean: ...@@ -15,10 +15,21 @@ clean:
%.o: %.S %.o: %.S
@cc -c $^ @cc -c $^
%.bin: %.o %.bin.nocrc: %.o
@objcopy -Obinary $^ $@ @objcopy -Obinary $^ $@
%.bin.ihex: %.o %.crc: %.bin.nocrc
@list=$$(for i in `seq 1 127`; do head -c$$i $^ | tail -c1 \
| hexdump -v -e '/1 "%02X+"'; done); \
echo "ibase=16;100-($${list%?})%100" | bc >$@
%.p: %.crc %.S
@cc -c -DCRC="$$(cat $*.crc)" -o $@ $*.S
%.bin: %.p
@objcopy -Obinary $^ $@
%.bin.ihex: %.p
@objcopy -Oihex $^ $@ @objcopy -Oihex $^ $@
@dos2unix $@ 2>/dev/null @dos2unix $@ 2>/dev/null
......
...@@ -47,9 +47,11 @@ ...@@ -47,9 +47,11 @@
#define mfgname2id(v1,v2,v3) \ #define mfgname2id(v1,v2,v3) \
((((v1-'@')&0x1f)<<10)+(((v2-'@')&0x1f)<<5)+((v3-'@')&0x1f)) ((((v1-'@')&0x1f)<<10)+(((v2-'@')&0x1f)<<5)+((v3-'@')&0x1f))
#define swap16(v1) ((v1>>8)+((v1&0xff)<<8)) #define swap16(v1) ((v1>>8)+((v1&0xff)<<8))
#define lsbs2(v1,v2) (((v1&0x0f)<<4)+(v2&0x0f))
#define msbs2(v1,v2) ((((v1>>8)&0x0f)<<4)+((v2>>8)&0x0f)) #define msbs2(v1,v2) ((((v1>>8)&0x0f)<<4)+((v2>>8)&0x0f))
#define msbs4(v1,v2,v3,v4) \ #define msbs4(v1,v2,v3,v4) \
(((v1&0x03)>>2)+((v2&0x03)>>4)+((v3&0x03)>>6)+((v4&0x03)>>8)) ((((v1>>8)&0x03)<<6)+(((v2>>8)&0x03)<<4)+\
(((v3>>4)&0x03)<<2)+((v4>>4)&0x03))
#define pixdpi2mm(pix,dpi) ((pix*25)/dpi) #define pixdpi2mm(pix,dpi) ((pix*25)/dpi)
#define xsize pixdpi2mm(XPIX,DPI) #define xsize pixdpi2mm(XPIX,DPI)
#define ysize pixdpi2mm(YPIX,DPI) #define ysize pixdpi2mm(YPIX,DPI)
...@@ -200,9 +202,9 @@ y_msbs: .byte msbs2(YPIX,YBLANK) ...@@ -200,9 +202,9 @@ y_msbs: .byte msbs2(YPIX,YBLANK)
x_snc_off_lsb: .byte XOFFSET&0xff x_snc_off_lsb: .byte XOFFSET&0xff
/* Horizontal sync pulse width pixels 8 lsbits (0-1023) */ /* Horizontal sync pulse width pixels 8 lsbits (0-1023) */
x_snc_pls_lsb: .byte XPULSE&0xff x_snc_pls_lsb: .byte XPULSE&0xff
/* Bits 7-4 Vertical sync offset lines 4 lsbits -63) /* Bits 7-4 Vertical sync offset lines 4 lsbits (0-63)
Bits 3-0 Vertical sync pulse width lines 4 lsbits -63) */ Bits 3-0 Vertical sync pulse width lines 4 lsbits (0-63) */
y_snc_lsb: .byte ((YOFFSET-63)<<4)+(YPULSE-63) y_snc_lsb: .byte lsbs2(YOFFSET, YPULSE)
/* Bits 7-6 Horizontal sync offset pixels 2 msbits /* Bits 7-6 Horizontal sync offset pixels 2 msbits
Bits 5-4 Horizontal sync pulse width pixels 2 msbits Bits 5-4 Horizontal sync pulse width pixels 2 msbits
Bits 3-2 Vertical sync offset lines 2 msbits Bits 3-2 Vertical sync offset lines 2 msbits
......
.. _admin_devices:
Linux allocated devices (4.x+ version) Linux allocated devices (4.x+ version)
====================================== ======================================
......
...@@ -110,8 +110,8 @@ If your query set is big, you can batch them too:: ...@@ -110,8 +110,8 @@ If your query set is big, you can batch them too::
~# cat query-batch-file > <debugfs>/dynamic_debug/control ~# cat query-batch-file > <debugfs>/dynamic_debug/control
A another way is to use wildcard. The match rule support ``*`` (matches Another way is to use wildcards. The match rule supports ``*`` (matches
zero or more characters) and ``?`` (matches exactly one character).For zero or more characters) and ``?`` (matches exactly one character). For
example, you can match all usb drivers:: example, you can match all usb drivers::
~# echo "file drivers/usb/* +p" > <debugfs>/dynamic_debug/control ~# echo "file drivers/usb/* +p" > <debugfs>/dynamic_debug/control
...@@ -258,7 +258,7 @@ this boot parameter for debugging purposes. ...@@ -258,7 +258,7 @@ this boot parameter for debugging purposes.
If ``foo`` module is not built-in, ``foo.dyndbg`` will still be processed at If ``foo`` module is not built-in, ``foo.dyndbg`` will still be processed at
boot time, without effect, but will be reprocessed when module is boot time, without effect, but will be reprocessed when module is
loaded later. ``dyndbg_query=`` and bare ``dyndbg=`` are only processed at loaded later. ``ddebug_query=`` and bare ``dyndbg=`` are only processed at
boot. boot.
...@@ -301,7 +301,7 @@ The ``dyndbg`` option is a "fake" module parameter, which means: ...@@ -301,7 +301,7 @@ The ``dyndbg`` option is a "fake" module parameter, which means:
For ``CONFIG_DYNAMIC_DEBUG`` kernels, any settings given at boot-time (or For ``CONFIG_DYNAMIC_DEBUG`` kernels, any settings given at boot-time (or
enabled by ``-DDEBUG`` flag during compilation) can be disabled later via enabled by ``-DDEBUG`` flag during compilation) can be disabled later via
the sysfs interface if the debug messages are no longer needed:: the debugfs interface if the debug messages are no longer needed::
echo "module module_name -p" > <debugfs>/dynamic_debug/control echo "module module_name -p" > <debugfs>/dynamic_debug/control
......
...@@ -76,6 +76,7 @@ configure specific aspects of kernel behavior to your liking. ...@@ -76,6 +76,7 @@ configure specific aspects of kernel behavior to your liking.
thunderbolt thunderbolt
LSM/index LSM/index
mm/index mm/index
perf-security
.. only:: subproject and html .. only:: subproject and html
......
...@@ -331,7 +331,7 @@ ...@@ -331,7 +331,7 @@
APC and your system crashes randomly. APC and your system crashes randomly.
apic= [APIC,X86] Advanced Programmable Interrupt Controller apic= [APIC,X86] Advanced Programmable Interrupt Controller
Change the output verbosity whilst booting Change the output verbosity while booting
Format: { quiet (default) | verbose | debug } Format: { quiet (default) | verbose | debug }
Change the amount of debugging information output Change the amount of debugging information output
when initialising the APIC and IO-APIC components. when initialising the APIC and IO-APIC components.
......
...@@ -4,13 +4,13 @@ ...@@ -4,13 +4,13 @@
Concepts overview Concepts overview
================= =================
The memory management in Linux is complex system that evolved over the The memory management in Linux is a complex system that evolved over the
years and included more and more functionality to support variety of years and included more and more functionality to support a variety of
systems from MMU-less microcontrollers to supercomputers. The memory systems from MMU-less microcontrollers to supercomputers. The memory
management for systems without MMU is called ``nommu`` and it management for systems without an MMU is called ``nommu`` and it
definitely deserves a dedicated document, which hopefully will be definitely deserves a dedicated document, which hopefully will be
eventually written. Yet, although some of the concepts are the same, eventually written. Yet, although some of the concepts are the same,
here we assume that MMU is available and CPU can translate a virtual here we assume that an MMU is available and a CPU can translate a virtual
address to a physical address. address to a physical address.
.. contents:: :local: .. contents:: :local:
...@@ -21,10 +21,10 @@ Virtual Memory Primer ...@@ -21,10 +21,10 @@ Virtual Memory Primer
The physical memory in a computer system is a limited resource and The physical memory in a computer system is a limited resource and
even for systems that support memory hotplug there is a hard limit on even for systems that support memory hotplug there is a hard limit on
the amount of memory that can be installed. The physical memory is not the amount of memory that can be installed. The physical memory is not
necessary contiguous, it might be accessible as a set of distinct necessarily contiguous; it might be accessible as a set of distinct
address ranges. Besides, different CPU architectures, and even address ranges. Besides, different CPU architectures, and even
different implementations of the same architecture have different view different implementations of the same architecture have different views
how these address ranges defined. of how these address ranges are defined.
All this makes dealing directly with physical memory quite complex and All this makes dealing directly with physical memory quite complex and
to avoid this complexity a concept of virtual memory was developed. to avoid this complexity a concept of virtual memory was developed.
...@@ -48,8 +48,8 @@ appropriate kernel configuration option. ...@@ -48,8 +48,8 @@ appropriate kernel configuration option.
Each physical memory page can be mapped as one or more virtual Each physical memory page can be mapped as one or more virtual
pages. These mappings are described by page tables that allow pages. These mappings are described by page tables that allow
translation from virtual address used by programs to real address in translation from a virtual address used by programs to the physical
the physical memory. The page tables organized hierarchically. memory address. The page tables are organized hierarchically.
The tables at the lowest level of the hierarchy contain physical The tables at the lowest level of the hierarchy contain physical
addresses of actual pages used by the software. The tables at higher addresses of actual pages used by the software. The tables at higher
...@@ -121,8 +121,8 @@ Nodes ...@@ -121,8 +121,8 @@ Nodes
Many multi-processor machines are NUMA - Non-Uniform Memory Access - Many multi-processor machines are NUMA - Non-Uniform Memory Access -
systems. In such systems the memory is arranged into banks that have systems. In such systems the memory is arranged into banks that have
different access latency depending on the "distance" from the different access latency depending on the "distance" from the
processor. Each bank is referred as `node` and for each node Linux processor. Each bank is referred to as a `node` and for each node Linux
constructs an independent memory management subsystem. A node has it's constructs an independent memory management subsystem. A node has its
own set of zones, lists of free and used pages and various statistics own set of zones, lists of free and used pages and various statistics
counters. You can find more details about NUMA in counters. You can find more details about NUMA in
:ref:`Documentation/vm/numa.rst <numa>` and in :ref:`Documentation/vm/numa.rst <numa>` and in
...@@ -149,9 +149,9 @@ for program's stack and heap or by explicit calls to mmap(2) system ...@@ -149,9 +149,9 @@ for program's stack and heap or by explicit calls to mmap(2) system
call. Usually, the anonymous mappings only define virtual memory areas call. Usually, the anonymous mappings only define virtual memory areas
that the program is allowed to access. The read accesses will result that the program is allowed to access. The read accesses will result
in creation of a page table entry that references a special physical in creation of a page table entry that references a special physical
page filled with zeroes. When the program performs a write, regular page filled with zeroes. When the program performs a write, a regular
physical page will be allocated to hold the written data. The page physical page will be allocated to hold the written data. The page
will be marked dirty and if the kernel will decide to repurpose it, will be marked dirty and if the kernel decides to repurpose it,
the dirty page will be swapped out. the dirty page will be swapped out.
Reclaim Reclaim
...@@ -181,8 +181,8 @@ pressure. ...@@ -181,8 +181,8 @@ pressure.
The process of freeing the reclaimable physical memory pages and The process of freeing the reclaimable physical memory pages and
repurposing them is called (surprise!) `reclaim`. Linux can reclaim repurposing them is called (surprise!) `reclaim`. Linux can reclaim
pages either asynchronously or synchronously, depending on the state pages either asynchronously or synchronously, depending on the state
of the system. When system is not loaded, most of the memory is free of the system. When the system is not loaded, most of the memory is free
and allocation request will be satisfied immediately from the free and allocation requests will be satisfied immediately from the free
pages supply. As the load increases, the amount of the free pages goes pages supply. As the load increases, the amount of the free pages goes
down and when it reaches a certain threshold (high watermark), an down and when it reaches a certain threshold (high watermark), an
allocation request will awaken the ``kswapd`` daemon. It will allocation request will awaken the ``kswapd`` daemon. It will
...@@ -190,7 +190,7 @@ asynchronously scan memory pages and either just free them if the data ...@@ -190,7 +190,7 @@ asynchronously scan memory pages and either just free them if the data
they contain is available elsewhere, or evict to the backing storage they contain is available elsewhere, or evict to the backing storage
device (remember those dirty pages?). As memory usage increases even device (remember those dirty pages?). As memory usage increases even
more and reaches another threshold - min watermark - an allocation more and reaches another threshold - min watermark - an allocation
will trigger the `direct reclaim`. In this case allocation is stalled will trigger `direct reclaim`. In this case allocation is stalled
until enough memory pages are reclaimed to satisfy the request. until enough memory pages are reclaimed to satisfy the request.
Compaction Compaction
...@@ -200,7 +200,7 @@ As the system runs, tasks allocate and free the memory and it becomes ...@@ -200,7 +200,7 @@ As the system runs, tasks allocate and free the memory and it becomes
fragmented. Although with virtual memory it is possible to present fragmented. Although with virtual memory it is possible to present
scattered physical pages as virtually contiguous range, sometimes it is scattered physical pages as virtually contiguous range, sometimes it is
necessary to allocate large physically contiguous memory areas. Such necessary to allocate large physically contiguous memory areas. Such
need may arise, for instance, when a device driver requires large need may arise, for instance, when a device driver requires a large
buffer for DMA, or when THP allocates a huge page. Memory `compaction` buffer for DMA, or when THP allocates a huge page. Memory `compaction`
addresses the fragmentation issue. This mechanism moves occupied pages addresses the fragmentation issue. This mechanism moves occupied pages
from the lower part of a memory zone to free pages in the upper part from the lower part of a memory zone to free pages in the upper part
...@@ -208,15 +208,16 @@ of the zone. When a compaction scan is finished free pages are grouped ...@@ -208,15 +208,16 @@ of the zone. When a compaction scan is finished free pages are grouped
together at the beginning of the zone and allocations of large together at the beginning of the zone and allocations of large
physically contiguous areas become possible. physically contiguous areas become possible.
Like reclaim, the compaction may happen asynchronously in ``kcompactd`` Like reclaim, the compaction may happen asynchronously in the ``kcompactd``
daemon or synchronously as a result of memory allocation request. daemon or synchronously as a result of a memory allocation request.
OOM killer OOM killer
========== ==========
It may happen, that on a loaded machine memory will be exhausted. When It is possible that on a loaded machine memory will be exhausted and the
the kernel detects that the system runs out of memory (OOM) it invokes kernel will be unable to reclaim enough memory to continue to operate. In
`OOM killer`. Its mission is simple: all it has to do is to select a order to save the rest of the system, it invokes the `OOM killer`.
task to sacrifice for the sake of the overall system health. The
selected task is killed in a hope that after it exits enough memory The `OOM killer` selects a task to sacrifice for the sake of the overall
will be freed to continue normal operation. system health. The selected task is killed in a hope that after it exits
enough memory will be freed to continue normal operation.
.. _perf_security:
Perf Events and tool security
=============================
Overview
--------
Usage of Performance Counters for Linux (perf_events) [1]_ , [2]_ , [3]_ can
impose a considerable risk of leaking sensitive data accessed by monitored
processes. The data leakage is possible both in scenarios of direct usage of
perf_events system call API [2]_ and over data files generated by Perf tool user
mode utility (Perf) [3]_ , [4]_ . The risk depends on the nature of data that
perf_events performance monitoring units (PMU) [2]_ collect and expose for
performance analysis. Having that said perf_events/Perf performance monitoring
is the subject for security access control management [5]_ .
perf_events/Perf access control
-------------------------------
To perform security checks, the Linux implementation splits processes into two
categories [6]_ : a) privileged processes (whose effective user ID is 0, referred
to as superuser or root), and b) unprivileged processes (whose effective UID is
nonzero). Privileged processes bypass all kernel security permission checks so
perf_events performance monitoring is fully available to privileged processes
without access, scope and resource restrictions.
Unprivileged processes are subject to a full security permission check based on
the process's credentials [5]_ (usually: effective UID, effective GID, and
supplementary group list).
Linux divides the privileges traditionally associated with superuser into
distinct units, known as capabilities [6]_ , which can be independently enabled
and disabled on per-thread basis for processes and files of unprivileged users.
Unprivileged processes with enabled CAP_SYS_ADMIN capability are treated as
privileged processes with respect to perf_events performance monitoring and
bypass *scope* permissions checks in the kernel.
Unprivileged processes using perf_events system call API is also subject for
PTRACE_MODE_READ_REALCREDS ptrace access mode check [7]_ , whose outcome
determines whether monitoring is permitted. So unprivileged processes provided
with CAP_SYS_PTRACE capability are effectively permitted to pass the check.
Other capabilities being granted to unprivileged processes can effectively
enable capturing of additional data required for later performance analysis of
monitored processes or a system. For example, CAP_SYSLOG capability permits
reading kernel space memory addresses from /proc/kallsyms file.
perf_events/Perf unprivileged users
-----------------------------------
perf_events/Perf *scope* and *access* control for unprivileged processes is
governed by perf_event_paranoid [2]_ setting:
-1:
Impose no *scope* and *access* restrictions on using perf_events performance
monitoring. Per-user per-cpu perf_event_mlock_kb [2]_ locking limit is
ignored when allocating memory buffers for storing performance data.
This is the least secure mode since allowed monitored *scope* is
maximized and no perf_events specific limits are imposed on *resources*
allocated for performance monitoring.
>=0:
*scope* includes per-process and system wide performance monitoring
but excludes raw tracepoints and ftrace function tracepoints monitoring.
CPU and system events happened when executing either in user or
in kernel space can be monitored and captured for later analysis.
Per-user per-cpu perf_event_mlock_kb locking limit is imposed but
ignored for unprivileged processes with CAP_IPC_LOCK [6]_ capability.
>=1:
*scope* includes per-process performance monitoring only and excludes
system wide performance monitoring. CPU and system events happened when
executing either in user or in kernel space can be monitored and
captured for later analysis. Per-user per-cpu perf_event_mlock_kb
locking limit is imposed but ignored for unprivileged processes with
CAP_IPC_LOCK capability.
>=2:
*scope* includes per-process performance monitoring only. CPU and system
events happened when executing in user space only can be monitored and
captured for later analysis. Per-user per-cpu perf_event_mlock_kb
locking limit is imposed but ignored for unprivileged processes with
CAP_IPC_LOCK capability.
Bibliography
------------
.. [1] `<https://lwn.net/Articles/337493/>`_
.. [2] `<http://man7.org/linux/man-pages/man2/perf_event_open.2.html>`_
.. [3] `<http://web.eece.maine.edu/~vweaver/projects/perf_events/>`_
.. [4] `<https://perf.wiki.kernel.org/index.php/Main_Page>`_
.. [5] `<https://www.kernel.org/doc/html/latest/security/credentials.html>`_
.. [6] `<http://man7.org/linux/man-pages/man7/capabilities.7.html>`_
.. [7] `<http://man7.org/linux/man-pages/man2/ptrace.2.html>`_
...@@ -54,7 +54,7 @@ those errors are correctable. ...@@ -54,7 +54,7 @@ those errors are correctable.
Types of errors Types of errors
--------------- ---------------
Most mechanisms used on modern systems use use technologies like Hamming Most mechanisms used on modern systems use technologies like Hamming
Codes that allow error correction when the number of errors on a bit packet Codes that allow error correction when the number of errors on a bit packet
is below a threshold. If the number of errors is above, those mechanisms is below a threshold. If the number of errors is above, those mechanisms
can indicate with a high degree of confidence that an error happened, but can indicate with a high degree of confidence that an error happened, but
......
...@@ -44,7 +44,7 @@ only valid reason for deferring the publication of a fix is to accommodate ...@@ -44,7 +44,7 @@ only valid reason for deferring the publication of a fix is to accommodate
the logistics of QA and large scale rollouts which require release the logistics of QA and large scale rollouts which require release
coordination. coordination.
Whilst embargoed information may be shared with trusted individuals in While embargoed information may be shared with trusted individuals in
order to develop a fix, such information will not be published alongside order to develop a fix, such information will not be published alongside
the fix or on any other disclosure channel without the permission of the the fix or on any other disclosure channel without the permission of the
reporter. This includes but is not limited to the original bug report reporter. This includes but is not limited to the original bug report
......
...@@ -126,7 +126,7 @@ tagged list. ...@@ -126,7 +126,7 @@ tagged list.
The boot loader must pass at a minimum the size and location of the The boot loader must pass at a minimum the size and location of the
system memory, and the root filesystem location. The dtb must be system memory, and the root filesystem location. The dtb must be
placed in a region of memory where the kernel decompressor will not placed in a region of memory where the kernel decompressor will not
overwrite it, whilst remaining within the region which will be covered overwrite it, while remaining within the region which will be covered
by the kernel's low-memory mapping. by the kernel's low-memory mapping.
A safe location is just above the 128MiB boundary from start of RAM. A safe location is just above the 128MiB boundary from start of RAM.
......
...@@ -55,7 +55,7 @@ out s3c2410 API, then here are some notes on the process. ...@@ -55,7 +55,7 @@ out s3c2410 API, then here are some notes on the process.
as they have the same arguments, and can either take the pin specific as they have the same arguments, and can either take the pin specific
values, or the more generic special-function-number arguments. values, or the more generic special-function-number arguments.
3) s3c2410_gpio_pullup() changes have the problem that whilst the 3) s3c2410_gpio_pullup() changes have the problem that while the
s3c2410_gpio_pullup(x, 1) can be easily translated to the s3c2410_gpio_pullup(x, 1) can be easily translated to the
s3c_gpio_setpull(x, S3C_GPIO_PULL_NONE), the s3c2410_gpio_pullup(x, 0) s3c_gpio_setpull(x, S3C_GPIO_PULL_NONE), the s3c2410_gpio_pullup(x, 0)
are not so easy. are not so easy.
......
...@@ -17,7 +17,7 @@ Introduction ...@@ -17,7 +17,7 @@ Introduction
versions. versions.
The S3C2416 and S3C2450 devices are very similar and S3C2450 support is The S3C2416 and S3C2450 devices are very similar and S3C2450 support is
included under the arch/arm/mach-s3c2416 directory. Note, whilst core included under the arch/arm/mach-s3c2416 directory. Note, while core
support for these SoCs is in, work on some of the extra peripherals support for these SoCs is in, work on some of the extra peripherals
and extra interrupts is still ongoing. and extra interrupts is still ongoing.
......
...@@ -87,7 +87,7 @@ Debugging ...@@ -87,7 +87,7 @@ Debugging
suspending, which means that use of printascii() or similar direct suspending, which means that use of printascii() or similar direct
access to the UARTs will cause the debug to stop. access to the UARTs will cause the debug to stop.
2) Whilst the pm code itself will attempt to re-enable the UART clocks, 2) While the pm code itself will attempt to re-enable the UART clocks,
care should be taken that any external clock sources that the UARTs care should be taken that any external clock sources that the UARTs
rely on are still enabled at that point. rely on are still enabled at that point.
......
...@@ -34,7 +34,7 @@ properties: ...@@ -34,7 +34,7 @@ properties:
8. The array can iterated over. The objects will not necessarily come out in 8. The array can iterated over. The objects will not necessarily come out in
key order. key order.
9. The array can be iterated over whilst it is being modified, provided the 9. The array can be iterated over while it is being modified, provided the
RCU readlock is being held by the iterator. Note, however, under these RCU readlock is being held by the iterator. Note, however, under these
circumstances, some objects may be seen more than once. If this is a circumstances, some objects may be seen more than once. If this is a
problem, the iterator should lock against modification. Objects will not problem, the iterator should lock against modification. Objects will not
...@@ -42,7 +42,7 @@ properties: ...@@ -42,7 +42,7 @@ properties:
10. Objects in the array can be looked up by means of their index key. 10. Objects in the array can be looked up by means of their index key.
11. Objects can be looked up whilst the array is being modified, provided the 11. Objects can be looked up while the array is being modified, provided the
RCU readlock is being held by the thread doing the look up. RCU readlock is being held by the thread doing the look up.
The implementation uses a tree of 16-pointer nodes internally that are indexed The implementation uses a tree of 16-pointer nodes internally that are indexed
...@@ -273,7 +273,7 @@ The function will return ``0`` if successful and ``-ENOMEM`` if there wasn't ...@@ -273,7 +273,7 @@ The function will return ``0`` if successful and ``-ENOMEM`` if there wasn't
enough memory. enough memory.
It is possible for other threads to iterate over or search the array under It is possible for other threads to iterate over or search the array under
the RCU read lock whilst this function is in progress. The caller should the RCU read lock while this function is in progress. The caller should
lock exclusively against other modifiers of the array. lock exclusively against other modifiers of the array.
......
.. _memory-allocation:
======================= =======================
Memory Allocation Guide Memory Allocation Guide
======================= =======================
......
...@@ -46,11 +46,20 @@ The Slab Cache ...@@ -46,11 +46,20 @@ The Slab Cache
.. kernel-doc:: mm/slab.c .. kernel-doc:: mm/slab.c
:export: :export:
.. kernel-doc:: mm/slab_common.c
:export:
.. kernel-doc:: mm/util.c .. kernel-doc:: mm/util.c
:functions: kfree_const kvmalloc_node kvfree :functions: kfree_const kvmalloc_node kvfree
More Memory Management Functions Virtually Contiguous Mappings
================================ =============================
.. kernel-doc:: mm/vmalloc.c
:export:
File Mapping and Page Cache
===========================
.. kernel-doc:: mm/readahead.c .. kernel-doc:: mm/readahead.c
:export: :export:
...@@ -58,23 +67,28 @@ More Memory Management Functions ...@@ -58,23 +67,28 @@ More Memory Management Functions
.. kernel-doc:: mm/filemap.c .. kernel-doc:: mm/filemap.c
:export: :export:
.. kernel-doc:: mm/memory.c .. kernel-doc:: mm/page-writeback.c
:export: :export:
.. kernel-doc:: mm/vmalloc.c .. kernel-doc:: mm/truncate.c
:export: :export:
.. kernel-doc:: mm/page_alloc.c Memory pools
:internal: ============
.. kernel-doc:: mm/mempool.c .. kernel-doc:: mm/mempool.c
:export: :export:
DMA pools
=========
.. kernel-doc:: mm/dmapool.c .. kernel-doc:: mm/dmapool.c
:export: :export:
.. kernel-doc:: mm/page-writeback.c More Memory Management Functions
:export: ================================
.. kernel-doc:: mm/truncate.c .. kernel-doc:: mm/memory.c
:export: :export:
.. kernel-doc:: mm/page_alloc.c
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
.. highlight:: none .. highlight:: none
.. _devtools_coccinelle:
Coccinelle Coccinelle
========== ==========
......
...@@ -4,7 +4,7 @@ Development tools for the kernel ...@@ -4,7 +4,7 @@ Development tools for the kernel
This document is a collection of documents about development tools that can This document is a collection of documents about development tools that can
be used to work on the kernel. For now, the documents have been pulled be used to work on the kernel. For now, the documents have been pulled
together without any significant effot to integrate them into a coherent together without any significant effort to integrate them into a coherent
whole; patches welcome! whole; patches welcome!
.. class:: toc-title .. class:: toc-title
......
...@@ -9,7 +9,7 @@ and booting a kernel. ...@@ -9,7 +9,7 @@ and booting a kernel.
On some systems, hot-plug tests could hang forever waiting for cpu and On some systems, hot-plug tests could hang forever waiting for cpu and
memory to be ready to be offlined. A special hot-plug target is created memory to be ready to be offlined. A special hot-plug target is created
to run full range of hot-plug tests. In default mode, hot-plug tests run to run the full range of hot-plug tests. In default mode, hot-plug tests run
in safe mode with a limited scope. In limited mode, cpu-hotplug test is in safe mode with a limited scope. In limited mode, cpu-hotplug test is
run on a single cpu as opposed to all hotplug capable cpus, and memory run on a single cpu as opposed to all hotplug capable cpus, and memory
hotplug test is run on 2% of hotplug capable memory instead of 10%. hotplug test is run on 2% of hotplug capable memory instead of 10%.
...@@ -89,9 +89,9 @@ Note that some tests will require root privileges. ...@@ -89,9 +89,9 @@ Note that some tests will require root privileges.
Install selftests Install selftests
================= =================
You can use kselftest_install.sh tool installs selftests in default You can use the kselftest_install.sh tool to install selftests in the
location which is tools/testing/selftests/kselftest or a user specified default location, which is tools/testing/selftests/kselftest, or in a
location. user specified location.
To install selftests in default location:: To install selftests in default location::
...@@ -109,7 +109,7 @@ Running installed selftests ...@@ -109,7 +109,7 @@ Running installed selftests
Kselftest install as well as the Kselftest tarball provide a script Kselftest install as well as the Kselftest tarball provide a script
named "run_kselftest.sh" to run the tests. named "run_kselftest.sh" to run the tests.
You can simply do the following to run the installed Kselftests. Please You can simply do the following to run the installed Kselftests. Please
note some tests will require root privileges:: note some tests will require root privileges::
$ cd kselftest $ cd kselftest
...@@ -139,7 +139,7 @@ Contributing new tests (details) ...@@ -139,7 +139,7 @@ Contributing new tests (details)
default. default.
TEST_CUSTOM_PROGS should be used by tests that require custom build TEST_CUSTOM_PROGS should be used by tests that require custom build
rule and prevent common build rule use. rules and prevent common build rule use.
TEST_PROGS are for test shell scripts. Please ensure shell script has TEST_PROGS are for test shell scripts. Please ensure shell script has
its exec bit set. Otherwise, lib.mk run_tests will generate a warning. its exec bit set. Otherwise, lib.mk run_tests will generate a warning.
......
...@@ -146,7 +146,7 @@ The target is named "raid" and it accepts the following parameters: ...@@ -146,7 +146,7 @@ The target is named "raid" and it accepts the following parameters:
[data_offset <sectors>] [data_offset <sectors>]
This option value defines the offset into each data device This option value defines the offset into each data device
where the data starts. This is used to provide out-of-place where the data starts. This is used to provide out-of-place
reshaping space to avoid writing over data whilst reshaping space to avoid writing over data while
changing the layout of stripes, hence an interruption/crash changing the layout of stripes, hence an interruption/crash
may happen at any time without the risk of losing data. may happen at any time without the risk of losing data.
E.g. when adding devices to an existing raid set during E.g. when adding devices to an existing raid set during
......
...@@ -142,7 +142,7 @@ characterised by the following graph: ...@@ -142,7 +142,7 @@ characterised by the following graph:
The graph is split in two parts delimited by time 1ms on the X-axis. The graph is split in two parts delimited by time 1ms on the X-axis.
The graph curve with X-axis values = { x | 0 < x < 1ms } has a steep slope The graph curve with X-axis values = { x | 0 < x < 1ms } has a steep slope
and denotes the energy costs incurred whilst entering and leaving the idle and denotes the energy costs incurred while entering and leaving the idle
state. state.
The graph curve in the area delimited by X-axis values = {x | x > 1ms } has The graph curve in the area delimited by X-axis values = {x | x > 1ms } has
shallower slope and essentially represents the energy consumption of the idle shallower slope and essentially represents the energy consumption of the idle
......
...@@ -56,7 +56,7 @@ For CAM, this 24-bit offset is: ...@@ -56,7 +56,7 @@ For CAM, this 24-bit offset is:
cfg_offset(bus, device, function, register) = cfg_offset(bus, device, function, register) =
bus << 16 | device << 11 | function << 8 | register bus << 16 | device << 11 | function << 8 | register
Whilst ECAM extends this by 4 bits to accommodate 4k of function space: While ECAM extends this by 4 bits to accommodate 4k of function space:
cfg_offset(bus, device, function, register) = cfg_offset(bus, device, function, register) =
bus << 20 | device << 15 | function << 12 | register bus << 20 | device << 15 | function << 12 | register
......
...@@ -16,7 +16,7 @@ Optional properties: ...@@ -16,7 +16,7 @@ Optional properties:
- linux,rs485-enabled-at-boot-time: empty property telling to enable the rs485 - linux,rs485-enabled-at-boot-time: empty property telling to enable the rs485
feature at boot time. It can be disabled later with proper ioctl. feature at boot time. It can be disabled later with proper ioctl.
- rs485-rx-during-tx: empty property that enables the receiving of data even - rs485-rx-during-tx: empty property that enables the receiving of data even
whilst sending data. while sending data.
RS485 example for Atmel USART: RS485 example for Atmel USART:
usart0: serial@fff8c000 { usart0: serial@fff8c000 {
......
...@@ -77,7 +77,7 @@ The general format of a function and function-like macro kernel-doc comment is:: ...@@ -77,7 +77,7 @@ The general format of a function and function-like macro kernel-doc comment is::
* Context: Describes whether the function can sleep, what locks it takes, * Context: Describes whether the function can sleep, what locks it takes,
* releases, or expects to be held. It can extend over multiple * releases, or expects to be held. It can extend over multiple
* lines. * lines.
* Return: Describe the return value of foobar. * Return: Describe the return value of function_name.
* *
* The return value description can also have multiple paragraphs, and should * The return value description can also have multiple paragraphs, and should
* be placed at the end of the comment block. * be placed at the end of the comment block.
......
.. _sphinxdoc:
Introduction Introduction
============ ============
......
...@@ -11,6 +11,10 @@ This small document introduces how to test DMA drivers using dmatest module. ...@@ -11,6 +11,10 @@ This small document introduces how to test DMA drivers using dmatest module.
capability of the following: DMA_MEMCPY (memory-to-memory), DMA_MEMSET capability of the following: DMA_MEMCPY (memory-to-memory), DMA_MEMSET
(const-to-memory or memory-to-memory, when emulated), DMA_XOR, DMA_PQ. (const-to-memory or memory-to-memory, when emulated), DMA_XOR, DMA_PQ.
.. note::
In case of any related questions use the official mailing list
dmaengine@vger.kernel.org.
Part 1 - How to build the test module Part 1 - How to build the test module
===================================== =====================================
......
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
.. |struct wakeup_source| replace:: :c:type:`struct wakeup_source <wakeup_source>` .. |struct wakeup_source| replace:: :c:type:`struct wakeup_source <wakeup_source>`
.. |struct device| replace:: :c:type:`struct device <device>` .. |struct device| replace:: :c:type:`struct device <device>`
.. _driverapi_pm_devices:
============================== ==============================
Device Power Management Basics Device Power Management Basics
============================== ==============================
......
...@@ -132,6 +132,13 @@ devres. Complexity is shifted from less maintained low level drivers ...@@ -132,6 +132,13 @@ devres. Complexity is shifted from less maintained low level drivers
to better maintained higher layer. Also, as init failure path is to better maintained higher layer. Also, as init failure path is
shared with exit path, both can get more testing. shared with exit path, both can get more testing.
Note though that when converting current calls or assignments to
managed devm_* versions it is up to you to check if internal operations
like allocating memory, have failed. Managed resources pertains to the
freeing of these resources *only* - all other checks needed are still
on you. In some cases this may mean introducing checks that were not
necessary before moving to the managed devm_* calls.
3. Devres group 3. Devres group
--------------- ---------------
......
...@@ -52,7 +52,7 @@ user root (0). INITRAMFS_ROOT_GID can be set to a group ID that needs ...@@ -52,7 +52,7 @@ user root (0). INITRAMFS_ROOT_GID can be set to a group ID that needs
to be mapped to group root (0). to be mapped to group root (0).
A source file must be directives in the format required by the A source file must be directives in the format required by the
usr/gen_init_cpio utility (run 'usr/gen_init_cpio --help' to get the usr/gen_init_cpio utility (run 'usr/gen_init_cpio -h' to get the
file format). The directives in the file will be passed directly to file format). The directives in the file will be passed directly to
usr/gen_init_cpio. usr/gen_init_cpio.
......
...@@ -704,7 +704,7 @@ FS-Cache provides some utilities that a cache backend may make use of: ...@@ -704,7 +704,7 @@ FS-Cache provides some utilities that a cache backend may make use of:
void fscache_get_retrieval(struct fscache_retrieval *op); void fscache_get_retrieval(struct fscache_retrieval *op);
void fscache_put_retrieval(struct fscache_retrieval *op); void fscache_put_retrieval(struct fscache_retrieval *op);
These two functions are used to retain a retrieval record whilst doing These two functions are used to retain a retrieval record while doing
asynchronous data retrieval and block allocation. asynchronous data retrieval and block allocation.
......
...@@ -45,7 +45,7 @@ filesystems are very specific in nature. ...@@ -45,7 +45,7 @@ filesystems are very specific in nature.
CacheFiles creates a misc character device - "/dev/cachefiles" - that is used CacheFiles creates a misc character device - "/dev/cachefiles" - that is used
to communication with the daemon. Only one thing may have this open at once, to communication with the daemon. Only one thing may have this open at once,
and whilst it is open, a cache is at least partially in existence. The daemon and while it is open, a cache is at least partially in existence. The daemon
opens this and sends commands down it to control the cache. opens this and sends commands down it to control the cache.
CacheFiles is currently limited to a single cache. CacheFiles is currently limited to a single cache.
...@@ -163,7 +163,7 @@ Do not mount other things within the cache as this will cause problems. The ...@@ -163,7 +163,7 @@ Do not mount other things within the cache as this will cause problems. The
kernel module contains its own very cut-down path walking facility that ignores kernel module contains its own very cut-down path walking facility that ignores
mountpoints, but the daemon can't avoid them. mountpoints, but the daemon can't avoid them.
Do not create, rename or unlink files and directories in the cache whilst the Do not create, rename or unlink files and directories in the cache while the
cache is active, as this may cause the state to become uncertain. cache is active, as this may cause the state to become uncertain.
Renaming files in the cache might make objects appear to be other objects (the Renaming files in the cache might make objects appear to be other objects (the
......
...@@ -382,7 +382,7 @@ MISCELLANEOUS OBJECT REGISTRATION ...@@ -382,7 +382,7 @@ MISCELLANEOUS OBJECT REGISTRATION
An optional step is to request an object of miscellaneous type be created in An optional step is to request an object of miscellaneous type be created in
the cache. This is almost identical to index cookie acquisition. The only the cache. This is almost identical to index cookie acquisition. The only
difference is that the type in the object definition should be something other difference is that the type in the object definition should be something other
than index type. Whilst the parent object could be an index, it's more likely than index type. While the parent object could be an index, it's more likely
it would be some other type of object such as a data file. it would be some other type of object such as a data file.
xattr->cache = xattr->cache =
......
...@@ -171,7 +171,7 @@ Operations are used through the following procedure: ...@@ -171,7 +171,7 @@ Operations are used through the following procedure:
(3) If the submitting thread wants to do the work itself, and has marked the (3) If the submitting thread wants to do the work itself, and has marked the
operation with FSCACHE_OP_MYTHREAD, then it should monitor operation with FSCACHE_OP_MYTHREAD, then it should monitor
FSCACHE_OP_WAITING as described above and check the state of the object if FSCACHE_OP_WAITING as described above and check the state of the object if
necessary (the object might have died whilst the thread was waiting). necessary (the object might have died while the thread was waiting).
When it has finished doing its processing, it should call When it has finished doing its processing, it should call
fscache_op_complete() and fscache_put_operation() on it. fscache_op_complete() and fscache_put_operation() on it.
......
...@@ -216,7 +216,7 @@ be called whenever userspace asks for a write(2) on the attribute. ...@@ -216,7 +216,7 @@ be called whenever userspace asks for a write(2) on the attribute.
[struct configfs_bin_attribute] [struct configfs_bin_attribute]
struct configfs_attribute { struct configfs_bin_attribute {
struct configfs_attribute cb_attr; struct configfs_attribute cb_attr;
void *cb_private; void *cb_private;
size_t cb_max_size; size_t cb_max_size;
......
...@@ -359,3 +359,24 @@ encryption of files and directories. ...@@ -359,3 +359,24 @@ encryption of files and directories.
:maxdepth: 2 :maxdepth: 2
fscrypt fscrypt
Pathname lookup
===============
This write-up is based on three articles published at lwn.net:
- <https://lwn.net/Articles/649115/> Pathname lookup in Linux
- <https://lwn.net/Articles/649729/> RCU-walk: faster pathname lookup in Linux
- <https://lwn.net/Articles/650786/> A walk among the symlinks
Written by Neil Brown with help from Al Viro and Jon Corbet.
It has subsequently been updated to reflect changes in the kernel
including:
- per-directory parallel name lookup.
.. toctree::
:maxdepth: 2
path-lookup.rst
<head>
<style> p { max-width:50em} ol, ul {max-width: 40em}</style>
</head>
Pathname lookup in Linux. Introduction to pathname lookup
========================= ===============================
This write-up is based on three articles published at lwn.net:
- <https://lwn.net/Articles/649115/> Pathname lookup in Linux
- <https://lwn.net/Articles/649729/> RCU-walk: faster pathname lookup in Linux
- <https://lwn.net/Articles/650786/> A walk among the symlinks
Written by Neil Brown with help from Al Viro and Jon Corbet.
Introduction
------------
The most obvious aspect of pathname lookup, which very little The most obvious aspect of pathname lookup, which very little
exploration is needed to discover, is that it is complex. There are exploration is needed to discover, is that it is complex. There are
...@@ -32,58 +18,58 @@ distinctions we need to clarify first. ...@@ -32,58 +18,58 @@ distinctions we need to clarify first.
There are two sorts of ... There are two sorts of ...
-------------------------- --------------------------
[`openat()`]: http://man7.org/linux/man-pages/man2/openat.2.html .. _openat: http://man7.org/linux/man-pages/man2/openat.2.html
Pathnames (sometimes "file names"), used to identify objects in the Pathnames (sometimes "file names"), used to identify objects in the
filesystem, will be familiar to most readers. They contain two sorts filesystem, will be familiar to most readers. They contain two sorts
of elements: "slashes" that are sequences of one or more "`/`" of elements: "slashes" that are sequences of one or more "``/``"
characters, and "components" that are sequences of one or more characters, and "components" that are sequences of one or more
non-"`/`" characters. These form two kinds of paths. Those that non-"``/``" characters. These form two kinds of paths. Those that
start with slashes are "absolute" and start from the filesystem root. start with slashes are "absolute" and start from the filesystem root.
The others are "relative" and start from the current directory, or The others are "relative" and start from the current directory, or
from some other location specified by a file descriptor given to a from some other location specified by a file descriptor given to a
"xxx`at`" system call such as "[`openat()`]". "``XXXat``" system call such as `openat() <openat_>`_.
[`execveat()`]: http://man7.org/linux/man-pages/man2/execveat.2.html .. _execveat: http://man7.org/linux/man-pages/man2/execveat.2.html
It is tempting to describe the second kind as starting with a It is tempting to describe the second kind as starting with a
component, but that isn't always accurate: a pathname can lack both component, but that isn't always accurate: a pathname can lack both
slashes and components, it can be empty, in other words. This is slashes and components, it can be empty, in other words. This is
generally forbidden in POSIX, but some of those "xxx`at`" system calls generally forbidden in POSIX, but some of those "xxx``at``" system calls
in Linux permit it when the `AT_EMPTY_PATH` flag is given. For in Linux permit it when the ``AT_EMPTY_PATH`` flag is given. For
example, if you have an open file descriptor on an executable file you example, if you have an open file descriptor on an executable file you
can execute it by calling [`execveat()`] passing the file descriptor, can execute it by calling `execveat() <execveat_>`_ passing
an empty path, and the `AT_EMPTY_PATH` flag. the file descriptor, an empty path, and the ``AT_EMPTY_PATH`` flag.
These paths can be divided into two sections: the final component and These paths can be divided into two sections: the final component and
everything else. The "everything else" is the easy bit. In all cases everything else. The "everything else" is the easy bit. In all cases
it must identify a directory that already exists, otherwise an error it must identify a directory that already exists, otherwise an error
such as `ENOENT` or `ENOTDIR` will be reported. such as ``ENOENT`` or ``ENOTDIR`` will be reported.
The final component is not so simple. Not only do different system The final component is not so simple. Not only do different system
calls interpret it quite differently (e.g. some create it, some do calls interpret it quite differently (e.g. some create it, some do
not), but it might not even exist: neither the empty pathname nor the not), but it might not even exist: neither the empty pathname nor the
pathname that is just slashes have a final component. If it does pathname that is just slashes have a final component. If it does
exist, it could be "`.`" or "`..`" which are handled quite differently exist, it could be "``.``" or "``..``" which are handled quite differently
from other components. from other components.
[POSIX]: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_12 .. _POSIX: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_12
If a pathname ends with a slash, such as "`/tmp/foo/`" it might be If a pathname ends with a slash, such as "``/tmp/foo/``" it might be
tempting to consider that to have an empty final component. In many tempting to consider that to have an empty final component. In many
ways that would lead to correct results, but not always. In ways that would lead to correct results, but not always. In
particular, `mkdir()` and `rmdir()` each create or remove a directory named particular, ``mkdir()`` and ``rmdir()`` each create or remove a directory named
by the final component, and they are required to work with pathnames by the final component, and they are required to work with pathnames
ending in "`/`". According to [POSIX] ending in "``/``". According to POSIX_
> A pathname that contains at least one non- &lt;slash> character and A pathname that contains at least one non- &lt;slash> character and
> that ends with one or more trailing &lt;slash> characters shall not that ends with one or more trailing &lt;slash> characters shall not
> be resolved successfully unless the last pathname component before be resolved successfully unless the last pathname component before
> the trailing <slash> characters names an existing directory or a the trailing <slash> characters names an existing directory or a
> directory entry that is to be created for a directory immediately directory entry that is to be created for a directory immediately
> after the pathname is resolved. after the pathname is resolved.
The Linux pathname walking code (mostly in `fs/namei.c`) deals with The Linux pathname walking code (mostly in ``fs/namei.c``) deals with
all of these issues: breaking the path into components, handling the all of these issues: breaking the path into components, handling the
"everything else" quite separately from the final component, and "everything else" quite separately from the final component, and
checking that the trailing slash is not used where it isn't checking that the trailing slash is not used where it isn't
...@@ -100,15 +86,15 @@ of the possible races are seen most clearly in the context of the ...@@ -100,15 +86,15 @@ of the possible races are seen most clearly in the context of the
"dcache" and an understanding of that is central to understanding "dcache" and an understanding of that is central to understanding
pathname lookup. pathname lookup.
More than just a cache. More than just a cache
----------------------- ----------------------
The "dcache" caches information about names in each filesystem to The "dcache" caches information about names in each filesystem to
make them quickly available for lookup. Each entry (known as a make them quickly available for lookup. Each entry (known as a
"dentry") contains three significant fields: a component name, a "dentry") contains three significant fields: a component name, a
pointer to a parent dentry, and a pointer to the "inode" which pointer to a parent dentry, and a pointer to the "inode" which
contains further information about the object in that parent with contains further information about the object in that parent with
the given name. The inode pointer can be `NULL` indicating that the the given name. The inode pointer can be ``NULL`` indicating that the
name doesn't exist in the parent. While there can be linkage in the name doesn't exist in the parent. While there can be linkage in the
dentry of a directory to the dentries of the children, that linkage is dentry of a directory to the dentries of the children, that linkage is
not used for pathname lookup, and so will not be considered here. not used for pathname lookup, and so will not be considered here.
...@@ -135,7 +121,7 @@ whether remote filesystems like NFS and 9P, or cluster filesystems ...@@ -135,7 +121,7 @@ whether remote filesystems like NFS and 9P, or cluster filesystems
like ocfs2 or cephfs. These filesystems allow the VFS to revalidate like ocfs2 or cephfs. These filesystems allow the VFS to revalidate
cached information, and must provide their own protection against cached information, and must provide their own protection against
awkward races. The VFS can detect these filesystems by the awkward races. The VFS can detect these filesystems by the
`DCACHE_OP_REVALIDATE` flag being set in the dentry. ``DCACHE_OP_REVALIDATE`` flag being set in the dentry.
REF-walk: simple concurrency management with refcounts and spinlocks REF-walk: simple concurrency management with refcounts and spinlocks
-------------------------------------------------------------------- --------------------------------------------------------------------
...@@ -144,22 +130,23 @@ With all of those divisions carefully classified, we can now start ...@@ -144,22 +130,23 @@ With all of those divisions carefully classified, we can now start
looking at the actual process of walking along a path. In particular looking at the actual process of walking along a path. In particular
we will start with the handling of the "everything else" part of a we will start with the handling of the "everything else" part of a
pathname, and focus on the "REF-walk" approach to concurrency pathname, and focus on the "REF-walk" approach to concurrency
management. This code is found in the `link_path_walk()` function, if management. This code is found in the ``link_path_walk()`` function, if
you ignore all the places that only run when "`LOOKUP_RCU`" you ignore all the places that only run when "``LOOKUP_RCU``"
(indicating the use of RCU-walk) is set. (indicating the use of RCU-walk) is set.
[Meet the Lockers]: https://lwn.net/Articles/453685/ .. _Meet the Lockers: https://lwn.net/Articles/453685/
REF-walk is fairly heavy-handed with locks and reference counts. Not REF-walk is fairly heavy-handed with locks and reference counts. Not
as heavy-handed as in the old "big kernel lock" days, but certainly not as heavy-handed as in the old "big kernel lock" days, but certainly not
afraid of taking a lock when one is needed. It uses a variety of afraid of taking a lock when one is needed. It uses a variety of
different concurrency controls. A background understanding of the different concurrency controls. A background understanding of the
various primitives is assumed, or can be gleaned from elsewhere such various primitives is assumed, or can be gleaned from elsewhere such
as in [Meet the Lockers]. as in `Meet the Lockers`_.
The locking mechanisms used by REF-walk include: The locking mechanisms used by REF-walk include:
### dentry->d_lockref ### dentry->d_lockref
~~~~~~~~~~~~~~~~~
This uses the lockref primitive to provide both a spinlock and a This uses the lockref primitive to provide both a spinlock and a
reference count. The special-sauce of this primitive is that the reference count. The special-sauce of this primitive is that the
...@@ -168,49 +155,51 @@ with a single atomic memory operation. ...@@ -168,49 +155,51 @@ with a single atomic memory operation.
Holding a reference on a dentry ensures that the dentry won't suddenly Holding a reference on a dentry ensures that the dentry won't suddenly
be freed and used for something else, so the values in various fields be freed and used for something else, so the values in various fields
will behave as expected. It also protects the `->d_inode` reference will behave as expected. It also protects the ``->d_inode`` reference
to the inode to some extent. to the inode to some extent.
The association between a dentry and its inode is fairly permanent. The association between a dentry and its inode is fairly permanent.
For example, when a file is renamed, the dentry and inode move For example, when a file is renamed, the dentry and inode move
together to the new location. When a file is created the dentry will together to the new location. When a file is created the dentry will
initially be negative (i.e. `d_inode` is `NULL`), and will be assigned initially be negative (i.e. ``d_inode`` is ``NULL``), and will be assigned
to the new inode as part of the act of creation. to the new inode as part of the act of creation.
When a file is deleted, this can be reflected in the cache either by When a file is deleted, this can be reflected in the cache either by
setting `d_inode` to `NULL`, or by removing it from the hash table setting ``d_inode`` to ``NULL``, or by removing it from the hash table
(described shortly) used to look up the name in the parent directory. (described shortly) used to look up the name in the parent directory.
If the dentry is still in use the second option is used as it is If the dentry is still in use the second option is used as it is
perfectly legal to keep using an open file after it has been deleted perfectly legal to keep using an open file after it has been deleted
and having the dentry around helps. If the dentry is not otherwise in and having the dentry around helps. If the dentry is not otherwise in
use (i.e. if the refcount in `d_lockref` is one), only then will use (i.e. if the refcount in ``d_lockref`` is one), only then will
`d_inode` be set to `NULL`. Doing it this way is more efficient for a ``d_inode`` be set to ``NULL``. Doing it this way is more efficient for a
very common case. very common case.
So as long as a counted reference is held to a dentry, a non-`NULL` `->d_inode` So as long as a counted reference is held to a dentry, a non-``NULL`` ``->d_inode``
value will never be changed. value will never be changed.
### dentry->d_lock ### dentry->d_lock
~~~~~~~~~~~~~~
`d_lock` is a synonym for the spinlock that is part of `d_lockref` above. ``d_lock`` is a synonym for the spinlock that is part of ``d_lockref`` above.
For our purposes, holding this lock protects against the dentry being For our purposes, holding this lock protects against the dentry being
renamed or unlinked. In particular, its parent (`d_parent`), and its renamed or unlinked. In particular, its parent (``d_parent``), and its
name (`d_name`) cannot be changed, and it cannot be removed from the name (``d_name``) cannot be changed, and it cannot be removed from the
dentry hash table. dentry hash table.
When looking for a name in a directory, REF-walk takes `d_lock` on When looking for a name in a directory, REF-walk takes ``d_lock`` on
each candidate dentry that it finds in the hash table and then checks each candidate dentry that it finds in the hash table and then checks
that the parent and name are correct. So it doesn't lock the parent that the parent and name are correct. So it doesn't lock the parent
while searching in the cache; it only locks children. while searching in the cache; it only locks children.
When looking for the parent for a given name (to handle "`..`"), When looking for the parent for a given name (to handle "``..``"),
REF-walk can take `d_lock` to get a stable reference to `d_parent`, REF-walk can take ``d_lock`` to get a stable reference to ``d_parent``,
but it first tries a more lightweight approach. As seen in but it first tries a more lightweight approach. As seen in
`dget_parent()`, if a reference can be claimed on the parent, and if ``dget_parent()``, if a reference can be claimed on the parent, and if
subsequently `d_parent` can be seen to have not changed, then there is subsequently ``d_parent`` can be seen to have not changed, then there is
no need to actually take the lock on the child. no need to actually take the lock on the child.
### rename_lock ### rename_lock
~~~~~~~~~~~
Looking up a given name in a given directory involves computing a hash Looking up a given name in a given directory involves computing a hash
from the two values (the name and the dentry of the directory), from the two values (the name and the dentry of the directory),
...@@ -224,71 +213,117 @@ happened to be looking at a dentry that was moved in this way, ...@@ -224,71 +213,117 @@ happened to be looking at a dentry that was moved in this way,
it might end up continuing the search down the wrong chain, it might end up continuing the search down the wrong chain,
and so miss out on part of the correct chain. and so miss out on part of the correct chain.
The name-lookup process (`d_lookup()`) does _not_ try to prevent this The name-lookup process (``d_lookup()``) does _not_ try to prevent this
from happening, but only to detect when it happens. from happening, but only to detect when it happens.
`rename_lock` is a seqlock that is updated whenever any dentry is ``rename_lock`` is a seqlock that is updated whenever any dentry is
renamed. If `d_lookup` finds that a rename happened while it renamed. If ``d_lookup`` finds that a rename happened while it
unsuccessfully scanned a chain in the hash table, it simply tries unsuccessfully scanned a chain in the hash table, it simply tries
again. again.
### inode->i_mutex ### inode->i_rwsem
~~~~~~~~~~~~~~
`i_mutex` is a mutex that serializes all changes to a particular ``i_rwsem`` is a read/write semaphore that serializes all changes to a particular
directory. This ensures that, for example, an `unlink()` and a `rename()` directory. This ensures that, for example, an ``unlink()`` and a ``rename()``
cannot both happen at the same time. It also keeps the directory cannot both happen at the same time. It also keeps the directory
stable while the filesystem is asked to look up a name that is not stable while the filesystem is asked to look up a name that is not
currently in the dcache. currently in the dcache or, optionally, when the list of entries in a
directory is being retrieved with ``readdir()``.
This has a complementary role to that of `d_lock`: `i_mutex` on a This has a complementary role to that of ``d_lock``: ``i_rwsem`` on a
directory protects all of the names in that directory, while `d_lock` directory protects all of the names in that directory, while ``d_lock``
on a name protects just one name in a directory. Most changes to the on a name protects just one name in a directory. Most changes to the
dcache hold `i_mutex` on the relevant directory inode and briefly take dcache hold ``i_rwsem`` on the relevant directory inode and briefly take
`d_lock` on one or more the dentries while the change happens. One ``d_lock`` on one or more the dentries while the change happens. One
exception is when idle dentries are removed from the dcache due to exception is when idle dentries are removed from the dcache due to
memory pressure. This uses `d_lock`, but `i_mutex` plays no role. memory pressure. This uses ``d_lock``, but ``i_rwsem`` plays no role.
The mutex affects pathname lookup in two distinct ways. Firstly it The semaphore affects pathname lookup in two distinct ways. Firstly it
serializes lookup of a name in a directory. `walk_component()` uses prevents changes during lookup of a name in a directory. ``walk_component()`` uses
`lookup_fast()` first which, in turn, checks to see if the name is in the cache, ``lookup_fast()`` first which, in turn, checks to see if the name is in the cache,
using only `d_lock` locking. If the name isn't found, then `walk_component()` using only ``d_lock`` locking. If the name isn't found, then ``walk_component()``
falls back to `lookup_slow()` which takes `i_mutex`, checks again that falls back to ``lookup_slow()`` which takes a shared lock on ``i_rwsem``, checks again that
the name isn't in the cache, and then calls in to the filesystem to get a the name isn't in the cache, and then calls in to the filesystem to get a
definitive answer. A new dentry will be added to the cache regardless of definitive answer. A new dentry will be added to the cache regardless of
the result. the result.
Secondly, when pathname lookup reaches the final component, it will Secondly, when pathname lookup reaches the final component, it will
sometimes need to take `i_mutex` before performing the last lookup so sometimes need to take an exclusive lock on ``i_rwsem`` before performing the last lookup so
that the required exclusion can be achieved. How path lookup chooses that the required exclusion can be achieved. How path lookup chooses
to take, or not take, `i_mutex` is one of the to take, or not take, ``i_rwsem`` is one of the
issues addressed in a subsequent section. issues addressed in a subsequent section.
### mnt->mnt_count ### If two threads attempt to look up the same name at the same time - a
name that is not yet in the dcache - the shared lock on ``i_rwsem`` will
`mnt_count` is a per-CPU reference counter on "`mount`" structures. not prevent them both adding new dentries with the same name. As this
would result in confusion an extra level of interlocking is used,
based around a secondary hash table (``in_lookup_hashtable``) and a
per-dentry flag bit (``DCACHE_PAR_LOOKUP``).
To add a new dentry to the cache while only holding a shared lock on
``i_rwsem``, a thread must call ``d_alloc_parallel()``. This allocates a
dentry, stores the required name and parent in it, checks if there
is already a matching dentry in the primary or secondary hash
tables, and if not, stores the newly allocated dentry in the secondary
hash table, with ``DCACHE_PAR_LOOKUP`` set.
If a matching dentry was found in the primary hash table then that is
returned and the caller can know that it lost a race with some other
thread adding the entry. If no matching dentry is found in either
cache, the newly allocated dentry is returned and the caller can
detect this from the presence of ``DCACHE_PAR_LOOKUP``. In this case it
knows that it has won any race and now is responsible for asking the
filesystem to perform the lookup and find the matching inode. When
the lookup is complete, it must call ``d_lookup_done()`` which clears
the flag and does some other house keeping, including removing the
dentry from the secondary hash table - it will normally have been
added to the primary hash table already. Note that a ``struct
waitqueue_head`` is passed to ``d_alloc_parallel()``, and
``d_lookup_done()`` must be called while this ``waitqueue_head`` is still
in scope.
If a matching dentry is found in the secondary hash table,
``d_alloc_parallel()`` has a little more work to do. It first waits for
``DCACHE_PAR_LOOKUP`` to be cleared, using a wait_queue that was passed
to the instance of ``d_alloc_parallel()`` that won the race and that
will be woken by the call to ``d_lookup_done()``. It then checks to see
if the dentry has now been added to the primary hash table. If it
has, the dentry is returned and the caller just sees that it lost any
race. If it hasn't been added to the primary hash table, the most
likely explanation is that some other dentry was added instead using
``d_splice_alias()``. In any case, ``d_alloc_parallel()`` repeats all the
look ups from the start and will normally return something from the
primary hash table.
mnt->mnt_count
~~~~~~~~~~~~~~
``mnt_count`` is a per-CPU reference counter on "``mount``" structures.
Per-CPU here means that incrementing the count is cheap as it only Per-CPU here means that incrementing the count is cheap as it only
uses CPU-local memory, but checking if the count is zero is expensive as uses CPU-local memory, but checking if the count is zero is expensive as
it needs to check with every CPU. Taking a `mnt_count` reference it needs to check with every CPU. Taking a ``mnt_count`` reference
prevents the mount structure from disappearing as the result of regular prevents the mount structure from disappearing as the result of regular
unmount operations, but does not prevent a "lazy" unmount. So holding unmount operations, but does not prevent a "lazy" unmount. So holding
`mnt_count` doesn't ensure that the mount remains in the namespace and, ``mnt_count`` doesn't ensure that the mount remains in the namespace and,
in particular, doesn't stabilize the link to the mounted-on dentry. It in particular, doesn't stabilize the link to the mounted-on dentry. It
does, however, ensure that the `mount` data structure remains coherent, does, however, ensure that the ``mount`` data structure remains coherent,
and it provides a reference to the root dentry of the mounted and it provides a reference to the root dentry of the mounted
filesystem. So a reference through `->mnt_count` provides a stable filesystem. So a reference through ``->mnt_count`` provides a stable
reference to the mounted dentry, but not the mounted-on dentry. reference to the mounted dentry, but not the mounted-on dentry.
### mount_lock ### mount_lock
~~~~~~~~~~
`mount_lock` is a global seqlock, a bit like `rename_lock`. It can be used to ``mount_lock`` is a global seqlock, a bit like ``rename_lock``. It can be used to
check if any change has been made to any mount points. check if any change has been made to any mount points.
While walking down the tree (away from the root) this lock is used when While walking down the tree (away from the root) this lock is used when
crossing a mount point to check that the crossing was safe. That is, crossing a mount point to check that the crossing was safe. That is,
the value in the seqlock is read, then the code finds the mount that the value in the seqlock is read, then the code finds the mount that
is mounted on the current directory, if there is one, and increments is mounted on the current directory, if there is one, and increments
the `mnt_count`. Finally the value in `mount_lock` is checked against the ``mnt_count``. Finally the value in ``mount_lock`` is checked against
the old value. If there is no change, then the crossing was safe. If there the old value. If there is no change, then the crossing was safe. If there
was a change, the `mnt_count` is decremented and the whole process is was a change, the ``mnt_count`` is decremented and the whole process is
retried. retried.
When walking up the tree (towards the root) by following a ".." link, When walking up the tree (towards the root) by following a ".." link,
...@@ -298,7 +333,8 @@ any changes to any mount points while stepping up. This locking is ...@@ -298,7 +333,8 @@ any changes to any mount points while stepping up. This locking is
needed to stabilize the link to the mounted-on dentry, which the needed to stabilize the link to the mounted-on dentry, which the
refcount on the mount itself doesn't ensure. refcount on the mount itself doesn't ensure.
### RCU ### RCU
~~~
Finally the global (but extremely lightweight) RCU read lock is held Finally the global (but extremely lightweight) RCU read lock is held
from time to time to ensure certain data structures don't get freed from time to time to ensure certain data structures don't get freed
...@@ -307,137 +343,141 @@ unexpectedly. ...@@ -307,137 +343,141 @@ unexpectedly.
In particular it is held while scanning chains in the dcache hash In particular it is held while scanning chains in the dcache hash
table, and the mount point hash table. table, and the mount point hash table.
Bringing it together with `struct nameidata` Bringing it together with ``struct nameidata``
-------------------------------------------- --------------------------------------------
[First edition Unix]: http://minnie.tuhs.org/cgi-bin/utree.pl?file=V1/u2.s .. _First edition Unix: http://minnie.tuhs.org/cgi-bin/utree.pl?file=V1/u2.s
Throughout the process of walking a path, the current status is stored Throughout the process of walking a path, the current status is stored
in a `struct nameidata`, "namei" being the traditional name - dating in a ``struct nameidata``, "namei" being the traditional name - dating
all the way back to [First Edition Unix] - of the function that all the way back to `First Edition Unix`_ - of the function that
converts a "name" to an "inode". `struct nameidata` contains (among converts a "name" to an "inode". ``struct nameidata`` contains (among
other fields): other fields):
### `struct path path` ### ``struct path path``
~~~~~~~~~~~~~~~~~~
A `path` contains a `struct vfsmount` (which is A ``path`` contains a ``struct vfsmount`` (which is
embedded in a `struct mount`) and a `struct dentry`. Together these embedded in a ``struct mount``) and a ``struct dentry``. Together these
record the current status of the walk. They start out referring to the record the current status of the walk. They start out referring to the
starting point (the current working directory, the root directory, or some other starting point (the current working directory, the root directory, or some other
directory identified by a file descriptor), and are updated on each directory identified by a file descriptor), and are updated on each
step. A reference through `d_lockref` and `mnt_count` is always step. A reference through ``d_lockref`` and ``mnt_count`` is always
held. held.
### `struct qstr last` ### ``struct qstr last``
~~~~~~~~~~~~~~~~~~
This is a string together with a length (i.e. _not_ `nul` terminated) This is a string together with a length (i.e. _not_ ``nul`` terminated)
that is the "next" component in the pathname. that is the "next" component in the pathname.
### `int last_type` ### ``int last_type``
~~~~~~~~~~~~~~~
This is one of `LAST_NORM`, `LAST_ROOT`, `LAST_DOT`, `LAST_DOTDOT`, or This is one of ``LAST_NORM``, ``LAST_ROOT``, ``LAST_DOT``, ``LAST_DOTDOT``, or
`LAST_BIND`. The `last` field is only valid if the type is ``LAST_BIND``. The ``last`` field is only valid if the type is
`LAST_NORM`. `LAST_BIND` is used when following a symlink and no ``LAST_NORM``. ``LAST_BIND`` is used when following a symlink and no
components of the symlink have been processed yet. Others should be components of the symlink have been processed yet. Others should be
fairly self-explanatory. fairly self-explanatory.
### `struct path root` ### ``struct path root``
~~~~~~~~~~~~~~~~~~
This is used to hold a reference to the effective root of the This is used to hold a reference to the effective root of the
filesystem. Often that reference won't be needed, so this field is filesystem. Often that reference won't be needed, so this field is
only assigned the first time it is used, or when a non-standard root only assigned the first time it is used, or when a non-standard root
is requested. Keeping a reference in the `nameidata` ensures that is requested. Keeping a reference in the ``nameidata`` ensures that
only one root is in effect for the entire path walk, even if it races only one root is in effect for the entire path walk, even if it races
with a `chroot()` system call. with a ``chroot()`` system call.
The root is needed when either of two conditions holds: (1) either the The root is needed when either of two conditions holds: (1) either the
pathname or a symbolic link starts with a "'/'", or (2) a "`..`" pathname or a symbolic link starts with a "'/'", or (2) a "``..``"
component is being handled, since "`..`" from the root must always stay component is being handled, since "``..``" from the root must always stay
at the root. The value used is usually the current root directory of at the root. The value used is usually the current root directory of
the calling process. An alternate root can be provided as when the calling process. An alternate root can be provided as when
`sysctl()` calls `file_open_root()`, and when NFSv4 or Btrfs call ``sysctl()`` calls ``file_open_root()``, and when NFSv4 or Btrfs call
`mount_subtree()`. In each case a pathname is being looked up in a very ``mount_subtree()``. In each case a pathname is being looked up in a very
specific part of the filesystem, and the lookup must not be allowed to specific part of the filesystem, and the lookup must not be allowed to
escape that subtree. It works a bit like a local `chroot()`. escape that subtree. It works a bit like a local ``chroot()``.
Ignoring the handling of symbolic links, we can now describe the Ignoring the handling of symbolic links, we can now describe the
"`link_path_walk()`" function, which handles the lookup of everything "``link_path_walk()``" function, which handles the lookup of everything
except the final component as: except the final component as:
> Given a path (`name`) and a nameidata structure (`nd`), check that the Given a path (``name``) and a nameidata structure (``nd``), check that the
> current directory has execute permission and then advance `name` current directory has execute permission and then advance ``name``
> over one component while updating `last_type` and `last`. If that over one component while updating ``last_type`` and ``last``. If that
> was the final component, then return, otherwise call was the final component, then return, otherwise call
> `walk_component()` and repeat from the top. ``walk_component()`` and repeat from the top.
`walk_component()` is even easier. If the component is `LAST_DOTS`, ``walk_component()`` is even easier. If the component is ``LAST_DOTS``,
it calls `handle_dots()` which does the necessary locking as already it calls ``handle_dots()`` which does the necessary locking as already
described. If it finds a `LAST_NORM` component it first calls described. If it finds a ``LAST_NORM`` component it first calls
"`lookup_fast()`" which only looks in the dcache, but will ask the "``lookup_fast()``" which only looks in the dcache, but will ask the
filesystem to revalidate the result if it is that sort of filesystem. filesystem to revalidate the result if it is that sort of filesystem.
If that doesn't get a good result, it calls "`lookup_slow()`" which If that doesn't get a good result, it calls "``lookup_slow()``" which
takes the `i_mutex`, rechecks the cache, and then asks the filesystem takes ``i_rwsem``, rechecks the cache, and then asks the filesystem
to find a definitive answer. Each of these will call to find a definitive answer. Each of these will call
`follow_managed()` (as described below) to handle any mount points. ``follow_managed()`` (as described below) to handle any mount points.
In the absence of symbolic links, `walk_component()` creates a new In the absence of symbolic links, ``walk_component()`` creates a new
`struct path` containing a counted reference to the new dentry and a ``struct path`` containing a counted reference to the new dentry and a
reference to the new `vfsmount` which is only counted if it is reference to the new ``vfsmount`` which is only counted if it is
different from the previous `vfsmount`. It then calls different from the previous ``vfsmount``. It then calls
`path_to_nameidata()` to install the new `struct path` in the ``path_to_nameidata()`` to install the new ``struct path`` in the
`struct nameidata` and drop the unneeded references. ``struct nameidata`` and drop the unneeded references.
This "hand-over-hand" sequencing of getting a reference to the new This "hand-over-hand" sequencing of getting a reference to the new
dentry before dropping the reference to the previous dentry may dentry before dropping the reference to the previous dentry may
seem obvious, but is worth pointing out so that we will recognize its seem obvious, but is worth pointing out so that we will recognize its
analogue in the "RCU-walk" version. analogue in the "RCU-walk" version.
Handling the final component. Handling the final component
----------------------------- ----------------------------
`link_path_walk()` only walks as far as setting `nd->last` and ``link_path_walk()`` only walks as far as setting ``nd->last`` and
`nd->last_type` to refer to the final component of the path. It does ``nd->last_type`` to refer to the final component of the path. It does
not call `walk_component()` that last time. Handling that final not call ``walk_component()`` that last time. Handling that final
component remains for the caller to sort out. Those callers are component remains for the caller to sort out. Those callers are
`path_lookupat()`, `path_parentat()`, `path_mountpoint()` and ``path_lookupat()``, ``path_parentat()``, ``path_mountpoint()`` and
`path_openat()` each of which handles the differing requirements of ``path_openat()`` each of which handles the differing requirements of
different system calls. different system calls.
`path_parentat()` is clearly the simplest - it just wraps a little bit ``path_parentat()`` is clearly the simplest - it just wraps a little bit
of housekeeping around `link_path_walk()` and returns the parent of housekeeping around ``link_path_walk()`` and returns the parent
directory and final component to the caller. The caller will be either directory and final component to the caller. The caller will be either
aiming to create a name (via `filename_create()`) or remove or rename aiming to create a name (via ``filename_create()``) or remove or rename
a name (in which case `user_path_parent()` is used). They will use a name (in which case ``user_path_parent()`` is used). They will use
`i_mutex` to exclude other changes while they validate and then ``i_rwsem`` to exclude other changes while they validate and then
perform their operation. perform their operation.
`path_lookupat()` is nearly as simple - it is used when an existing ``path_lookupat()`` is nearly as simple - it is used when an existing
object is wanted such as by `stat()` or `chmod()`. It essentially just object is wanted such as by ``stat()`` or ``chmod()``. It essentially just
calls `walk_component()` on the final component through a call to calls ``walk_component()`` on the final component through a call to
`lookup_last()`. `path_lookupat()` returns just the final dentry. ``lookup_last()``. ``path_lookupat()`` returns just the final dentry.
`path_mountpoint()` handles the special case of unmounting which must ``path_mountpoint()`` handles the special case of unmounting which must
not try to revalidate the mounted filesystem. It effectively not try to revalidate the mounted filesystem. It effectively
contains, through a call to `mountpoint_last()`, an alternate contains, through a call to ``mountpoint_last()``, an alternate
implementation of `lookup_slow()` which skips that step. This is implementation of ``lookup_slow()`` which skips that step. This is
important when unmounting a filesystem that is inaccessible, such as important when unmounting a filesystem that is inaccessible, such as
one provided by a dead NFS server. one provided by a dead NFS server.
Finally `path_openat()` is used for the `open()` system call; it Finally ``path_openat()`` is used for the ``open()`` system call; it
contains, in support functions starting with "`do_last()`", all the contains, in support functions starting with "``do_last()``", all the
complexity needed to handle the different subtleties of O_CREAT (with complexity needed to handle the different subtleties of O_CREAT (with
or without O_EXCL), final "`/`" characters, and trailing symbolic or without O_EXCL), final "``/``" characters, and trailing symbolic
links. We will revisit this in the final part of this series, which links. We will revisit this in the final part of this series, which
focuses on those symbolic links. "`do_last()`" will sometimes, but focuses on those symbolic links. "``do_last()``" will sometimes, but
not always, take `i_mutex`, depending on what it finds. not always, take ``i_rwsem``, depending on what it finds.
Each of these, or the functions which call them, need to be alert to Each of these, or the functions which call them, need to be alert to
the possibility that the final component is not `LAST_NORM`. If the the possibility that the final component is not ``LAST_NORM``. If the
goal of the lookup is to create something, then any value for goal of the lookup is to create something, then any value for
`last_type` other than `LAST_NORM` will result in an error. For ``last_type`` other than ``LAST_NORM`` will result in an error. For
example if `path_parentat()` reports `LAST_DOTDOT`, then the caller example if ``path_parentat()`` reports ``LAST_DOTDOT``, then the caller
won't try to create that name. They also check for trailing slashes won't try to create that name. They also check for trailing slashes
by testing `last.name[last.len]`. If there is any character beyond by testing ``last.name[last.len]``. If there is any character beyond
the final component, it must be a trailing slash. the final component, it must be a trailing slash.
Revalidation and automounts Revalidation and automounts
...@@ -448,12 +488,12 @@ process not yet covered. One is the handling of stale cache entries ...@@ -448,12 +488,12 @@ process not yet covered. One is the handling of stale cache entries
and the other is automounts. and the other is automounts.
On filesystems that require it, the lookup routines will call the On filesystems that require it, the lookup routines will call the
`->d_revalidate()` dentry method to ensure that the cached information ``->d_revalidate()`` dentry method to ensure that the cached information
is current. This will often confirm validity or update a few details is current. This will often confirm validity or update a few details
from a server. In some cases it may find that there has been change from a server. In some cases it may find that there has been change
further up the path and that something that was thought to be valid further up the path and that something that was thought to be valid
previously isn't really. When this happens the lookup of the whole previously isn't really. When this happens the lookup of the whole
path is aborted and retried with the "`LOOKUP_REVAL`" flag set. This path is aborted and retried with the "``LOOKUP_REVAL``" flag set. This
forces revalidation to be more thorough. We will see more details of forces revalidation to be more thorough. We will see more details of
this retry process in the next article. this retry process in the next article.
...@@ -465,52 +505,55 @@ tree, but a few notes specifically related to path lookup are in order ...@@ -465,52 +505,55 @@ tree, but a few notes specifically related to path lookup are in order
here. here.
The Linux VFS has a concept of "managed" dentries which is reflected The Linux VFS has a concept of "managed" dentries which is reflected
in function names such as "`follow_managed()`". There are three in function names such as "``follow_managed()``". There are three
potentially interesting things about these dentries corresponding potentially interesting things about these dentries corresponding
to three different flags that might be set in `dentry->d_flags`: to three different flags that might be set in ``dentry->d_flags``:
### `DCACHE_MANAGE_TRANSIT` ### ``DCACHE_MANAGE_TRANSIT``
~~~~~~~~~~~~~~~~~~~~~~~
If this flag has been set, then the filesystem has requested that the If this flag has been set, then the filesystem has requested that the
`d_manage()` dentry operation be called before handling any possible ``d_manage()`` dentry operation be called before handling any possible
mount point. This can perform two particular services: mount point. This can perform two particular services:
It can block to avoid races. If an automount point is being It can block to avoid races. If an automount point is being
unmounted, the `d_manage()` function will usually wait for that unmounted, the ``d_manage()`` function will usually wait for that
process to complete before letting the new lookup proceed and possibly process to complete before letting the new lookup proceed and possibly
trigger a new automount. trigger a new automount.
It can selectively allow only some processes to transit through a It can selectively allow only some processes to transit through a
mount point. When a server process is managing automounts, it may mount point. When a server process is managing automounts, it may
need to access a directory without triggering normal automount need to access a directory without triggering normal automount
processing. That server process can identify itself to the `autofs` processing. That server process can identify itself to the ``autofs``
filesystem, which will then give it a special pass through filesystem, which will then give it a special pass through
`d_manage()` by returning `-EISDIR`. ``d_manage()`` by returning ``-EISDIR``.
### `DCACHE_MOUNTED` ### ``DCACHE_MOUNTED``
~~~~~~~~~~~~~~~~
This flag is set on every dentry that is mounted on. As Linux This flag is set on every dentry that is mounted on. As Linux
supports multiple filesystem namespaces, it is possible that the supports multiple filesystem namespaces, it is possible that the
dentry may not be mounted on in *this* namespace, just in some dentry may not be mounted on in *this* namespace, just in some
other. So this flag is seen as a hint, not a promise. other. So this flag is seen as a hint, not a promise.
If this flag is set, and `d_manage()` didn't return `-EISDIR`, If this flag is set, and ``d_manage()`` didn't return ``-EISDIR``,
`lookup_mnt()` is called to examine the mount hash table (honoring the ``lookup_mnt()`` is called to examine the mount hash table (honoring the
`mount_lock` described earlier) and possibly return a new `vfsmount` ``mount_lock`` described earlier) and possibly return a new ``vfsmount``
and a new `dentry` (both with counted references). and a new ``dentry`` (both with counted references).
### `DCACHE_NEED_AUTOMOUNT` ### ``DCACHE_NEED_AUTOMOUNT``
~~~~~~~~~~~~~~~~~~~~~~~
If `d_manage()` allowed us to get this far, and `lookup_mnt()` didn't If ``d_manage()`` allowed us to get this far, and ``lookup_mnt()`` didn't
find a mount point, then this flag causes the `d_automount()` dentry find a mount point, then this flag causes the ``d_automount()`` dentry
operation to be called. operation to be called.
The `d_automount()` operation can be arbitrarily complex and may The ``d_automount()`` operation can be arbitrarily complex and may
communicate with server processes etc. but it should ultimately either communicate with server processes etc. but it should ultimately either
report that there was an error, that there was nothing to mount, or report that there was an error, that there was nothing to mount, or
should provide an updated `struct path` with new `dentry` and `vfsmount`. should provide an updated ``struct path`` with new ``dentry`` and ``vfsmount``.
In the latter case, `finish_automount()` will be called to safely In the latter case, ``finish_automount()`` will be called to safely
install the new mount point into the mount table. install the new mount point into the mount table.
There is no new locking of import here and it is important that no There is no new locking of import here and it is important that no
...@@ -567,7 +610,7 @@ isn't in the cache, then it tries to stop gracefully and switch to ...@@ -567,7 +610,7 @@ isn't in the cache, then it tries to stop gracefully and switch to
REF-walk. REF-walk.
This stopping requires getting a counted reference on the current This stopping requires getting a counted reference on the current
`vfsmount` and `dentry`, and ensuring that these are still valid - ``vfsmount`` and ``dentry``, and ensuring that these are still valid -
that a path walk with REF-walk would have found the same entries. that a path walk with REF-walk would have found the same entries.
This is an invariant that RCU-walk must guarantee. It can only make This is an invariant that RCU-walk must guarantee. It can only make
decisions, such as selecting the next step, that are decisions which decisions, such as selecting the next step, that are decisions which
...@@ -578,21 +621,21 @@ RCU-walk finds it cannot stop gracefully, it simply gives up and ...@@ -578,21 +621,21 @@ RCU-walk finds it cannot stop gracefully, it simply gives up and
restarts from the top with REF-walk. restarts from the top with REF-walk.
This pattern of "try RCU-walk, if that fails try REF-walk" can be This pattern of "try RCU-walk, if that fails try REF-walk" can be
clearly seen in functions like `filename_lookup()`, clearly seen in functions like ``filename_lookup()``,
`filename_parentat()`, `filename_mountpoint()`, ``filename_parentat()``, ``filename_mountpoint()``,
`do_filp_open()`, and `do_file_open_root()`. These five ``do_filp_open()``, and ``do_file_open_root()``. These five
correspond roughly to the four `path_`* functions we met earlier, correspond roughly to the four ``path_``* functions we met earlier,
each of which calls `link_path_walk()`. The `path_*` functions are each of which calls ``link_path_walk()``. The ``path_*`` functions are
called using different mode flags until a mode is found which works. called using different mode flags until a mode is found which works.
They are first called with `LOOKUP_RCU` set to request "RCU-walk". If They are first called with ``LOOKUP_RCU`` set to request "RCU-walk". If
that fails with the error `ECHILD` they are called again with no that fails with the error ``ECHILD`` they are called again with no
special flag to request "REF-walk". If either of those report the special flag to request "REF-walk". If either of those report the
error `ESTALE` a final attempt is made with `LOOKUP_REVAL` set (and no error ``ESTALE`` a final attempt is made with ``LOOKUP_REVAL`` set (and no
`LOOKUP_RCU`) to ensure that entries found in the cache are forcibly ``LOOKUP_RCU``) to ensure that entries found in the cache are forcibly
revalidated - normally entries are only revalidated if the filesystem revalidated - normally entries are only revalidated if the filesystem
determines that they are too old to trust. determines that they are too old to trust.
The `LOOKUP_RCU` attempt may drop that flag internally and switch to The ``LOOKUP_RCU`` attempt may drop that flag internally and switch to
REF-walk, but will never then try to switch back to RCU-walk. Places REF-walk, but will never then try to switch back to RCU-walk. Places
that trip up RCU-walk are much more likely to be near the leaves and that trip up RCU-walk are much more likely to be near the leaves and
so it is very unlikely that there will be much, if any, benefit from so it is very unlikely that there will be much, if any, benefit from
...@@ -602,7 +645,7 @@ RCU and seqlocks: fast and light ...@@ -602,7 +645,7 @@ RCU and seqlocks: fast and light
-------------------------------- --------------------------------
RCU is, unsurprisingly, critical to RCU-walk mode. The RCU is, unsurprisingly, critical to RCU-walk mode. The
`rcu_read_lock()` is held for the entire time that RCU-walk is walking ``rcu_read_lock()`` is held for the entire time that RCU-walk is walking
down a path. The particular guarantee it provides is that the key down a path. The particular guarantee it provides is that the key
data structures - dentries, inodes, super_blocks, and mounts - will data structures - dentries, inodes, super_blocks, and mounts - will
not be freed while the lock is held. They might be unlinked or not be freed while the lock is held. They might be unlinked or
...@@ -614,7 +657,7 @@ seqlocks. ...@@ -614,7 +657,7 @@ seqlocks.
As we saw above, REF-walk holds a counted reference to the current As we saw above, REF-walk holds a counted reference to the current
dentry and the current vfsmount, and does not release those references dentry and the current vfsmount, and does not release those references
before taking references to the "next" dentry or vfsmount. It also before taking references to the "next" dentry or vfsmount. It also
sometimes takes the `d_lock` spinlock. These references and locks are sometimes takes the ``d_lock`` spinlock. These references and locks are
taken to prevent certain changes from happening. RCU-walk must not taken to prevent certain changes from happening. RCU-walk must not
take those references or locks and so cannot prevent such changes. take those references or locks and so cannot prevent such changes.
Instead, it checks to see if a change has been made, and aborts or Instead, it checks to see if a change has been made, and aborts or
...@@ -624,123 +667,126 @@ To preserve the invariant mentioned above (that RCU-walk may only make ...@@ -624,123 +667,126 @@ To preserve the invariant mentioned above (that RCU-walk may only make
decisions that REF-walk could have made), it must make the checks at decisions that REF-walk could have made), it must make the checks at
or near the same places that REF-walk holds the references. So, when or near the same places that REF-walk holds the references. So, when
REF-walk increments a reference count or takes a spinlock, RCU-walk REF-walk increments a reference count or takes a spinlock, RCU-walk
samples the status of a seqlock using `read_seqcount_begin()` or a samples the status of a seqlock using ``read_seqcount_begin()`` or a
similar function. When REF-walk decrements the count or drops the similar function. When REF-walk decrements the count or drops the
lock, RCU-walk checks if the sampled status is still valid using lock, RCU-walk checks if the sampled status is still valid using
`read_seqcount_retry()` or similar. ``read_seqcount_retry()`` or similar.
However, there is a little bit more to seqlocks than that. If However, there is a little bit more to seqlocks than that. If
RCU-walk accesses two different fields in a seqlock-protected RCU-walk accesses two different fields in a seqlock-protected
structure, or accesses the same field twice, there is no a priori structure, or accesses the same field twice, there is no a priori
guarantee of any consistency between those accesses. When consistency guarantee of any consistency between those accesses. When consistency
is needed - which it usually is - RCU-walk must take a copy and then is needed - which it usually is - RCU-walk must take a copy and then
use `read_seqcount_retry()` to validate that copy. use ``read_seqcount_retry()`` to validate that copy.
`read_seqcount_retry()` not only checks the sequence number, but also ``read_seqcount_retry()`` not only checks the sequence number, but also
imposes a memory barrier so that no memory-read instruction from imposes a memory barrier so that no memory-read instruction from
*before* the call can be delayed until *after* the call, either by the *before* the call can be delayed until *after* the call, either by the
CPU or by the compiler. A simple example of this can be seen in CPU or by the compiler. A simple example of this can be seen in
`slow_dentry_cmp()` which, for filesystems which do not use simple ``slow_dentry_cmp()`` which, for filesystems which do not use simple
byte-wise name equality, calls into the filesystem to compare a name byte-wise name equality, calls into the filesystem to compare a name
against a dentry. The length and name pointer are copied into local against a dentry. The length and name pointer are copied into local
variables, then `read_seqcount_retry()` is called to confirm the two variables, then ``read_seqcount_retry()`` is called to confirm the two
are consistent, and only then is `->d_compare()` called. When are consistent, and only then is ``->d_compare()`` called. When
standard filename comparison is used, `dentry_cmp()` is called standard filename comparison is used, ``dentry_cmp()`` is called
instead. Notably it does _not_ use `read_seqcount_retry()`, but instead. Notably it does _not_ use ``read_seqcount_retry()``, but
instead has a large comment explaining why the consistency guarantee instead has a large comment explaining why the consistency guarantee
isn't necessary. A subsequent `read_seqcount_retry()` will be isn't necessary. A subsequent ``read_seqcount_retry()`` will be
sufficient to catch any problem that could occur at this point. sufficient to catch any problem that could occur at this point.
With that little refresher on seqlocks out of the way we can look at With that little refresher on seqlocks out of the way we can look at
the bigger picture of how RCU-walk uses seqlocks. the bigger picture of how RCU-walk uses seqlocks.
### `mount_lock` and `nd->m_seq` ### ``mount_lock`` and ``nd->m_seq``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
We already met the `mount_lock` seqlock when REF-walk used it to We already met the ``mount_lock`` seqlock when REF-walk used it to
ensure that crossing a mount point is performed safely. RCU-walk uses ensure that crossing a mount point is performed safely. RCU-walk uses
it for that too, but for quite a bit more. it for that too, but for quite a bit more.
Instead of taking a counted reference to each `vfsmount` as it Instead of taking a counted reference to each ``vfsmount`` as it
descends the tree, RCU-walk samples the state of `mount_lock` at the descends the tree, RCU-walk samples the state of ``mount_lock`` at the
start of the walk and stores this initial sequence number in the start of the walk and stores this initial sequence number in the
`struct nameidata` in the `m_seq` field. This one lock and one ``struct nameidata`` in the ``m_seq`` field. This one lock and one
sequence number are used to validate all accesses to all `vfsmounts`, sequence number are used to validate all accesses to all ``vfsmounts``,
and all mount point crossings. As changes to the mount table are and all mount point crossings. As changes to the mount table are
relatively rare, it is reasonable to fall back on REF-walk any time relatively rare, it is reasonable to fall back on REF-walk any time
that any "mount" or "unmount" happens. that any "mount" or "unmount" happens.
`m_seq` is checked (using `read_seqretry()`) at the end of an RCU-walk ``m_seq`` is checked (using ``read_seqretry()``) at the end of an RCU-walk
sequence, whether switching to REF-walk for the rest of the path or sequence, whether switching to REF-walk for the rest of the path or
when the end of the path is reached. It is also checked when stepping when the end of the path is reached. It is also checked when stepping
down over a mount point (in `__follow_mount_rcu()`) or up (in down over a mount point (in ``__follow_mount_rcu()``) or up (in
`follow_dotdot_rcu()`). If it is ever found to have changed, the ``follow_dotdot_rcu()``). If it is ever found to have changed, the
whole RCU-walk sequence is aborted and the path is processed again by whole RCU-walk sequence is aborted and the path is processed again by
REF-walk. REF-walk.
If RCU-walk finds that `mount_lock` hasn't changed then it can be sure If RCU-walk finds that ``mount_lock`` hasn't changed then it can be sure
that, had REF-walk taken counted references on each vfsmount, the that, had REF-walk taken counted references on each vfsmount, the
results would have been the same. This ensures the invariant holds, results would have been the same. This ensures the invariant holds,
at least for vfsmount structures. at least for vfsmount structures.
### `dentry->d_seq` and `nd->seq`. ### ``dentry->d_seq`` and ``nd->seq``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In place of taking a count or lock on `d_reflock`, RCU-walk samples In place of taking a count or lock on ``d_reflock``, RCU-walk samples
the per-dentry `d_seq` seqlock, and stores the sequence number in the the per-dentry ``d_seq`` seqlock, and stores the sequence number in the
`seq` field of the nameidata structure, so `nd->seq` should always be ``seq`` field of the nameidata structure, so ``nd->seq`` should always be
the current sequence number of `nd->dentry`. This number needs to be the current sequence number of ``nd->dentry``. This number needs to be
revalidated after copying, and before using, the name, parent, or revalidated after copying, and before using, the name, parent, or
inode of the dentry. inode of the dentry.
The handling of the name we have already looked at, and the parent is The handling of the name we have already looked at, and the parent is
only accessed in `follow_dotdot_rcu()` which fairly trivially follows only accessed in ``follow_dotdot_rcu()`` which fairly trivially follows
the required pattern, though it does so for three different cases. the required pattern, though it does so for three different cases.
When not at a mount point, `d_parent` is followed and its `d_seq` is When not at a mount point, ``d_parent`` is followed and its ``d_seq`` is
collected. When we are at a mount point, we instead follow the collected. When we are at a mount point, we instead follow the
`mnt->mnt_mountpoint` link to get a new dentry and collect its ``mnt->mnt_mountpoint`` link to get a new dentry and collect its
`d_seq`. Then, after finally finding a `d_parent` to follow, we must ``d_seq``. Then, after finally finding a ``d_parent`` to follow, we must
check if we have landed on a mount point and, if so, must find that check if we have landed on a mount point and, if so, must find that
mount point and follow the `mnt->mnt_root` link. This would imply a mount point and follow the ``mnt->mnt_root`` link. This would imply a
somewhat unusual, but certainly possible, circumstance where the somewhat unusual, but certainly possible, circumstance where the
starting point of the path lookup was in part of the filesystem that starting point of the path lookup was in part of the filesystem that
was mounted on, and so not visible from the root. was mounted on, and so not visible from the root.
The inode pointer, stored in `->d_inode`, is a little more The inode pointer, stored in ``->d_inode``, is a little more
interesting. The inode will always need to be accessed at least interesting. The inode will always need to be accessed at least
twice, once to determine if it is NULL and once to verify access twice, once to determine if it is NULL and once to verify access
permissions. Symlink handling requires a validated inode pointer too. permissions. Symlink handling requires a validated inode pointer too.
Rather than revalidating on each access, a copy is made on the first Rather than revalidating on each access, a copy is made on the first
access and it is stored in the `inode` field of `nameidata` from where access and it is stored in the ``inode`` field of ``nameidata`` from where
it can be safely accessed without further validation. it can be safely accessed without further validation.
`lookup_fast()` is the only lookup routine that is used in RCU-mode, ``lookup_fast()`` is the only lookup routine that is used in RCU-mode,
`lookup_slow()` being too slow and requiring locks. It is in ``lookup_slow()`` being too slow and requiring locks. It is in
`lookup_fast()` that we find the important "hand over hand" tracking ``lookup_fast()`` that we find the important "hand over hand" tracking
of the current dentry. of the current dentry.
The current `dentry` and current `seq` number are passed to The current ``dentry`` and current ``seq`` number are passed to
`__d_lookup_rcu()` which, on success, returns a new `dentry` and a ``__d_lookup_rcu()`` which, on success, returns a new ``dentry`` and a
new `seq` number. `lookup_fast()` then copies the inode pointer and new ``seq`` number. ``lookup_fast()`` then copies the inode pointer and
revalidates the new `seq` number. It then validates the old `dentry` revalidates the new ``seq`` number. It then validates the old ``dentry``
with the old `seq` number one last time and only then continues. This with the old ``seq`` number one last time and only then continues. This
process of getting the `seq` number of the new dentry and then process of getting the ``seq`` number of the new dentry and then
checking the `seq` number of the old exactly mirrors the process of checking the ``seq`` number of the old exactly mirrors the process of
getting a counted reference to the new dentry before dropping that for getting a counted reference to the new dentry before dropping that for
the old dentry which we saw in REF-walk. the old dentry which we saw in REF-walk.
### No `inode->i_mutex` or even `rename_lock` ### No ``inode->i_rwsem`` or even ``rename_lock``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A mutex is a fairly heavyweight lock that can only be taken when it is A semaphore is a fairly heavyweight lock that can only be taken when it is
permissible to sleep. As `rcu_read_lock()` forbids sleeping, permissible to sleep. As ``rcu_read_lock()`` forbids sleeping,
`inode->i_mutex` plays no role in RCU-walk. If some other thread does ``inode->i_rwsem`` plays no role in RCU-walk. If some other thread does
take `i_mutex` and modifies the directory in a way that RCU-walk needs take ``i_rwsem`` and modifies the directory in a way that RCU-walk needs
to notice, the result will be either that RCU-walk fails to find the to notice, the result will be either that RCU-walk fails to find the
dentry that it is looking for, or it will find a dentry which dentry that it is looking for, or it will find a dentry which
`read_seqretry()` won't validate. In either case it will drop down to ``read_seqretry()`` won't validate. In either case it will drop down to
REF-walk mode which can take whatever locks are needed. REF-walk mode which can take whatever locks are needed.
Though `rename_lock` could be used by RCU-walk as it doesn't require Though ``rename_lock`` could be used by RCU-walk as it doesn't require
any sleeping, RCU-walk doesn't bother. REF-walk uses `rename_lock` to any sleeping, RCU-walk doesn't bother. REF-walk uses ``rename_lock`` to
protect against the possibility of hash chains in the dcache changing protect against the possibility of hash chains in the dcache changing
while they are being searched. This can result in failing to find while they are being searched. This can result in failing to find
something that actually is there. When RCU-walk fails to find something that actually is there. When RCU-walk fails to find
...@@ -749,57 +795,57 @@ already drops down to REF-walk and tries again with appropriate ...@@ -749,57 +795,57 @@ already drops down to REF-walk and tries again with appropriate
locking. This neatly handles all cases, so adding extra checks on locking. This neatly handles all cases, so adding extra checks on
rename_lock would bring no significant value. rename_lock would bring no significant value.
`unlazy walk()` and `complete_walk()` ``unlazy walk()`` and ``complete_walk()``
------------------------------------- -------------------------------------
That "dropping down to REF-walk" typically involves a call to That "dropping down to REF-walk" typically involves a call to
`unlazy_walk()`, so named because "RCU-walk" is also sometimes ``unlazy_walk()``, so named because "RCU-walk" is also sometimes
referred to as "lazy walk". `unlazy_walk()` is called when referred to as "lazy walk". ``unlazy_walk()`` is called when
following the path down to the current vfsmount/dentry pair seems to following the path down to the current vfsmount/dentry pair seems to
have proceeded successfully, but the next step is problematic. This have proceeded successfully, but the next step is problematic. This
can happen if the next name cannot be found in the dcache, if can happen if the next name cannot be found in the dcache, if
permission checking or name revalidation couldn't be achieved while permission checking or name revalidation couldn't be achieved while
the `rcu_read_lock()` is held (which forbids sleeping), if an the ``rcu_read_lock()`` is held (which forbids sleeping), if an
automount point is found, or in a couple of cases involving symlinks. automount point is found, or in a couple of cases involving symlinks.
It is also called from `complete_walk()` when the lookup has reached It is also called from ``complete_walk()`` when the lookup has reached
the final component, or the very end of the path, depending on which the final component, or the very end of the path, depending on which
particular flavor of lookup is used. particular flavor of lookup is used.
Other reasons for dropping out of RCU-walk that do not trigger a call Other reasons for dropping out of RCU-walk that do not trigger a call
to `unlazy_walk()` are when some inconsistency is found that cannot be to ``unlazy_walk()`` are when some inconsistency is found that cannot be
handled immediately, such as `mount_lock` or one of the `d_seq` handled immediately, such as ``mount_lock`` or one of the ``d_seq``
seqlocks reporting a change. In these cases the relevant function seqlocks reporting a change. In these cases the relevant function
will return `-ECHILD` which will percolate up until it triggers a new will return ``-ECHILD`` which will percolate up until it triggers a new
attempt from the top using REF-walk. attempt from the top using REF-walk.
For those cases where `unlazy_walk()` is an option, it essentially For those cases where ``unlazy_walk()`` is an option, it essentially
takes a reference on each of the pointers that it holds (vfsmount, takes a reference on each of the pointers that it holds (vfsmount,
dentry, and possibly some symbolic links) and then verifies that the dentry, and possibly some symbolic links) and then verifies that the
relevant seqlocks have not been changed. If there have been changes, relevant seqlocks have not been changed. If there have been changes,
it, too, aborts with `-ECHILD`, otherwise the transition to REF-walk it, too, aborts with ``-ECHILD``, otherwise the transition to REF-walk
has been a success and the lookup process continues. has been a success and the lookup process continues.
Taking a reference on those pointers is not quite as simple as just Taking a reference on those pointers is not quite as simple as just
incrementing a counter. That works to take a second reference if you incrementing a counter. That works to take a second reference if you
already have one (often indirectly through another object), but it already have one (often indirectly through another object), but it
isn't sufficient if you don't actually have a counted reference at isn't sufficient if you don't actually have a counted reference at
all. For `dentry->d_lockref`, it is safe to increment the reference all. For ``dentry->d_lockref``, it is safe to increment the reference
counter to get a reference unless it has been explicitly marked as counter to get a reference unless it has been explicitly marked as
"dead" which involves setting the counter to `-128`. "dead" which involves setting the counter to ``-128``.
`lockref_get_not_dead()` achieves this. ``lockref_get_not_dead()`` achieves this.
For `mnt->mnt_count` it is safe to take a reference as long as For ``mnt->mnt_count`` it is safe to take a reference as long as
`mount_lock` is then used to validate the reference. If that ``mount_lock`` is then used to validate the reference. If that
validation fails, it may *not* be safe to just drop that reference in validation fails, it may *not* be safe to just drop that reference in
the standard way of calling `mnt_put()` - an unmount may have the standard way of calling ``mnt_put()`` - an unmount may have
progressed too far. So the code in `legitimize_mnt()`, when it progressed too far. So the code in ``legitimize_mnt()``, when it
finds that the reference it got might not be safe, checks the finds that the reference it got might not be safe, checks the
`MNT_SYNC_UMOUNT` flag to determine if a simple `mnt_put()` is ``MNT_SYNC_UMOUNT`` flag to determine if a simple ``mnt_put()`` is
correct, or if it should just decrement the count and pretend none of correct, or if it should just decrement the count and pretend none of
this ever happened. this ever happened.
Taking care in filesystems Taking care in filesystems
--------------------------- --------------------------
RCU-walk depends almost entirely on cached information and often will RCU-walk depends almost entirely on cached information and often will
not call into the filesystem at all. However there are two places, not call into the filesystem at all. However there are two places,
...@@ -809,26 +855,26 @@ careful. ...@@ -809,26 +855,26 @@ careful.
If the filesystem has non-standard permission-checking requirements - If the filesystem has non-standard permission-checking requirements -
such as a networked filesystem which may need to check with the server such as a networked filesystem which may need to check with the server
- the `i_op->permission` interface might be called during RCU-walk. - the ``i_op->permission`` interface might be called during RCU-walk.
In this case an extra "`MAY_NOT_BLOCK`" flag is passed so that it In this case an extra "``MAY_NOT_BLOCK``" flag is passed so that it
knows not to sleep, but to return `-ECHILD` if it cannot complete knows not to sleep, but to return ``-ECHILD`` if it cannot complete
promptly. `i_op->permission` is given the inode pointer, not the promptly. ``i_op->permission`` is given the inode pointer, not the
dentry, so it doesn't need to worry about further consistency checks. dentry, so it doesn't need to worry about further consistency checks.
However if it accesses any other filesystem data structures, it must However if it accesses any other filesystem data structures, it must
ensure they are safe to be accessed with only the `rcu_read_lock()` ensure they are safe to be accessed with only the ``rcu_read_lock()``
held. This typically means they must be freed using `kfree_rcu()` or held. This typically means they must be freed using ``kfree_rcu()`` or
similar. similar.
[`READ_ONCE()`]: https://lwn.net/Articles/624126/ .. _READ_ONCE: https://lwn.net/Articles/624126/
If the filesystem may need to revalidate dcache entries, then If the filesystem may need to revalidate dcache entries, then
`d_op->d_revalidate` may be called in RCU-walk too. This interface ``d_op->d_revalidate`` may be called in RCU-walk too. This interface
*is* passed the dentry but does not have access to the `inode` or the *is* passed the dentry but does not have access to the ``inode`` or the
`seq` number from the `nameidata`, so it needs to be extra careful ``seq`` number from the ``nameidata``, so it needs to be extra careful
when accessing fields in the dentry. This "extra care" typically when accessing fields in the dentry. This "extra care" typically
involves using [`READ_ONCE()`] to access fields, and verifying the involves using `READ_ONCE() <READ_ONCE_>`_ to access fields, and verifying the
result is not NULL before using it. This pattern can be seen in result is not NULL before using it. This pattern can be seen in
`nfs_lookup_revalidate()`. ``nfs_lookup_revalidate()``.
A pair of patterns A pair of patterns
------------------ ------------------
...@@ -839,14 +885,14 @@ being aware of. ...@@ -839,14 +885,14 @@ being aware of.
The first is "try quickly and check, if that fails try slowly". We The first is "try quickly and check, if that fails try slowly". We
can see that in the high-level approach of first trying RCU-walk and can see that in the high-level approach of first trying RCU-walk and
then trying REF-walk, and in places where `unlazy_walk()` is used to then trying REF-walk, and in places where ``unlazy_walk()`` is used to
switch to REF-walk for the rest of the path. We also saw it earlier switch to REF-walk for the rest of the path. We also saw it earlier
in `dget_parent()` when following a "`..`" link. It tries a quick way in ``dget_parent()`` when following a "``..``" link. It tries a quick way
to get a reference, then falls back to taking locks if needed. to get a reference, then falls back to taking locks if needed.
The second pattern is "try quickly and check, if that fails try The second pattern is "try quickly and check, if that fails try
again - repeatedly". This is seen with the use of `rename_lock` and again - repeatedly". This is seen with the use of ``rename_lock`` and
`mount_lock` in REF-walk. RCU-walk doesn't make use of this pattern - ``mount_lock`` in REF-walk. RCU-walk doesn't make use of this pattern -
if anything goes wrong it is much safer to just abort and try a more if anything goes wrong it is much safer to just abort and try a more
sedate approach. sedate approach.
...@@ -882,8 +928,8 @@ Conceptually, symbolic links could be handled by editing the path. If ...@@ -882,8 +928,8 @@ Conceptually, symbolic links could be handled by editing the path. If
a component name refers to a symbolic link, then that component is a component name refers to a symbolic link, then that component is
replaced by the body of the link and, if that body starts with a '/', replaced by the body of the link and, if that body starts with a '/',
then all preceding parts of the path are discarded. This is what the then all preceding parts of the path are discarded. This is what the
"`readlink -f`" command does, though it also edits out "`.`" and "``readlink -f``" command does, though it also edits out "``.``" and
"`..`" components. "``..``" components.
Directly editing the path string is not really necessary when looking Directly editing the path string is not really necessary when looking
up a path, and discarding early components is pointless as they aren't up a path, and discarding early components is pointless as they aren't
...@@ -899,19 +945,19 @@ There are two reasons for placing limits on how many symlinks can ...@@ -899,19 +945,19 @@ There are two reasons for placing limits on how many symlinks can
occur in a single path lookup. The most obvious is to avoid loops. occur in a single path lookup. The most obvious is to avoid loops.
If a symlink referred to itself either directly or through If a symlink referred to itself either directly or through
intermediaries, then following the symlink can never complete intermediaries, then following the symlink can never complete
successfully - the error `ELOOP` must be returned. Loops can be successfully - the error ``ELOOP`` must be returned. Loops can be
detected without imposing limits, but limits are the simplest solution detected without imposing limits, but limits are the simplest solution
and, given the second reason for restriction, quite sufficient. and, given the second reason for restriction, quite sufficient.
[outlined recently]: http://thread.gmane.org/gmane.linux.kernel/1934390/focus=1934550 .. _outlined recently: http://thread.gmane.org/gmane.linux.kernel/1934390/focus=1934550
The second reason was [outlined recently] by Linus: The second reason was `outlined recently`_ by Linus:
> Because it's a latency and DoS issue too. We need to react well to Because it's a latency and DoS issue too. We need to react well to
> true loops, but also to "very deep" non-loops. It's not about memory true loops, but also to "very deep" non-loops. It's not about memory
> use, it's about users triggering unreasonable CPU resources. use, it's about users triggering unreasonable CPU resources.
Linux imposes a limit on the length of any pathname: `PATH_MAX`, which Linux imposes a limit on the length of any pathname: ``PATH_MAX``, which
is 4096. There are a number of reasons for this limit; not letting the is 4096. There are a number of reasons for this limit; not letting the
kernel spend too much time on just one path is one of them. With kernel spend too much time on just one path is one of them. With
symbolic links you can effectively generate much longer paths so some symbolic links you can effectively generate much longer paths so some
...@@ -921,7 +967,7 @@ further limit of eight on the maximum depth of recursion, but that was ...@@ -921,7 +967,7 @@ further limit of eight on the maximum depth of recursion, but that was
raised to 40 when a separate stack was implemented, so there is now raised to 40 when a separate stack was implemented, so there is now
just the one limit. just the one limit.
The `nameidata` structure that we met in an earlier article contains a The ``nameidata`` structure that we met in an earlier article contains a
small stack that can be used to store the remaining part of up to two small stack that can be used to store the remaining part of up to two
symlinks. In many cases this will be sufficient. If it isn't, a symlinks. In many cases this will be sufficient. If it isn't, a
separate stack is allocated with room for 40 symlinks. Pathname separate stack is allocated with room for 40 symlinks. Pathname
...@@ -941,13 +987,13 @@ to external storage. It is particularly important for RCU-walk to be ...@@ -941,13 +987,13 @@ to external storage. It is particularly important for RCU-walk to be
able to find and temporarily hold onto these cached entries, so that able to find and temporarily hold onto these cached entries, so that
it doesn't need to drop down into REF-walk. it doesn't need to drop down into REF-walk.
[object-oriented design pattern]: https://lwn.net/Articles/446317/ .. _object-oriented design pattern: https://lwn.net/Articles/446317/
While each filesystem is free to make its own choice, symlinks are While each filesystem is free to make its own choice, symlinks are
typically stored in one of two places. Short symlinks are often typically stored in one of two places. Short symlinks are often
stored directly in the inode. When a filesystem allocates a `struct stored directly in the inode. When a filesystem allocates a ``struct
inode` it typically allocates extra space to store private data (a inode`` it typically allocates extra space to store private data (a
common [object-oriented design pattern] in the kernel). This will common `object-oriented design pattern`_ in the kernel). This will
sometimes include space for a symlink. The other common location is sometimes include space for a symlink. The other common location is
in the page cache, which normally stores the content of files. The in the page cache, which normally stores the content of files. The
pathname in a symlink can be seen as the content of that symlink and pathname in a symlink can be seen as the content of that symlink and
...@@ -962,13 +1008,13 @@ the inode which, itself, is protected by RCU or by a counted reference ...@@ -962,13 +1008,13 @@ the inode which, itself, is protected by RCU or by a counted reference
on the dentry. This means that the mechanisms that pathname lookup on the dentry. This means that the mechanisms that pathname lookup
uses to access the dcache and icache (inode cache) safely are quite uses to access the dcache and icache (inode cache) safely are quite
sufficient for accessing some cached symlinks safely. In these cases, sufficient for accessing some cached symlinks safely. In these cases,
the `i_link` pointer in the inode is set to point to wherever the the ``i_link`` pointer in the inode is set to point to wherever the
symlink is stored and it can be accessed directly whenever needed. symlink is stored and it can be accessed directly whenever needed.
When the symlink is stored in the page cache or elsewhere, the When the symlink is stored in the page cache or elsewhere, the
situation is not so straightforward. A reference on a dentry or even situation is not so straightforward. A reference on a dentry or even
on an inode does not imply any reference on cached pages of that on an inode does not imply any reference on cached pages of that
inode, and even an `rcu_read_lock()` is not sufficient to ensure that inode, and even an ``rcu_read_lock()`` is not sufficient to ensure that
a page will not disappear. So for these symlinks the pathname lookup a page will not disappear. So for these symlinks the pathname lookup
code needs to ask the filesystem to provide a stable reference and, code needs to ask the filesystem to provide a stable reference and,
significantly, needs to release that reference when it is finished significantly, needs to release that reference when it is finished
...@@ -978,48 +1024,48 @@ Taking a reference to a cache page is often possible even in RCU-walk ...@@ -978,48 +1024,48 @@ Taking a reference to a cache page is often possible even in RCU-walk
mode. It does require making changes to memory, which is best avoided, mode. It does require making changes to memory, which is best avoided,
but that isn't necessarily a big cost and it is better than dropping but that isn't necessarily a big cost and it is better than dropping
out of RCU-walk mode completely. Even filesystems that allocate out of RCU-walk mode completely. Even filesystems that allocate
space to copy the symlink into can use `GFP_ATOMIC` to often successfully space to copy the symlink into can use ``GFP_ATOMIC`` to often successfully
allocate memory without the need to drop out of RCU-walk. If a allocate memory without the need to drop out of RCU-walk. If a
filesystem cannot successfully get a reference in RCU-walk mode, it filesystem cannot successfully get a reference in RCU-walk mode, it
must return `-ECHILD` and `unlazy_walk()` will be called to return to must return ``-ECHILD`` and ``unlazy_walk()`` will be called to return to
REF-walk mode in which the filesystem is allowed to sleep. REF-walk mode in which the filesystem is allowed to sleep.
The place for all this to happen is the `i_op->follow_link()` inode The place for all this to happen is the ``i_op->follow_link()`` inode
method. In the present mainline code this is never actually called in method. In the present mainline code this is never actually called in
RCU-walk mode as the rewrite is not quite complete. It is likely that RCU-walk mode as the rewrite is not quite complete. It is likely that
in a future release this method will be passed an `inode` pointer when in a future release this method will be passed an ``inode`` pointer when
called in RCU-walk mode so it both (1) knows to be careful, and (2) has the called in RCU-walk mode so it both (1) knows to be careful, and (2) has the
validated pointer. Much like the `i_op->permission()` method we validated pointer. Much like the ``i_op->permission()`` method we
looked at previously, `->follow_link()` would need to be careful that looked at previously, ``->follow_link()`` would need to be careful that
all the data structures it references are safe to be accessed while all the data structures it references are safe to be accessed while
holding no counted reference, only the RCU lock. Though getting a holding no counted reference, only the RCU lock. Though getting a
reference with `->follow_link()` is not yet done in RCU-walk mode, the reference with ``->follow_link()`` is not yet done in RCU-walk mode, the
code is ready to release the reference when that does happen. code is ready to release the reference when that does happen.
This need to drop the reference to a symlink adds significant This need to drop the reference to a symlink adds significant
complexity. It requires a reference to the inode so that the complexity. It requires a reference to the inode so that the
`i_op->put_link()` inode operation can be called. In REF-walk, that ``i_op->put_link()`` inode operation can be called. In REF-walk, that
reference is kept implicitly through a reference to the dentry, so reference is kept implicitly through a reference to the dentry, so
keeping the `struct path` of the symlink is easiest. For RCU-walk, keeping the ``struct path`` of the symlink is easiest. For RCU-walk,
the pointer to the inode is kept separately. To allow switching from the pointer to the inode is kept separately. To allow switching from
RCU-walk back to REF-walk in the middle of processing nested symlinks RCU-walk back to REF-walk in the middle of processing nested symlinks
we also need the seq number for the dentry so we can confirm that we also need the seq number for the dentry so we can confirm that
switching back was safe. switching back was safe.
Finally, when providing a reference to a symlink, the filesystem also Finally, when providing a reference to a symlink, the filesystem also
provides an opaque "cookie" that must be passed to `->put_link()` so that it provides an opaque "cookie" that must be passed to ``->put_link()`` so that it
knows what to free. This might be the allocated memory area, or a knows what to free. This might be the allocated memory area, or a
pointer to the `struct page` in the page cache, or something else pointer to the ``struct page`` in the page cache, or something else
completely. Only the filesystem knows what it is. completely. Only the filesystem knows what it is.
In order for the reference to each symlink to be dropped when the walk completes, In order for the reference to each symlink to be dropped when the walk completes,
whether in RCU-walk or REF-walk, the symlink stack needs to contain, whether in RCU-walk or REF-walk, the symlink stack needs to contain,
along with the path remnants: along with the path remnants:
- the `struct path` to provide a reference to the inode in REF-walk - the ``struct path`` to provide a reference to the inode in REF-walk
- the `struct inode *` to provide a reference to the inode in RCU-walk - the ``struct inode *`` to provide a reference to the inode in RCU-walk
- the `seq` to allow the path to be safely switched from RCU-walk to REF-walk - the ``seq`` to allow the path to be safely switched from RCU-walk to REF-walk
- the `cookie` that tells `->put_path()` what to put. - the ``cookie`` that tells ``->put_path()`` what to put.
This means that each entry in the symlink stack needs to hold five This means that each entry in the symlink stack needs to hold five
pointers and an integer instead of just one pointer (the path pointers and an integer instead of just one pointer (the path
...@@ -1028,28 +1074,28 @@ with 40 entries it adds up to 1600 bytes total, which is less than ...@@ -1028,28 +1074,28 @@ with 40 entries it adds up to 1600 bytes total, which is less than
half a page. So it might seem like a lot, but is by no means half a page. So it might seem like a lot, but is by no means
excessive. excessive.
Note that, in a given stack frame, the path remnant (`name`) is not Note that, in a given stack frame, the path remnant (``name``) is not
part of the symlink that the other fields refer to. It is the remnant part of the symlink that the other fields refer to. It is the remnant
to be followed once that symlink has been fully parsed. to be followed once that symlink has been fully parsed.
Following the symlink Following the symlink
--------------------- ---------------------
The main loop in `link_path_walk()` iterates seamlessly over all The main loop in ``link_path_walk()`` iterates seamlessly over all
components in the path and all of the non-final symlinks. As symlinks components in the path and all of the non-final symlinks. As symlinks
are processed, the `name` pointer is adjusted to point to a new are processed, the ``name`` pointer is adjusted to point to a new
symlink, or is restored from the stack, so that much of the loop symlink, or is restored from the stack, so that much of the loop
doesn't need to notice. Getting this `name` variable on and off the doesn't need to notice. Getting this ``name`` variable on and off the
stack is very straightforward; pushing and popping the references is stack is very straightforward; pushing and popping the references is
a little more complex. a little more complex.
When a symlink is found, `walk_component()` returns the value `1` When a symlink is found, ``walk_component()`` returns the value ``1``
(`0` is returned for any other sort of success, and a negative number (``0`` is returned for any other sort of success, and a negative number
is, as usual, an error indicator). This causes `get_link()` to be is, as usual, an error indicator). This causes ``get_link()`` to be
called; it then gets the link from the filesystem. Providing that called; it then gets the link from the filesystem. Providing that
operation is successful, the old path `name` is placed on the stack, operation is successful, the old path ``name`` is placed on the stack,
and the new value is used as the `name` for a while. When the end of and the new value is used as the ``name`` for a while. When the end of
the path is found (i.e. `*name` is `'\0'`) the old `name` is restored the path is found (i.e. ``*name`` is ``'\0'``) the old ``name`` is restored
off the stack and path walking continues. off the stack and path walking continues.
Pushing and popping the reference pointers (inode, cookie, etc.) is more Pushing and popping the reference pointers (inode, cookie, etc.) is more
...@@ -1060,112 +1106,113 @@ the symlink-just-found to avoid leaving empty path remnants that would ...@@ -1060,112 +1106,113 @@ the symlink-just-found to avoid leaving empty path remnants that would
just get in the way. just get in the way.
It is most convenient to push the new symlink references onto the It is most convenient to push the new symlink references onto the
stack in `walk_component()` immediately when the symlink is found; stack in ``walk_component()`` immediately when the symlink is found;
`walk_component()` is also the last piece of code that needs to look at the ``walk_component()`` is also the last piece of code that needs to look at the
old symlink as it walks that last component. So it is quite old symlink as it walks that last component. So it is quite
convenient for `walk_component()` to release the old symlink and pop convenient for ``walk_component()`` to release the old symlink and pop
the references just before pushing the reference information for the the references just before pushing the reference information for the
new symlink. It is guided in this by two flags; `WALK_GET`, which new symlink. It is guided in this by two flags; ``WALK_GET``, which
gives it permission to follow a symlink if it finds one, and gives it permission to follow a symlink if it finds one, and
`WALK_PUT`, which tells it to release the current symlink after it has been ``WALK_PUT``, which tells it to release the current symlink after it has been
followed. `WALK_PUT` is tested first, leading to a call to followed. ``WALK_PUT`` is tested first, leading to a call to
`put_link()`. `WALK_GET` is tested subsequently (by ``put_link()``. ``WALK_GET`` is tested subsequently (by
`should_follow_link()`) leading to a call to `pick_link()` which sets ``should_follow_link()``) leading to a call to ``pick_link()`` which sets
up the stack frame. up the stack frame.
### Symlinks with no final component ### Symlinks with no final component
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A pair of special-case symlinks deserve a little further explanation. A pair of special-case symlinks deserve a little further explanation.
Both result in a new `struct path` (with mount and dentry) being set Both result in a new ``struct path`` (with mount and dentry) being set
up in the `nameidata`, and result in `get_link()` returning `NULL`. up in the ``nameidata``, and result in ``get_link()`` returning ``NULL``.
The more obvious case is a symlink to "`/`". All symlinks starting The more obvious case is a symlink to "``/``". All symlinks starting
with "`/`" are detected in `get_link()` which resets the `nameidata` with "``/``" are detected in ``get_link()`` which resets the ``nameidata``
to point to the effective filesystem root. If the symlink only to point to the effective filesystem root. If the symlink only
contains "`/`" then there is nothing more to do, no components at all, contains "``/``" then there is nothing more to do, no components at all,
so `NULL` is returned to indicate that the symlink can be released and so ``NULL`` is returned to indicate that the symlink can be released and
the stack frame discarded. the stack frame discarded.
The other case involves things in `/proc` that look like symlinks but The other case involves things in ``/proc`` that look like symlinks but
aren't really. aren't really::
> $ ls -l /proc/self/fd/1 $ ls -l /proc/self/fd/1
> lrwx------ 1 neilb neilb 64 Jun 13 10:19 /proc/self/fd/1 -> /dev/pts/4 lrwx------ 1 neilb neilb 64 Jun 13 10:19 /proc/self/fd/1 -> /dev/pts/4
Every open file descriptor in any process is represented in `/proc` by Every open file descriptor in any process is represented in ``/proc`` by
something that looks like a symlink. It is really a reference to the something that looks like a symlink. It is really a reference to the
target file, not just the name of it. When you `readlink` these target file, not just the name of it. When you ``readlink`` these
objects you get a name that might refer to the same file - unless it objects you get a name that might refer to the same file - unless it
has been unlinked or mounted over. When `walk_component()` follows has been unlinked or mounted over. When ``walk_component()`` follows
one of these, the `->follow_link()` method in "procfs" doesn't return one of these, the ``->follow_link()`` method in "procfs" doesn't return
a string name, but instead calls `nd_jump_link()` which updates the a string name, but instead calls ``nd_jump_link()`` which updates the
`nameidata` in place to point to that target. `->follow_link()` then ``nameidata`` in place to point to that target. ``->follow_link()`` then
returns `NULL`. Again there is no final component and `get_link()` returns ``NULL``. Again there is no final component and ``get_link()``
reports this by leaving the `last_type` field of `nameidata` as reports this by leaving the ``last_type`` field of ``nameidata`` as
`LAST_BIND`. ``LAST_BIND``.
Following the symlink in the final component Following the symlink in the final component
-------------------------------------------- --------------------------------------------
All this leads to `link_path_walk()` walking down every component, and All this leads to ``link_path_walk()`` walking down every component, and
following all symbolic links it finds, until it reaches the final following all symbolic links it finds, until it reaches the final
component. This is just returned in the `last` field of `nameidata`. component. This is just returned in the ``last`` field of ``nameidata``.
For some callers, this is all they need; they want to create that For some callers, this is all they need; they want to create that
`last` name if it doesn't exist or give an error if it does. Other ``last`` name if it doesn't exist or give an error if it does. Other
callers will want to follow a symlink if one is found, and possibly callers will want to follow a symlink if one is found, and possibly
apply special handling to the last component of that symlink, rather apply special handling to the last component of that symlink, rather
than just the last component of the original file name. These callers than just the last component of the original file name. These callers
potentially need to call `link_path_walk()` again and again on potentially need to call ``link_path_walk()`` again and again on
successive symlinks until one is found that doesn't point to another successive symlinks until one is found that doesn't point to another
symlink. symlink.
This case is handled by the relevant caller of `link_path_walk()`, such as This case is handled by the relevant caller of ``link_path_walk()``, such as
`path_lookupat()` using a loop that calls `link_path_walk()`, and then ``path_lookupat()`` using a loop that calls ``link_path_walk()``, and then
handles the final component. If the final component is a symlink handles the final component. If the final component is a symlink
that needs to be followed, then `trailing_symlink()` is called to set that needs to be followed, then ``trailing_symlink()`` is called to set
things up properly and the loop repeats, calling `link_path_walk()` things up properly and the loop repeats, calling ``link_path_walk()``
again. This could loop as many as 40 times if the last component of again. This could loop as many as 40 times if the last component of
each symlink is another symlink. each symlink is another symlink.
The various functions that examine the final component and possibly The various functions that examine the final component and possibly
report that it is a symlink are `lookup_last()`, `mountpoint_last()` report that it is a symlink are ``lookup_last()``, ``mountpoint_last()``
and `do_last()`, each of which use the same convention as and ``do_last()``, each of which use the same convention as
`walk_component()` of returning `1` if a symlink was found that needs ``walk_component()`` of returning ``1`` if a symlink was found that needs
to be followed. to be followed.
Of these, `do_last()` is the most interesting as it is used for Of these, ``do_last()`` is the most interesting as it is used for
opening a file. Part of `do_last()` runs with `i_mutex` held and this opening a file. Part of ``do_last()`` runs with ``i_rwsem`` held and this
part is in a separate function: `lookup_open()`. part is in a separate function: ``lookup_open()``.
Explaining `do_last()` completely is beyond the scope of this article, Explaining ``do_last()`` completely is beyond the scope of this article,
but a few highlights should help those interested in exploring the but a few highlights should help those interested in exploring the
code. code.
1. Rather than just finding the target file, `do_last()` needs to open 1. Rather than just finding the target file, ``do_last()`` needs to open
it. If the file was found in the dcache, then `vfs_open()` is used for it. If the file was found in the dcache, then ``vfs_open()`` is used for
this. If not, then `lookup_open()` will either call `atomic_open()` (if this. If not, then ``lookup_open()`` will either call ``atomic_open()`` (if
the filesystem provides it) to combine the final lookup with the open, or the filesystem provides it) to combine the final lookup with the open, or
will perform the separate `lookup_real()` and `vfs_create()` steps will perform the separate ``lookup_real()`` and ``vfs_create()`` steps
directly. In the later case the actual "open" of this newly found or directly. In the later case the actual "open" of this newly found or
created file will be performed by `vfs_open()`, just as if the name created file will be performed by ``vfs_open()``, just as if the name
were found in the dcache. were found in the dcache.
2. `vfs_open()` can fail with `-EOPENSTALE` if the cached information 2. ``vfs_open()`` can fail with ``-EOPENSTALE`` if the cached information
wasn't quite current enough. Rather than restarting the lookup from wasn't quite current enough. Rather than restarting the lookup from
the top with `LOOKUP_REVAL` set, `lookup_open()` is called instead, the top with ``LOOKUP_REVAL`` set, ``lookup_open()`` is called instead,
giving the filesystem a chance to resolve small inconsistencies. giving the filesystem a chance to resolve small inconsistencies.
If that doesn't work, only then is the lookup restarted from the top. If that doesn't work, only then is the lookup restarted from the top.
3. An open with O_CREAT **does** follow a symlink in the final component, 3. An open with O_CREAT **does** follow a symlink in the final component,
unlike other creation system calls (like `mkdir`). So the sequence: unlike other creation system calls (like ``mkdir``). So the sequence::
> ln -s bar /tmp/foo ln -s bar /tmp/foo
> echo hello > /tmp/foo echo hello > /tmp/foo
will create a file called `/tmp/bar`. This is not permitted if will create a file called ``/tmp/bar``. This is not permitted if
`O_EXCL` is set but otherwise is handled for an O_CREAT open much ``O_EXCL`` is set but otherwise is handled for an O_CREAT open much
like for a non-creating open: `should_follow_link()` returns `1`, and like for a non-creating open: ``should_follow_link()`` returns ``1``, and
so does `do_last()` so that `trailing_symlink()` gets called and the so does ``do_last()`` so that ``trailing_symlink()`` gets called and the
open process continues on the symlink that was found. open process continues on the symlink that was found.
Updating the access time Updating the access time
...@@ -1180,110 +1227,112 @@ footprints are best kept to a minimum. ...@@ -1180,110 +1227,112 @@ footprints are best kept to a minimum.
One other place where walking down a symlink can involve leaving One other place where walking down a symlink can involve leaving
footprints in a way that doesn't affect directories is in updating access times. footprints in a way that doesn't affect directories is in updating access times.
In Unix (and Linux) every filesystem object has a "last accessed In Unix (and Linux) every filesystem object has a "last accessed
time", or "`atime`". Passing through a directory to access a file time", or "``atime``". Passing through a directory to access a file
within is not considered to be an access for the purposes of within is not considered to be an access for the purposes of
`atime`; only listing the contents of a directory can update its `atime`. ``atime``; only listing the contents of a directory can update its ``atime``.
Symlinks are different it seems. Both reading a symlink (with `readlink()`) Symlinks are different it seems. Both reading a symlink (with ``readlink()``)
and looking up a symlink on the way to some other destination can and looking up a symlink on the way to some other destination can
update the atime on that symlink. update the atime on that symlink.
[clearest statement]: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_08 .. _clearest statement: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_08
It is not clear why this is the case; POSIX has little to say on the It is not clear why this is the case; POSIX has little to say on the
subject. The [clearest statement] is that, if a particular implementation subject. The `clearest statement`_ is that, if a particular implementation
updates a timestamp in a place not specified by POSIX, this must be updates a timestamp in a place not specified by POSIX, this must be
documented "except that any changes caused by pathname resolution need documented "except that any changes caused by pathname resolution need
not be documented". This seems to imply that POSIX doesn't really not be documented". This seems to imply that POSIX doesn't really
care about access-time updates during pathname lookup. care about access-time updates during pathname lookup.
[Linux 1.3.87]: https://git.kernel.org/cgit/linux/kernel/git/history/history.git/diff/fs/ext2/symlink.c?id=f806c6db77b8eaa6e00dcfb6b567706feae8dbb8 .. _Linux 1.3.87: https://git.kernel.org/cgit/linux/kernel/git/history/history.git/diff/fs/ext2/symlink.c?id=f806c6db77b8eaa6e00dcfb6b567706feae8dbb8
An examination of history shows that prior to [Linux 1.3.87], the ext2 An examination of history shows that prior to `Linux 1.3.87`_, the ext2
filesystem, at least, didn't update atime when following a link. filesystem, at least, didn't update atime when following a link.
Unfortunately we have no record of why that behavior was changed. Unfortunately we have no record of why that behavior was changed.
In any case, access time must now be updated and that operation can be In any case, access time must now be updated and that operation can be
quite complex. Trying to stay in RCU-walk while doing it is best quite complex. Trying to stay in RCU-walk while doing it is best
avoided. Fortunately it is often permitted to skip the `atime` avoided. Fortunately it is often permitted to skip the ``atime``
update. Because `atime` updates cause performance problems in various update. Because ``atime`` updates cause performance problems in various
areas, Linux supports the `relatime` mount option, which generally areas, Linux supports the ``relatime`` mount option, which generally
limits the updates of `atime` to once per day on files that aren't limits the updates of ``atime`` to once per day on files that aren't
being changed (and symlinks never change once created). Even without being changed (and symlinks never change once created). Even without
`relatime`, many filesystems record `atime` with a one-second ``relatime``, many filesystems record ``atime`` with a one-second
granularity, so only one update per second is required. granularity, so only one update per second is required.
It is easy to test if an `atime` update is needed while in RCU-walk It is easy to test if an ``atime`` update is needed while in RCU-walk
mode and, if it isn't, the update can be skipped and RCU-walk mode mode and, if it isn't, the update can be skipped and RCU-walk mode
continues. Only when an `atime` update is actually required does the continues. Only when an ``atime`` update is actually required does the
path walk drop down to REF-walk. All of this is handled in the path walk drop down to REF-walk. All of this is handled in the
`get_link()` function. ``get_link()`` function.
A few flags A few flags
----------- -----------
A suitable way to wrap up this tour of pathname walking is to list A suitable way to wrap up this tour of pathname walking is to list
the various flags that can be stored in the `nameidata` to guide the the various flags that can be stored in the ``nameidata`` to guide the
lookup process. Many of these are only meaningful on the final lookup process. Many of these are only meaningful on the final
component, others reflect the current state of the pathname lookup. component, others reflect the current state of the pathname lookup.
And then there is `LOOKUP_EMPTY`, which doesn't fit conceptually with And then there is ``LOOKUP_EMPTY``, which doesn't fit conceptually with
the others. If this is not set, an empty pathname causes an error the others. If this is not set, an empty pathname causes an error
very early on. If it is set, empty pathnames are not considered to be very early on. If it is set, empty pathnames are not considered to be
an error. an error.
### Global state flags ### Global state flags
~~~~~~~~~~~~~~~~~~
We have already met two global state flags: `LOOKUP_RCU` and We have already met two global state flags: ``LOOKUP_RCU`` and
`LOOKUP_REVAL`. These select between one of three overall approaches ``LOOKUP_REVAL``. These select between one of three overall approaches
to lookup: RCU-walk, REF-walk, and REF-walk with forced revalidation. to lookup: RCU-walk, REF-walk, and REF-walk with forced revalidation.
`LOOKUP_PARENT` indicates that the final component hasn't been reached ``LOOKUP_PARENT`` indicates that the final component hasn't been reached
yet. This is primarily used to tell the audit subsystem the full yet. This is primarily used to tell the audit subsystem the full
context of a particular access being audited. context of a particular access being audited.
`LOOKUP_ROOT` indicates that the `root` field in the `nameidata` was ``LOOKUP_ROOT`` indicates that the ``root`` field in the ``nameidata`` was
provided by the caller, so it shouldn't be released when it is no provided by the caller, so it shouldn't be released when it is no
longer needed. longer needed.
`LOOKUP_JUMPED` means that the current dentry was chosen not because ``LOOKUP_JUMPED`` means that the current dentry was chosen not because
it had the right name but for some other reason. This happens when it had the right name but for some other reason. This happens when
following "`..`", following a symlink to `/`, crossing a mount point following "``..``", following a symlink to ``/``, crossing a mount point
or accessing a "`/proc/$PID/fd/$FD`" symlink. In this case the or accessing a "``/proc/$PID/fd/$FD``" symlink. In this case the
filesystem has not been asked to revalidate the name (with filesystem has not been asked to revalidate the name (with
`d_revalidate()`). In such cases the inode may still need to be ``d_revalidate()``). In such cases the inode may still need to be
revalidated, so `d_op->d_weak_revalidate()` is called if revalidated, so ``d_op->d_weak_revalidate()`` is called if
`LOOKUP_JUMPED` is set when the look completes - which may be at the ``LOOKUP_JUMPED`` is set when the look completes - which may be at the
final component or, when creating, unlinking, or renaming, at the penultimate component. final component or, when creating, unlinking, or renaming, at the penultimate component.
### Final-component flags ### Final-component flags
~~~~~~~~~~~~~~~~~~~~~
Some of these flags are only set when the final component is being Some of these flags are only set when the final component is being
considered. Others are only checked for when considering that final considered. Others are only checked for when considering that final
component. component.
`LOOKUP_AUTOMOUNT` ensures that, if the final component is an automount ``LOOKUP_AUTOMOUNT`` ensures that, if the final component is an automount
point, then the mount is triggered. Some operations would trigger it point, then the mount is triggered. Some operations would trigger it
anyway, but operations like `stat()` deliberately don't. `statfs()` anyway, but operations like ``stat()`` deliberately don't. ``statfs()``
needs to trigger the mount but otherwise behaves a lot like `stat()`, so needs to trigger the mount but otherwise behaves a lot like ``stat()``, so
it sets `LOOKUP_AUTOMOUNT`, as does "`quotactl()`" and the handling of it sets ``LOOKUP_AUTOMOUNT``, as does "``quotactl()``" and the handling of
"`mount --bind`". "``mount --bind``".
`LOOKUP_FOLLOW` has a similar function to `LOOKUP_AUTOMOUNT` but for ``LOOKUP_FOLLOW`` has a similar function to ``LOOKUP_AUTOMOUNT`` but for
symlinks. Some system calls set or clear it implicitly, while symlinks. Some system calls set or clear it implicitly, while
others have API flags such as `AT_SYMLINK_FOLLOW` and others have API flags such as ``AT_SYMLINK_FOLLOW`` and
`UMOUNT_NOFOLLOW` to control it. Its effect is similar to ``UMOUNT_NOFOLLOW`` to control it. Its effect is similar to
`WALK_GET` that we already met, but it is used in a different way. ``WALK_GET`` that we already met, but it is used in a different way.
`LOOKUP_DIRECTORY` insists that the final component is a directory. ``LOOKUP_DIRECTORY`` insists that the final component is a directory.
Various callers set this and it is also set when the final component Various callers set this and it is also set when the final component
is found to be followed by a slash. is found to be followed by a slash.
Finally `LOOKUP_OPEN`, `LOOKUP_CREATE`, `LOOKUP_EXCL`, and Finally ``LOOKUP_OPEN``, ``LOOKUP_CREATE``, ``LOOKUP_EXCL``, and
`LOOKUP_RENAME_TARGET` are not used directly by the VFS but are made ``LOOKUP_RENAME_TARGET`` are not used directly by the VFS but are made
available to the filesystem and particularly the `->d_revalidate()` available to the filesystem and particularly the ``->d_revalidate()``
method. A filesystem can choose not to bother revalidating too hard method. A filesystem can choose not to bother revalidating too hard
if it knows that it will be asked to open or create the file soon. if it knows that it will be asked to open or create the file soon.
These flags were previously useful for `->lookup()` too but with the These flags were previously useful for ``->lookup()`` too but with the
introduction of `->atomic_open()` they are less relevant there. introduction of ``->atomic_open()`` they are less relevant there.
End of the road End of the road
--------------- ---------------
......
...@@ -125,6 +125,13 @@ process running on the system, which is named after the process ID (PID). ...@@ -125,6 +125,13 @@ process running on the system, which is named after the process ID (PID).
The link self points to the process reading the file system. Each process The link self points to the process reading the file system. Each process
subdirectory has the entries listed in Table 1-1. subdirectory has the entries listed in Table 1-1.
Note that an open a file descriptor to /proc/<pid> or to any of its
contained files or subdirectories does not prevent <pid> being reused
for some other process in the event that <pid> exits. Operations on
open /proc/<pid> file descriptors corresponding to dead processes
never act on any new process that the kernel may, through chance, have
also assigned the process ID <pid>. Instead, operations on these FDs
usually fail with ESRCH.
Table 1-1: Process specific entries in /proc Table 1-1: Process specific entries in /proc
.............................................................................. ..............................................................................
...@@ -194,8 +201,10 @@ read the file /proc/PID/status: ...@@ -194,8 +201,10 @@ read the file /proc/PID/status:
CapPrm: 0000000000000000 CapPrm: 0000000000000000
CapEff: 0000000000000000 CapEff: 0000000000000000
CapBnd: ffffffffffffffff CapBnd: ffffffffffffffff
CapAmb: 0000000000000000
NoNewPrivs: 0 NoNewPrivs: 0
Seccomp: 0 Seccomp: 0
Speculation_Store_Bypass: thread vulnerable
voluntary_ctxt_switches: 0 voluntary_ctxt_switches: 0
nonvoluntary_ctxt_switches: 1 nonvoluntary_ctxt_switches: 1
...@@ -215,7 +224,7 @@ asynchronous manner and the value may not be very precise. To see a precise ...@@ -215,7 +224,7 @@ asynchronous manner and the value may not be very precise. To see a precise
snapshot of a moment, you can see /proc/<pid>/smaps file and scan page table. snapshot of a moment, you can see /proc/<pid>/smaps file and scan page table.
It's slow but very precise. It's slow but very precise.
Table 1-2: Contents of the status files (as of 4.8) Table 1-2: Contents of the status files (as of 4.19)
.............................................................................. ..............................................................................
Field Content Field Content
Name filename of the executable Name filename of the executable
...@@ -270,8 +279,10 @@ Table 1-2: Contents of the status files (as of 4.8) ...@@ -270,8 +279,10 @@ Table 1-2: Contents of the status files (as of 4.8)
CapPrm bitmap of permitted capabilities CapPrm bitmap of permitted capabilities
CapEff bitmap of effective capabilities CapEff bitmap of effective capabilities
CapBnd bitmap of capabilities bounding set CapBnd bitmap of capabilities bounding set
CapAmb bitmap of ambient capabilities
NoNewPrivs no_new_privs, like prctl(PR_GET_NO_NEW_PRIV, ...) NoNewPrivs no_new_privs, like prctl(PR_GET_NO_NEW_PRIV, ...)
Seccomp seccomp mode, like prctl(PR_GET_SECCOMP, ...) Seccomp seccomp mode, like prctl(PR_GET_SECCOMP, ...)
Speculation_Store_Bypass speculative store bypass mitigation status
Cpus_allowed mask of CPUs on which this process may run Cpus_allowed mask of CPUs on which this process may run
Cpus_allowed_list Same as previous, but in "list format" Cpus_allowed_list Same as previous, but in "list format"
Mems_allowed mask of memory nodes allowed to this process Mems_allowed mask of memory nodes allowed to this process
......
...@@ -87,7 +87,7 @@ addressed with 16 direct blocks. ...@@ -87,7 +87,7 @@ addressed with 16 direct blocks.
For more than 16 blocks an indirect addressing in form of another tree is For more than 16 blocks an indirect addressing in form of another tree is
used. (scheme is the same as the one used for the superblock root nodes) used. (scheme is the same as the one used for the superblock root nodes)
The filesize is stored 64bit. Inode counting starts with 1. (whilst long The filesize is stored 64bit. Inode counting starts with 1. (while long
filename inodes start with 0) filename inodes start with 0)
Directories Directories
...@@ -155,7 +155,7 @@ Then userspace. ...@@ -155,7 +155,7 @@ Then userspace.
The requirement for a static, fixed preallocated system area comes from how The requirement for a static, fixed preallocated system area comes from how
qnx6fs deals with writes. qnx6fs deals with writes.
Each superblock got it's own half of the system area. So superblock #1 Each superblock got it's own half of the system area. So superblock #1
always uses blocks from the lower half whilst superblock #2 just writes to always uses blocks from the lower half while superblock #2 just writes to
blocks represented by the upper half bitmap system area bits. blocks represented by the upper half bitmap system area bits.
Bitmap blocks, Inode blocks and indirect addressing blocks for those two Bitmap blocks, Inode blocks and indirect addressing blocks for those two
......
...@@ -452,7 +452,7 @@ RETURN VALUE ...@@ -452,7 +452,7 @@ RETURN VALUE
ERRORS ERRORS
EACCESS EACCES
The current user does not have write access on the spufs mount The current user does not have write access on the spufs mount
point. point.
......
...@@ -1131,7 +1131,7 @@ struct dentry_operations { ...@@ -1131,7 +1131,7 @@ struct dentry_operations {
d_manage: called to allow the filesystem to manage the transition from a d_manage: called to allow the filesystem to manage the transition from a
dentry (optional). This allows autofs, for example, to hold up clients dentry (optional). This allows autofs, for example, to hold up clients
waiting to explore behind a 'mountpoint' whilst letting the daemon go waiting to explore behind a 'mountpoint' while letting the daemon go
past and construct the subtree there. 0 should be returned to let the past and construct the subtree there. 0 should be returned to let the
calling process continue. -EISDIR can be returned to tell pathwalk to calling process continue. -EISDIR can be returned to tell pathwalk to
use this directory as an ordinary directory and to ignore anything use this directory as an ordinary directory and to ignore anything
......
...@@ -110,7 +110,7 @@ owner field in the metadata object, we can immediately do top down validation to ...@@ -110,7 +110,7 @@ owner field in the metadata object, we can immediately do top down validation to
determine the scope of the problem. determine the scope of the problem.
Different types of metadata have different owner identifiers. For example, Different types of metadata have different owner identifiers. For example,
directory, attribute and extent tree blocks are all owned by an inode, whilst directory, attribute and extent tree blocks are all owned by an inode, while
freespace btree blocks are owned by an allocation group. Hence the size and freespace btree blocks are owned by an allocation group. Hence the size and
contents of the owner field are determined by the type of metadata object we are contents of the owner field are determined by the type of metadata object we are
looking at. The owner information can also identify misplaced writes (e.g. looking at. The owner information can also identify misplaced writes (e.g.
......
...@@ -417,7 +417,7 @@ level directory: ...@@ -417,7 +417,7 @@ level directory:
filesystem from ever unmounting fully in the case of "retry forever" filesystem from ever unmounting fully in the case of "retry forever"
handler configurations. handler configurations.
Note: there is no guarantee that fail_at_unmount can be set whilst an Note: there is no guarantee that fail_at_unmount can be set while an
unmount is in progress. It is possible that the sysfs entries are unmount is in progress. It is possible that the sysfs entries are
removed by the unmounting filesystem before a "retry forever" error removed by the unmounting filesystem before a "retry forever" error
handler configuration causes unmount to hang, and hence the filesystem handler configuration causes unmount to hang, and hence the filesystem
......
...@@ -190,11 +190,11 @@ ENOSPC: ...@@ -190,11 +190,11 @@ ENOSPC:
Simply running out of kernel/system memory is signalled through ENOMEM. Simply running out of kernel/system memory is signalled through ENOMEM.
EPERM/EACCESS: EPERM/EACCES:
Returned for an operation that is valid, but needs more privileges. Returned for an operation that is valid, but needs more privileges.
E.g. root-only or much more common, DRM master-only operations return E.g. root-only or much more common, DRM master-only operations return
this when when called by unpriviledged clients. There's no clear this when when called by unpriviledged clients. There's no clear
difference between EACCESS and EPERM. difference between EACCES and EPERM.
ENODEV: ENODEV:
The device is not (yet) present or fully initialized. The device is not (yet) present or fully initialized.
......
...@@ -15,7 +15,7 @@ existing subsystems with minimal additional code. Examples are the disk-activity ...@@ -15,7 +15,7 @@ existing subsystems with minimal additional code. Examples are the disk-activity
nand-disk and sharpsl-charge triggers. With led triggers disabled, the code nand-disk and sharpsl-charge triggers. With led triggers disabled, the code
optimises away. optimises away.
Complex triggers whilst available to all LEDs have LED specific Complex triggers while available to all LEDs have LED specific
parameters and work on a per LED basis. The timer trigger is an example. parameters and work on a per LED basis. The timer trigger is an example.
The timer trigger will periodically change the LED brightness between The timer trigger will periodically change the LED brightness between
LED_OFF and the current brightness setting. The "on" and "off" time can LED_OFF and the current brightness setting. The "on" and "off" time can
......
...@@ -4003,7 +4003,7 @@ demodulator. It receives radio frequency (RF) from the antenna and ...@@ -4003,7 +4003,7 @@ demodulator. It receives radio frequency (RF) from the antenna and
converts that received signal to lower intermediate frequency (IF) or converts that received signal to lower intermediate frequency (IF) or
baseband frequency (BB). Tuners that could do baseband output are often baseband frequency (BB). Tuners that could do baseband output are often
called Zero-IF tuners. Older tuners were typically simple PLL tuners called Zero-IF tuners. Older tuners were typically simple PLL tuners
inside a metal box, whilst newer ones are highly integrated chips inside a metal box, while newer ones are highly integrated chips
without a metal box "silicon tuners". These controls are mostly without a metal box "silicon tuners". These controls are mostly
applicable for new feature rich silicon tuners, just because older applicable for new feature rich silicon tuners, just because older
tuners does not have much adjustable features. tuners does not have much adjustable features.
......
...@@ -587,7 +587,7 @@ leading to the following situation: ...@@ -587,7 +587,7 @@ leading to the following situation:
(Q == &B) and (D == 2) ???? (Q == &B) and (D == 2) ????
Whilst this may seem like a failure of coherency or causality maintenance, it While this may seem like a failure of coherency or causality maintenance, it
isn't, and this behaviour can be observed on certain real CPUs (such as the DEC isn't, and this behaviour can be observed on certain real CPUs (such as the DEC
Alpha). Alpha).
...@@ -2008,7 +2008,7 @@ for each construct. These operations all imply certain barriers: ...@@ -2008,7 +2008,7 @@ for each construct. These operations all imply certain barriers:
Certain locking variants of the ACQUIRE operation may fail, either due to Certain locking variants of the ACQUIRE operation may fail, either due to
being unable to get the lock immediately, or due to receiving an unblocked being unable to get the lock immediately, or due to receiving an unblocked
signal whilst asleep waiting for the lock to become available. Failed signal while asleep waiting for the lock to become available. Failed
locks do not imply any sort of barrier. locks do not imply any sort of barrier.
[!] Note: one of the consequences of lock ACQUIREs and RELEASEs being only [!] Note: one of the consequences of lock ACQUIREs and RELEASEs being only
...@@ -2508,7 +2508,7 @@ CPU, that CPU's dependency ordering logic will take care of everything else. ...@@ -2508,7 +2508,7 @@ CPU, that CPU's dependency ordering logic will take care of everything else.
ATOMIC OPERATIONS ATOMIC OPERATIONS
----------------- -----------------
Whilst they are technically interprocessor interaction considerations, atomic While they are technically interprocessor interaction considerations, atomic
operations are noted specially as some of them imply full memory barriers and operations are noted specially as some of them imply full memory barriers and
some don't, but they're very heavily relied on as a group throughout the some don't, but they're very heavily relied on as a group throughout the
kernel. kernel.
...@@ -2531,7 +2531,7 @@ the device to malfunction. ...@@ -2531,7 +2531,7 @@ the device to malfunction.
Inside of the Linux kernel, I/O should be done through the appropriate accessor Inside of the Linux kernel, I/O should be done through the appropriate accessor
routines - such as inb() or writel() - which know how to make such accesses routines - such as inb() or writel() - which know how to make such accesses
appropriately sequential. Whilst this, for the most part, renders the explicit appropriately sequential. While this, for the most part, renders the explicit
use of memory barriers unnecessary, there are a couple of situations where they use of memory barriers unnecessary, there are a couple of situations where they
might be needed: might be needed:
...@@ -2555,7 +2555,7 @@ access the device. ...@@ -2555,7 +2555,7 @@ access the device.
This may be alleviated - at least in part - by disabling local interrupts (a This may be alleviated - at least in part - by disabling local interrupts (a
form of locking), such that the critical operations are all contained within form of locking), such that the critical operations are all contained within
the interrupt-disabled section in the driver. Whilst the driver's interrupt the interrupt-disabled section in the driver. While the driver's interrupt
routine is executing, the driver's core may not run on the same CPU, and its routine is executing, the driver's core may not run on the same CPU, and its
interrupt is not permitted to happen again until the current interrupt has been interrupt is not permitted to happen again until the current interrupt has been
handled, thus the interrupt handler does not need to lock against that. handled, thus the interrupt handler does not need to lock against that.
...@@ -2763,7 +2763,7 @@ CACHE COHERENCY ...@@ -2763,7 +2763,7 @@ CACHE COHERENCY
Life isn't quite as simple as it may appear above, however: for while the Life isn't quite as simple as it may appear above, however: for while the
caches are expected to be coherent, there's no guarantee that that coherency caches are expected to be coherent, there's no guarantee that that coherency
will be ordered. This means that whilst changes made on one CPU will will be ordered. This means that while changes made on one CPU will
eventually become visible on all CPUs, there's no guarantee that they will eventually become visible on all CPUs, there's no guarantee that they will
become apparent in the same order on those other CPUs. become apparent in the same order on those other CPUs.
...@@ -2799,7 +2799,7 @@ Imagine the system has the following properties: ...@@ -2799,7 +2799,7 @@ Imagine the system has the following properties:
(*) an even-numbered cache line may be in cache B, cache D or it may still be (*) an even-numbered cache line may be in cache B, cache D or it may still be
resident in memory; resident in memory;
(*) whilst the CPU core is interrogating one cache, the other cache may be (*) while the CPU core is interrogating one cache, the other cache may be
making use of the bus to access the rest of the system - perhaps to making use of the bus to access the rest of the system - perhaps to
displace a dirty cacheline or to do a speculative load; displace a dirty cacheline or to do a speculative load;
...@@ -2835,7 +2835,7 @@ now imagine that the second CPU wants to read those values: ...@@ -2835,7 +2835,7 @@ now imagine that the second CPU wants to read those values:
x = *q; x = *q;
The above pair of reads may then fail to happen in the expected order, as the The above pair of reads may then fail to happen in the expected order, as the
cacheline holding p may get updated in one of the second CPU's caches whilst cacheline holding p may get updated in one of the second CPU's caches while
the update to the cacheline holding v is delayed in the other of the second the update to the cacheline holding v is delayed in the other of the second
CPU's caches by some other cache event: CPU's caches by some other cache event:
...@@ -2855,7 +2855,7 @@ CPU's caches by some other cache event: ...@@ -2855,7 +2855,7 @@ CPU's caches by some other cache event:
<C:unbusy> <C:unbusy>
<C:commit v=2> <C:commit v=2>
Basically, whilst both cachelines will be updated on CPU 2 eventually, there's Basically, while both cachelines will be updated on CPU 2 eventually, there's
no guarantee that, without intervention, the order of update will be the same no guarantee that, without intervention, the order of update will be the same
as that committed on CPU 1. as that committed on CPU 1.
...@@ -2885,7 +2885,7 @@ coherency queue before processing any further requests: ...@@ -2885,7 +2885,7 @@ coherency queue before processing any further requests:
This sort of problem can be encountered on DEC Alpha processors as they have a This sort of problem can be encountered on DEC Alpha processors as they have a
split cache that improves performance by making better use of the data bus. split cache that improves performance by making better use of the data bus.
Whilst most CPUs do imply a data dependency barrier on the read when a memory While most CPUs do imply a data dependency barrier on the read when a memory
access depends on a read, not all do, so it may not be relied on. access depends on a read, not all do, so it may not be relied on.
Other CPUs may also have split caches, but must coordinate between the various Other CPUs may also have split caches, but must coordinate between the various
...@@ -2974,7 +2974,7 @@ assumption doesn't hold because: ...@@ -2974,7 +2974,7 @@ assumption doesn't hold because:
thus cutting down on transaction setup costs (memory and PCI devices may thus cutting down on transaction setup costs (memory and PCI devices may
both be able to do this); and both be able to do this); and
(*) the CPU's data cache may affect the ordering, and whilst cache-coherency (*) the CPU's data cache may affect the ordering, and while cache-coherency
mechanisms may alleviate this - once the store has actually hit the cache mechanisms may alleviate this - once the store has actually hit the cache
- there's no guarantee that the coherency management will be propagated in - there's no guarantee that the coherency management will be propagated in
order to other CPUs. order to other CPUs.
......
...@@ -84,7 +84,7 @@ ...@@ -84,7 +84,7 @@
Automedia detection is included so that in principle you can disconnect Automedia detection is included so that in principle you can disconnect
from, e.g. TP, reconnect to BNC and things will still work (after a from, e.g. TP, reconnect to BNC and things will still work (after a
pause whilst the driver figures out where its media went). My tests pause while the driver figures out where its media went). My tests
using ping showed that it appears to work.... using ping showed that it appears to work....
By default, the driver will now autodetect any DECchip based card. By default, the driver will now autodetect any DECchip based card.
......
...@@ -661,7 +661,7 @@ A server would be set up to accept operations in the following manner: ...@@ -661,7 +661,7 @@ A server would be set up to accept operations in the following manner:
setsockopt(server, SOL_RXRPC, RXRPC_SECURITY_KEYRING, "AFSkeys", 7); setsockopt(server, SOL_RXRPC, RXRPC_SECURITY_KEYRING, "AFSkeys", 7);
The keyring can be manipulated after it has been given to the socket. This The keyring can be manipulated after it has been given to the socket. This
permits the server to add more keys, replace keys, etc. whilst it is live. permits the server to add more keys, replace keys, etc. while it is live.
(3) A local address must then be bound: (3) A local address must then be bound:
...@@ -1032,7 +1032,7 @@ The kernel interface functions are as follows: ...@@ -1032,7 +1032,7 @@ The kernel interface functions are as follows:
struct sockaddr_rxrpc *srx, struct sockaddr_rxrpc *srx,
struct key *key); struct key *key);
This attempts to partially reinitialise a call and submit it again whilst This attempts to partially reinitialise a call and submit it again while
reusing the original call's Tx queue to avoid the need to repackage and reusing the original call's Tx queue to avoid the need to repackage and
re-encrypt the data to be sent. call indicates the call to retry, srx the re-encrypt the data to be sent. call indicates the call to retry, srx the
new address to send it to and key the encryption key to use for signing or new address to send it to and key the encryption key to use for signing or
...@@ -1066,7 +1066,7 @@ The kernel interface functions are as follows: ...@@ -1066,7 +1066,7 @@ The kernel interface functions are as follows:
alive after waiting for a suitable interval. alive after waiting for a suitable interval.
This allows the caller to work out if the server is still contactable and This allows the caller to work out if the server is still contactable and
if the call is still alive on the server whilst waiting for the server to if the call is still alive on the server while waiting for the server to
process a client operation. process a client operation.
The second function causes a ping ACK to be transmitted to try to provoke The second function causes a ping ACK to be transmitted to try to provoke
...@@ -1149,14 +1149,14 @@ adjusted through sysctls in /proc/net/rxrpc/: ...@@ -1149,14 +1149,14 @@ adjusted through sysctls in /proc/net/rxrpc/:
(*) connection_expiry (*) connection_expiry
The amount of time in seconds after a connection was last used before we The amount of time in seconds after a connection was last used before we
remove it from the connection list. Whilst a connection is in existence, remove it from the connection list. While a connection is in existence,
it serves as a placeholder for negotiated security; when it is deleted, it serves as a placeholder for negotiated security; when it is deleted,
the security must be renegotiated. the security must be renegotiated.
(*) transport_expiry (*) transport_expiry
The amount of time in seconds after a transport was last used before we The amount of time in seconds after a transport was last used before we
remove it from the transport list. Whilst a transport is in existence, it remove it from the transport list. While a transport is in existence, it
serves to anchor the peer data and keeps the connection ID counter. serves to anchor the peer data and keeps the connection ID counter.
(*) rxrpc_rx_window_size (*) rxrpc_rx_window_size
......
...@@ -22,7 +22,7 @@ Nomenclature ...@@ -22,7 +22,7 @@ Nomenclature
Some terms used in this document:- Some terms used in this document:-
o Regulator - Electronic device that supplies power to other devices. o Regulator - Electronic device that supplies power to other devices.
Most regulators can enable and disable their output whilst Most regulators can enable and disable their output while
some can control their output voltage and or current. some can control their output voltage and or current.
Input Voltage -> Regulator -> Output Voltage Input Voltage -> Regulator -> Output Voltage
......
.. _development_process_intro:
Introduction Introduction
============ ============
......
...@@ -315,7 +315,8 @@ variety of potential coding problems; it can also propose fixes for those ...@@ -315,7 +315,8 @@ variety of potential coding problems; it can also propose fixes for those
problems. Quite a few "semantic patches" for the kernel have been packaged problems. Quite a few "semantic patches" for the kernel have been packaged
under the scripts/coccinelle directory; running "make coccicheck" will run under the scripts/coccinelle directory; running "make coccicheck" will run
through those semantic patches and report on any problems found. See through those semantic patches and report on any problems found. See
Documentation/dev-tools/coccinelle.rst for more information. :ref:`Documentation/dev-tools/coccinelle.rst <devtools_coccinelle>`
for more information.
Other kinds of portability errors are best found by compiling your code for Other kinds of portability errors are best found by compiling your code for
other architectures. If you do not happen to have an S/390 system or a other architectures. If you do not happen to have an S/390 system or a
......
...@@ -9,9 +9,10 @@ kernel. Unsurprisingly, the kernel development community has evolved a set ...@@ -9,9 +9,10 @@ kernel. Unsurprisingly, the kernel development community has evolved a set
of conventions and procedures which are used in the posting of patches; of conventions and procedures which are used in the posting of patches;
following them will make life much easier for everybody involved. This following them will make life much easier for everybody involved. This
document will attempt to cover these expectations in reasonable detail; document will attempt to cover these expectations in reasonable detail;
more information can also be found in the files process/submitting-patches.rst, more information can also be found in the files
process/submitting-drivers.rst, and process/submit-checklist.rst in the kernel :ref:`Documentation/process/submitting-patches.rst <submittingpatches>`,
documentation directory. :ref:`Documentation/process/submitting-drivers.rst <submittingdrivers>`
and :ref:`Documentation/process/submit-checklist.rst <submitchecklist>`.
When to post When to post
...@@ -198,8 +199,10 @@ pass it to diff with the "-X" option. ...@@ -198,8 +199,10 @@ pass it to diff with the "-X" option.
The tags mentioned above are used to describe how various developers have The tags mentioned above are used to describe how various developers have
been associated with the development of this patch. They are described in been associated with the development of this patch. They are described in
detail in the process/submitting-patches.rst document; what follows here is a detail in
brief summary. Each of these lines has the format: the :ref:`Documentation/process/submitting-patches.rst <submittingpatches>`
document; what follows here is a brief summary. Each of these lines has
the format:
:: ::
...@@ -210,8 +213,8 @@ The tags in common use are: ...@@ -210,8 +213,8 @@ The tags in common use are:
- Signed-off-by: this is a developer's certification that he or she has - Signed-off-by: this is a developer's certification that he or she has
the right to submit the patch for inclusion into the kernel. It is an the right to submit the patch for inclusion into the kernel. It is an
agreement to the Developer's Certificate of Origin, the full text of agreement to the Developer's Certificate of Origin, the full text of
which can be found in Documentation/process/submitting-patches.rst. Code which can be found in :ref:`Documentation/process/submitting-patches.rst <submittingpatches>`
without a proper signoff cannot be merged into the mainline. Code without a proper signoff cannot be merged into the mainline.
- Co-developed-by: states that the patch was also created by another developer - Co-developed-by: states that the patch was also created by another developer
along with the original author. This is useful at times when multiple along with the original author. This is useful at times when multiple
...@@ -226,7 +229,7 @@ The tags in common use are: ...@@ -226,7 +229,7 @@ The tags in common use are:
it to work. it to work.
- Reviewed-by: the named developer has reviewed the patch for correctness; - Reviewed-by: the named developer has reviewed the patch for correctness;
see the reviewer's statement in Documentation/process/submitting-patches.rst see the reviewer's statement in :ref:`Documentation/process/submitting-patches.rst <submittingpatches>`
for more detail. for more detail.
- Reported-by: names a user who reported a problem which is fixed by this - Reported-by: names a user who reported a problem which is fixed by this
...@@ -253,8 +256,8 @@ take care of: ...@@ -253,8 +256,8 @@ take care of:
be examined in any detail. If there is any doubt at all, mail the patch be examined in any detail. If there is any doubt at all, mail the patch
to yourself and convince yourself that it shows up intact. to yourself and convince yourself that it shows up intact.
Documentation/process/email-clients.rst has some helpful hints on making :ref:`Documentation/process/email-clients.rst <email_clients>` has some
specific mail clients work for sending patches. helpful hints on making specific mail clients work for sending patches.
- Are you sure your patch is free of silly mistakes? You should always - Are you sure your patch is free of silly mistakes? You should always
run patches through scripts/checkpatch.pl and address the complaints it run patches through scripts/checkpatch.pl and address the complaints it
......
...@@ -5,9 +5,10 @@ For more information ...@@ -5,9 +5,10 @@ For more information
There are numerous sources of information on Linux kernel development and There are numerous sources of information on Linux kernel development and
related topics. First among those will always be the Documentation related topics. First among those will always be the Documentation
directory found in the kernel source distribution. The top-level process/howto.rst directory found in the kernel source distribution. The top-level :ref:`process/howto.rst <process_howto>`
file is an important starting point; process/submitting-patches.rst and file is an important starting point; :ref:`process/submitting-patches.rst <submittingpatches>`
process/submitting-drivers.rst are also something which all kernel developers should and :ref:`process/submitting-drivers.rst <submittingdrivers>`
are also something which all kernel developers should
read. Many internal kernel APIs are documented using the kerneldoc read. Many internal kernel APIs are documented using the kerneldoc
mechanism; "make htmldocs" or "make pdfdocs" can be used to generate those mechanism; "make htmldocs" or "make pdfdocs" can be used to generate those
documents in HTML or PDF format (though the version of TeX shipped by some documents in HTML or PDF format (though the version of TeX shipped by some
......
.. _addsyscalls:
Adding a New System Call Adding a New System Call
======================== ========================
......
...@@ -326,7 +326,7 @@ Kernel documentation ...@@ -326,7 +326,7 @@ Kernel documentation
Sphinx Sphinx
------ ------
Please see :ref:`sphinx_install` in ``Documentation/doc-guide/sphinx.rst`` Please see :ref:`sphinx_install` in :ref:`Documentation/doc-guide/sphinx.rst <sphinxdoc>`
for details about Sphinx requirements. for details about Sphinx requirements.
Getting updated software Getting updated software
......
...@@ -1075,5 +1075,5 @@ gcc internals and indent, all available from http://www.gnu.org/manual/ ...@@ -1075,5 +1075,5 @@ gcc internals and indent, all available from http://www.gnu.org/manual/
WG14 is the international standardization working group for the programming WG14 is the international standardization working group for the programming
language C, URL: http://www.open-std.org/JTC1/SC22/WG14/ language C, URL: http://www.open-std.org/JTC1/SC22/WG14/
Kernel process/coding-style.rst, by greg@kroah.com at OLS 2002: Kernel :ref:`process/coding-style.rst <codingstyle>`, by greg@kroah.com at OLS 2002:
http://www.kroah.com/linux/talks/ols_2002_kernel_codingstyle_talk/html/ http://www.kroah.com/linux/talks/ols_2002_kernel_codingstyle_talk/html/
.. _process_howto:
HOWTO do Linux kernel development HOWTO do Linux kernel development
================================= =================================
...@@ -296,9 +298,9 @@ two weeks, but it can be longer if there are no pressing problems. A ...@@ -296,9 +298,9 @@ two weeks, but it can be longer if there are no pressing problems. A
security-related problem, instead, can cause a release to happen almost security-related problem, instead, can cause a release to happen almost
instantly. instantly.
The file Documentation/process/stable-kernel-rules.rst in the kernel tree The file :ref:`Documentation/process/stable-kernel-rules.rst <stable_kernel_rules>`
documents what kinds of changes are acceptable for the -stable tree, and in the kernel tree documents what kinds of changes are acceptable for
how the release process works. the -stable tree, and how the release process works.
4.x -git patches 4.x -git patches
~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~
...@@ -358,7 +360,8 @@ tool. For details on how to use the kernel bugzilla, please see: ...@@ -358,7 +360,8 @@ tool. For details on how to use the kernel bugzilla, please see:
https://bugzilla.kernel.org/page.cgi?id=faq.html https://bugzilla.kernel.org/page.cgi?id=faq.html
The file admin-guide/reporting-bugs.rst in the main kernel source directory has a good The file :ref:`admin-guide/reporting-bugs.rst <reportingbugs>`
in the main kernel source directory has a good
template for how to report a possible kernel bug, and details what kind template for how to report a possible kernel bug, and details what kind
of information is needed by the kernel developers to help track down the of information is needed by the kernel developers to help track down the
problem. problem.
...@@ -424,7 +427,7 @@ add your statements between the individual quoted sections instead of ...@@ -424,7 +427,7 @@ add your statements between the individual quoted sections instead of
writing at the top of the mail. writing at the top of the mail.
If you add patches to your mail, make sure they are plain readable text If you add patches to your mail, make sure they are plain readable text
as stated in Documentation/process/submitting-patches.rst. as stated in :ref:`Documentation/process/submitting-patches.rst <submittingpatches>`.
Kernel developers don't want to deal with Kernel developers don't want to deal with
attachments or compressed patches; they may want to comment on attachments or compressed patches; they may want to comment on
individual lines of your patch, which works only that way. Make sure you individual lines of your patch, which works only that way. Make sure you
......
.. _process_statement_driver:
Kernel Driver Statement Kernel Driver Statement
----------------------- -----------------------
......
Linux Kernel Enforcement Statement .. _process_statement_kernel:
Linux Kernel Enforcement Statement
---------------------------------- ----------------------------------
As developers of the Linux kernel, we have a keen interest in how our software As developers of the Linux kernel, we have a keen interest in how our software
......
.. _magicnumbers:
Linux magic numbers Linux magic numbers
=================== ===================
......
...@@ -5,8 +5,9 @@ Linux kernel management style ...@@ -5,8 +5,9 @@ Linux kernel management style
This is a short document describing the preferred (or made up, depending This is a short document describing the preferred (or made up, depending
on who you ask) management style for the linux kernel. It's meant to on who you ask) management style for the linux kernel. It's meant to
mirror the process/coding-style.rst document to some degree, and mainly written to mirror the :ref:`process/coding-style.rst <codingstyle>` document to some
avoid answering [#f1]_ the same (or similar) questions over and over again. degree, and mainly written to avoid answering [#f1]_ the same (or similar)
questions over and over again.
Management style is very personal and much harder to quantify than Management style is very personal and much harder to quantify than
simple coding style rules, so this document may or may not have anything simple coding style rules, so this document may or may not have anything
......
...@@ -16,7 +16,8 @@ you should probably talk to XFree86 (http://www.xfree86.org/) and/or X.Org ...@@ -16,7 +16,8 @@ you should probably talk to XFree86 (http://www.xfree86.org/) and/or X.Org
Oh, and we don't really recommend submitting changes to XFree86 :) Oh, and we don't really recommend submitting changes to XFree86 :)
Also read the Documentation/process/submitting-patches.rst document. Also read the :ref:`Documentation/process/submitting-patches.rst <submittingpatches>`
document.
Allocating Device Numbers Allocating Device Numbers
...@@ -27,7 +28,8 @@ by the Linux assigned name and number authority (currently this is ...@@ -27,7 +28,8 @@ by the Linux assigned name and number authority (currently this is
Torben Mathiasen). The site is http://www.lanana.org/. This Torben Mathiasen). The site is http://www.lanana.org/. This
also deals with allocating numbers for devices that are not going to also deals with allocating numbers for devices that are not going to
be submitted to the mainstream kernel. be submitted to the mainstream kernel.
See Documentation/admin-guide/devices.rst for more information on this. See :ref:`Documentation/admin-guide/devices.rst <admin_devices>`
for more information on this.
If you don't use assigned numbers then when your device is submitted it will If you don't use assigned numbers then when your device is submitted it will
be given an assigned number even if that is different from values you may be given an assigned number even if that is different from values you may
...@@ -117,7 +119,7 @@ PM support: ...@@ -117,7 +119,7 @@ PM support:
anything. For the driver testing instructions see anything. For the driver testing instructions see
Documentation/power/drivers-testing.txt and for a relatively Documentation/power/drivers-testing.txt and for a relatively
complete overview of the power management issues related to complete overview of the power management issues related to
drivers see Documentation/driver-api/pm/devices.rst. drivers see :ref:`Documentation/driver-api/pm/devices.rst <driverapi_pm_devices>`.
Control: Control:
In general if there is active maintenance of a driver by In general if there is active maintenance of a driver by
......
...@@ -16,7 +16,7 @@ Sep 2002: Dynamically get 3270 input buffer ...@@ -16,7 +16,7 @@ Sep 2002: Dynamically get 3270 input buffer
Sep 2002: Fix tubfs kmalloc()s Sep 2002: Fix tubfs kmalloc()s
* Do read and write lengths correctly in fs3270_read() * Do read and write lengths correctly in fs3270_read()
and fs3270_write(), whilst never asking kmalloc() and fs3270_write(), while never asking kmalloc()
for more than 0x800 bytes. Affects tubfs.c and tubio.h. for more than 0x800 bytes. Affects tubfs.c and tubio.h.
Sep 2002: Recognize 3270 control unit type 3174 Sep 2002: Recognize 3270 control unit type 3174
......
...@@ -291,7 +291,7 @@ for example), it must be considered immutable, barring two exceptions: ...@@ -291,7 +291,7 @@ for example), it must be considered immutable, barring two exceptions:
1. The reference count may be altered. 1. The reference count may be altered.
2. Whilst the keyring subscriptions of a set of credentials may not be 2. While the keyring subscriptions of a set of credentials may not be
changed, the keyrings subscribed to may have their contents altered. changed, the keyrings subscribed to may have their contents altered.
To catch accidental credential alteration at compile time, struct task_struct To catch accidental credential alteration at compile time, struct task_struct
...@@ -358,7 +358,7 @@ Once a reference has been obtained, it must be released with ``put_cred()``, ...@@ -358,7 +358,7 @@ Once a reference has been obtained, it must be released with ``put_cred()``,
Accessing Another Task's Credentials Accessing Another Task's Credentials
------------------------------------ ------------------------------------
Whilst a task may access its own credentials without the need for locking, the While a task may access its own credentials without the need for locking, the
same is not true of a task wanting to access another task's credentials. It same is not true of a task wanting to access another task's credentials. It
must use the RCU read lock and ``rcu_dereference()``. must use the RCU read lock and ``rcu_dereference()``.
...@@ -382,7 +382,7 @@ This should be used inside the RCU read lock, as in the following example:: ...@@ -382,7 +382,7 @@ This should be used inside the RCU read lock, as in the following example::
} }
Should it be necessary to hold another task's credentials for a long period of Should it be necessary to hold another task's credentials for a long period of
time, and possibly to sleep whilst doing so, then the caller should get a time, and possibly to sleep while doing so, then the caller should get a
reference on them using:: reference on them using::
const struct cred *get_task_cred(struct task_struct *task); const struct cred *get_task_cred(struct task_struct *task);
...@@ -442,7 +442,7 @@ duplicate of the current process's credentials, returning with the mutex still ...@@ -442,7 +442,7 @@ duplicate of the current process's credentials, returning with the mutex still
held if successful. It returns NULL if not successful (out of memory). held if successful. It returns NULL if not successful (out of memory).
The mutex prevents ``ptrace()`` from altering the ptrace state of a process The mutex prevents ``ptrace()`` from altering the ptrace state of a process
whilst security checks on credentials construction and changing is taking place while security checks on credentials construction and changing is taking place
as the ptrace state may alter the outcome, particularly in the case of as the ptrace state may alter the outcome, particularly in the case of
``execve()``. ``execve()``.
......
...@@ -132,7 +132,7 @@ Negative Instantiation And Rejection ...@@ -132,7 +132,7 @@ Negative Instantiation And Rejection
Rather than instantiating a key, it is possible for the possessor of an Rather than instantiating a key, it is possible for the possessor of an
authorisation key to negatively instantiate a key that's under construction. authorisation key to negatively instantiate a key that's under construction.
This is a short duration placeholder that causes any attempt at re-requesting This is a short duration placeholder that causes any attempt at re-requesting
the key whilst it exists to fail with error ENOKEY if negated or the specified the key while it exists to fail with error ENOKEY if negated or the specified
error if rejected. error if rejected.
This is provided to prevent excessive repeated spawning of /sbin/request-key This is provided to prevent excessive repeated spawning of /sbin/request-key
......
...@@ -75,7 +75,7 @@ ...@@ -75,7 +75,7 @@
/* Set rts delay after send, if needed: */ /* Set rts delay after send, if needed: */
rs485conf.delay_rts_after_send = ...; rs485conf.delay_rts_after_send = ...;
/* Set this flag if you want to receive data even whilst sending data */ /* Set this flag if you want to receive data even while sending data */
rs485conf.flags |= SER_RS485_RX_DURING_TX; rs485conf.flags |= SER_RS485_RX_DURING_TX;
if (ioctl (fd, TIOCSRS485, &rs485conf) < 0) { if (ioctl (fd, TIOCSRS485, &rs485conf) < 0) {
......
...@@ -24,7 +24,7 @@ I2S ...@@ -24,7 +24,7 @@ I2S
=== ===
I2S is a common 4 wire DAI used in HiFi, STB and portable devices. The Tx and I2S is a common 4 wire DAI used in HiFi, STB and portable devices. The Tx and
Rx lines are used for audio transmission, whilst the bit clock (BCLK) and Rx lines are used for audio transmission, while the bit clock (BCLK) and
left/right clock (LRC) synchronise the link. I2S is flexible in that either the left/right clock (LRC) synchronise the link. I2S is flexible in that either the
controller or CODEC can drive (master) the BCLK and LRC clock lines. Bit clock controller or CODEC can drive (master) the BCLK and LRC clock lines. Bit clock
usually varies depending on the sample rate and the master system clock usually varies depending on the sample rate and the master system clock
...@@ -49,9 +49,9 @@ PCM ...@@ -49,9 +49,9 @@ PCM
PCM is another 4 wire interface, very similar to I2S, which can support a more PCM is another 4 wire interface, very similar to I2S, which can support a more
flexible protocol. It has bit clock (BCLK) and sync (SYNC) lines that are used flexible protocol. It has bit clock (BCLK) and sync (SYNC) lines that are used
to synchronise the link whilst the Tx and Rx lines are used to transmit and to synchronise the link while the Tx and Rx lines are used to transmit and
receive the audio data. Bit clock usually varies depending on sample rate receive the audio data. Bit clock usually varies depending on sample rate
whilst sync runs at the sample rate. PCM also supports Time Division while sync runs at the sample rate. PCM also supports Time Division
Multiplexing (TDM) in that several devices can use the bus simultaneously (this Multiplexing (TDM) in that several devices can use the bus simultaneously (this
is sometimes referred to as network mode). is sometimes referred to as network mode).
......
...@@ -218,7 +218,7 @@ like a BT phone call :- ...@@ -218,7 +218,7 @@ like a BT phone call :-
* * <----DAI5-----> FM * * <----DAI5-----> FM
************* *************
This allows the host CPU to sleep whilst the DSP, MODEM DAI and the BT DAI are This allows the host CPU to sleep while the DSP, MODEM DAI and the BT DAI are
still in operation. still in operation.
A BE DAI link can also set the codec to a dummy device if the code is a device A BE DAI link can also set the codec to a dummy device if the code is a device
......
...@@ -156,7 +156,7 @@ or increment/decrement function. ...@@ -156,7 +156,7 @@ or increment/decrement function.
Note that switching branches results in some locks being taken, Note that switching branches results in some locks being taken,
particularly the CPU hotplug lock (in order to avoid races against particularly the CPU hotplug lock (in order to avoid races against
CPUs being brought in the kernel whilst the kernel is getting CPUs being brought in the kernel while the kernel is getting
patched). Calling the static key API from within a hotplug notifier is patched). Calling the static key API from within a hotplug notifier is
thus a sure deadlock recipe. In order to still allow use of the thus a sure deadlock recipe. In order to still allow use of the
functionnality, the following functions are provided: functionnality, the following functions are provided:
......
...@@ -110,7 +110,7 @@ the permitted thermal "ramp" of the system. For instance, a lower ...@@ -110,7 +110,7 @@ the permitted thermal "ramp" of the system. For instance, a lower
`k_pu` value will provide a slower ramp, at the cost of capping `k_pu` value will provide a slower ramp, at the cost of capping
available capacity at a low temperature. On the other hand, a high available capacity at a low temperature. On the other hand, a high
value of `k_pu` will result in the governor granting very high power value of `k_pu` will result in the governor granting very high power
whilst temperature is low, and may lead to temperature overshooting. while temperature is low, and may lead to temperature overshooting.
The default value for `k_pu` is: The default value for `k_pu` is:
......
...@@ -24,13 +24,13 @@ It can be used for debugging or analyzing latencies and ...@@ -24,13 +24,13 @@ It can be used for debugging or analyzing latencies and
performance issues that take place outside of user-space. performance issues that take place outside of user-space.
Although ftrace is typically considered the function tracer, it Although ftrace is typically considered the function tracer, it
is really a frame work of several assorted tracing utilities. is really a framework of several assorted tracing utilities.
There's latency tracing to examine what occurs between interrupts There's latency tracing to examine what occurs between interrupts
disabled and enabled, as well as for preemption and from a time disabled and enabled, as well as for preemption and from a time
a task is woken to the task is actually scheduled in. a task is woken to the task is actually scheduled in.
One of the most common uses of ftrace is the event tracing. One of the most common uses of ftrace is the event tracing.
Through out the kernel is hundreds of static event points that Throughout the kernel is hundreds of static event points that
can be enabled via the tracefs file system to see what is can be enabled via the tracefs file system to see what is
going on in certain parts of the kernel. going on in certain parts of the kernel.
...@@ -462,7 +462,7 @@ of ftrace. Here is a list of some of the key files: ...@@ -462,7 +462,7 @@ of ftrace. Here is a list of some of the key files:
mono_raw: mono_raw:
This is the raw monotonic clock (CLOCK_MONOTONIC_RAW) This is the raw monotonic clock (CLOCK_MONOTONIC_RAW)
which is montonic but is not subject to any rate adjustments which is monotonic but is not subject to any rate adjustments
and ticks at the same rate as the hardware clocksource. and ticks at the same rate as the hardware clocksource.
boot: boot:
...@@ -914,8 +914,8 @@ The above is mostly meaningful for kernel developers. ...@@ -914,8 +914,8 @@ The above is mostly meaningful for kernel developers.
current trace and the next trace. current trace and the next trace.
- '$' - greater than 1 second - '$' - greater than 1 second
- '@' - greater than 100 milisecond - '@' - greater than 100 millisecond
- '*' - greater than 10 milisecond - '*' - greater than 10 millisecond
- '#' - greater than 1000 microsecond - '#' - greater than 1000 microsecond
- '!' - greater than 100 microsecond - '!' - greater than 100 microsecond
- '+' - greater than 10 microsecond - '+' - greater than 10 microsecond
...@@ -2541,7 +2541,7 @@ At compile time every C file object is run through the ...@@ -2541,7 +2541,7 @@ At compile time every C file object is run through the
recordmcount program (located in the scripts directory). This recordmcount program (located in the scripts directory). This
program will parse the ELF headers in the C object to find all program will parse the ELF headers in the C object to find all
the locations in the .text section that call mcount. Starting the locations in the .text section that call mcount. Starting
with gcc verson 4.6, the -mfentry has been added for x86, which with gcc version 4.6, the -mfentry has been added for x86, which
calls "__fentry__" instead of "mcount". Which is called before calls "__fentry__" instead of "mcount". Which is called before
the creation of the stack frame. the creation of the stack frame.
...@@ -2978,7 +2978,7 @@ The following commands are supported: ...@@ -2978,7 +2978,7 @@ The following commands are supported:
When the function is hit, it will dump the contents of the ftrace When the function is hit, it will dump the contents of the ftrace
ring buffer to the console. This is useful if you need to debug ring buffer to the console. This is useful if you need to debug
something, and want to dump the trace when a certain function something, and want to dump the trace when a certain function
is hit. Perhaps its a function that is called before a tripple is hit. Perhaps it's a function that is called before a triple
fault happens and does not allow you to get a regular dump. fault happens and does not allow you to get a regular dump.
- cpudump: - cpudump:
......
.. include:: ../disclaimer-ita.rst
:Original: :ref:`Documentation/admin-guide/README.rst <readme>`
.. _it_readme:
Rilascio del kernel Linux 4.x <http://kernel.org/>
===================================================
.. warning::
TODO ancora da tradurre
.. include:: ../disclaimer-ita.rst
:Original: :ref:`Documentation/admin-guide/security-bugs.rst <securitybugs>`
.. _it_securitybugs:
Bachi di sicurezza
==================
.. warning::
TODO ancora da tradurre
...@@ -107,7 +107,7 @@ macro simil-funzioni è il seguente:: ...@@ -107,7 +107,7 @@ macro simil-funzioni è il seguente::
* Context: Describes whether the function can sleep, what locks it takes, * Context: Describes whether the function can sleep, what locks it takes,
* releases, or expects to be held. It can extend over multiple * releases, or expects to be held. It can extend over multiple
* lines. * lines.
* Return: Describe the return value of foobar. * Return: Describe the return value of function_name.
* *
* The return value description can also have multiple paragraphs, and should * The return value description can also have multiple paragraphs, and should
* be placed at the end of the comment block. * be placed at the end of the comment block.
......
...@@ -86,6 +86,7 @@ vostre modifiche molto più semplice ...@@ -86,6 +86,7 @@ vostre modifiche molto più semplice
.. toctree:: .. toctree::
:maxdepth: 2 :maxdepth: 2
process/index
doc-guide/index doc-guide/index
kernel-hacking/index kernel-hacking/index
......
...@@ -593,8 +593,8 @@ l'opzione ``GFP_KERNEL`` che è permessa solo in contesto utente. Ho supposto ...@@ -593,8 +593,8 @@ l'opzione ``GFP_KERNEL`` che è permessa solo in contesto utente. Ho supposto
che :c:func:`cache_add()` venga chiamata dal contesto utente, altrimenti che :c:func:`cache_add()` venga chiamata dal contesto utente, altrimenti
questa opzione deve diventare un parametro di :c:func:`cache_add()`. questa opzione deve diventare un parametro di :c:func:`cache_add()`.
Exposing Objects Outside This File Esporre gli oggetti al di fuori del file
---------------------------------- ----------------------------------------
Se i vostri oggetti contengono più informazioni, potrebbe non essere Se i vostri oggetti contengono più informazioni, potrebbe non essere
sufficiente copiare i dati avanti e indietro: per esempio, altre parti del sufficiente copiare i dati avanti e indietro: per esempio, altre parti del
......
.. include:: ../disclaimer-ita.rst
:Original: :ref:`Documentation/process/1.Intro.rst <development_process_intro>`
:Translator: Alessia Mantegazza <amantegazza@vaga.pv.it>
.. _it_development_intro:
Introduzione
============
Riepilogo generale
------------------
Il resto di questa sezione riguarda il processo di sviluppo del kernel e
quella sorta di frustrazione che gli sviluppatori e i loro datori di lavoro
potrebbero dover affrontare. Ci sono molte ragioni per le quali del codice
per il kernel debba essere incorporato nel kernel ufficiale, fra le quali:
disponibilità immediata agli utilizzatori, supporto della comunità in
differenti modalità, e la capacità di influenzare la direzione dello sviluppo
del kernel.
Il codice che contribuisce al kernel Linux deve essere reso disponibile sotto
una licenza GPL-compatibile.
La sezione :ref:`it_development_process` introduce il processo di sviluppo,
il ciclo di rilascio del kernel, ed i meccanismi della finestra
d'incorporazione. Il capitolo copre le varie fasi di una modifica: sviluppo,
revisione e ciclo d'incorporazione. Ci sono alcuni dibattiti su strumenti e
liste di discussione. Gli sviluppatori che sono in attesa di poter sviluppare
qualcosa per il kernel sono invitati ad individuare e sistemare bachi come
esercizio iniziale.
La sezione :ref:`it_development_early_stage` copre i primi stadi della
pianificazione di un progetto di sviluppo, con particolare enfasi sul
coinvolgimento della comunità, il prima possibile.
La sezione :ref:`it_development_coding` riguarda il processo di scrittura
del codice. Qui, sono esposte le diverse insidie che sono state già affrontate
da altri sviluppatori. Il capitolo copre anche alcuni dei requisiti per le
modifiche, ed esiste un'introduzione ad alcuni strumenti che possono aiutarvi
nell'assicurarvi che le modifiche per il kernel siano corrette.
La sezione :ref:`it_development_posting` parla del processo di pubblicazione
delle modifiche per la revisione. Per essere prese in considerazione dalla
comunità di sviluppo, le modifiche devono essere propriamente formattate ed
esposte, e devono essere inviate nel posto giusto. Seguire i consigli presenti
in questa sezione dovrebbe essere d'aiuto nell'assicurare la migliore
accoglienza possibile del vostro lavoro.
La sezione :ref:`it_development_followthrough` copre ciò che accade dopo
la pubblicazione delle modifiche; a questo punto il lavoro è lontano
dall'essere concluso. Lavorare con i revisori è una parte cruciale del
processo di sviluppo; questa sezione offre una serie di consigli su come
evitare problemi in questa importante fase. Gli sviluppatori sono diffidenti
nell'affermare che il lavoro è concluso quando una modifica è incorporata nei
sorgenti principali.
La sezione :ref:`it_development_advancedtopics` introduce un paio di argomenti
"avanzati": gestire le modifiche con git e controllare le modifiche pubblicate
da altri.
La sezione :ref:`it_development_conclusion` chiude il documento con dei
riferimenti ad altre fonti che forniscono ulteriori informazioni sullo sviluppo
del kernel.
Di cosa parla questo documento
------------------------------
Il kernel Linux, ha oltre 8 milioni di linee di codice e ben oltre 1000
contributori ad ogni rilascio; è uno dei più vasti e più attivi software
liberi progettati mai esistiti. Sin dal sul modesto inizio nel 1991,
questo kernel si è evoluto nel miglior componente per sistemi operativi
che fanno funzionare piccoli riproduttori musicali, PC, grandi super computer
e tutte le altre tipologie di sistemi fra questi estremi. È una soluzione
robusta, efficiente ed adattabile a praticamente qualsiasi situazione.
Con la crescita di Linux è arrivato anche un aumento di sviluppatori
(ed aziende) desiderosi di partecipare a questo sviluppo. I produttori di
hardware vogliono assicurarsi che il loro prodotti siano supportati da Linux,
rendendo questi prodotti attrattivi agli utenti Linux. I produttori di
sistemi integrati, che usano Linux come componente di un prodotto integrato,
vogliono che Linux sia capace ed adeguato agli obiettivi ed il più possibile
alla mano. Fornitori ed altri produttori di software che basano i propri
prodotti su Linux hanno un chiaro interesse verso capacità, prestazioni ed
affidabilità del kernel Linux. E gli utenti finali, anche, spesso vorrebbero
cambiare Linux per renderlo più aderente alle proprie necessità.
Una delle caratteristiche più coinvolgenti di Linux è quella dell'accessibilità
per gli sviluppatori; chiunque con le capacità richieste può migliorare
Linux ed influenzarne la direzione di sviluppo. Prodotti non open-source non
possono offrire questo tipo di apertura, che è una caratteristica del software
libero. Ma, anzi, il kernel è persino più aperto rispetto a molti altri
progetti di software libero. Un classico ciclo di sviluppo trimestrale può
coinvolgere 1000 sviluppatori che lavorano per più di 100 differenti aziende
(o per nessuna azienda).
Lavorare con la comunità di sviluppo del kernel non è particolarmente
difficile. Ma, ciononostante, diversi potenziali contributori hanno trovato
delle difficoltà quando hanno cercato di lavorare sul kernel. La comunità del
kernel utilizza un proprio modo di operare che gli permette di funzionare
agevolmente (e genera un prodotto di alta qualità) in un ambiente dove migliaia
di stringhe di codice sono modificate ogni giorni. Quindi non deve sorprendere
che il processo di sviluppo del kernel differisca notevolmente dai metodi di
sviluppo privati.
Il processo di sviluppo del Kernel può, dall'altro lato, risultare
intimidatorio e strano ai nuovi sviluppatori, ma ha dietro di se buone ragioni
e solide esperienze. Uno sviluppatore che non comprende i modi della comunità
del kernel (o, peggio, che cerchi di aggirarli o violarli) avrà un'esperienza
deludente nel proprio bagaglio. La comunità di sviluppo, sebbene sia utile
a coloro che cercano di imparare, ha poco tempo da dedicare a coloro che non
ascoltano o coloro che non sono interessati al processo di sviluppo.
Si spera che coloro che leggono questo documento saranno in grado di evitare
queste esperienze spiacevoli. C'è molto materiale qui, ma lo sforzo della
lettura sarà ripagato in breve tempo. La comunità di sviluppo ha sempre
bisogno di sviluppatori che vogliano aiutare a rendere il kernel migliore;
il testo seguente potrebbe esservi d'aiuto - o essere d'aiuto ai vostri
collaboratori- per entrare a far parte della nostra comunità.
Crediti
-------
Questo documento è stato scritto da Jonathan Corbet, corbet@lwn.net.
È stato migliorato da Johannes Berg, James Berry, Alex Chiang, Roland
Dreier, Randy Dunlap, Jake Edge, Jiri Kosina, Matt Mackall, Arthur Marsh,
Amanda McPherson, Andrew Morton, Andrew Price, Tsugikazu Shibata e Jochen Voß.
Questo lavoro è stato supportato dalla Linux Foundation; un ringraziamento
speciale ad Amanda McPherson, che ha visto il valore di questo lavoro e lo ha
reso possibile.
L'importanza d'avere il codice nei sorgenti principali
------------------------------------------------------
Alcune aziende e sviluppatori ogni tanto si domandano perché dovrebbero
preoccuparsi di apprendere come lavorare con la comunità del kernel e di
inserire il loro codice nel ramo di sviluppo principale (per ramo principale
s'intende quello mantenuto da Linus Torvalds e usato come base dai
distributori Linux). Nel breve termine, contribuire al codice può sembrare
un costo inutile; può sembra più facile tenere separato il proprio codice e
supportare direttamente i suoi utilizzatori. La verità è che il tenere il
codice separato ("fuori dai sorgenti", *"out-of-tree"*) è un falso risparmio.
Per dimostrare i costi di un codice "fuori dai sorgenti", eccovi
alcuni aspetti rilevanti del processo di sviluppo kernel; la maggior parte
di essi saranno approfonditi dettagliatamente più avanti in questo documento.
Considerate:
- Il codice che è stato inserito nel ramo principale del kernel è disponibile
a tutti gli utilizzatori Linux. Sarà automaticamente presente in tutte le
distribuzioni che lo consentono. Non c'è bisogno di: driver per dischi,
scaricare file, o della scocciatura del dover supportare diverse versioni di
diverse distribuzioni; funziona già tutto, per gli sviluppatori e per gli
utilizzatori. L'inserimento nel ramo principale risolve un gran numero di
problemi di distribuzione e di supporto.
- Nonostante gli sviluppatori kernel si sforzino di tenere stabile
l'interfaccia dello spazio utente, quella interna al kernel è in continuo
cambiamento. La mancanza di un'interfaccia interna è deliberatamente una
decisione di progettazione; ciò permette che i miglioramenti fondamentali
vengano fatti in un qualsiasi momento e che risultino fatti con un codice di
alta qualità. Ma una delle conseguenze di questa politica è che qualsiasi
codice "fuori dai sorgenti" richiede costante manutenzione per renderlo
funzionante coi kernel più recenti. Tenere un codice "fuori dai sorgenti"
richiede una mole di lavoro significativa solo per farlo funzionare.
Invece, il codice che si trova nel ramo principale non necessita di questo
tipo di lavoro poiché ad ogni sviluppatore che faccia una modifica alle
interfacce viene richiesto di sistemare anche il codice che utilizza
quell'interfaccia. Quindi, il codice che è stato inserito nel ramo principale
ha dei costi di mantenimento significativamente più bassi.
- Oltre a ciò, spesso il codice che è all'interno del kernel sarà migliorato da
altri sviluppatori. Dare pieni poteri alla vostra comunità di utenti e ai
clienti può portare a sorprendenti risultati che migliorano i vostri
prodotti.
- Il codice kernel è soggetto a revisioni, sia prima che dopo l'inserimento
nel ramo principale. Non importa quanto forti fossero le abilità dello
sviluppatore originale, il processo di revisione troverà il modo di migliore
il codice. Spesso la revisione trova bachi importanti e problemi di
sicurezza. Questo è particolarmente vero per il codice che è stato
sviluppato in un ambiente chiuso; tale codice ottiene un forte beneficio
dalle revisioni provenienti da sviluppatori esteri. Il codice
"fuori dai sorgenti", invece, è un codice di bassa qualità.
- La partecipazione al processo di sviluppo costituisce la vostra via per
influenzare la direzione di sviluppo del kernel. Gli utilizzatori che
"reclamano da bordo campo" sono ascoltati, ma gli sviluppatori attivi
hanno una voce più forte - e la capacità di implementare modifiche che
renderanno il kernel più funzionale alle loro necessità.
- Quando il codice è gestito separatamente, esiste sempre la possibilità che
terze parti contribuiscano con una differente implementazione che fornisce
le stesse funzionalità. Se dovesse accadere, l'inserimento del codice
diventerà molto più difficile - fino all'impossibilità. Poi, dovrete far
fronte a delle alternative poco piacevoli, come: (1) mantenere un elemento
non standard "fuori dai sorgenti" per un tempo indefinito, o (2) abbandonare
il codice e far migrare i vostri utenti alla versione "nei sorgenti".
- Contribuire al codice è l'azione fondamentale che fa funzionare tutto il
processo. Contribuendo attraverso il vostro codice potete aggiungere nuove
funzioni al kernel e fornire competenze ed esempi che saranno utili ad
altri sviluppatori. Se avete sviluppato del codice Linux (o state pensando
di farlo), avete chiaramente interesse nel far proseguire il successo di
questa piattaforma. Contribuire al codice è une delle migliori vie per
aiutarne il successo.
Il ragionamento sopra citato si applica ad ogni codice "fuori dai sorgenti"
dal kernel, incluso il codice proprietario distribuito solamente in formato
binario. Ci sono, comunque, dei fattori aggiuntivi che dovrebbero essere
tenuti in conto prima di prendere in considerazione qualsiasi tipo di
distribuzione binaria di codice kernel. Questo include che:
- Le questioni legali legate alla distribuzione di moduli kernel proprietari
sono molto nebbiose; parecchi detentori di copyright sul kernel credono che
molti moduli binari siano prodotti derivati del kernel e che, come risultato,
la loro diffusione sia una violazione della licenza generale di GNU (della
quale si parlerà più avanti). L'autore qui non è un avvocato, e
niente in questo documento può essere considerato come un consiglio legale.
Il vero stato legale dei moduli proprietari può essere determinato
esclusivamente da un giudice. Ma l'incertezza che perseguita quei moduli
è lì comunque.
- I moduli binari aumentano di molto la difficoltà di fare debugging del
kernel, al punto che la maggior parte degli sviluppatori del kernel non
vorranno nemmeno tentare. Quindi la diffusione di moduli esclusivamente
binari renderà difficile ai vostri utilizzatori trovare un supporto dalla
comunità.
- Il supporto è anche difficile per i distributori di moduli binari che devono
fornire una versione del modulo per ogni distribuzione e per ogni versione
del kernel che vogliono supportate. Per fornire una copertura ragionevole e
comprensiva, può essere richiesto di produrre dozzine di singoli moduli.
E inoltre i vostri utilizzatori dovranno aggiornare il vostro modulo
separatamente ogni volta che aggiornano il loro kernel.
- Tutto ciò che è stato detto prima riguardo alla revisione del codice si
applica doppiamente al codice proprietario. Dato che questo codice non è
del tutto disponibile, non può essere revisionato dalla comunità e avrà,
senza dubbio, seri problemi.
I produttori di sistemi integrati, in particolare, potrebbero esser tentati
dall'evitare molto di ciò che è stato detto in questa sezione, credendo che
stiano distribuendo un prodotto finito che utilizza una versione del kernel
immutabile e che non richiede un ulteriore sviluppo dopo il rilascio. Questa
idea non comprende il valore di una vasta revisione del codice e il valore
del permettere ai propri utenti di aggiungere funzionalità al vostro prodotto.
Ma anche questi prodotti, hanno una vita commerciale limitata, dopo la quale
deve essere rilasciata una nuova versione. A quel punto, i produttori il cui
codice è nel ramo principale di sviluppo avranno un codice ben mantenuto e
saranno in una posizione migliore per ottenere velocemente un nuovo prodotto
pronto per essere distribuito.
Licenza
-------
IL codice Linux utilizza diverse licenze, ma il codice completo deve essere
compatibile con la seconda versione della licenza GNU General Public License
(GPLv2), che è la licenza che copre la distribuzione del kernel.
Nella pratica, ciò significa che tutti i contributi al codice sono coperti
anche'essi dalla GPLv2 (con, opzionalmente, una dicitura che permette la
possibilità di distribuirlo con licenze più recenti di GPL) o dalla licenza
three-clause BSD. Qualsiasi contributo che non è coperto da una licenza
compatibile non verrà accettata nel kernel.
Per il codice sottomesso al kernel non è necessario (o richiesto) la
concessione del Copyright. Tutto il codice inserito nel ramo principale del
kernel conserva la sua proprietà originale; ne risulta che ora il kernel abbia
migliaia di proprietari.
Una conseguenza di questa organizzazione della proprietà è che qualsiasi
tentativo di modifica della licenza del kernel è destinata ad un quasi sicuro
fallimento. Esistono alcuni scenari pratici nei quali il consenso di tutti
i detentori di copyright può essere ottenuto (o il loro codice verrà rimosso
dal kernel). Quindi, in sostanza, non esiste la possibilità che si giunga ad
una versione 3 della licenza GPL nel prossimo futuro.
È imperativo che tutto il codice che contribuisce al kernel sia legittimamente
software libero. Per questa ragione, un codice proveniente da un contributore
anonimo (o sotto pseudonimo) non verrà accettato. È richiesto a tutti i
contributori di firmare il proprio codice, attestando così che quest'ultimo
può essere distribuito insieme al kernel sotto la licenza GPL. Il codice che
non è stato licenziato come software libero dal proprio creatore, o che
potrebbe creare problemi di copyright per il kernel (come il codice derivante
da processi di ingegneria inversa senza le opportune tutele), non può essere
diffuso.
Domande relative a questioni legate al copyright sono frequenti nelle liste
di discussione dedicate allo sviluppo di Linux. Tali quesiti, normalmente,
non riceveranno alcuna risposta, ma una cosa deve essere tenuta presente:
le persone che risponderanno a quelle domande non sono avvocati e non possono
fornire supporti legali. Se avete questioni legali relative ai sorgenti
del codice Linux, non esiste alternativa che quella di parlare con un
avvocato esperto nel settore. Fare affidamento sulle risposte ottenute da
una lista di discussione tecnica è rischioso.
.. include:: ../disclaimer-ita.rst
:Original: :ref:`Documentation/process/2.Process.rst <development_process>`
:Translator: Alessia Mantegazza <amantegazza@vaga.pv.it>
.. _it_development_process:
Come funziona il processo di sviluppo
=====================================
Lo sviluppo del Kernel agli inizi degli anno '90 era abbastanza libero, con
un numero di utenti e sviluppatori relativamente basso. Con una base
di milioni di utenti e con 2000 sviluppatori coinvolti nel giro di un anno,
il kernel da allora ha messo in atto un certo numero di procedure per rendere
lo sviluppo più agevole. È richiesta una solida conoscenza di come tale
processo si svolge per poter esserne parte attiva.
Il quadro d'insieme
-------------------
Gli sviluppatori kernel utilizzano un calendario di rilascio generico, dove
ogni due o tre mesi viene effettuata un rilascio importante del kernel.
I rilasci più recenti sono stati:
====== =================
4.11 Aprile 30, 2017
4.12 Luglio 2, 2017
4.13 Settembre 3, 2017
4.14 Novembre 12, 2017
4.15 Gennaio 28, 2018
4.16 Aprile 1, 2018
====== =================
Ciascun rilascio 4.x è un importante rilascio del kernel con nuove
funzionalità, modifiche interne dell'API, e molto altro. Un tipico
rilascio 4.x contiene quasi 13,000 gruppi di modifiche con ulteriori
modifiche a parecchie migliaia di linee di codice. La 4.x. è pertanto la
linea di confine nello sviluppo del kernel Linux; il kernel utilizza un sistema
di sviluppo continuo che integra costantemente nuove importanti modifiche.
Viene seguita una disciplina abbastanza lineare per l'inclusione delle
patch di ogni rilascio. All'inizio di ogni ciclo di sviluppo, la
"finestra di inclusione" viene dichiarata aperta. In quel momento il codice
ritenuto sufficientemente stabile(e che è accettato dalla comunità di sviluppo)
viene incluso nel ramo principale del kernel. La maggior parte delle
patch per un nuovo ciclo di sviluppo (e tutte le più importanti modifiche)
saranno inserite durante questo periodo, ad un ritmo che si attesta sulle
1000 modifiche ("patch" o "gruppo di modifiche") al giorno.
(per inciso, vale la pena notare che i cambiamenti integrati durante la
"finestra di inclusione" non escono dal nulla; questi infatti, sono stati
raccolti e, verificati in anticipo. Il funzionamento di tale procedimento
verrà descritto dettagliatamente più avanti).
La finestra di inclusione resta attiva approssimativamente per due settimane.
Al termine di questo periodo, Linus Torvald dichiarerà che la finestra è
chiusa e rilascerà il primo degli "rc" del kernel.
Per il kernel che è destinato ad essere 2.6.40, per esempio, il rilascio
che emerge al termine della finestra d'inclusione si chiamerà 2.6.40-rc1.
Questo rilascio indica che il momento di aggiungere nuovi componenti è
passato, e che è iniziato il periodo di stabilizzazione del prossimo kernel.
Nelle successive sei/dieci settimane, potranno essere sottoposte solo modifiche
che vanno a risolvere delle problematiche. Occasionalmente potrà essere
consentita una modifica più consistente, ma tali occasioni sono rare.
Gli sviluppatori che tenteranno di aggiungere nuovi elementi al di fuori della
finestra di inclusione, tendenzialmente, riceveranno un accoglienza poco
amichevole. Come regola generale: se vi perdete la finestra di inclusione per
un dato componente, la cosa migliore da fare è aspettare il ciclo di sviluppo
successivo (un'eccezione può essere fatta per i driver per hardware non
supportati in precedenza; se toccano codice non facente parte di quello
attuale, che non causino regressioni e che potrebbero essere aggiunti in
sicurezza in un qualsiasi momento)
Mentre le correzioni si aprono la loro strada all'interno del ramo principale,
il ritmo delle modifiche rallenta col tempo. Linus rilascia un nuovo
kernel -rc circa una volta alla settimana; e ne usciranno circa 6 o 9 prima
che il kernel venga considerato sufficientemente stabile e che il rilascio
finale 2.6.x venga fatto. A quel punto tutto il processo ricomincerà.
Esempio: ecco com'è andato il ciclo di sviluppo della versione 4.16
(tutte le date si collocano nel 2018)
============== =======================================
Gennaio 28 4.15 rilascio stabile
Febbraio 11 4.16-rc1, finestra di inclusione chiusa
Febbraio 18 4.16-rc2
Febbraio 25 4.16-rc3
Marzo 4 4.16-rc4
Marzo 11 4.16-rc5
Marzo 18 4.16-rc6
Marzo 25 4.16-rc7
Aprile 1 4.17 rilascio stabile
============== =======================================
In che modo gli sviluppatori decidono quando chiudere il ciclo di sviluppo e
creare quindi una rilascio stabile? Un metro valido è il numero di regressioni
rilevate nel precedente rilascio. Nessun baco è il benvenuto, ma quelli che
procurano problemi su sistemi che hanno funzionato in passato sono considerati
particolarmente seri. Per questa ragione, le modifiche che portano ad una
regressione sono viste sfavorevolmente e verranno quasi sicuramente annullate
durante il periodo di stabilizzazione.
L'obiettivo degli sviluppatori è quello di aggiustare tutte le regressioni
conosciute prima che avvenga il rilascio stabile. Nel mondo reale, questo
tipo di perfezione difficilmente viene raggiunta; esistono troppe variabili
in un progetto di questa portata. Arriva un punto dove ritardare il rilascio
finale peggiora la situazione; la quantità di modifiche in attesa della
prossima finestra di inclusione crescerà enormemente, creando ancor più
regressioni al giro successivo. Quindi molti kernel 4.x escono con una
manciata di regressioni delle quali, si spera, nessuna è grave.
Una volta che un rilascio stabile è fatto, il suo costante mantenimento è
affidato al "squadra stabilità", attualmente composta da Greg Kroah-Hartman.
Questa squadra rilascia occasionalmente degli aggiornamenti relativi al
rilascio stabile usando la numerazione 4.x.y. Per essere presa in
considerazione per un rilascio d'aggiornamento, una modifica deve:
(1) correggere un baco importante (2) essere già inserita nel ramo principale
per il prossimo sviluppo del kernel. Solitamente, passato il loro rilascio
iniziale, i kernel ricevono aggiornamenti per più di un ciclo di sviluppo.
Quindi, per esempio, la storia del kernel 4.13 appare così:
============== ===============================
Settembre 3 4.13 rilascio stabile
Settembre 13 4.13.1
Settembre 20 4.13.2
Settembre 27 4.13.3
Ottobre 5 4.13.4
Ottobre 12 4.13.5
... ...
Novembre 24 4.13.16
============== ===============================
La 4.13.16 fu l'aggiornamento finale per la versione 4.13.
Alcuni kernel sono destinati ad essere kernel a "lungo termine"; questi
riceveranno assistenza per un lungo periodo di tempo. Al momento in cui
scriviamo, i manutentori dei kernel stabili a lungo termine sono:
====== ====================== ==========================================
3.16 Ben Hutchings (kernel stabile molto più a lungo termine)
4.1 Sasha Levin
4.4 Greg Kroah-Hartman (kernel stabile molto più a lungo termine)
4.9 Greg Kroah-Hartman
4.14 Greg Kroah-Hartman
====== ====================== ==========================================
Questa selezione di kernel di lungo periodo sono puramente dovuti ai loro
manutentori, alla loro necessità e al tempo per tenere aggiornate proprio
quelle versioni. Non ci sono altri kernel a lungo termine in programma per
alcun rilascio in arrivo.
Il ciclo di vita di una patch
-----------------------------
Le patch non passano direttamente dalla tastiera dello sviluppatori
al ramo principale del kernel. Esiste, invece, una procedura disegnata
per assicurare che ogni patch sia di buona qualità e desiderata nel
ramo principale. Questo processo avviene velocemente per le correzioni
meno importanti, o, nel caso di patch ampie e controverse, va avanti per anni.
Per uno sviluppatore la maggior frustrazione viene dalla mancanza di
comprensione di questo processo o dai tentativi di aggirarlo.
Nella speranza di ridurre questa frustrazione, questo documento spiegherà
come una patch viene inserita nel kernel. Ciò che segue è un'introduzione
che descrive il processo ideale. Approfondimenti verranno invece trattati
più avanti.
Una patch attraversa, generalmente, le seguenti fasi:
- Progetto. In questa fase sono stabilite quelli che sono i requisiti
della modifica - e come verranno soddisfatti. Il lavoro di progettazione
viene spesso svolto senza coinvolgere la comunità, ma è meglio renderlo
il più aperto possibile; questo può far risparmiare molto tempo evitando
eventuali riprogettazioni successive.
- Prima revisione. Le patch vengono pubblicate sulle liste di discussione
interessate, e gli sviluppatori in quella lista risponderanno coi loro
commenti. Se si svolge correttamente, questo procedimento potrebbe far
emergere problemi rilevanti in una patch.
- Revisione più ampia. Quando la patch è quasi pronta per essere inserita
nel ramo principale, un manutentore importante del sottosistema dovrebbe
accettarla - anche se, questa accettazione non è una garanzia che la
patch arriverà nel ramo principale. La patch sarà visibile nei sorgenti
del sottosistema in questione e nei sorgenti -next (descritti sotto).
Quando il processo va a buon fine, questo passo porta ad una revisione
più estesa della patch e alla scoperta di problemi d'integrazione
con il lavoro altrui.
- Per favore, tenete da conto che la maggior parte dei manutentori ha
anche un lavoro quotidiano, quindi integrare le vostre patch potrebbe
non essere la loro priorità più alta. Se una vostra patch riceve
dei suggerimenti su dei cambiamenti necessari, dovreste applicare
quei cambiamenti o giustificare perché non sono necessari. Se la vostra
patch non riceve alcuna critica ma non è stata integrata dal
manutentore del driver o sottosistema, allora dovreste continuare con
i necessari aggiornamenti per mantenere la patch aggiornata al kernel
più recente cosicché questa possa integrarsi senza problemi; continuate
ad inviare gli aggiornamenti per essere revisionati e integrati.
- Inclusione nel ramo principale. Eventualmente, una buona patch verrà
inserita all'interno nel repositorio principale, gestito da
Linus Torvalds. In questa fase potrebbero emergere nuovi problemi e/o
commenti; è importante che lo sviluppatore sia collaborativo e che sistemi
ogni questione che possa emergere.
- Rilascio stabile. Ora, il numero di utilizzatori che sono potenzialmente
toccati dalla patch è aumentato, quindi, ancora una volta, potrebbero
emergere nuovi problemi.
- Manutenzione di lungo periodo. Nonostante sia possibile che uno sviluppatore
si dimentichi del codice dopo la sua integrazione, questo comportamento
lascia una brutta impressione nella comunità di sviluppo. Integrare il
codice elimina alcuni degli oneri facenti parte della manutenzione, in
particolare, sistemerà le problematiche causate dalle modifiche all'API.
Ma lo sviluppatore originario dovrebbe continuare ad assumersi la
responsabilità per il codice se quest'ultimo continua ad essere utile
nel lungo periodo.
Uno dei più grandi errori fatti dagli sviluppatori kernel (o dai loro datori
di lavoro) è quello di cercare di ridurre tutta la procedura ad una singola
"integrazione nel remo principale". Questo approccio inevitabilmente conduce
a una condizione di frustrazione per tutti coloro che sono coinvolti.
Come le modifiche finiscono nel Kernel
--------------------------------------
Esiste una sola persona che può inserire le patch nel repositorio principale
del kernel: Linus Torvalds. Ma, di tutte le 9500 patch che entrarono nella
versione 2.6.38 del kernel, solo 112 (circa l'1,3%) furono scelte direttamente
da Linus in persona. Il progetto del kernel è cresciuto fino a raggiungere
una dimensione tale per cui un singolo sviluppatore non può controllare e
selezionare indipendentemente ogni modifica senza essere supportato.
La via scelta dagli sviluppatori per indirizzare tale crescita è stata quella
di utilizzare un sistema di "sottotenenti" basato sulla fiducia.
Il codice base del kernel è spezzato in una serie si sottosistemi: rete,
supporto per specifiche architetture, gestione della memoria, video e
strumenti, etc. Molti sottosistemi hanno un manutentore designato: ovvero uno
sviluppatore che ha piena responsabilità di tutto il codice presente in quel
sottosistema. Tali manutentori di sottosistema sono i guardiani
(in un certo senso) della parte di kernel che gestiscono; sono coloro che
(solitamente) accetteranno una patch per l'inclusione nel ramo principale
del kernel.
I manutentori di sottosistema gestiscono ciascuno la propria parte dei sorgenti
del kernel, utilizzando abitualmente (ma certamente non sempre) git.
Strumenti come git (e affini come quilt o mercurial) permettono ai manutentori
di stilare una lista delle patch, includendo informazioni sull'autore ed
altri metadati. In ogni momento, il manutentore può individuare quale patch
nel sua repositorio non si trova nel ramo principale.
Quando la "finestra di integrazione" si apre, i manutentori di alto livello
chiederanno a Linus di "prendere" dai loro repositori le modifiche che hanno
selezionato per l'inclusione. Se Linus acconsente, il flusso di patch si
convoglierà nel repositorio di quest ultimo, divenendo così parte del ramo
principale del kernel. La quantità d'attenzione che Linus presta alle
singole patch ricevute durante l'operazione di integrazione varia.
È chiaro che, qualche volta, guardi più attentamente. Ma, come regola
generale, Linus confida nel fatto che i manutentori di sottosistema non
selezionino pessime patch.
I manutentori di sottosistemi, a turno, possono "prendere" patch
provenienti da altri manutentori. Per esempio, i sorgenti per la rete rete
sono costruiti da modifiche che si sono accumulate inizialmente nei sorgenti
dedicati ai driver per dispositivi di rete, rete senza fili, ecc. Tale
catena di repositori può essere più o meno lunga, benché raramente ecceda
i due o tre collegamenti. Questo processo è conosciuto come
"la catena della fiducia", perché ogni manutentore all'interno della
catena si fida di coloro che gestiscono i livelli più bassi.
Chiaramente, in un sistema come questo, l'inserimento delle patch all'interno
del kernel si basa sul trovare il manutentore giusto. Di norma, inviare
patch direttamente a Linus non è la via giusta.
Sorgenti -next
--------------
La catena di sottosistemi guida il flusso di patch all'interno del kernel,
ma solleva anche un interessante quesito: se qualcuno volesse vedere tutte le
patch pronte per la prossima finestra di integrazione?
Gli sviluppatori si interesseranno alle patch in sospeso per verificare
che non ci siano altri conflitti di cui preoccuparsi; una modifica che, per
esempio, cambia il prototipo di una funzione fondamentale del kernel andrà in
conflitto con qualsiasi altra modifica che utilizzi la vecchia versione di
quella funzione. Revisori e tester vogliono invece avere accesso alle
modifiche nella loro totalità prima che approdino nel ramo principale del
kernel. Uno potrebbe prendere le patch provenienti da tutti i sottosistemi
d'interesse, ma questo sarebbe un lavoro enorme e fallace.
La risposta ci viene sotto forma di sorgenti -next, dove i sottosistemi sono
raccolti per essere testati e controllati. Il più vecchio di questi sorgenti,
gestito da Andrew Morton, è chiamato "-mm" (memory management, che è l'inizio
di tutto). L'-mm integra patch proveniente da una lunga lista di sottosistemi;
e ha, inoltre, alcune patch destinate al supporto del debugging.
Oltre a questo, -mm contiene una raccolta significativa di patch che sono
state selezionate da Andrew direttamente. Queste patch potrebbero essere
state inviate in una lista di discussione, o possono essere applicate ad una
parte del kernel per la quale non esiste un sottosistema dedicato.
Di conseguenza, -mm opera come una specie di sottosistema "ultima spiaggia";
se per una patch non esiste una via chiara per entrare nel ramo principale,
allora è probabile che finirà in -mm. Le patch passate per -mm
eventualmente finiranno nel sottosistema più appropriato o saranno inviate
direttamente a Linus. In un tipico ciclo di sviluppo, circa il 5-10% delle
patch andrà nel ramo principale attraverso -mm.
La patch -mm correnti sono disponibili nella cartella "mmotm" (-mm of
the moment) all'indirizzo:
http://www.ozlabs.org/~akpm/mmotm/
È molto probabile che l'uso dei sorgenti MMOTM diventi un'esperienza
frustrante; ci sono buone probabilità che non compili nemmeno.
I sorgenti principali per il prossimo ciclo d'integrazione delle patch
è linux-next, gestito da Stephen Rothwell. I sorgenti linux-next sono, per
definizione, un'istantanea di come dovrà apparire il ramo principale dopo che
la prossima finestra di inclusione si chiuderà. I linux-next sono annunciati
sulla lista di discussione linux-kernel e linux-next nel momento in cui
vengono assemblati; e possono essere scaricate da:
http://www.kernel.org/pub/linux/kernel/next/
Linux-next è divenuto parte integrante del processo di sviluppo del kernel;
tutte le patch incorporate durante una finestra di integrazione dovrebbero
aver trovato la propria strada in linux-next, a volte anche prima dell'apertura
della finestra di integrazione.
Sorgenti in preparazione
------------------------
Nei sorgenti del kernel esiste la cartella drivers/staging/, dove risiedono
molte sotto-cartelle per i driver o i filesystem che stanno per essere aggiunti
al kernel. Questi restano nella cartella drivers/staging fintanto che avranno
bisogno di maggior lavoro; una volta completato, possono essere spostate
all'interno del kernel nel posto più appropriato. Questo è il modo di tener
traccia dei driver che non sono ancora in linea con gli standard di codifica
o qualità, ma che le persone potrebbero voler usare ugualmente e tracciarne
lo sviluppo.
Greg Kroah-Hartman attualmente gestisce i sorgenti in preparazione. I driver
che non sono completamente pronti vengono inviati a lui, e ciascun driver avrà
la propria sotto-cartella in drivers/staging/. Assieme ai file sorgenti
dei driver, dovrebbe essere presente nella stessa cartella anche un file TODO.
Il file TODO elenca il lavoro ancora da fare su questi driver per poter essere
accettati nel kernel, e indica anche la lista di persone da inserire in copia
conoscenza per ogni modifica fatta. Le regole attuali richiedono che i
driver debbano, come minimo, compilare adeguatamente.
La *preparazione* può essere una via relativamente facile per inserire nuovi
driver all'interno del ramo principale, dove, con un po' di fortuna, saranno
notati da altri sviluppatori e migliorati velocemente. Entrare nella fase
di preparazione non è però la fine della storia, infatti, il codice che si
trova nella cartella staging che non mostra regolari progressi potrebbe
essere rimosso. Le distribuzioni, inoltre, tendono a dimostrarsi relativamente
riluttanti nell'attivare driver in preparazione. Quindi lo preparazione è,
nel migliore dei casi, una tappa sulla strada verso il divenire un driver
del ramo principale.
Strumenti
---------
Come è possibile notare dal testo sopra, il processo di sviluppo del kernel
dipende pesantemente dalla capacità di guidare la raccolta di patch in
diverse direzioni. L'intera cosa non funzionerebbe se non venisse svolta
con l'uso di strumenti appropriati e potenti. Spiegare l'uso di tali
strumenti non è lo scopo di questo documento, ma c'è spazio per alcuni
consigli.
In assoluto, nella comunità del kernel, predomina l'uso di git come sistema
di gestione dei sorgenti. Git è una delle diverse tipologie di sistemi
distribuiti di controllo versione che sono stati sviluppati nella comunità
del software libero. Esso è calibrato per lo sviluppo del kernel, e si
comporta abbastanza bene quando ha a che fare con repositori grandi e con un
vasto numero di patch. Git ha inoltre la reputazione di essere difficile
da imparare e utilizzare, benché stia migliorando. Agli sviluppatori
del kernel viene richiesta un po' di familiarità con git; anche se non lo
utilizzano per il proprio lavoro, hanno bisogno di git per tenersi al passo
con il lavoro degli altri sviluppatori (e con il ramo principale).
Git è ora compreso in quasi tutte le distribuzioni Linux. Esiste una sito che
potete consultare:
http://git-scm.com/
Qui troverete i riferimenti alla documentazione e alle guide passo-passo.
Tra gli sviluppatori Kernel che non usano git, la scelta alternativa più
popolare è quasi sicuramente Mercurial:
http://www.selenic.com/mercurial/
Mercurial condivide diverse caratteristiche con git, ma fornisce
un'interfaccia che potrebbe risultare più semplice da utilizzare.
L'altro strumento che vale la pena conoscere è Quilt:
http://savannah.nongnu.org/projects/quilt/
Quilt è un sistema di gestione delle patch, piuttosto che un sistema
di gestione dei sorgenti. Non mantiene uno storico degli eventi; ma piuttosto
è orientato verso il tracciamento di uno specifico insieme di modifiche
rispetto ad un codice in evoluzione. Molti dei più grandi manutentori di
sottosistema utilizzano quilt per gestire le patch che dovrebbero essere
integrate. Per la gestione di certe tipologie di sorgenti (-mm, per esempio),
quilt è il miglior strumento per svolgere il lavoro.
Liste di discussione
--------------------
Una grossa parte del lavoro di sviluppo del Kernel Linux viene svolto tramite
le liste di discussione. È difficile essere un membro della comunità
pienamente coinvolto se non si partecipa almeno ad una lista da qualche
parte. Ma, le liste di discussione di Linux rappresentano un potenziale
problema per gli sviluppatori, che rischiano di venir sepolti da un mare di
email, restare incagliati nelle convenzioni in vigore nelle liste Linux,
o entrambi.
Molte delle liste di discussione del Kernel girano su vger.kernel.org;
l'elenco principale lo si trova sul sito:
http://vger.kernel.org/vger-lists.html
Esistono liste gestite altrove; un certo numero di queste sono in
lists.redhat.com.
La lista di discussione principale per lo sviluppo del kernel è, ovviamente,
linux-kernel. Questa lista è un luogo ostile dove trovarsi; i volumi possono
raggiungere i 500 messaggi al giorno, la quantità di "rumore" è elevata,
la conversazione può essere strettamente tecnica e i partecipanti non sono
sempre preoccupati di mostrare un alto livello di educazione. Ma non esiste
altro luogo dove la comunità di sviluppo del kernel si unisce per intero;
gli sviluppatori che evitano tale lista si perderanno informazioni importanti.
Ci sono alcuni consigli che possono essere utili per sopravvivere a
linux-kernel:
- Tenete la lista in una cartella separata, piuttosto che inserirla nella
casella di posta principale. Così da essere in grado di ignorare il flusso
di mail per un certo periodo di tempo.
- Non cercate di seguire ogni conversazione - nessuno lo fa. È importante
filtrare solo gli argomenti d'interesse (sebbene va notato che le
conversazioni di lungo periodo possono deviare dall'argomento originario
senza cambiare il titolo della mail) e le persone che stanno partecipando.
- Non alimentate i troll. Se qualcuno cerca di creare nervosismo, ignoratelo.
- Quando rispondete ad una mail linux-kernel (o ad altre liste) mantenete
tutti i Cc:. In assenza di importanti motivazioni (come una richiesta
esplicita), non dovreste mai togliere destinatari. Assicuratevi sempre che
la persona alla quale state rispondendo sia presente nella lista Cc. Questa
usanza fa si che divenga inutile chiedere esplicitamente di essere inseriti
in copia nel rispondere al vostro messaggio.
- Cercate nell'archivio della lista (e nella rete nella sua totalità) prima
di far domande. Molti sviluppatori possono divenire impazienti con le
persone che chiaramente non hanno svolto i propri compiti a casa.
- Evitate il *top-posting* (cioè la pratica di mettere la vostra risposta sopra
alla frase alla quale state rispondendo). Ciò renderebbe la vostra risposta
difficile da leggere e genera scarsa impressione.
- Chiedete nella lista di discussione corretta. Linux-kernel può essere un
punto di incontro generale, ma non è il miglior posto dove trovare
sviluppatori da tutti i sottosistemi.
Infine, la ricerca della corretta lista di discussione è uno degli errori più
comuni per gli sviluppatori principianti. Qualcuno che pone una domanda
relativa alla rete su linux-kernel riceverà quasi certamente il suggerimento
di chiedere sulla lista netdev, che è la lista frequentata dagli sviluppatori
di rete. Ci sono poi altre liste per i sottosistemi SCSI, video4linux, IDE,
filesystem, etc. Il miglior posto dove cercare una lista di discussione è il
file MAINTAINERS che si trova nei sorgenti del kernel.
Iniziare con lo sviluppo del Kernel
-----------------------------------
Sono comuni le domande sul come iniziare con lo sviluppo del kernel - sia da
singole persone che da aziende. Altrettanto comuni sono i passi falsi che
rendono l'inizio di tale relazione più difficile di quello che dovrebbe essere.
Le aziende spesso cercano di assumere sviluppatori noti per creare un gruppo
di sviluppo iniziale. Questo, in effetti, può essere una tecnica efficace.
Ma risulta anche essere dispendiosa e non va ad accrescere il bacino di
sviluppatori kernel con esperienza. È possibile anche "portare a casa"
sviluppatori per accelerare lo sviluppo del kernel, dando comunque
all'investimento un po' di tempo. Prendersi questo tempo può fornire
al datore di lavoro un gruppo di sviluppatori che comprendono sia il kernel
che l'azienda stessa, e che possono supportare la formazione di altre persone.
Nel medio periodo, questa è spesso uno delle soluzioni più proficue.
I singoli sviluppatori sono spesso, comprensibilmente, una perdita come punto
di partenza. Iniziare con un grande progetto può rivelarsi intimidatorio;
spesso all'inizio si vuole solo verificare il terreno con qualcosa di piccolo.
Questa è una delle motivazioni per le quali molti sviluppatori saltano alla
creazione di patch che vanno a sistemare errori di battitura o
problematiche minori legate allo stile del codice. Sfortunatamente, tali
patch creano un certo livello di rumore che distrae l'intera comunità di
sviluppo, quindi, sempre di più, esse vengono degradate. I nuovi sviluppatori
che desiderano presentarsi alla comunità non riceveranno l'accoglienza
che vorrebbero con questi mezzi.
Andrew Morton da questo consiglio agli aspiranti sviluppatori kernel
::
Il primo progetto per un neofita del kernel dovrebbe essere
sicuramente quello di "assicurarsi che il kernel funzioni alla
perfezione sempre e su tutte le macchine sulle quali potete stendere
la vostra mano". Solitamente il modo per fare ciò è quello di
collaborare con gli altri nel sistemare le cose (questo richiede
persistenza!) ma va bene - è parte dello sviluppo kernel.
(http://lwn.net/Articles/283982/).
In assenza di problemi ovvi da risolvere, si consiglia agli sviluppatori
di consultare, in generale, la lista di regressioni e di bachi aperti.
Non c'è mai carenza di problematiche bisognose di essere sistemate;
accollandosi tali questioni gli sviluppatori accumuleranno esperienza con
la procedura, ed allo stesso tempo, aumenteranno la loro rispettabilità
all'interno della comunità di sviluppo.
.. include:: ../disclaimer-ita.rst
:Original: :ref:`Documentation/process/3.Early-stage.rst <development_early_stage>`
:Translator: Alessia Mantegazza <amantegazza@vaga.pv.it>
.. _it_development_early_stage:
I primi passi della pianificazione
==================================
Osservando un progetto di sviluppo per il kernel Linux, si potrebbe essere
tentati dal saltare tutto e iniziare a codificare. Tuttavia, come ogni
progetto significativo, molta della preparazione per giungere al successo
viene fatta prima che una sola linea di codice venga scritta. Il tempo speso
nella pianificazione e la comunicazione può far risparmiare molto
tempo in futuro.
Specificare il problema
-----------------------
Come qualsiasi progetto ingegneristico, un miglioramento del kernel di
successo parte con una chiara descrizione del problema da risolvere.
In alcuni casi, questo passaggio è facile: ad esempio quando un driver è
richiesto per un particolare dispositivo. In altri casi invece, si
tende a confondere il problema reale con le soluzioni proposte e questo
può portare all'emergere di problemi.
Facciamo un esempio: qualche anno fa, gli sviluppatori che lavoravano con
linux audio cercarono un modo per far girare le applicazioni senza dropouts
o altri artefatti dovuti all'eccessivo ritardo nel sistema. La soluzione
alla quale giunsero fu un modulo del kernel destinato ad agganciarsi al
framework Linux Security Module (LSM); questo modulo poteva essere
configurato per dare ad una specifica applicazione accesso allo
schedulatore *realtime*. Tale modulo fu implementato e inviato nella
lista di discussione linux-kernel, dove incontrò subito dei problemi.
Per gli sviluppatori audio, questo modulo di sicurezza era sufficiente a
risolvere il loro problema nell'immediato. Per l'intera comunità kernel,
invece, era un uso improprio del framework LSM (che non è progettato per
conferire privilegi a processi che altrimenti non avrebbero potuto ottenerli)
e un rischio per la stabilità del sistema. Le loro soluzioni di punta nel
breve periodo, comportavano un accesso alla schedulazione realtime attraverso
il meccanismo rlimit, e nel lungo periodo un costante lavoro nella riduzione
dei ritardi.
La comunità audio, comunque, non poteva vedere al di là della singola
soluzione che avevano implementato; erano riluttanti ad accettare alternative.
Il conseguente dissenso lasciò in quegli sviluppatori un senso di
disillusione nei confronti dell'intero processo di sviluppo; uno di loro
scrisse questo messaggio:
Ci sono numerosi sviluppatori del kernel Linux davvero bravi, ma
rischiano di restare sovrastati da una vasta massa di stolti arroganti.
Cercare di comunicare le richieste degli utenti a queste persone è
una perdita di tempo. Loro sono troppo "intelligenti" per stare ad
ascoltare dei poveri mortali.
(http://lwn.net/Articles/131776/).
La realtà delle cose fu differente; gli sviluppatori del kernel erano molto
più preoccupati per la stabilità del sistema, per la manutenzione di lungo
periodo e cercavano la giusta soluzione alla problematica esistente con uno
specifico modulo. La morale della storia è quella di concentrarsi sul
problema - non su di una specifica soluzione- e di discuterne con la comunità
di sviluppo prima di investire tempo nella scrittura del codice.
Quindi, osservando un progetto di sviluppo del kernel, si dovrebbe
rispondere a questa lista di domande:
- Qual'è, precisamente, il problema che dev'essere risolto?
- Chi sono gli utenti coinvolti da tal problema? A quale caso dovrebbe
essere indirizzata la soluzione?
- In che modo il kernel risulta manchevole nell'indirizzare il problema
in questione?
Solo dopo ha senso iniziare a considerare le possibili soluzioni.
Prime discussioni
-----------------
Quando si pianifica un progetto di sviluppo per il kernel, sarebbe quanto meno
opportuno discuterne inizialmente con la comunità prima di lanciarsi
nell'implementazione. Una discussione preliminare può far risparmiare sia
tempo che problemi in svariati modi:
- Potrebbe essere che il problema sia già stato risolto nel kernel in
una maniera che non avete ancora compreso. Il kernel Linux è grande e ha
una serie di funzionalità e capacità che non sono scontate nell'immediato.
Non tutte le capacità del kernel sono documentate così bene come ci
piacerebbe, ed è facile perdersi qualcosa. Il vostro autore ha assistito
alla pubblicazione di un driver intero che duplica un altro driver
esistente di cui il nuovo autore era ignaro. Il codice che rinnova
ingranaggi già esistenti non è soltanto dispendioso; non verrà nemmeno
accettato nel ramo principale del kernel.
- Potrebbero esserci proposte che non sono considerate accettabili per
l'integrazione all'interno del ramo principale. È meglio affrontarle
prima di scrivere il codice.
- È possibile che altri sviluppatori abbiano pensato al problema; potrebbero
avere delle idee per soluzioni migliori, e potrebbero voler contribuire
alla loro creazione.
Anni di esperienza con la comunità di sviluppo del kernel hanno impartito una
chiara lezione: il codice per il kernel che è pensato e sviluppato a porte
chiuse, inevitabilmente, ha problematiche che si rivelano solo quando il
codice viene rilasciato pubblicamente. Qualche volta tali problemi sono
importanti e richiedono mesi o anni di sforzi prima che il codice possa
raggiungere gli standard richiesti della comunità.
Alcuni esempi possono essere:
- La rete Devicescape è stata creata e implementata per sistemi
mono-processore. Non avrebbe potuto essere inserita nel ramo principale
fino a che non avesse supportato anche i sistemi multi-processore.
Riadattare i meccanismi di sincronizzazione e simili è un compito difficile;
come risultato, l'inserimento di questo codice (ora chiamato mac80211)
fu rimandato per più di un anno.
- Il filesystem Reiser4 include una seria di funzionalità che, secondo
l'opinione degli sviluppatori principali del kernel, avrebbero dovuto
essere implementate a livello di filesystem virtuale. Comprende
anche funzionalità che non sono facilmente implementabili senza esporre
il sistema al rischio di uno stallo. La scoperta tardiva di questi
problemi - e il diniego a risolverne alcuni - ha avuto come conseguenza
il fatto che Raiser4 resta fuori dal ramo principale del kernel.
- Il modulo di sicurezza AppArmor utilizzava strutture dati del
filesystem virtuale interno in modi che sono stati considerati rischiosi e
inattendibili. Questi problemi (tra le altre cose) hanno tenuto AppArmor
fuori dal ramo principale per anni.
Ciascuno di questi casi è stato un travaglio e ha richiesto del lavoro
straordinario, cose che avrebbero potuto essere evitate con alcune
"chiacchierate" preliminari con gli sviluppatori kernel.
Con chi parlare?
----------------
Quando gli sviluppatori hanno deciso di rendere pubblici i propri progetti, la
domanda successiva sarà: da dove partiamo? La risposta è quella di trovare
la giusta lista di discussione e il giusto manutentore. Per le liste di
discussione, il miglior approccio è quello di cercare la lista più adatta
nel file MAINTAINERS. Se esiste una lista di discussione di sottosistema,
è preferibile pubblicare lì piuttosto che sulla lista di discussione generale
del kernel Linux; avrete maggiori probabilità di trovare sviluppatori con
esperienza sul tema, e l'ambiente che troverete potrebbe essere più
incoraggiante.
Trovare manutentori può rivelarsi un po' difficoltoso. Ancora, il file
MAINTAINERS è il posto giusto da dove iniziare. Il file potrebbe non essere
sempre aggiornato, inoltre, non tutti i sottosistemi sono rappresentati qui.
Coloro che sono elencati nel file MAINTAINERS potrebbero, in effetti, non
essere le persone che attualmente svolgono quel determinato ruolo. Quindi,
quando c'è un dubbio su chi contattare, un trucco utile è quello di usare
git (git log in particolare) per vedere chi attualmente è attivo all'interno
del sottosistema interessato. Controllate chi sta scrivendo le patch,
e chi, se non ci fosse nessuno, sta aggiungendo la propria firma
(Signed-off-by) a quelle patch. Quelle sono le persone maggiormente
qualificate per aiutarvi con lo sviluppo di nuovo progetto.
Il compito di trovare il giusto manutentore, a volte, è una tale sfida che
ha spinto gli sviluppatori del kernel a scrivere uno script che li aiutasse
in questa ricerca:
::
.../scripts/get_maintainer.pl
Se questo script viene eseguito con l'opzione "-f" ritornerà il
manutentore(i) attuale per un dato file o cartella. Se viene passata una
patch sulla linea di comando, lo script elencherà i manutentori che
dovrebbero riceverne una copia. Ci sono svariate opzioni che regolano
quanto a fondo get_maintainer.pl debba cercare i manutentori;
siate quindi prudenti nell'utilizzare le opzioni più aggressive poiché
potreste finire per includere sviluppatori che non hanno un vero interesse
per il codice che state modificando.
Se tutto ciò dovesse fallire, parlare con Andrew Morton potrebbe essere
un modo efficace per capire chi è il manutentore di un dato pezzo di codice.
Quando pubblicare
-----------------
Se potete, pubblicate i vostri intenti durante le fasi preliminari, sarà
molto utile. Descrivete il problema da risolvere e ogni piano che è stato
elaborato per l'implementazione. Ogni informazione fornita può aiutare
la comunità di sviluppo a fornire spunti utili per il progetto.
Un evento che potrebbe risultare scoraggiate e che potrebbe accadere in
questa fase non è il ricevere una risposta ostile, ma, invece, ottenere
una misera o inesistente reazione. La triste verità è che: (1) gli
sviluppatori del kernel tendono ad essere occupati, (2) ci sono tante persone
con grandi progetti e poco codice (o anche solo la prospettiva di
avere un codice) a cui riferirsi e (3) nessuno è obbligato a revisionare
o a fare osservazioni in merito ad idee pubblicate da altri. Oltre a
questo, progetti di alto livello spesso nascondono problematiche che si
rivelano solo quando qualcuno cerca di implementarle; per questa ragione
gli sviluppatori kernel preferirebbero vedere il codice.
Quindi, se una richiesta pubblica di commenti riscuote poco successo, non
pensate che ciò significhi che non ci sia interesse nel progetto.
Sfortunatamente, non potete nemmeno assumere che non ci siano problemi con
la vostra idea. La cosa migliore da fare in questa situazione è quella di
andare avanti e tenere la comunità informata mentre procedete.
Ottenere riscontri ufficiali
----------------------------
Se il vostro lavoro è stato svolto in un ambiente aziendale - come molto
del lavoro fatto su Linux - dovete, ovviamente, avere il permesso dei
dirigenti prima che possiate pubblicare i progetti, o il codice aziendale,
su una lista di discussione pubblica. La pubblicazione di codice che non
è stato rilascio espressamente con licenza GPL-compatibile può rivelarsi
problematico; prima la dirigenza, e il personale legale, troverà una decisione
sulla pubblicazione di un progetto, meglio sarà per tutte le persone coinvolte.
A questo punto, alcuni lettori potrebbero pensare che il loro lavoro sul
kernel è preposto a supportare un prodotto che non è ancora ufficialmente
riconosciuto. Rivelare le intenzioni dei propri datori di lavori in una
lista di discussione pubblica potrebbe non essere una soluzione valida.
In questi casi, vale la pena considerare se la segretezza sia necessaria
o meno; spesso non c'è una reale necessità di mantenere chiusi i progetti di
sviluppo.
Detto ciò, ci sono anche casi dove l'azienda legittimamente non può rivelare
le proprie intenzioni in anticipo durante il processo di sviluppo. Le aziende
che hanno sviluppatori kernel esperti possono scegliere di procedere a
carte coperte partendo dall'assunto che saranno in grado di evitare, o gestire,
in futuro, eventuali problemi d'integrazione. Per le aziende senza questo tipo
di esperti, la migliore opzione è spesso quella di assumere uno sviluppatore
esterno che revisioni i progetti con un accordo di segretezza.
La Linux Foundation applica un programma di NDA creato appositamente per
aiutare le aziende in questa particolare situazione; potrete trovare più
informazioni sul sito:
http://www.linuxfoundation.org/en/NDA_program
Questa tipologia di revisione è spesso sufficiente per evitare gravi problemi
senza che sia richiesta l'esposizione pubblica del progetto.
.. include:: ../disclaimer-ita.rst
:Original: :ref:`Documentation/process/4.Coding.rst <development_coding>`
:Translator: Alessia Mantegazza <amantegazza@vaga.pv.it>
.. _it_development_coding:
Scrivere codice corretto
========================
Nonostante ci sia molto da dire sul processo di creazione, sulla sua solidità
e sul suo orientamento alla comunità, la prova di ogni progetto di sviluppo
del kernel si trova nel codice stesso. È il codice che sarà esaminato dagli
altri sviluppatori ed inserito (o no) nel ramo principale. Quindi è la
qualità di questo codice che determinerà il successo finale del progetto.
Questa sezione esaminerà il processo di codifica. Inizieremo con uno sguardo
sulle diverse casistiche nelle quali gli sviluppatori kernel possono
sbagliare. Poi, l'attenzione si sposterà verso "il fare le cose
correttamente" e sugli strumenti che possono essere utili in questa missione.
Trappole
--------
Lo stile del codice
*******************
Il kernel ha da tempo delle norme sullo stile di codifica che sono descritte in
:ref:`Documentation/translations/it_IT/process/coding-style.rst <codingstyle>`.
Per la maggior parte del tempo, la politica descritta in quel file è stata
praticamente informativa. Ne risulta che ci sia una quantità sostanziale di
codice nel kernel che non rispetta le linee guida relative allo stile.
La presenza di quel codice conduce a due distinti pericoli per gli
sviluppatori kernel.
Il primo di questi è credere che gli standard di codifica del kernel
non sono importanti e possono non essere applicati. La verità è che
aggiungere nuovo codice al kernel è davvero difficile se questo non
rispetta le norme; molti sviluppatori richiederanno che il codice sia
riformulato prima che anche solo lo revisionino. Una base di codice larga
quanto il kernel richiede una certa uniformità, in modo da rendere possibile
per gli sviluppatori una comprensione veloce di ogni sua parte. Non ci sono,
quindi, più spazi per un codice formattato alla carlona.
Occasionalmente, lo stile di codifica del kernel andrà in conflitto con lo
stile richiesto da un datore di lavoro. In alcuni casi, lo stile del kernel
dovrà prevalere prima che il codice venga inserito. Mettere il codice
all'interno del kernel significa rinunciare a un certo grado di controllo
in differenti modi - incluso il controllo sul come formattare il codice.
L’altra trappola è quella di pensare che il codice già presente nel kernel
abbia urgentemente bisogno di essere sistemato. Gli sviluppatori potrebbero
iniziare a generare patch che correggono lo stile come modo per prendere
famigliarità con il processo, o come modo per inserire i propri nomi nei
changelog del kernel – o entrambe. La comunità di sviluppo vede un attività
di codifica puramente correttiva come "rumore"; queste attività riceveranno
una fredda accoglienza. Di conseguenza è meglio evitare questo tipo di patch.
Mentre si lavora su un pezzo di codice è normale correggerne anche lo stile,
ma le modifiche di stile non dovrebbero essere fatte fini a se stesse.
Il documento sullo stile del codice non dovrebbe essere letto come una legge
assoluta che non può mai essere trasgredita. Se c’è un a buona ragione
(per esempio, una linea che diviene poco leggibile se divisa per rientrare
nel limite di 80 colonne), fatelo e basta.
Notate che potete utilizzare lo strumento “clang-format” per aiutarvi con
le regole, per una riformattazione automatica e veloce del vostro codice
e per revisionare interi file per individuare errori nello stile di codifica,
refusi e possibili miglioramenti. Inoltre è utile anche per classificare gli
``#includes``, per allineare variabili/macro, per testi derivati ed altri
compiti del genere. Consultate il file
:ref:`Documentation/translations/it_IT/process/clang-format.rst <clangformat>`
per maggiori dettagli
Livelli di astrazione
*********************
I professori di Informatica insegnano ai propri studenti a fare ampio uso dei
livelli di astrazione nel nome della flessibilità e del nascondere informazioni.
Certo il kernel fa un grande uso dell'astrazione; nessun progetto con milioni
di righe di codice potrebbe fare altrimenti e sopravvivere. Ma l'esperienza
ha dimostrato che un'eccessiva o prematura astrazione può rivelarsi dannosa
al pari di una prematura ottimizzazione. L'astrazione dovrebbe essere usata
fino al livello necessario e non oltre.
Ad un livello base, considerate una funzione che ha un argomento che viene
sempre impostato a zero da tutti i chiamanti. Uno potrebbe mantenere
quell'argomento nell'eventualità qualcuno volesse sfruttare la flessibilità
offerta. In ogni caso, tuttavia, ci sono buone possibilità che il codice
che va ad implementare questo argomento aggiuntivo, sia stato rotto in maniera
sottile, in un modo che non è mai stato notato - perché non è mai stato usato.
Oppure, quando sorge la necessità di avere più flessibilità, questo argomento
non la fornisce in maniera soddisfacente. Gli sviluppatori di Kernel,
sottopongono costantemente patch che vanno a rimuovere gli argomenti
inutilizzate; anche se, in generale, non avrebbero dovuto essere aggiunti.
I livelli di astrazione che nascondono l'accesso all'hardware -
spesso per poter usare dei driver su diversi sistemi operativi - vengono
particolarmente disapprovati. Tali livelli oscurano il codice e possono
peggiorare le prestazioni; essi non appartengono al kernel Linux.
D'altro canto, se vi ritrovate a dover copiare una quantità significativa di
codice proveniente da un altro sottosistema del kernel, è tempo di chiedersi
se, in effetti, non avrebbe più senso togliere parte di quel codice e metterlo
in una libreria separata o di implementare quella funzionalità ad un livello
più elevato. Non c'è utilità nel replicare lo stesso codice per tutto
il kernel.
#ifdef e l'uso del preprocessore in generale
********************************************
Il preprocessore C sembra essere una fonte di attrazione per qualche
programmatore C, che ci vede una via per ottenere una grande flessibilità
all'interno di un file sorgente. Ma il preprocessore non è scritto in C,
e un suo massiccio impiego conduce a un codice che è molto più difficile
da leggere per gli altri e che rende più difficile il lavoro di verifica del
compilatore. L'uso eccessivo del preprocessore è praticamente sempre il segno
di un codice che necessita di un certo lavoro di pulizia.
La compilazione condizionata con #ifdef è, in effetti, un potente strumento,
ed esso viene usato all'interno del kernel. Ma esiste un piccolo desiderio:
quello di vedere il codice coperto solo da una leggera spolverata di
blocchi #ifdef. Come regola generale, quando possibile, l'uso di #ifdef
dovrebbe essere confinato nei file d'intestazione. Il codice compilato
condizionatamente può essere confinato a funzioni tali che, nel caso in cui
il codice non deve essere presente, diventano vuote. Il compilatore poi
ottimizzerà la chiamata alla funzione vuota rimuovendola. Il risultato è
un codice molto più pulito, più facile da seguire.
Le macro del preprocessore C presentano una serie di pericoli, inclusi
valutazioni multiple di espressioni che hanno effetti collaterali e non
garantiscono una sicurezza rispetto ai tipi. Se siete tentati dal definire
una macro, considerate l'idea di creare invece una funzione inline. Il codice
che ne risulterà sarà lo stesso, ma le funzioni inline sono più leggibili,
non considerano i propri argomenti più volte, e permettono al compilatore di
effettuare controlli sul tipo degli argomenti e del valore di ritorno.
Funzioni inline
***************
Comunque, anche le funzioni inline hanno i loro pericoli. I programmatori
potrebbero innamorarsi dell'efficienza percepita derivata dalla rimozione
di una chiamata a funzione. Queste funzioni, tuttavia, possono ridurre le
prestazioni. Dato che il loro codice viene replicato ovunque vi sia una
chiamata ad esse, si finisce per gonfiare le dimensioni del kernel compilato.
Questi, a turno, creano pressione sulla memoria cache del processore, e questo
può causare rallentamenti importanti. Le funzioni inline, di norma, dovrebbero
essere piccole e usate raramente. Il costo di una chiamata a funzione, dopo
tutto, non è così alto; la creazione di molte funzioni inline è il classico
esempio di un'ottimizzazione prematura.
In generale, i programmatori del kernel ignorano gli effetti della cache a
loro rischio e pericolo. Il classico compromesso tempo/spazio teorizzato
all'inizio delle lezioni sulle strutture dati spesso non si applica
all'hardware moderno. Lo spazio *è* tempo, in questo senso un programma
più grande sarà più lento rispetto ad uno più compatto.
I compilatori più recenti hanno preso un ruolo attivo nel decidere se
una data funzione deve essere resa inline oppure no. Quindi l'uso
indiscriminato della parola chiave "inline" potrebbe non essere non solo
eccessivo, ma anche irrilevante.
Sincronizzazione
****************
Nel maggio 2006, il sistema di rete "Devicescape" fu rilasciato in pompa magna
sotto la licenza GPL e reso disponibile per la sua inclusione nella ramo
principale del kernel. Questa donazione fu una notizia bene accolta;
il supporto per le reti senza fili era considerata, nel migliore dei casi,
al di sotto degli standard; il sistema Deviscape offrì la promessa di una
risoluzione a tale situazione. Tuttavia, questo codice non fu inserito nel
ramo principale fino al giugno del 2007 (2.6.22). Cosa accadde?
Quel codice mostrava numerosi segnali di uno sviluppo in azienda avvenuto
a porte chiuse. Ma in particolare, un grosso problema fu che non fu
progettato per girare in un sistema multiprocessore. Prima che questo
sistema di rete (ora chiamato mac80211) potesse essere inserito, fu necessario
un lavoro sugli schemi di sincronizzazione.
Una volta, il codice del kernel Linux poteva essere sviluppato senza pensare
ai problemi di concorrenza presenti nei sistemi multiprocessore. Ora,
comunque, questo documento è stato scritto su di un portatile dual-core.
Persino su sistemi a singolo processore, il lavoro svolto per incrementare
la capacità di risposta aumenterà il livello di concorrenza interno al kernel.
I giorni nei quali il codice poteva essere scritto senza pensare alla
sincronizzazione sono da passati tempo.
Ogni risorsa (strutture dati, registri hardware, etc.) ai quali si potrebbe
avere accesso simultaneo da più di un thread deve essere sincronizzato. Il
nuovo codice dovrebbe essere scritto avendo tale accortezza in testa;
riadattare la sincronizzazione a posteriori è un compito molto più difficile.
Gli sviluppatori del kernel dovrebbero prendersi il tempo di comprendere bene
le primitive di sincronizzazione, in modo da sceglier lo strumento corretto
per eseguire un compito. Il codice che presenta una mancanza di attenzione
alla concorrenza avrà un percorso difficile all'interno del ramo principale.
Regressioni
***********
Vale la pena menzionare un ultimo pericolo: potrebbe rivelarsi accattivante
l'idea di eseguire un cambiamento (che potrebbe portare a grandi
miglioramenti) che porterà ad alcune rotture per gli utenti esistenti.
Questa tipologia di cambiamento è chiamata "regressione", e le regressioni son
diventate mal viste nel ramo principale del kernel. Con alcune eccezioni,
i cambiamenti che causano regressioni saranno fermati se quest'ultime non
potranno essere corrette in tempo utile. È molto meglio quindi evitare
la regressione fin dall'inizio.
Spesso si è argomentato che una regressione può essere giustificata se essa
porta risolve più problemi di quanti non ne crei. Perché, dunque, non fare
un cambiamento se questo porta a nuove funzionalità a dieci sistemi per
ognuno dei quali esso determina una rottura? La migliore risposta a questa
domanda ci è stata fornita da Linus nel luglio 2007:
::
Dunque, noi non sistemiamo bachi introducendo nuovi problemi. Quella
via nasconde insidie, e nessuno può sapere del tutto se state facendo
dei progressi reali. Sono due passi avanti e uno indietro, oppure
un passo avanti e due indietro?
(http://lwn.net/Articles/243460/).
Una particolare tipologia di regressione mal vista consiste in una qualsiasi
sorta di modifica all'ABI dello spazio utente. Una volta che un'interfaccia
viene esportata verso lo spazio utente, dev'essere supportata all'infinito.
Questo fatto rende la creazione di interfacce per lo spazio utente
particolarmente complicato: dato che non possono venir cambiate introducendo
incompatibilità, esse devono essere fatte bene al primo colpo. Per questa
ragione sono sempre richieste: ampie riflessioni, documentazione chiara e
ampie revisioni dell'interfaccia verso lo spazio utente.
Strumenti di verifica del codice
--------------------------------
Almeno per ora la scrittura di codice priva di errori resta un ideale
irraggiungibile ai più. Quello che speriamo di poter fare, tuttavia, è
trovare e correggere molti di questi errori prima che il codice entri nel
ramo principale del kernel. A tal scopo gli sviluppatori del kernel devono
mettere insieme una schiera impressionante di strumenti che possano
localizzare automaticamente un'ampia varietà di problemi. Qualsiasi problema
trovato dal computer è un problema che non affliggerà l'utente in seguito,
ne consegue che gli strumenti automatici dovrebbero essere impiegati ovunque
possibile.
Il primo passo consiste semplicemente nel fare attenzione agli avvertimenti
proveniente dal compilatore. Versioni moderne di gcc possono individuare
(e segnalare) un gran numero di potenziali errori. Molto spesso, questi
avvertimenti indicano problemi reali. Di regola, il codice inviato per la
revisione non dovrebbe produrre nessun avvertimento da parte del compilatore.
Per mettere a tacere gli avvertimenti, cercate di comprenderne le cause reali
e cercate di evitare le "riparazioni" che fan sparire l'avvertimento senza
però averne trovato la causa.
Tenete a mente che non tutti gli avvertimenti sono disabilitati di default.
Costruite il kernel con "make EXTRA_CFLAGS=-W" per ottenerli tutti.
Il kernel fornisce differenti opzioni che abilitano funzionalità di debugging;
molti di queste sono trovano all'interno del sotto menu "kernel hacking".
La maggior parte di queste opzioni possono essere attivate per qualsiasi
kernel utilizzato per lo sviluppo o a scopo di test. In particolare dovreste
attivare:
- ENABLE_WARN_DEPRECATED, ENABLE_MUST_CHECK, e FRAME_WARN per ottenere degli
avvertimenti dedicati a problemi come l'uso di interfacce deprecate o
l'ignorare un importante valore di ritorno di una funzione. Il risultato
generato da questi avvertimenti può risultare verboso, ma non bisogna
preoccuparsi per gli avvertimenti provenienti da altre parti del kernel.
- DEBUG_OBJECTS aggiungerà un codice per tracciare il ciclo di vita di
diversi oggetti creati dal kernel e avvisa quando qualcosa viene eseguito
fuori controllo. Se state aggiungendo un sottosistema che crea (ed
esporta) oggetti complessi propri, considerate l'aggiunta di un supporto
al debugging dell'oggetto.
- DEBUG_SLAB può trovare svariati errori di uso e di allocazione di memoria;
esso dovrebbe esser usato dalla maggior parte dei kernel di sviluppo.
- DEBUG_SPINLOCK, DEBUG_ATOMIC_SLEEP, e DEBUG_MUTEXES troveranno un certo
numero di errori comuni di sincronizzazione.
Esistono ancora delle altre opzioni di debugging, di alcune di esse
discuteremo qui sotto. Alcune di esse hanno un forte impatto e non dovrebbero
essere usate tutte le volte. Ma qualche volta il tempo speso nell'capire
le opzioni disponibili porterà ad un risparmio di tempo nel breve termine.
Uno degli strumenti di debugging più tosti è il *locking checker*, o
"lockdep". Questo strumento traccerà qualsiasi acquisizione e rilascio di
ogni *lock* (spinlock o mutex) nel sistema, l'ordine con il quale i *lock*
sono acquisiti in relazione l'uno con l'altro, l'ambiente corrente di
interruzione, eccetera. Inoltre esso può assicurare che i *lock* vengano
acquisiti sempre nello stesso ordine, che le stesse assunzioni sulle
interruzioni si applichino in tutte le occasioni, e così via. In altre parole,
lockdep può scovare diversi scenari nei quali il sistema potrebbe, in rari
casi, trovarsi in stallo. Questa tipologia di problema può essere grave
(sia per gli sviluppatori che per gli utenti) in un sistema in uso; lockdep
permette di trovare tali problemi automaticamente e in anticipo.
In qualità di programmatore kernel diligente, senza dubbio, dovrete controllare
il valore di ritorno di ogni operazione (come l'allocazione della memoria)
poiché esso potrebbe fallire. Il nocciolo della questione è che i percorsi
di gestione degli errori, con grande probabilità, non sono mai stati
collaudati del tutto. Il codice collaudato tende ad essere codice bacato;
potrete quindi essere più a vostro agio con il vostro codice se tutti questi
percorsi fossero stati verificati un po' di volte.
Il kernel fornisce un framework per l'inserimento di fallimenti che fa
esattamente al caso, specialmente dove sono coinvolte allocazioni di memoria.
Con l'opzione per l'inserimento dei fallimenti abilitata, una certa percentuale
di allocazione di memoria sarà destinata al fallimento; questi fallimenti
possono essere ridotti ad uno specifico pezzo di codice. Procedere con
l'inserimento dei fallimenti attivo permette al programmatore di verificare
come il codice risponde quando le cose vanno male. Consultate:
Documentation/fault-injection/fault-injection.txt per avere maggiori
informazioni su come utilizzare questo strumento.
Altre tipologie di errori possono essere riscontrati con lo strumento di
analisi statica "sparse". Con Sparse, il programmatore può essere avvisato
circa la confusione tra gli indirizzi dello spazio utente e dello spazio
kernel, un miscuglio fra quantità big-endian e little-endian, il passaggio
di un valore intero dove ci sia aspetta un gruppo di flag, e così via.
Sparse deve essere installato separatamente (se il vostra distribuzione non
lo prevede, potete trovarlo su https://sparse.wiki.kernel.org/index.php/Main_Page);
può essere attivato sul codice aggiungendo "C=1" al comando make.
Lo strumento "Coccinelle" (http://coccinelle.lip6.fr/) è in grado di trovare
una vasta varietà di potenziali problemi di codifica; e può inoltre proporre
soluzioni per risolverli. Un buon numero di "patch semantiche" per il kernel
sono state preparate nella cartella scripts/coccinelle; utilizzando
"make coccicheck" esso percorrerà tali patch semantiche e farà rapporto su
qualsiasi problema trovato. Per maggiori informazioni, consultate
:ref:`Documentation/dev-tools/coccinelle.rst <devtools_coccinelle>`.
Altri errori di portabilità sono meglio scovati compilando il vostro codice
per altre architetture. Se non vi accade di avere un sistema S/390 o una
scheda di sviluppo Blackfin sotto mano, potete comunque continuare la fase
di compilazione. Un vasto numero di cross-compilatori per x86 possono
essere trovati al sito:
http://www.kernel.org/pub/tools/crosstool/
Il tempo impiegato nell'installare e usare questi compilatori sarà d'aiuto
nell'evitare situazioni imbarazzanti nel futuro.
Documentazione
--------------
La documentazione è spesso stata più un'eccezione che una regola nello
sviluppo del kernel. Nonostante questo, un'adeguata documentazione aiuterà
a facilitare l'inserimento di nuovo codice nel kernel, rende la vita più
facile per gli altri sviluppatori e sarà utile per i vostri utenti. In molti
casi, la documentazione è divenuta sostanzialmente obbligatoria.
La prima parte di documentazione per qualsiasi patch è il suo changelog.
Questi dovrebbero descrivere le problematiche risolte, la tipologia di
soluzione, le persone che lavorano alla patch, ogni effetto rilevante
sulle prestazioni e tutto ciò che può servire per la comprensione della
patch. Assicuratevi che il changelog dica *perché*, vale la pena aggiungere
la patch; un numero sorprendente di sviluppatori sbaglia nel fornire tale
informazione.
Qualsiasi codice che aggiunge una nuova interfaccia in spazio utente - inclusi
nuovi file in sysfs o /proc - dovrebbe includere la documentazione di tale
interfaccia così da permette agli sviluppatori dello spazio utente di sapere
con cosa stanno lavorando. Consultate: Documentation/ABI/README per avere una
descrizione di come questi documenti devono essere impostati e quali
informazioni devono essere fornite.
Il file :ref:`Documentation/translations/it_IT/admin-guide/kernel-parameters.rst <kernelparameters>`
descrive tutti i parametri di avvio del kernel. Ogni patch che aggiunga
nuovi parametri dovrebbe aggiungere nuove voci a questo file.
Ogni nuova configurazione deve essere accompagnata da un testo di supporto
che spieghi chiaramente le opzioni e spieghi quando l'utente potrebbe volerle
selezionare.
Per molti sottosistemi le informazioni sull'API interna sono documentate sotto
forma di commenti formattati in maniera particolare; questi commenti possono
essere estratti e formattati in differenti modi attraverso lo script
"kernel-doc". Se state lavorando all'interno di un sottosistema che ha
commenti kerneldoc dovreste mantenerli e aggiungerli, in maniera appropriata,
per le funzioni disponibili esternamente. Anche in aree che non sono molto
documentate, non c'è motivo per non aggiungere commenti kerneldoc per il
futuro; infatti, questa può essere un'attività utile per sviluppatori novizi
del kernel. Il formato di questi commenti, assieme alle informazione su come
creare modelli per kerneldoc, possono essere trovati in
:ref:`Documentation/translations/it_IT/doc-guide/ <doc_guide>`.
Chiunque legga un ammontare significativo di codice kernel noterà che, spesso,
i commenti si fanno maggiormente notare per la loro assenza. Ancora una volta,
le aspettative verso il nuovo codice sono più alte rispetto al passato;
inserire codice privo di commenti sarà più difficile. Detto ciò, va aggiunto
che non si desiderano commenti prolissi per il codice. Il codice dovrebbe
essere, di per sé, leggibile, con dei commenti che spieghino gli aspetti più
sottili.
Determinate cose dovrebbero essere sempre commentate. L'uso di barriere
di memoria dovrebbero essere accompagnate da una riga che spieghi perché sia
necessaria. Le regole di sincronizzazione per le strutture dati, generalmente,
necessitano di una spiegazioni da qualche parte. Le strutture dati più
importanti, in generale, hanno bisogno di una documentazione onnicomprensiva.
Le dipendenze che non sono ovvie tra bit separati di codice dovrebbero essere
indicate. Tutto ciò che potrebbe indurre un inserviente del codice a fare
una "pulizia" incorretta, ha bisogno di un commento che dica perché è stato
fatto in quel modo. E così via.
Cambiamenti interni dell'API
----------------------------
L'interfaccia binaria fornita dal kernel allo spazio utente non può essere
rotta tranne che in circostanze eccezionali. L'interfaccia di programmazione
interna al kernel, invece, è estremamente fluida e può essere modificata al
bisogno. Se vi trovate a dover lavorare attorno ad un'API del kernel o
semplicemente non state utilizzando una funzionalità offerta perché questa
non rispecchia i vostri bisogni, allora questo potrebbe essere un segno che
l'API ha bisogno di essere cambiata. In qualità di sviluppatore del kernel,
hai il potere di fare questo tipo di modifica.
Ci sono ovviamente alcuni punti da cogliere. I cambiamenti API possono essere
fatti, ma devono essere giustificati. Quindi ogni patch che porta ad una
modifica dell'API interna dovrebbe essere accompagnata da una descrizione
della modifica in sé e del perché essa è necessaria. Questo tipo di
cambiamenti dovrebbero, inoltre, essere fatti in una patch separata, invece di
essere sepolti all'interno di una patch più grande.
L'altro punto da cogliere consiste nel fatto che uno sviluppatore che
modifica l'API deve, in generale, essere responsabile della correzione
di tutto il codice del kernel che viene rotto per via della sua modifica.
Per una funzione ampiamente usata, questo compito può condurre letteralmente
a centinaia o migliaia di modifiche, molte delle quali sono in conflitto con
il lavoro svolto da altri sviluppatori. Non c'è bisogno di dire che questo
può essere un lavoro molto grosso, quindi è meglio essere sicuri che la
motivazione sia ben solida. Notate che lo strumento Coccinelle può fornire
un aiuto con modifiche estese dell'API.
Quando viene fatta una modifica API incompatibile, una persona dovrebbe,
quando possibile, assicurarsi che quel codice non aggiornato sia trovato
dal compilatore. Questo vi aiuterà ad essere sicuri d'avere trovato,
tutti gli usi di quell'interfaccia. Inoltre questo avviserà gli sviluppatori
di codice fuori dal kernel che c'è un cambiamento per il quale è necessario del
lavoro. Il supporto al codice fuori dal kernel non è qualcosa di cui gli
sviluppatori del kernel devono preoccuparsi, ma non dobbiamo nemmeno rendere
più difficile del necessario la vita agli sviluppatori di questo codice.
.. include:: ../disclaimer-ita.rst
:Original: :ref:`Documentation/process/5.Posting.rst <development_posting>`
:Translator: Federico Vaga <federico.vaga@vaga.pv.it>
.. _it_development_posting:
Pubblicare modifiche
====================
Prima o poi arriva il momento in cui il vostro lavoro è pronto per essere
presentato alla comunità per una revisione ed eventualmente per la sua
inclusione nel ramo principale del kernel. Com'era prevedibile,
la comunità di sviluppo del kernel ha elaborato un insieme di convenzioni
e di procedure per la pubblicazione delle patch; seguirle renderà la vita
più facile a tutti quanti. Questo documento cercherà di coprire questi
argomenti con un ragionevole livello di dettaglio; più informazioni possono
essere trovare nella cartella 'Documentation', nei file
:ref:`translations/it_IT/process/submitting-patches.rst <it_submittingpatches>`,
:ref:`translations/it_IT/process/submitting-drivers.rst <it_submittingdrivers>`, e
:ref:`translations/it_IT/process/submit-checklist.rst <it_submitchecklist>`.
Quando pubblicarle
------------------
C'è sempre una certa resistenza nel pubblicare patch finché non sono
veramente "pronte". Per semplici patch questo non è un problema.
Ma quando il lavoro è di una certa complessità, c'è molto da guadagnare
dai riscontri che la comunità può darvi prima che completiate il lavoro.
Dovreste considerare l'idea di pubblicare un lavoro incompleto, o anche
preparare un ramo git disponibile agli sviluppatori interessati, cosicché
possano stare al passo col vostro lavoro in qualunque momento.
Quando pubblicate del codice che non è considerato pronto per l'inclusione,
è bene che lo diciate al momento della pubblicazione. Inoltre, aggiungete
informazioni sulle cose ancora da sviluppare e sui problemi conosciuti.
Poche persone guarderanno delle patch che si sa essere fatte a metà,
ma quelli che lo faranno penseranno di potervi aiutare a condurre il vostro
sviluppo nella giusta direzione.
Prima di creare patch
---------------------
Ci sono un certo numero di cose che dovreste fare prima di considerare
l'invio delle patch alla comunità di sviluppo. Queste cose includono:
- Verificare il codice fino al massimo che vi è consentito. Usate gli
strumenti di debug del kernel, assicuratevi che il kernel compili con
tutte le più ragionevoli combinazioni d'opzioni, usate cross-compilatori
per compilare il codice per differenti architetture, eccetera.
- Assicuratevi che il vostro codice sia conforme alla linee guida del
kernel sullo stile del codice.
- La vostra patch ha delle conseguenze in termini di prestazioni?
Se è così, dovreste eseguire dei *benchmark* che mostrino il loro
impatto (anche positivo); un riassunto dei risultati dovrebbe essere
incluso nella patch.
- Siate certi d'avere i diritti per pubblicare il codice. Se questo
lavoro è stato fatto per un datore di lavoro, egli avrà dei diritti su
questo lavoro e dovrà quindi essere d'accordo alla sua pubblicazione
con una licenza GPL
Come regola generale, pensarci un po' di più prima di inviare il codice
ripaga quasi sempre lo sforzo.
Preparazione di una patch
-------------------------
La preparazione delle patch per la pubblicazione può richiedere una quantità
di lavoro significativa, ma, ripetiamolo ancora, generalmente sconsigliamo
di risparmiare tempo in questa fase, anche sul breve periodo.
Le patch devono essere preparate per una specifica versione del kernel.
Come regola generale, una patch dovrebbe basarsi sul ramo principale attuale
così come lo si trova nei sorgenti git di Linus. Quando vi basate sul ramo
principale, cominciate da un punto di rilascio ben noto - uno stabile o
un -rc - piuttosto che creare il vostro ramo da quello principale in un punto
a caso.
Per facilitare una revisione e una verifica più estesa, potrebbe diventare
necessaria la produzione di versioni per -mm, linux-next o i sorgenti di un
sottosistema. Basare questa patch sui suddetti sorgenti potrebbe richiedere
un lavoro significativo nella risoluzione dei conflitti e nella correzione dei
cambiamenti di API; questo potrebbe variare a seconda dell'area d'interesse
della vostra patch e da quello che succede altrove nel kernel.
Solo le modifiche più semplici dovrebbero essere preparate come una singola
patch; tutto il resto dovrebbe essere preparato come una serie logica di
modifiche. Spezzettare le patch è un po' un'arte; alcuni sviluppatori
passano molto tempo nel capire come farlo in modo che piaccia alla comunità.
Ci sono alcune regole spannometriche, che comunque possono aiutare
considerevolmente:
- La serie di patch che pubblicherete, quasi sicuramente, non sarà
come quella che trovate nel vostro sistema di controllo di versione.
Invece, le vostre modifiche dovranno essere considerate nella loro forma
finale, e quindi separate in parti che abbiano un senso. Gli sviluppatori
sono interessati in modifiche che siano discrete e indipendenti, non
alla strada che avete percorso per ottenerle.
- Ogni modifica logicamente indipendente dovrebbe essere preparata come una
patch separata. Queste modifiche possono essere piccole ("aggiunto un
campo in questa struttura") o grandi (l'aggiunta di un driver nuovo,
per esempio), ma dovrebbero essere concettualmente piccole da permettere
una descrizione in una sola riga. Ogni patch dovrebbe fare modifiche
specifiche che si possano revisionare indipendentemente e di cui si possa
verificare la veridicità.
- Giusto per riaffermare quando detto sopra: non mischiate diversi tipi di
modifiche nella stessa patch. Se una modifica corregge un baco critico
per la sicurezza, riorganizza alcune strutture, e riformatta il codice,
ci sono buone probabilità che venga ignorata e che la correzione importante
venga persa.
- Ogni modifica dovrebbe portare ad un kernel che compila e funziona
correttamente; se la vostra serie di patch si interrompe a metà il
risultato dovrebbe essere comunque un kernel funzionante. L'applicazione
parziale di una serie di patch è uno scenario comune nel quale il
comando "git bisect" viene usato per trovare delle regressioni; se il
risultato è un kernel guasto, renderete la vita degli sviluppatori più
difficile così come quella di chi s'impegna nel nobile lavoro di
scovare i problemi.
- Però, non strafate. Una volta uno sviluppatore pubblicò una serie di 500
patch che modificavano un unico file - un atto che non lo rese la persona
più popolare sulla lista di discussione del kernel. Una singola patch
può essere ragionevolmente grande fintanto che contenga un singolo
cambiamento *logico*.
- Potrebbe essere allettante l'idea di aggiungere una nuova infrastruttura
come una serie di patch, ma di lasciare questa infrastruttura inutilizzata
finché l'ultima patch della serie non abilita tutto quanto. Quando è
possibile, questo dovrebbe essere evitato; se questa serie aggiunge delle
regressioni, "bisect" indicherà quest'ultima patch come causa del
problema anche se il baco si trova altrove. Possibilmente, quando una
patch aggiunge del nuovo codice dovrebbe renderlo attivo immediatamente.
Lavorare per creare la serie di patch perfetta potrebbe essere frustrante
perché richiede un certo tempo e soprattutto dopo che il "vero lavoro" è
già stato fatto. Quando ben fatto, comunque, è tempo ben speso.
Formattazione delle patch e i changelog
---------------------------------------
Quindi adesso avete una serie perfetta di patch pronte per la pubblicazione,
ma il lavoro non è davvero finito. Ogni patch deve essere preparata con
un messaggio che spieghi al resto del mondo, in modo chiaro e veloce,
il suo scopo. Per ottenerlo, ogni patch sarà composta dai seguenti elementi:
- Un campo opzionale "From" col nome dell'autore della patch. Questa riga
è necessaria solo se state passando la patch di qualcun altro via email,
ma nel dubbio non fa di certo male aggiungerlo.
- Una descrizione di una riga che spieghi cosa fa la patch. Questo
messaggio dovrebbe essere sufficiente per far comprendere al lettore lo
scopo della patch senza altre informazioni. Questo messaggio,
solitamente, presenta in testa il nome del sottosistema a cui si riferisce,
seguito dallo scopo della patch. Per esempio:
::
gpio: fix build on CONFIG_GPIO_SYSFS=n
- Una riga bianca seguita da una descrizione dettagliata della patch.
Questa descrizione può essere lunga tanto quanto serve; dovrebbe spiegare
cosa fa e perché dovrebbe essere aggiunta al kernel.
- Una o più righe etichette, con, minimo, una riga *Signed-off-by:*
col nome dall'autore della patch. Queste etichette verranno descritte
meglio più avanti.
Gli elementi qui sopra, assieme, formano il changelog di una patch.
Scrivere un buon changelog è cruciale ma è spesso un'arte trascurata;
vale la pena spendere qualche parola in più al riguardo. Quando scrivete
un changelog dovreste tenere ben presente che molte persone leggeranno
le vostre parole. Queste includono i manutentori di un sotto-sistema, e i
revisori che devono decidere se la patch debba essere inclusa o no,
le distribuzioni e altri manutentori che cercano di valutare se la patch
debba essere applicata su kernel più vecchi, i cacciatori di bachi che si
chiederanno se la patch è la causa di un problema che stanno cercando,
gli utenti che vogliono sapere com'è cambiato il kernel, e molti altri.
Un buon changelog fornisce le informazioni necessarie a tutte queste
persone nel modo più diretto e conciso possibile.
A questo scopo, la riga riassuntiva dovrebbe descrivere gli effetti della
modifica e la motivazione della patch nel modo migliore possibile nonostante
il limite di una sola riga. La descrizione dettagliata può spiegare meglio
i temi e fornire maggiori informazioni. Se una patch corregge un baco,
citate, se possibile, il commit che lo introdusse (e per favore, quando
citate un commit aggiungete sia il suo identificativo che il titolo),
Se il problema è associabile ad un file di log o all' output del compilatore,
includeteli al fine d'aiutare gli altri a trovare soluzioni per lo stesso
problema. Se la modifica ha lo scopo di essere di supporto a sviluppi
successivi, ditelo. Se le API interne vengono cambiate, dettagliate queste
modifiche e come gli altri dovrebbero agire per applicarle. In generale,
più riuscirete ad entrare nei panni di tutti quelli che leggeranno il
vostro changelog, meglio sarà il changelog (e il kernel nel suo insieme).
Non serve dirlo, un changelog dovrebbe essere il testo usato nel messaggio
di commit in un sistema di controllo di versione. Sarà seguito da:
- La patch stessa, nel formato unificato per patch ("-u"). Usare
l'opzione "-p" assocerà alla modifica il nome della funzione alla quale
si riferisce, rendendo il risultato più facile da leggere per gli altri.
Dovreste evitare di includere nelle patch delle modifiche per file
irrilevanti (quelli generati dal processo di generazione, per esempio, o i file
di backup del vostro editor). Il file "dontdiff" nella cartella Documentation
potrà esservi d'aiuto su questo punto; passatelo a diff con l'opzione "-X".
Le etichette sopra menzionante sono usate per descrivere come i vari
sviluppatori sono stati associati allo sviluppo di una patch. Sono descritte
in dettaglio nel documento :ref:`translations/it_IT/process/submitting-patches.rst <it_submittingpatches>`;
quello che segue è un breve riassunto. Ognuna di queste righe ha il seguente
formato:
::
tag: Full Name <email address> optional-other-stuff
Le etichette in uso più comuni sono:
- Signed-off-by: questa è la certificazione che lo sviluppatore ha il diritto
di sottomettere la patch per l'integrazione nel kernel. Questo rappresenta
il consenso verso il certificato d'origine degli sviluppatori, il testo
completo potrà essere trovato in
:ref:`Documentation/translations/it_IT/process/submitting-patches.rst <it_submittingpatches>`.
Codice che non presenta una firma appropriata non potrà essere integrato.
- Co-developed-by: indica che la patch è stata sviluppata anche da un altro
sviluppatore assieme all'autore originale. Questo è utile quando più
persone lavorano sulla stessa patch. Da notare che questa persona deve
avere anche una riga "Signed-off-by:" nella patch.
- Acked-by: indica il consenso di un altro sviluppatore (spesso il manutentore
del codice in oggetto) all'integrazione della patch nel kernel.
- Tested-by: menziona la persona che ha verificato la patch e l'ha trovata
funzionante.
- Reviwed-by: menziona lo sviluppatore che ha revisionato la patch; per
maggiori dettagli leggete la dichiarazione dei revisori in
:ref:`Documentation/translations/it_IT/process/submitting-patches.rst <it_submittingpatches>`
- Reported-by: menziona l'utente che ha riportato il problema corretto da
questa patch; quest'etichetta viene usata per dare credito alle persone
che hanno verificato il codice e ci hanno fatto sapere quando le cose non
funzionavano correttamente.
- Cc: la persona menzionata ha ricevuto una copia della patch ed ha avuto
l'opportunità di commentarla.
State attenti ad aggiungere queste etichette alla vostra patch: solo
"Cc:" può essere aggiunta senza il permesso esplicito della persona menzionata.
Inviare la modifica
-------------------
Prima di inviare la vostra patch, ci sarebbero ancora un paio di cose di cui
dovreste aver cura:
- Siete sicuri che il vostro programma di posta non corromperà le patch?
Le patch che hanno spazi bianchi in libertà o andate a capo aggiunti
dai programmi di posta non funzioneranno per chi le riceve, e spesso
non verranno nemmeno esaminate in dettaglio. Se avete un qualsiasi dubbio,
inviate la patch a voi stessi e verificate che sia integra.
:ref:`Documentation/translations/it_IT/process/email-clients.rst <it_email_clients>`
contiene alcuni suggerimenti utili sulla configurazione dei programmi
di posta al fine di inviare patch.
- Siete sicuri che la vostra patch non contenga sciocchi errori? Dovreste
sempre processare le patch con scripts/checkpatch.pl e correggere eventuali
problemi riportati. Per favore tenete ben presente che checkpatch.pl non è
più intelligente di voi, nonostante sia il risultato di un certa quantità di
ragionamenti su come debba essere una patch per il kernel. Se seguire
i suggerimenti di checkpatch.pl rende il codice peggiore, allora non fatelo.
Le patch dovrebbero essere sempre inviate come testo puro. Per favore non
inviatele come allegati; questo rende molto più difficile, per i revisori,
citare parti della patch che si vogliono commentare. Invece, mettete la vostra
patch direttamente nel messaggio.
Quando inviate le patch, è importante inviarne una copia a tutte le persone che
potrebbero esserne interessate. Al contrario di altri progetti, il kernel
incoraggia le persone a peccare nell'invio di tante copie; non presumente che
le persone interessate vedano i vostri messaggi sulla lista di discussione.
In particolare le copie dovrebbero essere inviate a:
- I manutentori dei sottosistemi affetti della modifica. Come descritto
in precedenza, il file MAINTAINERS è il primo luogo dove cercare i nomi
di queste persone.
- Altri sviluppatori che hanno lavorato nello stesso ambiente - specialmente
quelli che potrebbero lavorarci proprio ora. Usate git potrebbe essere
utile per vedere chi altri ha modificato i file su cui state lavorando.
- Se state rispondendo a un rapporto su un baco, o a una richiesta di
funzionalità, includete anche gli autori di quei rapporti/richieste.
- Inviate una copia alle liste di discussione interessate, o, se nient'altro
è adatto, alla lista linux-kernel
- Se state correggendo un baco, pensate se la patch dovrebbe essere inclusa
nel prossimo rilascio stabile. Se è così, la lista di discussione
stable@vger.kernel.org dovrebbe riceverne una copia. Aggiungete anche
l'etichetta "Cc: stable@vger.kernel.org" nella patch stessa; questo
permetterà alla squadra *stable* di ricevere una notifica quando questa
correzione viene integrata nel ramo principale.
Quando scegliete i destinatari della patch, è bene avere un'idea di chi
pensiate che sia colui che, eventualmente, accetterà la vostra patch e
la integrerà. Nonostante sia possibile inviare patch direttamente a
Linus Torvalds, e lasciare che sia lui ad integrarle,solitamente non è la
strada migliore da seguire. Linus è occupato, e ci sono dei manutentori di
sotto-sistema che controllano una parte specifica del kernel. Solitamente,
vorreste che siano questi manutentori ad integrare le vostre patch. Se non
c'è un chiaro manutentore, l'ultima spiaggia è spesso Andrew Morton.
Le patch devono avere anche un buon oggetto. Il tipico formato per l'oggetto
di una patch assomiglia a questo:
::
[PATCH nn/mm] subsys: one-line description of the patch
dove "nn" è il numero ordinale della patch, "mm" è il numero totale delle patch
nella serie, e "subsys" è il nome del sottosistema interessato. Chiaramente,
nn/mm può essere omesso per una serie composta da una singola patch.
Se avete una significative serie di patch, è prassi inviare una descrizione
introduttiva come parte zero. Tuttavia questa convenzione non è universalmente
seguita; se la usate, ricordate che le informazioni nell'introduzione non
faranno parte del changelog del kernel. Quindi per favore, assicuratevi che
ogni patch abbia un changelog completo.
In generale, la seconda parte e quelle successive di una patch "composta"
dovrebbero essere inviate come risposta alla prima, cosicché vengano viste
come un unico *thread*. Strumenti come git e quilt hanno comandi per inviare
gruppi di patch con la struttura appropriata. Se avete una serie lunga
e state usando git, per favore state alla larga dall'opzione --chain-reply-to
per evitare di creare un annidamento eccessivo.
.. include:: ../disclaimer-ita.rst
:Original: :ref:`Documentation/process/6.Followthrough.rst <development_followthrough>`
:Translator: Alessia Mantegazza <amantegazza@vaga.pv.it>
.. _it_development_followthrough:
=============
Completamento
=============
A questo punto, avete seguito le linee guida fino a questo punto e, con
l'aggiunta delle vostre capacità ingegneristiche, avete pubblicato una serie
perfetta di patch. Uno dei più grandi errori che possono essere commessi
persino da sviluppatori kernel esperti è quello di concludere che il
lavoro sia ormai finito. In verità, la pubblicazione delle patch
simboleggia una transizione alla fase successiva del processo, con,
probabilmente, ancora un po' di lavoro da fare.
È raro che una modifica sia così bella alla sua prima pubblicazione che non
ci sia alcuno spazio di miglioramento. Il programma di sviluppo del kernel
riconosce questo fatto e quindi, è fortemente orientato al miglioramento
del codice pubblicato. Voi, in qualità di autori del codice, dovrete
lavorare con la comunità del kernel per assicurare che il vostro codice
mantenga gli standard qualitativi richiesti. Un fallimento in questo
processo è quasi come impedire l'inclusione delle vostre patch nel
ramo principale.
Lavorare con i revisori
=======================
Una patch che abbia una certa rilevanza avrà ricevuto numerosi commenti
da parte di altri sviluppatori dato che avranno revisionato il codice.
Lavorare con i revisori può rivelarsi, per molti sviluppatori, la parte
più intimidatoria del processo di sviluppo del kernel. La vita può esservi
resa molto più facile se tenete presente alcuni dettagli:
- Se avete descritto la vostra modifica correttamente, i revisori ne
comprenderanno il valore e il perché vi siete presi il disturbo di
scriverla. Ma tale valore non li tratterrà dal porvi una domanda
fondamentale: come verrà mantenuto questo codice nel kernel nei prossimi
cinque o dieci anni? Molti dei cambiamenti che potrebbero esservi
richiesti - da piccoli problemi di stile a sostanziali ristesure -
vengono dalla consapevolezza che Linux resterà in circolazione e in
continuo sviluppo ancora per diverse decadi.
- La revisione del codice è un duro lavoro, ed è un mestiere poco
riconosciuto; le persone ricordano chi ha scritto il codice, ma meno
fama è attribuita a chi lo ha revisionato. Quindi i revisori potrebbero
divenire burberi, specialmente quando vendono i medesimi errori venire
fatti ancora e ancora. Se ricevete una revisione che vi sembra abbia
un tono arrabbiato, insultante o addirittura offensivo, resistente alla
tentazione di rispondere a tono. La revisione riguarda il codice e non
la persona, e i revisori non vi stanno attaccando personalmente.
- Similarmente, i revisori del codice non stanno cercando di promuovere
i loro interessi a vostre spese. Gli sviluppatori del kernel spesso si
aspettano di lavorare sul kernel per anni, ma sanno che il loro datore
di lavoro può cambiare. Davvero, senza praticamente eccezioni, loro
stanno lavorando per la creazione del miglior kernel possibile; non
stanno cercando di creare un disagio ad aziende concorrenti.
Quello che si sta cercando di dire è che, quando i revisori vi inviano degli
appunti dovete fare attenzione alle osservazioni tecniche che vi stanno
facendo. Non lasciate che il loro modo di esprimersi o il vostro orgoglio
impediscano che ciò accada. Quando avete dei suggerimenti sulla revisione,
prendetevi il tempo per comprendere cosa il revisore stia cercando di
comunicarvi. Se possibile, sistemate le cose che il revisore vi chiede di
modificare. E rispondete al revisore ringraziandolo e spiegando come
intendete fare.
Notate che non dovete per forza essere d'accordo con ogni singola modifica
suggerita dai revisori. Se credete che il revisore non abbia compreso
il vostro codice, spiegateglielo. Se avete un'obiezione tecnica da fargli
su di una modifica suggerita, spiegatela inserendo anche la vostra soluzione
al problema. Se la vostra spiegazione ha senso, il revisore la accetterà.
Tuttavia, la vostra motivazione potrebbe non essere del tutto persuasiva,
specialmente se altri iniziano ad essere d'accordo con il revisore.
Prendetevi quindi un po' di tempo per pensare ancora alla cosa. Può risultare
facile essere accecati dalla propria soluzione al punto che non realizzate che
c'è qualcosa di fondamentalmente sbagliato o, magari, non state nemmeno
risolvendo il problema giusto.
Andrew Morton suggerisce che ogni suggerimento di revisione che non è
presente nella modifica del codice dovrebbe essere inserito in un commento
aggiuntivo; ciò può essere d'aiuto ai futuri revisori nell'evitare domande
che sorgono al primo sguardo.
Un errore fatale è quello di ignorare i commenti di revisione nella speranza
che se ne andranno. Non andranno via. Se pubblicherete nuovamente il
codice senza aver risposto ai commenti ricevuti, probabilmente le vostre
modifiche non andranno da nessuna parte.
Parlando di ripubblicazione del codice: per favore tenete a mente che i
revisori non ricorderanno tutti i dettagli del codice che avete pubblicato
l'ultima volta. Quindi è sempre una buona idea quella di ricordare ai
revisori le questioni sollevate precedetemene e come le avete risolte.
I revisori non dovrebbero star lì a cercare all'interno degli archivi per
famigliarizzare con ciò che è stato detto l'ultima volta; se li aiutate
in questo senso, saranno di umore migliore quando riguarderanno il vostro
codice.
Se invece avete cercato di far tutto correttamente ma le cose continuano
a non andar bene? Molti disaccordi di natura tecnica possono essere risolti
attraverso la discussione, ma ci sono volte dove qualcuno deve prendere
una decisione. Se credete veramente che tale decisione andrà contro di voi
ingiustamente, potete sempre tentare di rivolgervi a qualcuno più
in alto di voi. Per cose di questo genere la persona con più potere è
Andrew Morton. Andrew è una figura molto rispettata all'interno della
comunità di sviluppo del kernel; lui può spesso sbrogliare situazioni che
sembrano irrimediabilmente bloccate. Rivolgersi ad Andrew non deve essere
fatto alla leggera, e non deve essere fatto prima di aver esplorato tutte
le altre alternative. E tenete a mente, ovviamente, che nemmeno lui
potrebbe non essere d'accordo con voi.
Cosa accade poi
===============
Se la modifica è ritenuta un elemento valido da essere aggiunta al kernel,
e una volta che la maggior parte degli appunti dei revisori sono stati
sistemati, il passo successivo solitamente è quello di entrare in un
sottosistema gestito da un manutentore. Come ciò avviene dipende dal
sottosistema medesimo; ogni manutentore ha il proprio modo di fare le cose.
In particolare, ci potrebbero essere diversi sorgenti - uno, magari, dedicato
alle modifiche pianificate per la finestra di fusione successiva, e un altro
per il lavoro di lungo periodo.
Per le modifiche proposte in aree per le quali non esiste un sottosistema
preciso (modifiche di gestione della memoria, per esempio), i sorgenti di
ripiego finiscono per essere -mm. Ed anche le modifiche che riguardano
più sottosistemi possono finire in quest'ultimo.
L'inclusione nei sorgenti di un sottosistema può comportare per una patch,
un alto livello di visibilità. Ora altri sviluppatori che stanno lavorando
in quei medesimi sorgenti avranno le vostre modifiche. I sottosistemi
solitamente riforniscono anche Linux-next, rendendo i propri contenuti
visibili all'intera comunità di sviluppo. A questo punto, ci sono buone
possibilità per voi di ricevere ulteriori commenti da un nuovo gruppo di
revisori; anche a questi commenti dovrete rispondere come avete già fatto per
gli altri.
Ciò che potrebbe accadere a questo punto, in base alla natura della vostra
modifica, riguarda eventuali conflitti con il lavoro svolto da altri.
Nella peggiore delle situazioni, i conflitti più pesanti tra modifiche possono
concludersi con la messa a lato di alcuni dei lavori svolti cosicché le
modifiche restanti possano funzionare ed essere integrate. Altre volte, la
risoluzione dei conflitti richiederà del lavoro con altri sviluppatori e,
possibilmente, lo spostamento di alcune patch da dei sorgenti a degli altri
in modo da assicurare che tutto sia applicato in modo pulito. Questo lavoro
può rivelarsi una spina nel fianco, ma consideratevi fortunati: prima
dell'avvento dei sorgenti linux-next, questi conflitti spesso emergevano solo
durante l'apertura della finestra di integrazione e dovevano essere smaltiti
in fretta. Ora essi possono essere risolti comodamente, prima dell'apertura
della finestra.
Un giorno, se tutto va bene, vi collegherete e vedrete che la vostra patch
è stata inserita nel ramo principale de kernel. Congratulazioni! Terminati
i festeggiamenti (nel frattempo avrete inserito il vostro nome nel file
MAINTAINERS) vale la pena ricordare una piccola cosa, ma importante: il
lavoro non è ancora finito. L'inserimento nel ramo principale porta con se
nuove sfide.
Cominciamo con il dire che ora la visibilità della vostra modifica è
ulteriormente cresciuta. Ci potrebbe portare ad una nuova fase di
commenti dagli sviluppatori che non erano ancora a conoscenza della vostra
patch. Ignorarli potrebbe essere allettante dato che non ci sono più
dubbi sull'integrazione della modifica. Resistete a tale tentazione, dovete
mantenervi disponibili agli sviluppatori che hanno domande o suggerimenti
per voi.
Ancora più importante: l'inclusione nel ramo principale mette il vostro
codice nelle mani di un gruppo di *tester* molto più esteso. Anche se avete
contribuito ad un driver per un hardware che non è ancora disponibile, sarete
sorpresi da quante persone inseriranno il vostro codice nei loro kernel.
E, ovviamente, dove ci sono *tester*, ci saranno anche dei rapporti su
eventuali bachi.
La peggior specie di rapporti sono quelli che indicano delle regressioni.
Se la vostra modifica causa una regressione, avrete un gran numero di
occhi puntati su di voi; la regressione deve essere sistemata il prima
possibile. Se non vorrete o non sarete capaci di sistemarla (e nessuno
lo farà per voi), la vostra modifica sarà quasi certamente rimossa durante
la fase di stabilizzazione. Oltre alla perdita di tutto il lavoro svolto
per far si che la vostra modifica fosse inserita nel ramo principale,
l'avere una modifica rimossa a causa del fallimento nel sistemare una
regressione, potrebbe rendere più difficile per voi far accettare
il vostro lavoro in futuro.
Dopo che ogni regressione è stata affrontata, ci potrebbero essere altri
bachi ordinari da "sconfiggere". Il periodo di stabilizzazione è la
vostra migliore opportunità per sistemare questi bachi e assicurarvi che
il debutto del vostro codice nel ramo principale del kernel sia il più solido
possibile. Quindi, per favore, rispondete ai rapporti sui bachi e ponete
rimedio, se possibile, a tutti i problemi. È a questo che serve il periodo
di stabilizzazione; potete iniziare creando nuove fantastiche modifiche
una volta che ogni problema con le vecchie sia stato risolto.
Non dimenticate che esistono altre pietre miliari che possono generare
rapporti sui bachi: il successivo rilascio stabile, quando una distribuzione
importante usa una versione del kernel nel quale è presente la vostra
modifica, eccetera. Il continuare a rispondere a questi rapporti è fonte di
orgoglio per il vostro lavoro. Se questa non è una sufficiente motivazione,
allora, è anche consigliabile considera che la comunità di sviluppo ricorda
gli sviluppatori che hanno perso interesse per il loro codice una volta
integrato. La prossima volta che pubblicherete una patch, la comunità
la valuterà anche sulla base del fatto che non sarete disponibili a
prendervene cura anche nel futuro.
Altre cose che posso accadere
=============================
Un giorno, potreste aprire la vostra email e vedere che qualcuno vi ha
inviato una patch per il vostro codice. Questo, dopo tutto, è uno dei
vantaggi di avere il vostro codice "là fuori". Se siete d'accordo con
la modifica, potrete anche inoltrarla ad un manutentore di sottosistema
(assicuratevi di includere la riga "From:" cosicché l'attribuzione sia
corretta, e aggiungete una vostra firma "Signed-off-by"), oppure inviate
un "Acked-by:" e lasciate che l'autore originale la invii.
Se non siete d'accordo con la patch, inviate una risposta educata
spiegando il perché. Se possibile, dite all'autore quali cambiamenti
servirebbero per rendere la patch accettabile da voi. C'è una certa
riluttanza nell'inserire modifiche con un conflitto fra autore
e manutentore del codice, ma solo fino ad un certo punto. Se siete visti
come qualcuno che blocca un buon lavoro senza motivo, quelle patch vi
passeranno oltre e andranno nel ramo principale in ogni caso. Nel kernel
Linux, nessuno ha potere di veto assoluto su alcun codice. Eccezione
fatta per Linus, forse.
In rarissime occasioni, potreste vedere qualcosa di completamente diverso:
un altro sviluppatore che pubblica una soluzione differente al vostro
problema. A questo punto, c'è una buona probabilità che una delle due
modifiche non verrà integrata, e il "c'ero prima io" non è considerato
un argomento tecnico rilevante. Se la modifica di qualcun'altro rimpiazza
la vostra ed entra nel ramo principale, esiste un unico modo di reagire:
siate contenti che il vostro problema sia stato risolto e andate avanti con
il vostro lavoro. L'avere un vostro lavoro spintonato da parte in questo
modo può essere avvilente e scoraggiante, ma la comunità ricorderà come
avrete reagito anche dopo che avrà dimenticato quale fu la modifica accettata.
.. include:: ../disclaimer-ita.rst
:Original: :ref:`Documentation/process/7.AdvancedTopics.rst <development_advancedtopics>`
:Translator: Federico Vaga <federico.vaga@vaga.pv.it>
.. _it_development_advancedtopics:
Argomenti avanzati
==================
A questo punto, si spera, dovreste avere un'idea su come funziona il processo
di sviluppo. Ma rimane comunque molto da imparare! Questo capitolo copre
alcuni argomenti che potrebbero essere utili per gli sviluppatori che stanno
per diventare parte integrante del processo di sviluppo del kernel.
Gestire le modifiche con git
-----------------------------
L'uso di un sistema distribuito per il controllo delle versioni del kernel
ebbe iniziò nel 2002 quando Linux iniziò a provare il programma proprietario
BitKeeper. Nonostante l'uso di BitKeeper fosse opinabile, di certo il suo
approccio alla gestione dei sorgenti non lo era. Un sistema distribuito per
il controllo delle versioni accelerò immediatamente lo sviluppo del kernel.
Oggigiorno, ci sono diverse alternative libere a BitKeeper. Per il meglio o il
peggio, il progetto del kernel ha deciso di usare git per gestire i sorgenti.
Gestire le modifiche con git può rendere la vita dello sviluppatore molto
più facile, specialmente quando il volume delle modifiche cresce.
Git ha anche i suoi lati taglienti che possono essere pericolosi; è uno
strumento giovane e potente che è ancora in fase di civilizzazione da parte
dei suoi sviluppatori. Questo documento non ha lo scopo di insegnare l'uso
di git ai suoi lettori; ci sarebbe materiale a sufficienza per un lungo
documento al riguardo. Invece, qui ci concentriamo in particolare su come
git è parte del processo di sviluppo del kernel. Gli sviluppatori che
desiderassero diventare agili con git troveranno più informazioni ai
seguenti indirizzi:
http://git-scm.com/
http://www.kernel.org/pub/software/scm/git/docs/user-manual.html
e su varie guide che potrete trovare su internet.
La prima cosa da fare prima di usarlo per produrre patch che saranno
disponibili ad altri, è quella di leggere i siti qui sopra e di acquisire una
base solida su come funziona git. Uno sviluppatore che sappia usare git
dovrebbe essere capace di ottenere una copia del repositorio principale,
esplorare la storia della revisione, registrare le modifiche, usare i rami,
eccetera. Una certa comprensione degli strumenti git per riscrivere la storia
(come ``rebase``) è altrettanto utile. Git ha i propri concetti e la propria
terminologia; un nuovo utente dovrebbe conoscere *refs*, *remote branch*,
*index*, *fast-forward merge*, *push* e *pull*, *detached head*, eccetera.
Il tutto potrebbe essere un po' intimidatorio visto da fuori, ma con un po'
di studio i concetti non saranno così difficili da capire.
Utilizzare git per produrre patch da sottomettere via email può essere
un buon esercizio da fare mentre si sta prendendo confidenza con lo strumento.
Quando sarete in grado di creare rami git che siano guardabili da altri,
vi servirà, ovviamente, un server dal quale sia possibile attingere le vostre
modifiche. Se avete un server accessibile da Internet, configurarlo per
eseguire git-daemon è relativamente semplice . Altrimenti, iniziano a
svilupparsi piattaforme che offrono spazi pubblici, e gratuiti (Github,
per esempio). Gli sviluppatori permanenti possono ottenere un account
su kernel.org, ma non è proprio facile da ottenere; per maggiori informazioni
consultate la pagina web http://kernel.org/faq/.
In git è normale avere a che fare con tanti rami. Ogni linea di sviluppo
può essere separata in "rami per argomenti" e gestiti indipendentemente.
In git i rami sono facilissimi, per cui non c'è motivo per non usarli
in libertà. In ogni caso, non dovreste sviluppare su alcun ramo dal
quale altri potrebbero attingere. I rami disponibili pubblicamente dovrebbero
essere creati con attenzione; integrate patch dai rami di sviluppo
solo quando sono complete e pronte ad essere consegnate - non prima.
Git offre alcuni strumenti che vi permettono di riscrivere la storia del
vostro sviluppo. Una modifica errata (diciamo, una che rompe la bisezione,
oppure che ha un qualche tipo di baco evidente) può essere corretta sul posto
o fatta sparire completamente dalla storia. Una serie di patch può essere
riscritta come se fosse stata scritta in cima al ramo principale di oggi,
anche se ci avete lavorato per mesi. Le modifiche possono essere spostate
in modo trasparente da un ramo ad un altro. E così via. Un uso giudizioso
di git per revisionare la storia può aiutare nella creazione di una serie
di patch pulite e con meno problemi.
Un uso eccessivo può portare ad altri tipi di problemi, tuttavia, oltre
alla semplice ossessione per la creazione di una storia del progetto che sia
perfetta. Riscrivere la storia riscriverà le patch contenute in quella
storia, trasformando un kernel verificato (si spera) in uno da verificare.
Ma, oltre a questo, gli sviluppatori non possono collaborare se non condividono
la stessa vista sulla storia del progetto; se riscrivete la storia dalla quale
altri sviluppatori hanno attinto per i loro repositori, renderete la loro vita
molto più difficile. Quindi tenete conto di questa semplice regola generale:
la storia che avete esposto ad altri, generalmente, dovrebbe essere vista come
immutabile.
Dunque, una volta che il vostro insieme di patch è stato reso disponibile
pubblicamente non dovrebbe essere più sovrascritto. Git tenterà di imporre
questa regola, e si rifiuterà di pubblicare nuove patch che non risultino
essere dirette discendenti di quelle pubblicate in precedenza (in altre parole,
patch che non condividono la stessa storia). È possibile ignorare questo
controllo, e ci saranno momenti in cui sarà davvero necessario riscrivere
un ramo già pubblicato. Un esempio è linux-next dove le patch vengono
spostate da un ramo all'altro al fine di evitare conflitti. Ma questo tipo
d'azione dovrebbe essere un'eccezione. Questo è uno dei motivi per cui lo
sviluppo dovrebbe avvenire in rami privati (che possono essere sovrascritti
quando lo si ritiene necessario) e reso pubblico solo quando è in uno stato
avanzato.
Man mano che il ramo principale (o altri rami su cui avete basato le
modifiche) avanza, diventa allettante l'idea di integrare tutte le patch
per rimanere sempre aggiornati. Per un ramo privato, il *rebase* può essere
un modo semplice per rimanere aggiornati, ma questa non è un'opzione nel
momento in cui il vostro ramo è stato esposto al mondo intero.
*Merge* occasionali possono essere considerati di buon senso, ma quando
diventano troppo frequenti confondono inutilmente la storia. La tecnica
suggerita in questi casi è quella di fare *merge* raramente, e più in generale
solo nei momenti di rilascio (per esempio gli -rc del ramo principale).
Se siete nervosi circa alcune patch in particolare, potete sempre fare
dei *merge* di test in un ramo privato. In queste situazioni git "rerere"
può essere utile; questo strumento si ricorda come i conflitti di *merge*
furono risolti in passato cosicché non dovrete fare lo stesso lavoro due volte.
Una delle lamentele più grosse e ricorrenti sull'uso di strumenti come git
è il grande movimento di patch da un repositorio all'altro che rende
facile l'integrazione nel ramo principale di modifiche mediocri, il tutto
sotto il naso dei revisori. Gli sviluppatori del kernel tendono ad essere
scontenti quando vedono succedere queste cose; preparare un ramo git con
patch che non hanno ricevuto alcuna revisione o completamente avulse, potrebbe
influire sulla vostra capacita di proporre, in futuro, l'integrazione dei
vostri rami. Citando Linus
::
Potete inviarmi le vostre patch, ma per far si che io integri una
vostra modifica da git, devo sapere che voi sappiate cosa state
facendo, e ho bisogno di fidarmi *senza* dover passare tutte
le modifiche manualmente una per una.
(http://lwn.net/Articles/224135/).
Per evitare queste situazioni, assicuratevi che tutte le patch in un ramo
siano strettamente correlate al tema delle modifiche; un ramo "driver fixes"
non dovrebbe fare modifiche al codice principale per la gestione della memoria.
E, più importante ancora, non usate un repositorio git per tentare di
evitare il processo di revisione. Pubblicate un sommario di quello che il
vostro ramo contiene sulle liste di discussione più opportune, e , quando
sarà il momento, richiedete che il vostro ramo venga integrato in linux-next.
Se e quando altri inizieranno ad inviarvi patch per essere incluse nel
vostro repositorio, non dovete dimenticare di revisionarle. Inoltre
assicuratevi di mantenerne le informazioni di paternità; al riguardo git "am"
fa del suo meglio, ma potreste dover aggiungere una riga "From:" alla patch
nel caso in cui sia arrivata per vie traverse.
Quando richiedete l'integrazione, siate certi di fornire tutte le informazioni:
dov'è il vostro repositorio, quale ramo integrare, e quali cambiamenti si
otterranno dall'integrazione. Il comando git request-pull può essere d'aiuto;
preparerà una richiesta nel modo in cui gli altri sviluppatori se l'aspettano,
e verificherà che vi siate ricordati di pubblicare quelle patch su un
server pubblico.
Revisionare le patch
--------------------
Alcuni lettori potrebbero avere obiezioni sulla presenza di questa sezione
negli "argomenti avanzati" sulla base che anche gli sviluppatori principianti
dovrebbero revisionare le patch. É certamente vero che non c'è modo
migliore di imparare come programmare per il kernel che guardare il codice
pubblicato dagli altri. In aggiunta, i revisori sono sempre troppo pochi;
guardando il codice potete apportare un significativo contributo all'intero
processo.
Revisionare il codice potrebbe risultare intimidatorio, specialmente per i
nuovi arrivati che potrebbero sentirsi un po' nervosi nel questionare
il codice - in pubblico - pubblicato da sviluppatori più esperti. Perfino
il codice scritto dagli sviluppatori più esperti può essere migliorato.
Forse il suggerimento migliore per i revisori (tutti) è questo: formulate
i commenti come domande e non come critiche. Chiedere "Come viene rilasciato
il *lock* in questo percorso?" funziona sempre molto meglio che
"qui la sincronizzazione è sbagliata".
Diversi sviluppatori revisioneranno il codice con diversi punti di vista.
Alcuni potrebbero concentrarsi principalmente sullo stile del codice e se
alcune linee hanno degli spazio bianchi di troppo. Altri si chiederanno
se accettare una modifica interamente è una cosa positiva per il kernel
o no. E altri ancora si focalizzeranno sui problemi di sincronizzazione,
l'uso eccessivo di *stack*, problemi di sicurezza, duplicazione del codice
in altri contesti, documentazione, effetti negativi sulle prestazioni, cambi
all'ABI dello spazio utente, eccetera. Qualunque tipo di revisione è ben
accetta e di valore, se porta ad avere un codice migliore nel kernel.
.. include:: ../disclaimer-ita.rst
:Original: :ref:`Documentation/process/8.Conclusion.rst <development_conclusion>`
:Translator: Alessia Mantegazza <amantegazza@vaga.pv.it>
.. _it_development_conclusion:
Per maggiori informazioni
=========================
Esistono numerose fonti di informazioni sullo sviluppo del kernel Linux
e argomenti correlati. Primo tra questi sarà sempre la cartella Documentation
che si trova nei sorgenti kernel.
Il file :ref:`process/howto.rst <it_process_howto>` è un punto di partenza
importante; :ref:`process/submitting-patches.rst <it_submittingpatches>` e
:ref:`process/submitting-drivers.rst <it_submittingdrivers>` sono
anch'essi qualcosa che tutti gli sviluppatori del kernel dovrebbero leggere.
Molte API interne al kernel sono documentate utilizzando il meccanismo
kerneldoc; "make htmldocs" o "make pdfdocs" possono essere usati per generare
quei documenti in HTML o PDF (sebbene le versioni di TeX di alcune
distribuzioni hanno dei limiti interni e fallisce nel processare
appropriatamente i documenti).
Diversi siti web approfondiscono lo sviluppo del kernel ad ogni livello
di dettaglio. Il vostro autore vorrebbe umilmente suggerirvi
http://lwn.net/ come fonte; usando l'indice 'kernel' su LWN troverete
molti argomenti specifici sul kernel:
http://lwn.net/Kernel/Index/
Oltre a ciò, una risorsa valida per gli sviluppatori kernel è:
http://kernelnewbies.org/
E, ovviamente, una fonte da non dimenticare è http://kernel.org/, il luogo
definitivo per le informazioni sui rilasci del kernel.
Ci sono numerosi libri sullo sviluppo del kernel:
Linux Device Drivers, 3rd Edition (Jonathan Corbet, Alessandro
Rubini, and Greg Kroah-Hartman). In linea all'indirizzo
http://lwn.net/Kernel/LDD3/.
Linux Kernel Development (Robert Love).
Understanding the Linux Kernel (Daniel Bovet and Marco Cesati).
Tutti questi libri soffrono di un errore comune: tendono a risultare in un
certo senso obsoleti dal momento che si trovano in libreria da diverso
tempo. Comunque contengono informazioni abbastanza buone.
La documentazione per git la troverete su:
http://www.kernel.org/pub/software/scm/git/docs/
http://www.kernel.org/pub/software/scm/git/docs/user-manual.html
Conclusioni
===========
Congratulazioni a chiunque ce l'abbia fatta a terminare questo documento di
lungo-respiro. Si spera che abbia fornito un'utile comprensione d'insieme
di come il kernel Linux viene sviluppato e di come potete partecipare a
tale processo.
Infine, quello che conta è partecipare. Qualsiasi progetto software
open-source non è altro che la somma di quello che i suoi contributori
mettono al suo interno. Il kernel Linux è cresciuto velocemente e bene
perché ha ricevuto il supporto di un impressionante gruppo di sviluppatori,
ognuno dei quali sta lavorando per renderlo migliore. Il kernel è un esempio
importante di cosa può essere fatto quando migliaia di persone lavorano
insieme verso un obiettivo comune.
Il kernel può sempre beneficiare di una larga base di sviluppatori, tuttavia,
c'è sempre molto lavoro da fare. Ma, cosa non meno importante, molti degli
altri partecipanti all'ecosistema Linux possono trarre beneficio attraverso
il contributo al kernel. Inserire codice nel ramo principale è la chiave
per arrivare ad una qualità del codice più alta, bassa manutenzione e
bassi prezzi di distribuzione, alti livelli d'influenza sulla direzione
dello sviluppo del kernel, e molto altro. È una situazione nella quale
tutti coloro che sono coinvolti vincono. Mollate il vostro editor e
raggiungeteci; sarete più che benvenuti.
.. include:: ../disclaimer-ita.rst
:Original: :ref:`Documentation/process/adding-syscalls.rst <addsyscalls>`
:Translator: Federico Vaga <federico.vaga@vaga.pv.it>
.. _it_addsyscalls:
Aggiungere una nuova chiamata di sistema
========================================
Questo documento descrive quello che è necessario sapere per aggiungere
nuove chiamate di sistema al kernel Linux; questo è da considerarsi come
un'aggiunta ai soliti consigli su come proporre nuove modifiche
:ref:`Documentation/translations/it_IT/process/submitting-patches.rst <it_submittingpatches>`.
Alternative alle chiamate di sistema
------------------------------------
La prima considerazione da fare quando si aggiunge una nuova chiamata di
sistema è quella di valutare le alternative. Nonostante le chiamate di sistema
siano il punto di interazione fra spazio utente e kernel più tradizionale ed
ovvio, esistono altre possibilità - scegliete quella che meglio si adatta alle
vostra interfaccia.
- Se le operazioni coinvolte possono rassomigliare a quelle di un filesystem,
allora potrebbe avere molto più senso la creazione di un nuovo filesystem o
dispositivo. Inoltre, questo rende più facile incapsulare la nuova
funzionalità in un modulo kernel piuttosto che essere sviluppata nel cuore
del kernel.
- Se la nuova funzionalità prevede operazioni dove il kernel notifica
lo spazio utente su un avvenimento, allora restituire un descrittore
di file all'oggetto corrispondente permette allo spazio utente di
utilizzare ``poll``/``select``/``epoll`` per ricevere quelle notifiche.
- Tuttavia, le operazioni che non si sposano bene con operazioni tipo
:manpage:`read(2)`/:manpage:`write(2)` dovrebbero essere implementate
come chiamate :manpage:`ioctl(2)`, il che potrebbe portare ad un'API in
un qualche modo opaca.
- Se dovete esporre solo delle informazioni sul sistema, un nuovo nodo in
sysfs (vedere ``Documentation/translations/it_IT/filesystems/sysfs.txt``) o
in procfs potrebbe essere sufficiente. Tuttavia, l'accesso a questi
meccanismi richiede che il filesystem sia montato, il che potrebbe non
essere sempre vero (per esempio, in ambienti come namespace/sandbox/chroot).
Evitate d'aggiungere nuove API in debugfs perché questo non viene
considerata un'interfaccia di 'produzione' verso lo spazio utente.
- Se l'operazione è specifica ad un particolare file o descrittore, allora
potrebbe essere appropriata l'aggiunta di un comando :manpage:`fcntl(2)`.
Tuttavia, :manpage:`fcntl(2)` è una chiamata di sistema multiplatrice che
nasconde una notevole complessità, quindi è ottima solo quando la nuova
funzione assomiglia a quelle già esistenti in :manpage:`fcntl(2)`, oppure
la nuova funzionalità è veramente semplice (per esempio, leggere/scrivere
un semplice flag associato ad un descrittore di file).
- Se l'operazione è specifica ad un particolare processo, allora
potrebbe essere appropriata l'aggiunta di un comando :manpage:`prctl(2)`.
Come per :manpage:`fcntl(2)`, questa chiamata di sistema è un complesso
multiplatore quindi è meglio usarlo per cose molto simili a quelle esistenti
nel comando ``prctl`` oppure per leggere/scrivere un semplice flag relativo
al processo.
Progettare l'API: pianificare le estensioni
-------------------------------------------
Una nuova chiamata di sistema diventerà parte dell'API del kernel, e
dev'essere supportata per un periodo indefinito. Per questo, è davvero
un'ottima idea quella di discutere apertamente l'interfaccia sulla lista
di discussione del kernel, ed è altrettanto importante pianificarne eventuali
estensioni future.
(Nella tabella delle chiamate di sistema sono disseminati esempi dove questo
non fu fatto, assieme ai corrispondenti aggiornamenti -
``eventfd``/``eventfd2``, ``dup2``/``dup3``, ``inotify_init``/``inotify_init1``,
``pipe``/``pipe2``, ``renameat``/``renameat2`` --quindi imparate dalla storia
del kernel e pianificate le estensioni fin dall'inizio)
Per semplici chiamate di sistema che accettano solo un paio di argomenti,
il modo migliore di permettere l'estensibilità è quello di includere un
argomento *flags* alla chiamata di sistema. Per assicurarsi che i programmi
dello spazio utente possano usare in sicurezza *flags* con diverse versioni
del kernel, verificate se *flags* contiene un qualsiasi valore sconosciuto,
in qual caso rifiutate la chiamata di sistema (con ``EINVAL``)::
if (flags & ~(THING_FLAG1 | THING_FLAG2 | THING_FLAG3))
return -EINVAL;
(Se *flags* non viene ancora utilizzato, verificate che l'argomento sia zero)
Per chiamate di sistema più sofisticate che coinvolgono un numero più grande di
argomenti, il modo migliore è quello di incapsularne la maggior parte in una
struttura dati che verrà passata per puntatore. Questa struttura potrà
funzionare con future estensioni includendo un campo *size*::
struct xyzzy_params {
u32 size; /* userspace sets p->size = sizeof(struct xyzzy_params) */
u32 param_1;
u64 param_2;
u64 param_3;
};
Fintanto che un qualsiasi campo nuovo, diciamo ``param_4``, è progettato per
offrire il comportamento precedente quando vale zero, allora questo permetterà
di gestire un conflitto di versione in entrambe le direzioni:
- un vecchio kernel può gestire l'accesso di una versione moderna di un
programma in spazio utente verificando che la memoria oltre la dimensione
della struttura dati attesa sia zero (in pratica verificare che
``param_4 == 0``).
- un nuovo kernel può gestire l'accesso di una versione vecchia di un
programma in spazio utente estendendo la struttura dati con zeri (in pratica
``param_4 = 0``).
Vedere :manpage:`perf_event_open(2)` e la funzione ``perf_copy_attr()`` (in
``kernel/events/core.c``) per un esempio pratico di questo approccio.
Progettare l'API: altre considerazioni
--------------------------------------
Se la vostra nuova chiamata di sistema permette allo spazio utente di fare
riferimento ad un oggetto del kernel, allora questa dovrebbe usare un
descrittore di file per accesso all'oggetto - non inventatevi nuovi tipi di
accesso da spazio utente quando il kernel ha già dei meccanismi e una semantica
ben definita per utilizzare i descrittori di file.
Se la vostra nuova chiamata di sistema :manpage:`xyzzy(2)` ritorna un nuovo
descrittore di file, allora l'argomento *flags* dovrebbe includere un valore
equivalente a ``O_CLOEXEC`` per i nuovi descrittori. Questo rende possibile,
nello spazio utente, la chiusura della finestra temporale fra le chiamate a
``xyzzy()`` e ``fcntl(fd, F_SETFD, FD_CLOEXEC)``, dove un inaspettato
``fork()`` o ``execve()`` potrebbe trasferire il descrittore al programma
eseguito (Comunque, resistete alla tentazione di riutilizzare il valore di
``O_CLOEXEC`` dato che è specifico dell'architettura e fa parte di una
enumerazione di flag ``O_*`` che è abbastanza ricca).
Se la vostra nuova chiamata di sistema ritorna un nuovo descrittore di file,
dovreste considerare che significato avrà l'uso delle chiamate di sistema
della famiglia di :manpage:`poll(2)`. Rendere un descrittore di file pronto
per la lettura o la scrittura è il tipico modo del kernel per notificare lo
spazio utente circa un evento associato all'oggetto del kernel.
Se la vostra nuova chiamata di sistema :manpage:`xyzzy(2)` ha un argomento
che è il percorso ad un file::
int sys_xyzzy(const char __user *path, ..., unsigned int flags);
dovreste anche considerare se non sia più appropriata una versione
:manpage:`xyzzyat(2)`::
int sys_xyzzyat(int dfd, const char __user *path, ..., unsigned int flags);
Questo permette più flessibilità su come lo spazio utente specificherà il file
in questione; in particolare, permette allo spazio utente di richiedere la
funzionalità su un descrittore di file già aperto utilizzando il *flag*
``AT_EMPTY_PATH``, in pratica otterremmo gratuitamente l'operazione
:manpage:`fxyzzy(3)`::
- xyzzyat(AT_FDCWD, path, ..., 0) is equivalent to xyzzy(path,...)
- xyzzyat(fd, "", ..., AT_EMPTY_PATH) is equivalent to fxyzzy(fd, ...)
(Per maggiori dettagli sulla logica delle chiamate \*at(), leggete la pagina
man :manpage:`openat(2)`; per un esempio di AT_EMPTY_PATH, leggere la pagina
man :manpage:`fstatat(2)`).
Se la vostra nuova chiamata di sistema :manpage:`xyzzy(2)` prevede un parametro
per descrivere uno scostamento all'interno di un file, usate ``loff_t`` come
tipo cosicché scostamenti a 64-bit potranno essere supportati anche su
architetture a 32-bit.
Se la vostra nuova chiamata di sistema :manpage:`xyzzy(2)` prevede l'uso di
funzioni riservate, allora dev'essere gestita da un opportuno bit di privilegio
(verificato con una chiamata a ``capable()``), come descritto nella pagina man
:manpage:`capabilities(7)`. Scegliete un bit di privilegio già esistente per
gestire la funzionalità associata, ma evitate la combinazione di diverse
funzionalità vagamente collegate dietro lo stesso bit, in quanto va contro il
principio di *capabilities* di separare i poteri di root. In particolare,
evitate di aggiungere nuovi usi al fin-troppo-generico privilegio
``CAP_SYS_ADMIN``.
Se la vostra nuova chiamata di sistema :manpage:`xyzzy(2)` manipola altri
processi oltre a quello chiamato, allora dovrebbe essere limitata (usando
la chiamata ``ptrace_may_access()``) di modo che solo un processo chiamante
con gli stessi permessi del processo in oggetto, o con i necessari privilegi,
possa manipolarlo.
Infine, state attenti che in alcune architetture non-x86 la vita delle chiamate
di sistema con argomenti a 64-bit viene semplificata se questi argomenti
ricadono in posizioni dispari (pratica, i parametri 1, 3, 5); questo permette
l'uso di coppie contigue di registri a 32-bit. (Questo non conta se gli
argomenti sono parte di una struttura dati che viene passata per puntatore).
Proporre l'API
--------------
Al fine di rendere le nuove chiamate di sistema di facile revisione, è meglio
che dividiate le modifiche i pezzi separati. Questi dovrebbero includere
almeno le seguenti voci in *commit* distinti (ognuno dei quali sarà descritto
più avanti):
- l'essenza dell'implementazione della chiamata di sistema, con i prototipi,
i numeri generici, le modifiche al Kconfig e l'implementazione *stub* di
ripiego.
- preparare la nuova chiamata di sistema per un'architettura specifica,
solitamente x86 (ovvero tutti: x86_64, x86_32 e x32).
- un programma di auto-verifica da mettere in ``tools/testing/selftests/``
che mostri l'uso della chiamata di sistema.
- una bozza di pagina man per la nuova chiamata di sistema. Può essere
scritta nell'email di presentazione, oppure come modifica vera e propria
al repositorio delle pagine man.
Le proposte di nuove chiamate di sistema, come ogni altro modifica all'API del
kernel, deve essere sottomessa alla lista di discussione
linux-api@vger.kernel.org.
Implementazione di chiamate di sistema generiche
------------------------------------------------
Il principale punto d'accesso alla vostra nuova chiamata di sistema
:manpage:`xyzzy(2)` verrà chiamato ``sys_xyzzy()``; ma, piuttosto che in modo
esplicito, lo aggiungerete tramite la macro ``SYSCALL_DEFINEn``. La 'n'
indica il numero di argomenti della chiamata di sistema; la macro ha come
argomento il nome della chiamata di sistema, seguito dalle coppie (tipo, nome)
per definire i suoi parametri. L'uso di questa macro permette di avere
i metadati della nuova chiamata di sistema disponibili anche per altri
strumenti.
Il nuovo punto d'accesso necessita anche del suo prototipo di funzione in
``include/linux/syscalls.h``, marcato come asmlinkage di modo da abbinargli
il modo in cui quelle chiamate di sistema verranno invocate::
asmlinkage long sys_xyzzy(...);
Alcune architetture (per esempio x86) hanno le loro specifiche tabelle di
chiamate di sistema (syscall), ma molte altre architetture condividono una
tabella comune di syscall. Aggiungete alla lista generica la vostra nuova
chiamata di sistema aggiungendo un nuovo elemento alla lista in
``include/uapi/asm-generic/unistd.h``::
#define __NR_xyzzy 292
__SYSCALL(__NR_xyzzy, sys_xyzzy)
Aggiornate anche il contatore __NR_syscalls di modo che sia coerente con
l'aggiunta della nuove chiamate di sistema; va notato che se più di una nuova
chiamata di sistema viene aggiunga nella stessa finestra di sviluppo, il numero
della vostra nuova syscall potrebbe essere aggiustato al fine di risolvere i
conflitti.
Il file ``kernel/sys_ni.c`` fornisce le implementazioni *stub* di ripiego che
ritornano ``-ENOSYS``. Aggiungete la vostra nuova chiamata di sistema anche
qui::
COND_SYSCALL(xyzzy);
La vostra nuova funzionalità del kernel, e la chiamata di sistema che la
controlla, dovrebbero essere opzionali. Quindi, aggiungete un'opzione
``CONFIG`` (solitamente in ``init/Kconfig``). Come al solito per le nuove
opzioni ``CONFIG``:
- Includete una descrizione della nuova funzionalità e della chiamata di
sistema che la controlla.
- Rendete l'opzione dipendente da EXPERT se dev'essere nascosta agli utenti
normali.
- Nel Makefile, rendere tutti i nuovi file sorgenti, che implementano la
nuova funzionalità, dipendenti dall'opzione CONFIG (per esempio
``obj-$(CONFIG_XYZZY_SYSCALL) += xyzzy.o``).
- Controllate due volte che sia possibile generare il kernel con la nuova
opzione CONFIG disabilitata.
Per riassumere, vi serve un *commit* che includa:
- un'opzione ``CONFIG``per la nuova funzione, normalmente in ``init/Kconfig``
- ``SYSCALL_DEFINEn(xyzzy, ...)`` per il punto d'accesso
- il corrispondente prototipo in ``include/linux/syscalls.h``
- un elemento nella tabella generica in ``include/uapi/asm-generic/unistd.h``
- *stub* di ripiego in ``kernel/sys_ni.c``
Implementazione delle chiamate di sistema x86
---------------------------------------------
Per collegare la vostra nuova chiamate di sistema alle piattaforme x86,
dovete aggiornate la tabella principale di syscall. Assumendo che la vostra
nuova chiamata di sistema non sia particolarmente speciale (vedere sotto),
dovete aggiungere un elemento *common* (per x86_64 e x32) in
arch/x86/entry/syscalls/syscall_64.tbl::
333 common xyzzy sys_xyzzy
e un elemento per *i386* ``arch/x86/entry/syscalls/syscall_32.tbl``::
380 i386 xyzzy sys_xyzzy
Ancora una volta, questi numeri potrebbero essere cambiati se generano
conflitti durante la finestra di integrazione.
Chiamate di sistema compatibili (generico)
------------------------------------------
Per molte chiamate di sistema, la stessa implementazione a 64-bit può essere
invocata anche quando il programma in spazio utente è a 32-bit; anche se la
chiamata di sistema include esplicitamente un puntatore, questo viene gestito
in modo trasparente.
Tuttavia, ci sono un paio di situazione dove diventa necessario avere un
livello di gestione della compatibilità per risolvere le differenze di
dimensioni fra 32-bit e 64-bit.
Il primo caso è quando un kernel a 64-bit supporta anche programmi in spazio
utente a 32-bit, perciò dovrà ispezionare aree della memoria (``__user``) che
potrebbero contenere valori a 32-bit o a 64-bit. In particolar modo, questo
è necessario quando un argomento di una chiamata di sistema è:
- un puntatore ad un puntatore
- un puntatore ad una struttura dati contenente a sua volta un puntatore
( ad esempio ``struct iovec __user *``)
- un puntatore ad un tipo intero di dimensione variabile (``time_t``,
``off_t``, ``long``, ...)
- un puntatore ad una struttura dati contenente un tipo intero di dimensione
variabile.
Il secondo caso che richiede un livello di gestione della compatibilità è
quando uno degli argomenti di una chiamata a sistema è esplicitamente un tipo
a 64-bit anche su architetture a 32-bit, per esempio ``loff_t`` o ``__u64``.
In questo caso, un valore che arriva ad un kernel a 64-bit da un'applicazione
a 32-bit verrà diviso in due valori a 32-bit che dovranno essere riassemblati
in questo livello di compatibilità.
(Da notare che non serve questo livello di compatibilità per argomenti che
sono puntatori ad un tipo esplicitamente a 64-bit; per esempio, in
:manpage:`splice(2)` l'argomento di tipo ``loff_t __user *`` non necessita
di una chiamata di sistema ``compat_``)
La versione compatibile della nostra chiamata di sistema si chiamerà
``compat_sys_xyzzy()``, e viene aggiunta utilizzando la macro
``COMPAT_SYSCALL_DEFINEn()`` (simile a SYSCALL_DEFINEn). Questa versione
dell'implementazione è parte del kernel a 64-bit ma accetta parametri a 32-bit
che trasformerà secondo le necessità (tipicamente, la versione
``compat_sys_`` converte questi valori nello loro corrispondente a 64-bit e
può chiamare la versione ``sys_`` oppure invocare una funzione che implementa
le parti comuni).
Il punto d'accesso *compat* deve avere il corrispondente prototipo di funzione
in ``include/linux/compat.h``, marcato come asmlinkage di modo da abbinargli
il modo in cui quelle chiamate di sistema verranno invocate::
asmlinkage long compat_sys_xyzzy(...);
Se la chiamata di sistema prevede una struttura dati organizzata in modo
diverso per sistemi a 32-bit e per quelli a 64-bit, diciamo
``struct xyzzy_args``, allora il file d'intestazione
``then the include/linux/compat.h`` deve includere la sua versione
*compatibile* (``struct compat_xyzzy_args``); ogni variabile con
dimensione variabile deve avere il proprio tipo ``compat_`` corrispondente
a quello in ``struct xyzzy_args``. La funzione ``compat_sys_xyzzy()``
può usare la struttura ``compat_`` per analizzare gli argomenti ricevuti
da una chiamata a 32-bit.
Per esempio, se avete i seguenti campi::
struct xyzzy_args {
const char __user *ptr;
__kernel_long_t varying_val;
u64 fixed_val;
/* ... */
};
nella struttura ``struct xyzzy_args``, allora la struttura
``struct compat_xyzzy_args`` dovrebbe avere::
struct compat_xyzzy_args {
compat_uptr_t ptr;
compat_long_t varying_val;
u64 fixed_val;
/* ... */
};
La lista generica delle chiamate di sistema ha bisogno di essere
aggiustata al fine di permettere l'uso della versione *compatibile*;
la voce in ``include/uapi/asm-generic/unistd.h`` dovrebbero usare
``__SC_COMP`` piuttosto di ``__SYSCALL``::
#define __NR_xyzzy 292
__SC_COMP(__NR_xyzzy, sys_xyzzy, compat_sys_xyzzy)
Riassumendo, vi serve:
- un ``COMPAT_SYSCALL_DEFINEn(xyzzy, ...)`` per il punto d'accesso
*compatibile*
- un prototipo in ``include/linux/compat.h``
- (se necessario) una struttura di compatibilità a 32-bit in
``include/linux/compat.h``
- una voce ``__SC_COMP``, e non ``__SYSCALL``, in
``include/uapi/asm-generic/unistd.h``
Compatibilità delle chiamate di sistema (x86)
---------------------------------------------
Per collegare una chiamata di sistema, su un'architettura x86, con la sua
versione *compatibile*, è necessario aggiustare la voce nella tabella
delle syscall.
Per prima cosa, la voce in ``arch/x86/entry/syscalls/syscall_32.tbl`` prende
un argomento aggiuntivo per indicare che un programma in spazio utente
a 32-bit, eseguito su un kernel a 64-bit, dovrebbe accedere tramite il punto
d'accesso compatibile::
380 i386 xyzzy sys_xyzzy __ia32_compat_sys_xyzzy
Secondo, dovete capire cosa dovrebbe succedere alla nuova chiamata di sistema
per la versione dell'ABI x32. Qui C'è una scelta da fare: gli argomenti
possono corrisponde alla versione a 64-bit o a quella a 32-bit.
Se c'è un puntatore ad un puntatore, la decisione è semplice: x32 è ILP32,
quindi gli argomenti dovrebbero corrispondere a quelli a 32-bit, e la voce in
``arch/x86/entry/syscalls/syscall_64.tbl`` sarà divisa cosicché i programmi
x32 eseguano la chiamata *compatibile*::
333 64 xyzzy sys_xyzzy
...
555 x32 xyzzy __x32_compat_sys_xyzzy
Se non ci sono puntatori, allora è preferibile riutilizzare la chiamata di
sistema a 64-bit per l'ABI x32 (e di conseguenza la voce in
arch/x86/entry/syscalls/syscall_64.tbl rimane immutata).
In ambo i casi, dovreste verificare che i tipi usati dagli argomenti
abbiano un'esatta corrispondenza da x32 (-mx32) al loro equivalente a
32-bit (-m32) o 64-bit (-m64).
Chiamate di sistema che ritornano altrove
-----------------------------------------
Nella maggior parte delle chiamate di sistema, al termine della loro
esecuzione, i programmi in spazio utente riprendono esattamente dal punto
in cui si erano interrotti -- quindi dall'istruzione successiva, con lo
stesso *stack* e con la maggior parte del registri com'erano stati
lasciati prima della chiamata di sistema, e anche con la stessa memoria
virtuale.
Tuttavia, alcune chiamata di sistema fanno le cose in modo differente.
Potrebbero ritornare ad un punto diverso (``rt_sigreturn``) o cambiare
la memoria in spazio utente (``fork``/``vfork``/``clone``) o perfino
l'architettura del programma (``execve``/``execveat``).
Per permettere tutto ciò, l'implementazione nel kernel di questo tipo di
chiamate di sistema potrebbero dover salvare e ripristinare registri
aggiuntivi nello *stack* del kernel, permettendo così un controllo completo
su dove e come l'esecuzione dovrà continuare dopo l'esecuzione della
chiamata di sistema.
Queste saranno specifiche per ogni architettura, ma tipicamente si definiscono
dei punti d'accesso in *assembly* per salvare/ripristinare i registri
aggiuntivi e quindi chiamare il vero punto d'accesso per la chiamata di
sistema.
Per l'architettura x86_64, questo è implementato come un punto d'accesso
``stub_xyzzy`` in ``arch/x86/entry/entry_64.S``, e la voce nella tabella
di syscall (``arch/x86/entry/syscalls/syscall_64.tbl``) verrà corretta di
conseguenza::
333 common xyzzy stub_xyzzy
L'equivalente per programmi a 32-bit eseguiti su un kernel a 64-bit viene
normalmente chiamato ``stub32_xyzzy`` e implementato in
``arch/x86/entry/entry_64_compat.S`` con la corrispondente voce nella tabella
di syscall ``arch/x86/entry/syscalls/syscall_32.tbl`` corretta nel
seguente modo::
380 i386 xyzzy sys_xyzzy stub32_xyzzy
Se una chiamata di sistema necessita di un livello di compatibilità (come
nella sezione precedente), allora la versione ``stub32_`` deve invocare
la versione ``compat_sys_`` piuttosto che quella nativa a 64-bit. In aggiunta,
se l'implementazione dell'ABI x32 è diversa da quella x86_64, allora la sua
voce nella tabella di syscall dovrà chiamare uno *stub* che invoca la versione
``compat_sys_``,
Per completezza, sarebbe carino impostare una mappatura cosicché
*user-mode* Linux (UML) continui a funzionare -- la sua tabella di syscall
farà riferimento a stub_xyzzy, ma UML non include l'implementazione
in ``arch/x86/entry/entry_64.S`` (perché UML simula i registri eccetera).
Correggerlo è semplice, basta aggiungere una #define in
``arch/x86/um/sys_call_table_64.c``::
#define stub_xyzzy sys_xyzzy
Altri dettagli
--------------
La maggior parte dei kernel tratta le chiamate di sistema allo stesso modo,
ma possono esserci rare eccezioni per le quali potrebbe essere necessario
l'aggiornamento della vostra chiamata di sistema.
Il sotto-sistema di controllo (*audit subsystem*) è uno di questi casi
speciali; esso include (per architettura) funzioni che classificano alcuni
tipi di chiamate di sistema -- in particolare apertura dei file
(``open``/``openat``), esecuzione dei programmi (``execve``/``exeveat``)
oppure multiplatori di socket (``socketcall``). Se la vostra nuova chiamata
di sistema è simile ad una di queste, allora il sistema di controllo dovrebbe
essere aggiornato.
Più in generale, se esiste una chiamata di sistema che è simile alla vostra,
vale la pena fare una ricerca con ``grep`` su tutto il kernel per la chiamata
di sistema esistente per verificare che non ci siano altri casi speciali.
Verifica
--------
Una nuova chiamata di sistema dev'essere, ovviamente, provata; è utile fornire
ai revisori un programma in spazio utente che mostri l'uso della chiamata di
sistema. Un buon modo per combinare queste cose è quello di aggiungere un
semplice programma di auto-verifica in una nuova cartella in
``tools/testing/selftests/``.
Per una nuova chiamata di sistema, ovviamente, non ci sarà alcuna funzione
in libc e quindi il programma di verifica dovrà invocarla usando ``syscall()``;
inoltre, se la nuova chiamata di sistema prevede un nuova struttura dati
visibile in spazio utente, il file d'intestazione necessario dev'essere
installato al fine di compilare il programma.
Assicuratevi che il programma di auto-verifica possa essere eseguito
correttamente su tutte le architetture supportate. Per esempio, verificate che
funzioni quando viene compilato per x86_64 (-m64), x86_32 (-m32) e x32 (-mx32).
Al fine di una più meticolosa ed estesa verifica della nuova funzionalità,
dovreste considerare l'aggiunta di nuove verifica al progetto 'Linux Test',
oppure al progetto xfstests per cambiamenti relativi al filesystem.
- https://linux-test-project.github.io/
- git://git.kernel.org/pub/scm/fs/xfs/xfstests-dev.git
Pagine man
----------
Tutte le nuove chiamate di sistema dovrebbero avere una pagina man completa,
idealmente usando i marcatori groff, ma anche il puro testo può andare. Se
state usando groff, è utile che includiate nella email di presentazione una
versione già convertita in formato ASCII: semplificherà la vita dei revisori.
Le pagine man dovrebbero essere in copia-conoscenza verso
linux-man@vger.kernel.org
Per maggiori dettagli, leggere
https://www.kernel.org/doc/man-pages/patches.html
Non invocate chiamate di sistema dal kernel
-------------------------------------------
Le chiamate di sistema sono, come già detto prima, punti di interazione fra
lo spazio utente e il kernel. Perciò, le chiamate di sistema come
``sys_xyzzy()`` o ``compat_sys_xyzzy()`` dovrebbero essere chiamate solo dallo
spazio utente attraverso la tabella syscall, ma non da nessun altro punto nel
kernel. Se la nuova funzionalità è utile all'interno del kernel, per esempio
dev'essere condivisa fra una vecchia e una nuova chiamata di sistema o
dev'essere utilizzata da una chiamata di sistema e la sua variante compatibile,
allora dev'essere implementata come una funzione di supporto
(*helper function*) (per esempio ``kern_xyzzy()``). Questa funzione potrà
essere chiamata dallo *stub* (``sys_xyzzy()``), dalla variante compatibile
(``compat_sys_xyzzy()``), e/o da altri parti del kernel.
Sui sistemi x86 a 64-bit, a partire dalla versione v4.17 è un requisito
fondamentale quello di non invocare chiamate di sistema all'interno del kernel.
Esso usa una diversa convenzione per l'invocazione di chiamate di sistema dove
``struct pt_regs`` viene decodificata al volo in una funzione che racchiude
la chiamata di sistema la quale verrà eseguita successivamente.
Questo significa che verranno passati solo i parametri che sono davvero
necessari ad una specifica chiamata di sistema, invece che riempire ogni volta
6 registri del processore con contenuti presi dallo spazio utente (potrebbe
causare seri problemi nella sequenza di chiamate).
Inoltre, le regole su come i dati possano essere usati potrebbero differire
fra il kernel e l'utente. Questo è un altro motivo per cui invocare
``sys_xyzzy()`` è generalmente una brutta idea.
Eccezioni a questa regola vengono accettate solo per funzioni d'architetture
che surclassano quelle generiche, per funzioni d'architettura di compatibilità,
o per altro codice in arch/
Riferimenti e fonti
-------------------
- Articolo di Michael Kerris su LWN sull'uso dell'argomento flags nelle
chiamate di sistema: https://lwn.net/Articles/585415/
- Articolo di Michael Kerris su LWN su come gestire flag sconosciuti in
una chiamata di sistema: https://lwn.net/Articles/588444/
- Articolo di Jake Edge su LWN che descrive i limiti degli argomenti a 64-bit
delle chiamate di sistema: https://lwn.net/Articles/311630/
- Una coppia di articoli di David Drysdale che descrivono i dettagli del
percorso implementativo di una chiamata di sistema per la versione v3.14:
- https://lwn.net/Articles/604287/
- https://lwn.net/Articles/604515/
- Requisiti specifici alle architetture sono discussi nella pagina man
:manpage:`syscall(2)` :
http://man7.org/linux/man-pages/man2/syscall.2.html#NOTES
- Collezione di email di Linux Torvalds sui problemi relativi a ``ioctl()``:
http://yarchive.net/comp/linux/ioctl.html
- "Come non inventare interfacce del kernel", Arnd Bergmann,
http://www.ukuug.org/events/linux2007/2007/papers/Bergmann.pdf
- Articolo di Michael Kerris su LWN sull'evitare nuovi usi di CAP_SYS_ADMIN:
https://lwn.net/Articles/486306/
- Raccomandazioni da Andrew Morton circa il fatto che tutte le informazioni
su una nuova chiamata di sistema dovrebbero essere contenute nello stesso
filone di discussione di email: https://lkml.org/lkml/2014/7/24/641
- Raccomandazioni da Michael Kerrisk circa il fatto che le nuove chiamate di
sistema dovrebbero avere una pagina man: https://lkml.org/lkml/2014/6/13/309
- Consigli da Thomas Gleixner sul fatto che il collegamento all'architettura
x86 dovrebbe avvenire in un *commit* differente:
https://lkml.org/lkml/2014/11/19/254
- Consigli da Greg Kroah-Hartman circa la bontà d'avere una pagina man e un
programma di auto-verifica per le nuove chiamate di sistema:
https://lkml.org/lkml/2014/3/19/710
- Discussione di Michael Kerrisk sulle nuove chiamate di sistema contro
le estensioni :manpage:`prctl(2)`: https://lkml.org/lkml/2014/6/3/411
- Consigli da Ingo Molnar che le chiamate di sistema con più argomenti
dovrebbero incapsularli in una struttura che includa un argomento
*size* per garantire l'estensibilità futura:
https://lkml.org/lkml/2015/7/30/117
- Un certo numero di casi strani emersi dall'uso (riuso) dei flag O_*:
- commit 75069f2b5bfb ("vfs: renumber FMODE_NONOTIFY and add to uniqueness
check")
- commit 12ed2e36c98a ("fanotify: FMODE_NONOTIFY and __O_SYNC in sparc
conflict")
- commit bb458c644a59 ("Safer ABI for O_TMPFILE")
- Discussion from Matthew Wilcox about restrictions on 64-bit arguments:
https://lkml.org/lkml/2008/12/12/187
- Raccomandazioni da Greg Kroah-Hartman sul fatto che i flag sconosciuti dovrebbero
essere controllati: https://lkml.org/lkml/2014/7/17/577
- Raccomandazioni da Linus Torvalds che le chiamate di sistema x32 dovrebbero
favorire la compatibilità con le versioni a 64-bit piuttosto che quelle a 32-bit:
https://lkml.org/lkml/2011/8/31/244
.. include:: ../disclaimer-ita.rst
:Original: :ref:`Documentation/process/applying-patches.rst <applying_patches>`
.. _it_applying_patches:
Applicare modifiche al kernel Linux
===================================
.. warning::
TODO ancora da tradurre
.. include:: ../disclaimer-ita.rst
:Original: :ref:`Documentation/process/changes.rst <changes>`
.. _it_changes:
Requisiti minimi per compilare il kernel
++++++++++++++++++++++++++++++++++++++++
.. warning::
TODO ancora da tradurre
.. include:: ../disclaimer-ita.rst
:Original: :ref:`Documentation/process/clang-format.rst <clangformat>`
:Translator: Federico Vaga <federico.vaga@vaga.pv.it>
.. _it_clangformat:
clang-format
============
``clang-format`` è uno strumento per formattare codice C/C++/... secondo
un gruppo di regole ed euristiche. Come tutti gli strumenti, non è perfetto
e non copre tutti i singoli casi, ma è abbastanza buono per essere utile.
``clang-format`` può essere usato per diversi fini:
- Per riformattare rapidamente un blocco di codice secondo lo stile del
kernel. Particolarmente utile quando si sposta del codice e lo si
allinea/ordina. Vedere it_clangformatreformat_.
- Identificare errori di stile, refusi e possibili miglioramenti nei
file che mantieni, le modifiche che revisioni, le differenze,
eccetera. Vedere it_clangformatreview_.
- Ti aiuta a seguire lo stile del codice, particolarmente utile per i
nuovi arrivati o per coloro che lavorano allo stesso tempo su diversi
progetti con stili di codifica differenti.
Il suo file di configurazione è ``.clang-format`` e si trova nella cartella
principale dei sorgenti del kernel. Le regole scritte in quel file tentano
di approssimare le lo stile di codifica del kernel. Si tenta anche di seguire
il più possibile
:ref:`Documentation/translations/it_IT/process/coding-style.rst <it_codingstyle>`.
Dato che non tutto il kernel segue lo stesso stile, potreste voler aggiustare
le regole di base per un particolare sottosistema o cartella. Per farlo,
potete sovrascriverle scrivendole in un altro file ``.clang-format`` in
una sottocartella.
Questo strumento è già stato incluso da molto tempo nelle distribuzioni
Linux più popolari. Cercate ``clang-format`` nel vostro repositorio.
Altrimenti, potete scaricare una versione pre-generata dei binari di LLVM/clang
oppure generarlo dai codici sorgenti:
http://releases.llvm.org/download.html
Troverete più informazioni ai seguenti indirizzi:
https://clang.llvm.org/docs/ClangFormat.html
https://clang.llvm.org/docs/ClangFormatStyleOptions.html
.. _it_clangformatreview:
Revisionare lo stile di codifica per file e modifiche
-----------------------------------------------------
Eseguendo questo programma, potrete revisionare un intero sottosistema,
cartella o singoli file alla ricerca di errori di stile, refusi o
miglioramenti.
Per farlo, potete eseguire qualcosa del genere::
# Make sure your working directory is clean!
clang-format -i kernel/*.[ch]
E poi date un'occhiata a *git diff*.
Osservare le righe di questo diff è utile a migliorare/aggiustare
le opzioni di stile nel file di configurazione; così come per verificare
le nuove funzionalità/versioni di ``clang-format``.
``clang-format`` è in grado di leggere diversi diff unificati, quindi
potrete revisionare facilmente delle modifiche e *git diff*.
La documentazione si trova al seguente indirizzo:
https://clang.llvm.org/docs/ClangFormat.html#script-for-patch-reformatting
Per evitare che ``clang-format`` formatti alcune parti di un file, potete
scrivere nel codice::
int formatted_code;
// clang-format off
void unformatted_code ;
// clang-format on
void formatted_code_again;
Nonostante si attraente l'idea di utilizzarlo per mantenere un file
sempre in sintonia con ``clang-format``, specialmente per file nuovi o
se siete un manutentore, ricordatevi che altre persone potrebbero usare
una versione diversa di ``clang-format`` oppure non utilizzarlo del tutto.
Quindi, dovreste trattenervi dall'usare questi marcatori nel codice del
kernel; almeno finché non vediamo che ``clang-format`` è diventato largamente
utilizzato.
.. _it_clangformatreformat:
Riformattare blocchi di codice
------------------------------
Utilizzando dei plugin per il vostro editor, potete riformattare una
blocco (selezione) di codice con una singola combinazione di tasti.
Questo è particolarmente utile: quando si riorganizza il codice, per codice
complesso, macro multi-riga (e allineare le loro "barre"), eccetera.
Ricordatevi che potete sempre aggiustare le modifiche in quei casi dove
questo strumento non ha fatto un buon lavoro. Ma come prima approssimazione,
può essere davvero molto utile.
Questo programma si integra con molti dei più popolari editor. Alcuni di
essi come vim, emacs, BBEdit, Visaul Studio, lo supportano direttamente.
Al seguente indirizzo troverete le istruzioni:
https://clang.llvm.org/docs/ClangFormat.html
Per Atom, Eclipse, Sublime Text, Visual Studio Code, XCode e altri editor
e IDEs dovreste essere in grado di trovare dei plugin pronti all'uso.
Per questo caso d'uso, considerate l'uso di un secondo ``.clang-format``
che potete personalizzare con le vostre opzioni.
Consultare it_clangformatextra_.
.. _it_clangformatmissing:
Cose non supportate
-------------------
``clang-format`` non ha il supporto per alcune cose che sono comuni nel
codice del kernel. Sono facili da ricordare; quindi, se lo usate
regolarmente, imparerete rapidamente a evitare/ignorare certi problemi.
In particolare, quelli più comuni che noterete sono:
- Allineamento di ``#define`` su una singola riga, per esempio::
#define TRACING_MAP_BITS_DEFAULT 11
#define TRACING_MAP_BITS_MAX 17
#define TRACING_MAP_BITS_MIN 7
contro::
#define TRACING_MAP_BITS_DEFAULT 11
#define TRACING_MAP_BITS_MAX 17
#define TRACING_MAP_BITS_MIN 7
- Allineamento dei valori iniziali, per esempio::
static const struct file_operations uprobe_events_ops = {
.owner = THIS_MODULE,
.open = probes_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
.write = probes_write,
};
contro::
static const struct file_operations uprobe_events_ops = {
.owner = THIS_MODULE,
.open = probes_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
.write = probes_write,
};
.. _it_clangformatextra:
Funzionalità e opzioni aggiuntive
---------------------------------
Al fine di minimizzare le differenze fra il codice attuale e l'output
del programma, alcune opzioni di stile e funzionalità non sono abilitate
nella configurazione base. In altre parole, lo scopo è di rendere le
differenze le più piccole possibili, permettendo la semplificazione
della revisione di file, differenze e modifiche.
In altri casi (per esempio un particolare sottosistema/cartella/file), lo
stile del kernel potrebbe essere diverso e abilitare alcune di queste
opzioni potrebbe dare risultati migliori.
Per esempio:
- Allineare assegnamenti (``AlignConsecutiveAssignments``).
- Allineare dichiarazioni (``AlignConsecutiveDeclarations``).
- Riorganizzare il testo nei commenti (``ReflowComments``).
- Ordinare gli ``#include`` (``SortIncludes``).
Piuttosto che per interi file, solitamente sono utili per la riformattazione
di singoli blocchi. In alternativa, potete creare un altro file
``.clang-format`` da utilizzare con il vostro editor/IDE.
.. include:: ../disclaimer-ita.rst
:Original: :ref:`Documentation/process/code-of-conduct.rst <code_of_conduct>`
.. _it_code_of_conduct:
Accordo dei contributori sul codice di condotta
+++++++++++++++++++++++++++++++++++++++++++++++
.. warning::
TODO ancora da tradurre
.. include:: ../disclaimer-ita.rst
:Original: :ref:`Documentation/process/coding-style.rst <codingstyle>`
:Translator: Federico Vaga <federico.vaga@vaga.pv.it>
.. _it_codingstyle:
Stile del codice per il kernel Linux
====================================
Questo è un breve documento che descrive lo stile di codice preferito per
il kernel Linux. Lo stile di codifica è molto personale e non voglio
**forzare** nessuno ad accettare il mio, ma questo stile è quello che
dev'essere usato per qualsiasi cosa che io sia in grado di mantenere, e l'ho
preferito anche per molte altre cose. Per favore, almeno tenete in
considerazione le osservazioni espresse qui.
La prima cosa che suggerisco è quella di stamparsi una copia degli standard
di codifica GNU e di NON leggerla. Bruciatela, è un grande gesto simbolico.
Comunque, ecco i punti:
1) Indentazione
---------------
La tabulazione (tab) è di 8 caratteri e così anche le indentazioni. Ci sono
alcuni movimenti di eretici che vorrebbero l'indentazione a 4 (o perfino 2!)
caratteri di profondità, che è simile al tentativo di definire il valore del
pi-greco a 3.
Motivazione: l'idea dell'indentazione è di definire chiaramente dove un blocco
di controllo inizia e finisce. Specialmente quando siete rimasti a guardare lo
schermo per 20 ore a file, troverete molto più facile capire i livelli di
indentazione se questi sono larghi.
Ora, alcuni rivendicano che un'indentazione da 8 caratteri sposta il codice
troppo a destra e che quindi rende difficile la lettura su schermi a 80
caratteri. La risposta a questa affermazione è che se vi servono più di 3
livelli di indentazione, siete comunque fregati e dovreste correggere il vostro
programma.
In breve, l'indentazione ad 8 caratteri rende più facile la lettura, e in
aggiunta vi avvisa quando state annidando troppo le vostre funzioni.
Tenete ben a mente questo avviso.
Al fine di facilitare l'indentazione del costrutto switch, si preferisce
allineare sulla stessa colonna la parola chiave ``switch`` e i suoi
subordinati ``case``. In questo modo si evita una doppia indentazione per
i ``case``. Un esempio.:
.. code-block:: c
switch (suffix) {
case 'G':
case 'g':
mem <<= 30;
break;
case 'M':
case 'm':
mem <<= 20;
break;
case 'K':
case 'k':
mem <<= 10;
/* fall through */
default:
break;
}
A meno che non vogliate nascondere qualcosa, non mettete più istruzioni sulla
stessa riga:
.. code-block:: c
if (condition) do_this;
do_something_everytime;
né mettete più assegnamenti sulla stessa riga. Lo stile del kernel
è ultrasemplice. Evitate espressioni intricate.
Al di fuori dei commenti, della documentazione ed escludendo i Kconfig, gli
spazi non vengono mai usati per l'indentazione, e l'esempio qui sopra è
volutamente errato.
Procuratevi un buon editor di testo e non lasciate spazi bianchi alla fine
delle righe.
2) Spezzare righe lunghe e stringhe
-----------------------------------
Lo stile del codice riguarda la leggibilità e la manutenibilità utilizzando
strumenti comuni.
Il limite delle righe è di 80 colonne e questo e un limite fortemente
desiderato.
Espressioni più lunghe di 80 colonne saranno spezzettate in pezzi più piccoli,
a meno che eccedere le 80 colonne non aiuti ad aumentare la leggibilità senza
nascondere informazioni. I pezzi derivati sono sostanzialmente più corti degli
originali e vengono posizionati più a destra. Lo stesso si applica, nei file
d'intestazione, alle funzioni con una lista di argomenti molto lunga. Tuttavia,
non spezzettate mai le stringhe visibili agli utenti come i messaggi di
printk, questo perché inibireste la possibilità d'utilizzare grep per cercarle.
3) Posizionamento di parentesi graffe e spazi
---------------------------------------------
Un altro problema che s'affronta sempre quando si parla di stile in C è
il posizionamento delle parentesi graffe. Al contrario della dimensione
dell'indentazione, non ci sono motivi tecnici sulla base dei quali scegliere
una strategia di posizionamento o un'altra; ma il modo qui preferito,
come mostratoci dai profeti Kernighan e Ritchie, è quello di
posizionare la parentesi graffa di apertura per ultima sulla riga, e quella
di chiusura per prima su una nuova riga, così:
.. code-block:: c
if (x is true) {
we do y
}
Questo è valido per tutte le espressioni che non siano funzioni (if, switch,
for, while, do). Per esempio:
.. code-block:: c
switch (action) {
case KOBJ_ADD:
return "add";
case KOBJ_REMOVE:
return "remove";
case KOBJ_CHANGE:
return "change";
default:
return NULL;
}
Tuttavia, c'è il caso speciale, le funzioni: queste hanno la parentesi graffa
di apertura all'inizio della riga successiva, quindi:
.. code-block:: c
int function(int x)
{
body of function
}
Eretici da tutto il mondo affermano che questa incoerenza è ...
insomma ... incoerente, ma tutte le persone ragionevoli sanno che (a)
K&R hanno **ragione** e (b) K&R hanno ragione. A parte questo, le funzioni
sono comunque speciali (non potete annidarle in C).
Notate che la graffa di chiusura è da sola su una riga propria, ad
**eccezione** di quei casi dove è seguita dalla continuazione della stessa
espressione, in pratica ``while`` nell'espressione do-while, oppure ``else``
nell'espressione if-else, come questo:
.. code-block:: c
do {
body of do-loop
} while (condition);
e
.. code-block:: c
if (x == y) {
..
} else if (x > y) {
...
} else {
....
}
Motivazione: K&R.
Inoltre, notate che questo posizionamento delle graffe minimizza il numero
di righe vuote senza perdere di leggibilità. In questo modo, dato che le
righe sul vostro schermo non sono una risorsa illimitata (pensate ad uno
terminale con 25 righe), avrete delle righe vuote da riempire con dei
commenti.
Non usate inutilmente le graffe dove una singola espressione è sufficiente.
.. code-block:: c
if (condition)
action();
e
.. code-block:: none
if (condition)
do_this();
else
do_that();
Questo non vale nel caso in cui solo un ramo dell'espressione if-else
contiene una sola espressione; in quest'ultimo caso usate le graffe per
entrambe i rami:
.. code-block:: c
if (condition) {
do_this();
do_that();
} else {
otherwise();
}
Inoltre, usate le graffe se un ciclo contiene più di una semplice istruzione:
.. code-block:: c
while (condition) {
if (test)
do_something();
}
3.1) Spazi
**********
Lo stile del kernel Linux per quanto riguarda gli spazi, dipende
(principalmente) dalle funzioni e dalle parole chiave. Usate una spazio dopo
(quasi tutte) le parole chiave. L'eccezioni più evidenti sono sizeof, typeof,
alignof, e __attribute__, il cui aspetto è molto simile a quello delle
funzioni (e in Linux, solitamente, sono usate con le parentesi, anche se il
linguaggio non lo richiede; come ``sizeof info`` dopo aver dichiarato
``struct fileinfo info``).
Quindi utilizzate uno spazio dopo le seguenti parole chiave::
if, switch, case, for, do, while
ma non con sizeof, typeof, alignof, o __attribute__. Ad esempio,
.. code-block:: c
s = sizeof(struct file);
Non aggiungete spazi attorno (dentro) ad un'espressione fra parentesi. Questo
esempio è **brutto**:
.. code-block:: c
s = sizeof( struct file );
Quando dichiarate un puntatore ad una variabile o una funzione che ritorna un
puntatore, il posto suggerito per l'asterisco ``*`` è adiacente al nome della
variabile o della funzione, e non adiacente al nome del tipo. Esempi:
.. code-block:: c
char *linux_banner;
unsigned long long memparse(char *ptr, char **retptr);
char *match_strdup(substring_t *s);
Usate uno spazio attorno (da ogni parte) alla maggior parte degli operatori
binari o ternari, come i seguenti::
= + - < > * / % | & ^ <= >= == != ? :
ma non mettete spazi dopo gli operatori unari::
& * + - ~ ! sizeof typeof alignof __attribute__ defined
nessuno spazio dopo l'operatore unario suffisso di incremento o decremento::
++ --
nessuno spazio dopo l'operatore unario prefisso di incremento o decremento::
++ --
e nessuno spazio attorno agli operatori dei membri di una struttura ``.`` e
``->``.
Non lasciate spazi bianchi alla fine delle righe. Alcuni editor con
l'indentazione ``furba`` inseriranno gli spazi bianchi all'inizio di una nuova
riga in modo appropriato, quindi potrete scrivere la riga di codice successiva
immediatamente. Tuttavia, alcuni di questi stessi editor non rimuovono
questi spazi bianchi quando non scrivete nulla sulla nuova riga, ad esempio
perché volete lasciare una riga vuota. Il risultato è che finirete per avere
delle righe che contengono spazi bianchi in coda.
Git vi avviserà delle modifiche che aggiungono questi spazi vuoti di fine riga,
e può opzionalmente rimuoverli per conto vostro; tuttavia, se state applicando
una serie di modifiche, questo potrebbe far fallire delle modifiche successive
perché il contesto delle righe verrà cambiato.
4) Assegnare nomi
-----------------
C è un linguaggio spartano, e così dovrebbero esserlo i vostri nomi. Al
contrario dei programmatori Modula-2 o Pascal, i programmatori C non usano
nomi graziosi come ThisVariableIsATemporaryCounter. Un programmatore C
chiamerebbe questa variabile ``tmp``, che è molto più facile da scrivere e
non è una delle più difficili da capire.
TUTTAVIA, nonostante i nomi con notazione mista siano da condannare, i nomi
descrittivi per variabili globali sono un dovere. Chiamare una funzione
globale ``pippo`` è un insulto.
Le variabili GLOBALI (da usare solo se vi servono **davvero**) devono avere
dei nomi descrittivi, così come le funzioni globali. Se avete una funzione
che conta gli utenti attivi, dovreste chiamarla ``count_active_users()`` o
qualcosa di simile, **non** dovreste chiamarla ``cntusr()``.
Codificare il tipo di funzione nel suo nome (quella cosa chiamata notazione
ungherese) fa male al cervello - il compilatore conosce comunque il tipo e
può verificarli, e inoltre confonde i programmatori. Non c'è da
sorprendersi che MicroSoft faccia programmi bacati.
Le variabili LOCALI dovrebbero avere nomi corti, e significativi. Se avete
un qualsiasi contatore di ciclo, probabilmente sarà chiamato ``i``.
Chiamarlo ``loop_counter`` non è produttivo, non ci sono possibilità che
``i`` possa non essere capito. Analogamente, ``tmp`` può essere una qualsiasi
variabile che viene usata per salvare temporaneamente un valore.
Se avete paura di fare casino coi nomi delle vostre variabili locali, allora
avete un altro problema che è chiamato sindrome dello squilibrio dell'ormone
della crescita delle funzioni. Vedere il capitolo 6 (funzioni).
5) Definizione di tipi (typedef)
--------------------------------
Per favore non usate cose come ``vps_t``.
Usare il typedef per strutture e puntatori è uno **sbaglio**. Quando vedete:
.. code-block:: c
vps_t a;
nei sorgenti, cosa significa?
Se, invece, dicesse:
.. code-block:: c
struct virtual_container *a;
potreste dire cos'è effettivamente ``a``.
Molte persone pensano che la definizione dei tipi ``migliori la leggibilità``.
Non molto. Sono utili per:
(a) gli oggetti completamente opachi (dove typedef viene proprio usato allo
scopo di **nascondere** cosa sia davvero l'oggetto).
Esempio: ``pte_t`` eccetera sono oggetti opachi che potete usare solamente
con le loro funzioni accessorie.
.. note::
Gli oggetti opachi e le ``funzioni accessorie`` non sono, di per se,
una bella cosa. Il motivo per cui abbiamo cose come pte_t eccetera è
che davvero non c'è alcuna informazione portabile.
(b) i tipi chiaramente interi, dove l'astrazione **aiuta** ad evitare
confusione sul fatto che siano ``int`` oppure ``long``.
u8/u16/u32 sono typedef perfettamente accettabili, anche se ricadono
nella categoria (d) piuttosto che in questa.
.. note::
Ancora - dev'esserci una **ragione** per farlo. Se qualcosa è
``unsigned long``, non c'è alcun bisogno di avere:
typedef unsigned long myfalgs_t;
ma se ci sono chiare circostanze in cui potrebbe essere ``unsigned int``
e in altre configurazioni ``unsigned long``, allora certamente typedef
è una buona scelta.
(c) quando di rado create letteralmente dei **nuovi** tipi su cui effettuare
verifiche.
(d) circostanze eccezionali, in cui si definiscono nuovi tipi identici a
quelli definiti dallo standard C99.
Nonostante ci voglia poco tempo per abituare occhi e cervello all'uso dei
tipi standard come ``uint32_t``, alcune persone ne obiettano l'uso.
Perciò, i tipi specifici di Linux ``u8/u16/u32/u64`` e i loro equivalenti
con segno, identici ai tipi standard, sono permessi- tuttavia, non sono
obbligatori per il nuovo codice.
(e) i tipi sicuri nella spazio utente.
In alcune strutture dati visibili dallo spazio utente non possiamo
richiedere l'uso dei tipi C99 e nemmeno i vari ``u32`` descritti prima.
Perciò, utilizziamo __u32 e tipi simili in tutte le strutture dati
condivise con lo spazio utente.
Magari ci sono altri casi validi, ma la regola di base dovrebbe essere di
non usare MAI MAI un typedef a meno che non rientri in una delle regole
descritte qui.
In generale, un puntatore, o una struttura a cui si ha accesso diretto in
modo ragionevole, non dovrebbero **mai** essere definite con un typedef.
6) Funzioni
-----------
Le funzioni dovrebbero essere brevi e carine, e fare una cosa sola. Dovrebbero
occupare uno o due schermi di testo (come tutti sappiamo, la dimensione
di uno schermo secondo ISO/ANSI è di 80x24), e fare una cosa sola e bene.
La massima lunghezza di una funziona è inversamente proporzionale alla sua
complessità e al livello di indentazione di quella funzione. Quindi, se avete
una funzione che è concettualmente semplice ma che è implementata come un
lunga (ma semplice) sequenza di caso-istruzione, dove avete molte piccole cose
per molti casi differenti, allora va bene avere funzioni più lunghe.
Comunque, se avete una funzione complessa e sospettate che uno studente
non particolarmente dotato del primo anno delle scuole superiori potrebbe
non capire cosa faccia la funzione, allora dovreste attenervi strettamente ai
limiti. Usate funzioni di supporto con nomi descrittivi (potete chiedere al
compilatore di renderle inline se credete che sia necessario per le
prestazioni, e probabilmente farà un lavoro migliore di quanto avreste potuto
fare voi).
Un'altra misura delle funzioni sono il numero di variabili locali. Non
dovrebbero eccedere le 5-10, oppure state sbagliando qualcosa. Ripensate la
funzione, e dividetela in pezzettini. Generalmente, un cervello umano può
seguire facilmente circa 7 cose diverse, di più lo confonderebbe. Lo sai
d'essere brillante, ma magari vorresti riuscire a capire cos'avevi fatto due
settimane prima.
Nei file sorgenti, separate le funzioni con una riga vuota. Se la funzione è
esportata, la macro **EXPORT** per questa funzione deve seguire immediatamente
la riga della parentesi graffa di chiusura. Ad esempio:
.. code-block:: c
int system_is_up(void)
{
return system_state == SYSTEM_RUNNING;
}
EXPORT_SYMBOL(system_is_up);
Nei prototipi di funzione, includete i nomi dei parametri e i loro tipi.
Nonostante questo non sia richiesto dal linguaggio C, in Linux viene preferito
perché è un modo semplice per aggiungere informazioni importanti per il
lettore.
7) Centralizzare il ritorno delle funzioni
------------------------------------------
Sebbene sia deprecata da molte persone, l'istruzione goto è impiegata di
frequente dai compilatori sotto forma di salto incondizionato.
L'istruzione goto diventa utile quando una funzione ha punti d'uscita multipli
e vanno eseguite alcune procedure di pulizia in comune. Se non è necessario
pulire alcunché, allora ritornate direttamente.
Assegnate un nome all'etichetta di modo che suggerisca cosa fa la goto o
perché esiste. Un esempio di un buon nome potrebbe essere ``out_free_buffer:``
se la goto libera (free) un ``buffer``. Evitate l'uso di nomi GW-BASIC come
``err1:`` ed ``err2:``, potreste doverli riordinare se aggiungete o rimuovete
punti d'uscita, e inoltre rende difficile verificarne la correttezza.
I motivo per usare le goto sono:
- i salti incondizionati sono più facili da capire e seguire
- l'annidamento si riduce
- si evita di dimenticare, per errore, di aggiornare un singolo punto d'uscita
- aiuta il compilatore ad ottimizzare il codice ridondante ;)
.. code-block:: c
int fun(int a)
{
int result = 0;
char *buffer;
buffer = kmalloc(SIZE, GFP_KERNEL);
if (!buffer)
return -ENOMEM;
if (condition1) {
while (loop1) {
...
}
result = 1;
goto out_free_buffer;
}
...
out_free_buffer:
kfree(buffer);
return result;
}
Un baco abbastanza comune di cui bisogna prendere nota è il ``one err bugs``
che assomiglia a questo:
.. code-block:: c
err:
kfree(foo->bar);
kfree(foo);
return ret;
Il baco in questo codice è che in alcuni punti d'uscita la variabile ``foo`` è
NULL. Normalmente si corregge questo baco dividendo la gestione dell'errore in
due parti ``err_free_bar:`` e ``err_free_foo:``:
.. code-block:: c
err_free_bar:
kfree(foo->bar);
err_free_foo:
kfree(foo);
return ret;
Idealmente, dovreste simulare condizioni d'errore per verificare i vostri
percorsi d'uscita.
8) Commenti
-----------
I commenti sono una buona cosa, ma c'è anche il rischio di esagerare. MAI
spiegare COME funziona il vostro codice in un commento: è molto meglio
scrivere il codice di modo che il suo funzionamento sia ovvio, inoltre
spiegare codice scritto male è una perdita di tempo.
Solitamente, i commenti devono dire COSA fa il codice, e non COME lo fa.
Inoltre, cercate di evitare i commenti nel corpo della funzione: se la
funzione è così complessa che dovete commentarla a pezzi, allora dovreste
tornare al punto 6 per un momento. Potete mettere dei piccoli commenti per
annotare o avvisare il lettore circa un qualcosa di particolarmente arguto
(o brutto), ma cercate di non esagerare. Invece, mettete i commenti in
testa alla funzione spiegando alle persone cosa fa, e possibilmente anche
il PERCHÉ.
Per favore, quando commentate una funzione dell'API del kernel usate il
formato kernel-doc. Per maggiori dettagli, leggete i file in
:ref::ref:`Documentation/translations/it_IT/doc-guide/ <it_doc_guide>` e in
``script/kernel-doc``.
Lo stile preferito per i commenti più lunghi (multi-riga) è:
.. code-block:: c
/*
* This is the preferred style for multi-line
* comments in the Linux kernel source code.
* Please use it consistently.
*
* Description: A column of asterisks on the left side,
* with beginning and ending almost-blank lines.
*/
Per i file in net/ e in drivers/net/ lo stile preferito per i commenti
più lunghi (multi-riga) è leggermente diverso.
.. code-block:: c
/* The preferred comment style for files in net/ and drivers/net
* looks like this.
*
* It is nearly the same as the generally preferred comment style,
* but there is no initial almost-blank line.
*/
È anche importante commentare i dati, sia per i tipi base che per tipi
derivati. A questo scopo, dichiarate un dato per riga (niente virgole
per una dichiarazione multipla). Questo vi lascerà spazio per un piccolo
commento per spiegarne l'uso.
9) Avete fatto un pasticcio
---------------------------
Va bene, li facciamo tutti. Probabilmente vi è stato detto dal vostro
aiutante Unix di fiducia che ``GNU emacs`` formatta automaticamente il
codice C per conto vostro, e avete notato che sì, in effetti lo fa, ma che
i modi predefiniti non sono proprio allettanti (infatti, sono peggio che
premere tasti a caso - un numero infinito di scimmie che scrivono in
GNU emacs non faranno mai un buon programma).
Quindi, potete sbarazzarvi di GNU emacs, o riconfigurarlo con valori più
sensati. Per fare quest'ultima cosa, potete appiccicare il codice che
segue nel vostro file .emacs:
.. code-block:: none
(defun c-lineup-arglist-tabs-only (ignored)
"Line up argument lists by tabs, not spaces"
(let* ((anchor (c-langelem-pos c-syntactic-element))
(column (c-langelem-2nd-pos c-syntactic-element))
(offset (- (1+ column) anchor))
(steps (floor offset c-basic-offset)))
(* (max steps 1)
c-basic-offset)))
(add-hook 'c-mode-common-hook
(lambda ()
;; Add kernel style
(c-add-style
"linux-tabs-only"
'("linux" (c-offsets-alist
(arglist-cont-nonempty
c-lineup-gcc-asm-reg
c-lineup-arglist-tabs-only))))))
(add-hook 'c-mode-hook
(lambda ()
(let ((filename (buffer-file-name)))
;; Enable kernel mode for the appropriate files
(when (and filename
(string-match (expand-file-name "~/src/linux-trees")
filename))
(setq indent-tabs-mode t)
(setq show-trailing-whitespace t)
(c-set-style "linux-tabs-only")))))
Questo farà funzionare meglio emacs con lo stile del kernel per i file che
si trovano nella cartella ``~/src/linux-trees``.
Ma anche se doveste fallire nell'ottenere una formattazione sensata in emacs
non tutto è perduto: usate ``indent``.
Ora, ancora, GNU indent ha la stessa configurazione decerebrata di GNU emacs,
ed è per questo che dovete passargli alcune opzioni da riga di comando.
Tuttavia, non è così terribile, perché perfino i creatori di GNU indent
riconoscono l'autorità di K&R (le persone del progetto GNU non sono cattive,
sono solo mal indirizzate sull'argomento), quindi date ad indent le opzioni
``-kr -i8`` (che significa ``K&R, 8 caratteri di indentazione``), o utilizzate
``scripts/Lindent`` che indenterà usando l'ultimo stile.
``indent`` ha un sacco di opzioni, e specialmente quando si tratta di
riformattare i commenti dovreste dare un'occhiata alle pagine man.
Ma ricordatevi: ``indent`` non è un correttore per una cattiva programmazione.
Da notare che potete utilizzare anche ``clang-format`` per aiutarvi con queste
regole, per riformattare rapidamente ad automaticamente alcune parti del
vostro codice, e per revisionare interi file al fine di identificare errori
di stile, refusi e possibilmente anche delle migliorie. È anche utile per
ordinare gli ``#include``, per allineare variabili/macro, per ridistribuire
il testo e altre cose simili.
Per maggiori dettagli, consultate il file
:ref:`Documentation/translations/it_IT/process/clang-format.rst <it_clangformat>`.
10) File di configurazione Kconfig
----------------------------------
Per tutti i file di configurazione Kconfig* che si possono trovare nei
sorgenti, l'indentazione è un po' differente. Le linee dopo un ``config``
sono indentate con un tab, mentre il testo descrittivo è indentato di
ulteriori due spazi. Esempio::
config AUDIT
bool "Auditing support"
depends on NET
help
Enable auditing infrastructure that can be used with another
kernel subsystem, such as SELinux (which requires this for
logging of avc messages output). Does not do system-call
auditing without CONFIG_AUDITSYSCALL.
Le funzionalità davvero pericolose (per esempio il supporto alla scrittura
per certi filesystem) dovrebbero essere dichiarate chiaramente come tali
nella stringa di titolo::
config ADFS_FS_RW
bool "ADFS write support (DANGEROUS)"
depends on ADFS_FS
...
Per la documentazione completa sui file di configurazione, consultate
il documento Documentation/translations/it_IT/kbuild/kconfig-language.txt
11) Strutture dati
------------------
Le strutture dati che hanno una visibilità superiore al contesto del
singolo thread in cui vengono create e distrutte, dovrebbero sempre
avere un contatore di riferimenti. Nel kernel non esiste un
*garbage collector* (e fuori dal kernel i *garbage collector* sono lenti
e inefficienti), questo significa che **dovete** assolutamente avere un
contatore di riferimenti per ogni cosa che usate.
Avere un contatore di riferimenti significa che potete evitare la
sincronizzazione e permette a più utenti di accedere alla struttura dati
in parallelo - e non doversi preoccupare di una struttura dati che
improvvisamente sparisce dalla loro vista perché il loro processo dormiva
o stava facendo altro per un attimo.
Da notare che la sincronizzazione **non** si sostituisce al conteggio dei
riferimenti. La sincronizzazione ha lo scopo di mantenere le strutture
dati coerenti, mentre il conteggio dei riferimenti è una tecnica di gestione
della memoria. Solitamente servono entrambe le cose, e non vanno confuse fra
di loro.
Quando si hanno diverse classi di utenti, le strutture dati possono avere
due livelli di contatori di riferimenti. Il contatore di classe conta
il numero dei suoi utenti, e il contatore globale viene decrementato una
sola volta quando il contatore di classe va a zero.
Un esempio di questo tipo di conteggio dei riferimenti multi-livello può
essere trovato nella gestore della memoria (``struct mm_sturct``: mm_user e
mm_count), e nel codice dei filesystem (``struct super_block``: s_count e
s_active).
Ricordatevi: se un altro thread può trovare la vostra struttura dati, e non
avete un contatore di riferimenti per essa, quasi certamente avete un baco.
12) Macro, enumerati e RTL
---------------------------
I nomi delle macro che definiscono delle costanti e le etichette degli
enumerati sono scritte in maiuscolo.
.. code-block:: c
#define CONSTANT 0x12345
Gli enumerati sono da preferire quando si definiscono molte costanti correlate.
I nomi delle macro in MAIUSCOLO sono preferibili ma le macro che assomigliano
a delle funzioni possono essere scritte in minuscolo.
Generalmente, le funzioni inline sono preferibili rispetto alle macro che
sembrano funzioni.
Le macro che contengono più istruzioni dovrebbero essere sempre chiuse in un
blocco do - while:
.. code-block:: c
#define macrofun(a, b, c) \
do { \
if (a == 5) \
do_this(b, c); \
} while (0)
Cose da evitare quando si usano le macro:
1) le macro che hanno effetti sul flusso del codice:
.. code-block:: c
#define FOO(x) \
do { \
if (blah(x) < 0) \
return -EBUGGERED; \
} while (0)
sono **proprio** una pessima idea. Sembra una chiamata a funzione ma termina
la funzione chiamante; non cercate di rompere il decodificatore interno di
chi legge il codice.
2) le macro che dipendono dall'uso di una variabile locale con un nome magico:
.. code-block:: c
#define FOO(val) bar(index, val)
potrebbe sembrare una bella cosa, ma è dannatamente confusionario quando uno
legge il codice e potrebbe romperlo con una cambiamento che sembra innocente.
3) le macro con argomenti che sono utilizzati come l-values; questo potrebbe
ritorcervisi contro se qualcuno, per esempio, trasforma FOO in una funzione
inline.
4) dimenticatevi delle precedenze: le macro che definiscono espressioni devono
essere racchiuse fra parentesi. State attenti a problemi simili con le macro
parametrizzate.
.. code-block:: c
#define CONSTANT 0x4000
#define CONSTEXP (CONSTANT | 3)
5) collisione nello spazio dei nomi quando si definisce una variabile locale in
una macro che sembra una funzione:
.. code-block:: c
#define FOO(x) \
({ \
typeof(x) ret; \
ret = calc_ret(x); \
(ret); \
})
ret è un nome comune per una variabile locale - __foo_ret difficilmente
andrà in conflitto con una variabile già esistente.
Il manuale di cpp si occupa esaustivamente delle macro. Il manuale di sviluppo
di gcc copre anche l'RTL che viene usato frequentemente nel kernel per il
linguaggio assembler.
13) Visualizzare i messaggi del kernel
--------------------------------------
Agli sviluppatori del kernel piace essere visti come dotti. Tenete un occhio
di riguardo per l'ortografia e farete una belle figura. In inglese, evitate
l'uso di parole mozzate come ``dont``: usate ``do not`` oppure ``don't``.
Scrivete messaggi concisi, chiari, e inequivocabili.
I messaggi del kernel non devono terminare con un punto fermo.
Scrivere i numeri fra parentesi (%d) non migliora alcunché e per questo
dovrebbero essere evitati.
Ci sono alcune macro per la diagnostica in <linux/device.h> che dovreste
usare per assicurarvi che i messaggi vengano associati correttamente ai
dispositivi e ai driver, e che siano etichettati correttamente: dev_err(),
dev_warn(), dev_info(), e così via. Per messaggi che non sono associati ad
alcun dispositivo, <linux/printk.h> definisce pr_info(), pr_warn(), pr_err(),
eccetera.
Tirar fuori un buon messaggio di debug può essere una vera sfida; e quando
l'avete può essere d'enorme aiuto per risolvere problemi da remoto.
Tuttavia, i messaggi di debug sono gestiti differentemente rispetto agli
altri. Le funzioni pr_XXX() stampano incondizionatamente ma pr_debug() no;
essa non viene compilata nella configurazione predefinita, a meno che
DEBUG o CONFIG_DYNAMIC_DEBUG non vengono impostati. Questo vale anche per
dev_dbg() e in aggiunta VERBOSE_DEBUG per aggiungere i messaggi dev_vdbg().
Molti sottosistemi hanno delle opzioni di debug in Kconfig che aggiungono
-DDEBUG nei corrispettivi Makefile, e in altri casi aggiungono #define DEBUG
in specifici file. Infine, quando un messaggio di debug dev'essere stampato
incondizionatamente, per esempio perché siete già in una sezione di debug
racchiusa in #ifdef, potete usare printk(KERN_DEBUG ...).
14) Assegnare memoria
---------------------
Il kernel fornisce i seguenti assegnatori ad uso generico:
kmalloc(), kzalloc(), kmalloc_array(), kcalloc(), vmalloc(), e vzalloc().
Per maggiori informazioni, consultate la documentazione dell'API.
Il modo preferito per passare la dimensione di una struttura è il seguente:
.. code-block:: c
p = kmalloc(sizeof(*p), ...);
La forma alternativa, dove il nome della struttura viene scritto interamente,
peggiora la leggibilità e introduce possibili bachi quando il tipo di
puntatore cambia tipo ma il corrispondente sizeof non viene aggiornato.
Il valore di ritorno è un puntatore void, effettuare un cast su di esso è
ridondante. La conversione fra un puntatore void e un qualsiasi altro tipo
di puntatore è garantito dal linguaggio di programmazione C.
Il modo preferito per assegnare un vettore è il seguente:
.. code-block:: c
p = kmalloc_array(n, sizeof(...), ...);
Il modo preferito per assegnare un vettore a zero è il seguente:
.. code-block:: c
p = kcalloc(n, sizeof(...), ...);
Entrambe verificano la condizione di overflow per la dimensione
d'assegnamento n * sizeof(...), se accade ritorneranno NULL.
15) Il morbo inline
-------------------
Sembra che ci sia la percezione errata che gcc abbia una qualche magica
opzione "rendimi più veloce" chiamata ``inline``. In alcuni casi l'uso di
inline è appropriato (per esempio in sostituzione delle macro, vedi
capitolo 12), ma molto spesso non lo è. L'uso abbondante della parola chiave
inline porta ad avere un kernel più grande, che si traduce in un sistema nel
suo complesso più lento per via di una cache per le istruzioni della CPU più
grande e poi semplicemente perché ci sarà meno spazio disponibile per una
pagina di cache. Pensateci un attimo; una fallimento nella cache causa una
ricerca su disco che può tranquillamente richiedere 5 millisecondi. Ci sono
TANTI cicli di CPU che potrebbero essere usati in questi 5 millisecondi.
Spesso le persone dicono che aggiungere inline a delle funzioni dichiarate
static e utilizzare una sola volta è sempre una scelta vincente perché non
ci sono altri compromessi. Questo è tecnicamente vero ma gcc è in grado di
trasformare automaticamente queste funzioni in inline; i problemi di
manutenzione del codice per rimuovere gli inline quando compare un secondo
utente surclassano il potenziale vantaggio nel suggerire a gcc di fare una
cosa che avrebbe fatto comunque.
16) Nomi e valori di ritorno delle funzioni
-------------------------------------------
Le funzioni possono ritornare diversi tipi di valori, e uno dei più comuni
è quel valore che indica se una funzione ha completato con successo o meno.
Questo valore può essere rappresentato come un codice di errore intero
(-Exxx = fallimento, 0 = successo) oppure un booleano di successo
(0 = fallimento, non-zero = successo).
Mischiare questi due tipi di rappresentazioni è un terreno fertile per
i bachi più insidiosi. Se il linguaggio C includesse una forte distinzione
fra gli interi e i booleani, allora il compilatore potrebbe trovare questi
errori per conto nostro ... ma questo non c'è. Per evitare di imbattersi
in questo tipo di baco, seguite sempre la seguente convenzione::
Se il nome di una funzione è un'azione o un comando imperativo,
essa dovrebbe ritornare un codice di errore intero. Se il nome
è un predicato, la funzione dovrebbe ritornare un booleano di
"successo"
Per esempio, ``add work`` è un comando, e la funzione add_work() ritorna 0
in caso di successo o -EBUSY in caso di fallimento. Allo stesso modo,
``PCI device present`` è un predicato, e la funzione pci_dev_present() ritorna
1 se trova il dispositivo corrispondente con successo, altrimenti 0.
Tutte le funzioni esportate (EXPORT) devono rispettare questa convenzione, e
così dovrebbero anche tutte le funzioni pubbliche. Le funzioni private
(static) possono non seguire questa convenzione, ma è comunque raccomandato
che lo facciano.
Le funzioni il cui valore di ritorno è il risultato di una computazione,
piuttosto che l'indicazione sul successo di tale computazione, non sono
soggette a questa regola. Solitamente si indicano gli errori ritornando un
qualche valore fuori dai limiti. Un tipico esempio è quello delle funzioni
che ritornano un puntatore; queste utilizzano NULL o ERR_PTR come meccanismo
di notifica degli errori.
17) Non reinventate le macro del kernel
---------------------------------------
Il file di intestazione include/linux/kernel.h contiene un certo numero
di macro che dovreste usare piuttosto che implementarne una qualche variante.
Per esempio, se dovete calcolare la lunghezza di un vettore, sfruttate la
macro:
.. code-block:: c
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
Analogamente, se dovete calcolare la dimensione di un qualche campo di una
struttura, usate
.. code-block:: c
#define FIELD_SIZEOF(t, f) (sizeof(((t*)0)->f))
Ci sono anche le macro min() e max() che, se vi serve, effettuano un controllo
rigido sui tipi. Sentitevi liberi di leggere attentamente questo file
d'intestazione per scoprire cos'altro è stato definito che non dovreste
reinventare nel vostro codice.
18) Linee di configurazione degli editor e altre schifezze
-----------------------------------------------------------
Alcuni editor possono interpretare dei parametri di configurazione integrati
nei file sorgenti e indicati con dai marcatori speciali. Per esempio, emacs
interpreta le linee marcate nel seguente modo:
.. code-block:: c
-*- mode: c -*-
O come queste:
.. code-block:: c
/*
Local Variables:
compile-command: "gcc -DMAGIC_DEBUG_FLAG foo.c"
End:
*/
Vim interpreta i marcatori come questi:
.. code-block:: c
/* vim:set sw=8 noet */
Non includete nessuna di queste cose nei file sorgenti. Le persone hanno le
proprie configurazioni personali per l'editor, e i vostri sorgenti non
dovrebbero sovrascrivergliele. Questo vale anche per i marcatori
d'indentazione e di modalità d'uso. Le persone potrebbero aver configurato una
modalità su misura, oppure potrebbero avere qualche altra magia per far
funzionare bene l'indentazione.
19) Inline assembly
---------------------
Nel codice specifico per un'architettura, potreste aver bisogno di codice
*inline assembly* per interfacciarvi col processore o con una funzionalità
specifica della piattaforma. Non esitate a farlo quando è necessario.
Comunque, non usatele gratuitamente quando il C può fare la stessa cosa.
Potete e dovreste punzecchiare l'hardware in C quando è possibile.
Considerate la scrittura di una semplice funzione che racchiude pezzi comuni
di codice assembler piuttosto che continuare a riscrivere delle piccole
varianti. Ricordatevi che l' *inline assembly* può utilizzare i parametri C.
Il codice assembler più corposo e non banale dovrebbe andare nei file .S,
coi rispettivi prototipi C definiti nei file d'intestazione. I prototipi C
per le funzioni assembler dovrebbero usare ``asmlinkage``.
Potreste aver bisogno di marcare il vostro codice asm come volatile al fine
d'evitare che GCC lo rimuova quando pensa che non ci siano effetti collaterali.
Non c'è sempre bisogno di farlo, e farlo quando non serve limita le
ottimizzazioni.
Quando scrivete una singola espressione *inline assembly* contenente più
istruzioni, mettete ognuna di queste istruzioni in una stringa e riga diversa;
ad eccezione dell'ultima stringa/istruzione, ognuna deve terminare con ``\n\t``
al fine di allineare correttamente l'assembler che verrà generato:
.. code-block:: c
asm ("magic %reg1, #42\n\t"
"more_magic %reg2, %reg3"
: /* outputs */ : /* inputs */ : /* clobbers */);
20) Compilazione sotto condizione
---------------------------------
Ovunque sia possibile, non usate le direttive condizionali del preprocessore
(#if, #ifdef) nei file .c; farlo rende il codice difficile da leggere e da
seguire. Invece, usate queste direttive nei file d'intestazione per definire
le funzioni usate nei file .c, fornendo i relativi stub nel caso #else,
e quindi chiamate queste funzioni senza condizioni di preprocessore. Il
compilatore non produrrà alcun codice per le funzioni stub, produrrà gli
stessi risultati, e la logica rimarrà semplice da seguire.
È preferibile non compilare intere funzioni piuttosto che porzioni d'esse o
porzioni d'espressioni. Piuttosto che mettere una ifdef in un'espressione,
fattorizzate parte dell'espressione, o interamente, in funzioni e applicate
la direttiva condizionale su di esse.
Se avete una variabile o funzione che potrebbe non essere usata in alcune
configurazioni, e quindi il compilatore potrebbe avvisarvi circa la definizione
inutilizzata, marcate questa definizione come __maybe_used piuttosto che
racchiuderla in una direttiva condizionale del preprocessore. (Comunque,
se una variabile o funzione è *sempre* inutilizzata, rimuovetela).
Nel codice, dov'è possibile, usate la macro IS_ENABLED per convertire i
simboli Kconfig in espressioni booleane C, e quindi usatela nelle classiche
condizioni C:
.. code-block:: c
if (IS_ENABLED(CONFIG_SOMETHING)) {
...
}
Il compilatore valuterà la condizione come costante (constant-fold), e quindi
includerà o escluderà il blocco di codice come se fosse in un #ifdef, quindi
non ne aumenterà il tempo di esecuzione. Tuttavia, questo permette al
compilatore C di vedere il codice nel blocco condizionale e verificarne la
correttezza (sintassi, tipi, riferimenti ai simboli, eccetera). Quindi
dovete comunque utilizzare #ifdef se il codice nel blocco condizionale esiste
solo quando la condizione è soddisfatta.
Alla fine di un blocco corposo di #if o #ifdef (più di alcune linee),
mettete un commento sulla stessa riga di #endif, annotando la condizione
che termina. Per esempio:
.. code-block:: c
#ifdef CONFIG_SOMETHING
...
#endif /* CONFIG_SOMETHING */
Appendice I) riferimenti
------------------------
The C Programming Language, Second Edition
by Brian W. Kernighan and Dennis M. Ritchie.
Prentice Hall, Inc., 1988.
ISBN 0-13-110362-8 (paperback), 0-13-110370-9 (hardback).
The Practice of Programming
by Brian W. Kernighan and Rob Pike.
Addison-Wesley, Inc., 1999.
ISBN 0-201-61586-X.
Manuali GNU - nei casi in cui sono compatibili con K&R e questo documento -
per indent, cpp, gcc e i suoi dettagli interni, tutto disponibile qui
http://www.gnu.org/manual/
WG14 è il gruppo internazionale di standardizzazione per il linguaggio C,
URL: http://www.open-std.org/JTC1/SC22/WG14/
Kernel process/coding-style.rst, by greg@kroah.com at OLS 2002:
http://www.kroah.com/linux/talks/ols_2002_kernel_codingstyle_talk/html/
.. include:: ../disclaimer-ita.rst
:Original: :ref:`Documentation/process/development-process.rst <development_process_main>`
:Translator: Alessia Mantegazza <amantegazza@vaga.pv.it>
.. _it_development_process_main:
Una guida al processo di sviluppo del Kernel
============================================
Contenuti:
.. toctree::
:numbered:
:maxdepth: 2
1.Intro
2.Process
3.Early-stage
4.Coding
5.Posting
6.Followthrough
7.AdvancedTopics
8.Conclusion
Lo scopo di questo documento è quello di aiutare gli sviluppatori (ed i loro
supervisori) a lavorare con la communità di sviluppo con il minimo sforzo. È
un tentativo di documentare il funzionamento di questa communità in modo che
sia accessibile anche a coloro che non hanno famigliarità con lo sviluppo del
Kernel Linux (o, anzi, con lo sviluppo di software libero in generale). Benchè
qui sia presente del materiale tecnico, questa è una discussione rivolta in
particolare al procedimento, e quindi per essere compreso non richiede una
conoscenza approfondità sullo sviluppo del kernel.
.. include:: ../disclaimer-ita.rst
:Original: :ref:`Documentation/process/email-clients.rst <email_clients>`
.. _it_email_clients:
Informazioni sui programmi di posta elettronica per Linux
=========================================================
.. warning::
TODO ancora da tradurre
.. include:: ../disclaimer-ita.rst
:Original: :ref:`Documentation/process/howto.rst <process_howto>`
:Translator: Alessia Mantegazza <amantegazza@vaga.pv.it>
.. _it_process_howto:
Come partecipare allo sviluppo del kernel Linux
===============================================
Questo è il documento fulcro di quanto trattato sull'argomento.
Esso contiene le istruzioni su come diventare uno sviluppatore
del kernel Linux e spiega come lavorare con la comunità di
sviluppo kernel Linux. Il documento non tratterà alcun aspetto
tecnico relativo alla programmazione del kernel, ma vi aiuterà
indirizzandovi sulla corretta strada.
Se qualsiasi cosa presente in questo documento diventasse obsoleta,
vi preghiamo di inviare le correzioni agli amministratori di questo
file, indicati in fondo al presente documento.
Introduzione
------------
Dunque, volete imparare come diventare sviluppatori del kernel Linux?
O vi è stato detto dal vostro capo, "Vai, scrivi un driver Linux per
questo dispositivo". Bene, l'obbiettivo di questo documento è quello
di insegnarvi tutto ciò che dovete sapere per raggiungere il vostro
scopo descrivendo il procedimento da seguire e consigliandovi
su come lavorare con la comunità. Il documento cercherà, inoltre,
di spiegare alcune delle ragioni per le quali la comunità lavora in un
modo suo particolare.
Il kernel è scritto prevalentemente nel linguaggio C con alcune parti
specifiche dell'architettura scritte in linguaggio assembly.
Per lo sviluppo kernel è richiesta una buona conoscenza del linguaggio C.
L'assembly (di qualsiasi architettura) non è richiesto, a meno che non
pensiate di fare dello sviluppo di basso livello per un'architettura.
Sebbene essi non siano un buon sostituto ad un solido studio del
linguaggio C o ad anni di esperienza, i seguenti libri sono, se non
altro, utili riferimenti:
- "The C Programming Language" di Kernighan e Ritchie [Prentice Hall]
- "Practical C Programming" di Steve Oualline [O'Reilly]
- "C: A Reference Manual" di Harbison and Steele [Prentice Hall]
Il kernel è stato scritto usando GNU C e la toolchain GNU.
Sebbene si attenga allo standard ISO C89, esso utilizza una serie di
estensioni che non sono previste in questo standard. Il kernel è un
ambiente C indipendente, che non ha alcuna dipendenza dalle librerie
C standard, così alcune parti del C standard non sono supportate.
Le divisioni ``long long`` e numeri in virgola mobile non sono permessi.
Qualche volta è difficile comprendere gli assunti che il kernel ha
riguardo gli strumenti e le estensioni in uso, e sfortunatamente non
esiste alcuna indicazione definitiva. Per maggiori informazioni, controllate,
la pagina `info gcc`.
Tenete a mente che state cercando di apprendere come lavorare con la comunità
di sviluppo già esistente. Questo è un gruppo eterogeneo di persone, con alti
standard di codifica, di stile e di procedura. Questi standard sono stati
creati nel corso del tempo basandosi su quanto hanno riscontrato funzionare al
meglio per un squadra così grande e geograficamente sparsa. Cercate di
imparare, in anticipo, il più possibile circa questi standard, poichè ben
spiegati; non aspettatevi che gli altri si adattino al vostro modo di fare
o a quello della vostra azienda.
Note legali
------------
Il codice sorgente del kernel Linux è rilasciato sotto GPL. Siete pregati
di visionare il file, COPYING, presente nella cartella principale dei
sorgente, per eventuali dettagli sulla licenza. Se avete ulteriori domande
sulla licenza, contattate un avvocato, non chiedete sulle liste di discussione
del kernel Linux. Le persone presenti in queste liste non sono avvocati,
e non dovreste basarvi sulle loro dichiarazioni in materia giuridica.
Per domande più frequenti e risposte sulla licenza GPL, guardare:
https://www.gnu.org/licenses/gpl-faq.html
Documentazione
--------------
I sorgenti del kernel Linux hanno una vasta base di documenti che vi
insegneranno come interagire con la comunità del kernel. Quando nuove
funzionalità vengono aggiunte al kernel, si raccomanda di aggiungere anche i
relativi file di documentatione che spiegano come usarele.
Quando un cambiamento del kernel genera anche un cambiamento nell'interfaccia
con lo spazio utente, è raccomandabile che inviate una notifica o una
correzione alle pagine *man* spiegando tale modifica agli amministratori di
queste pagine all'indirizzo mtk.manpages@gmail.com, aggiungendo
in CC la lista linux-api@vger.kernel.org.
Di seguito una lista di file che sono presenti nei sorgente del kernel e che
è richiesto che voi leggiate:
:ref:`Documentation/translations/it_IT/admin-guide/README.rst <it_readme>`
Questo file da una piccola anteprima del kernel Linux e descrive il
minimo necessario per configurare e generare il kernel. I novizi
del kernel dovrebbero iniziare da qui.
:ref:`Documentation/translations/it_IT/process/changes.rst <it_changes>`
Questo file fornisce una lista dei pacchetti software necessari
a compilare e far funzionare il kernel con successo.
:ref:`Documentation/translations/it_IT/process/coding-style.rst <it_codingstyle>`
Questo file descrive lo stile della codifica per il kernel Linux,
e parte delle motivazioni che ne sono alla base. Tutto il nuovo codice deve
seguire le linee guida in questo documento. Molti amministratori
accetteranno patch solo se queste osserveranno tali regole, e molte
persone revisioneranno il codice solo se scritto nello stile appropriato.
:ref:`Documentation/translations/it_IT/process/submitting-patches.rst <it_submittingpatches>` e
:ref:`Documentation/translations/it_IT/process/submitting-drivers.rst <it_submittingdrivers>`
Questo file descrive dettagliatamente come creare ed inviare una patch
con successo, includendo (ma non solo questo):
- Contenuto delle email
- Formato delle email
- I destinatari delle email
Seguire tali regole non garantirà il successo (tutte le patch sono soggette
a controlli realitivi a contenuto e stile), ma non seguirle lo precluderà
sempre.
Altre ottime descrizioni di come creare buone patch sono:
"The Perfect Patch"
https://www.ozlabs.org/~akpm/stuff/tpp.txt
"Linux kernel patch submission format"
http://linux.yyz.us/patch-format.html
:ref:`Documentation/process/translations/it_IT/stable-api-nonsense.rst <it_stable_api_nonsense>`
Questo file descrive la motivazioni sottostanti la conscia decisione di
non avere un API stabile all'interno del kernel, incluso cose come:
- Sottosistemi shim-layers (per compatibilità?)
- Portabilità fra Sistemi Operativi dei driver.
- Attenuare i rapidi cambiamenti all'interno dei sorgenti del kernel
(o prevenirli)
Questo documento è vitale per la comprensione della filosifia alla base
dello sviluppo di Linux ed è molto importante per le persone che arrivano
da esperienze con altri Sistemi Operativi.
:ref:`Documentation/translations/it_IT/admin-guide/security-bugs.rst <it_securitybugs>`
Se ritenete di aver trovato un problema di sicurezza nel kernel Linux,
seguite i passaggi scritti in questo documento per notificarlo agli
sviluppatori del kernel, ed aiutare la risoluzione del problema.
:ref:`Documentation/translations/it_IT/process/management-style.rst <it_managementstyle>`
Questo documento descrive come i manutentori del kernel Linux operano
e la filosofia comune alla base del loro metodo. Questa è un'importante
lettura per tutti coloro che sono nuovi allo sviluppo del kernel (o per
chi è semplicemente curioso), poiché risolve molti dei più comuni
fraintendimenti e confusioni dovuti al particolare comportamento dei
manutentori del kernel.
:ref:`Documentation/translations/it_IT/process/stable-kernel-rules.rst <it_stable_kernel_rules>`
Questo file descrive le regole sulle quali vengono basati i rilasci del
kernel, e spiega cosa fare se si vuole che una modifica venga inserita
in uno di questi rilasci.
:ref:`Documentation/translations/it_IT/process/kernel-docs.rst <it_kernel_docs>`
Una lista di documenti pertinenti allo sviluppo del kernel.
Per favore consultate questa lista se non trovate ciò che cercate nella
documentazione interna del kernel.
:ref:`Documentation/translations/it_IT/process/applying-patches.rst <it_applying_patches>`
Una buona introduzione che descrivere esattamente cos'è una patch e come
applicarla ai differenti rami di sviluppo del kernel.
Il kernel inoltre ha un vasto numero di documenti che possono essere
automaticamente generati dal codice sorgente stesso o da file
ReStructuredText (ReST), come questo. Esso include una completa
descrizione dell'API interna del kernel, e le regole su come gestire la
sincronizzazione (locking) correttamente
Tutte queste tipologie di documenti possono essere generati in PDF o in
HTML utilizzando::
make pdfdocs
make htmldocs
rispettivamente dalla cartella principale dei sorgenti del kernel.
I documenti che impiegano ReST saranno generati nella cartella
Documentation/output.
Questi posso essere generati anche in formato LaTex e ePub con::
make latexdocs
make epubdocs
Diventare uno sviluppatore del kernel
-------------------------------------
Se non sapete nulla sullo sviluppo del kernel Linux, dovreste dare uno
sguardo al progetto *Linux KernelNewbies*:
https://kernelnewbies.org
Esso prevede un'utile lista di discussione dove potete porre più o meno ogni
tipo di quesito relativo ai concetti fondamentali sullo sviluppo del kernel
(assicuratevi di cercare negli archivi, prima di chiedere qualcosa alla
quale è già stata fornita risposta in passato). Esistono inoltre, un canale IRC
che potete usare per formulare domande in tempo reale, e molti documenti utili
che vi faciliteranno nell'apprendimento dello sviluppo del kernel Linux.
Il sito internet contiene informazioni di base circa l'organizzazione del
codice, sottosistemi e progetti attuali (sia interni che esterni a Linux).
Esso descrive, inoltre, informazioni logistiche di base, riguardanti ad esempio
la compilazione del kernel e l'applicazione di una modifica.
Se non sapete dove cominciare, ma volete cercare delle attività dalle quali
partire per partecipare alla comunità di sviluppo, andate al progetto Linux
Kernel Janitor's.
https://kernelnewbies.org/KernelJanitors
È un buon posto da cui iniziare. Esso presenta una lista di problematiche
relativamente semplici da sistemare e pulire all'interno della sorgente del
kernel Linux. Lavorando con gli sviluppatori incaricati di questo progetto,
imparerete le basi per l'inserimento delle vostre modifiche all'interno dei
sorgenti del kernel Linux, e possibilmente, sarete indirizzati al lavoro
successivo da svolgere, se non ne avrete ancora idea.
Prima di apportare una qualsiasi modifica al codice del kernel Linux,
è imperativo comprendere come tale codice funziona. A questo scopo, non c'è
nulla di meglio che leggerlo direttamente (la maggior parte dei bit più
complessi sono ben commentati), eventualmente anche con l'aiuto di strumenti
specializzati. Uno degli strumenti che è particolarmente raccomandato è
il progetto Linux Cross-Reference, che è in grado di presentare codice
sorgente in un formato autoreferenziale ed indicizzato. Un eccellente ed
aggiornata fonte di consultazione del codice del kernel la potete trovare qui:
http://lxr.free-electrons.com/
Il processo di sviluppo
-----------------------
Il processo di sviluppo del kernel Linux si compone di pochi "rami" principali
e di molti altri rami per specifici sottosistemi. Questi rami sono:
- I sorgenti kernel 4.x
- I sorgenti stabili del kernel 4.x.y -stable
- Le modifiche in 4.x -git
- Sorgenti dei sottosistemi del kernel e le loro modifiche
- Il kernel 4.x -next per test d'integrazione
I sorgenti kernel 4.x
~~~~~~~~~~~~~~~~~~~~~
I kernel 4.x sono amministrati da Linus Torvald, e possono essere trovati
su https://kernel.org nella cartella pub/linux/kernel/v4.x/. Il processo
di sviluppo è il seguente:
- Non appena un nuovo kernel viene rilasciato si apre una finestra di due
settimane. Durante questo periodo i manutentori possono proporre a Linus
dei grossi cambiamenti; solitamente i cambiamenti che sono già stati
inseriti nel ramo -next del kernel per alcune settimane. Il modo migliore
per sottoporre dei cambiamenti è attraverso git (lo strumento usato per
gestire i sorgenti del kernel, più informazioni sul sito
https://git-scm.com/) ma anche delle patch vanno bene.
- Al termine delle due settimane un kernel -rc1 viene rilasciato e
l'obbiettivo ora è quello di renderlo il più solido possibile. A questo
punto la maggior parte delle patch dovrebbero correggere un'eventuale
regressione. I bachi che sono sempre esistiti non sono considerabili come
regressioni, quindi inviate questo tipo di cambiamenti solo se sono
importanti. Notate che un intero driver (o filesystem) potrebbe essere
accettato dopo la -rc1 poiché non esistono rischi di una possibile
regressione con tale cambiamento, fintanto che quest'ultimo è
auto-contenuto e non influisce su aree esterne al codice che è stato
aggiunto. git può essere utilizzato per inviare le patch a Linus dopo che
la -rc1 è stata rilasciata, ma è anche necessario inviare le patch ad
una lista di discussione pubblica per un'ulteriore revisione.
- Una nuova -rc viene rilasciata ogni volta che Linus reputa che gli attuali
sorgenti siano in uno stato di salute ragionevolmente adeguato ai test.
L'obiettivo è quello di rilasciare una nuova -rc ogni settimana.
- Il processo continua fino a che il kernel è considerato "pronto"; tale
processo dovrebbe durare circa in 6 settimane.
È utile menzionare quanto scritto da Andrew Morton sulla lista di discussione
kernel-linux in merito ai rilasci del kernel:
*"Nessuno sa quando un kernel verrà rilasciato, poichè questo è
legato allo stato dei bachi e non ad una cronologia preventiva."*
I sorgenti stabili del kernel 4.x.y -stable
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
I kernel con versioni in 3-parti sono "kernel stabili". Essi contengono
correzioni critiche relativamente piccole nell'ambito della sicurezza
oppure significative regressioni scoperte in un dato 4.x kernel.
Questo è il ramo raccomandato per gli utenti che vogliono un kernel recente
e stabile e non sono interessati a dare il proprio contributo alla verifica
delle versioni di sviluppo o sperimentali.
Se non è disponibile alcun kernel 4.x.y., quello più aggiornato e stabile
sarà il kernel 4.x con la numerazione più alta.
4.x.y sono amministrati dal gruppo "stable" <stable@vger.kernel.org>, e sono
rilasciati a seconda delle esigenze. Il normale periodo di rilascio è
approssimativamente di due settimane, ma può essere più lungo se non si
verificano problematiche urgenti. Un problema relativo alla sicurezza, invece,
può determinare un rilascio immediato.
Il file Documentation/process/stable-kernel-rules.rst (nei sorgenti) documenta
quali tipologie di modifiche sono accettate per i sorgenti -stable, e come
avviene il processo di rilascio.
Le modifiche in 4.x -git
~~~~~~~~~~~~~~~~~~~~~~~~
Queste sono istantanee quotidiane del kernel di Linus e sono gestite in
una repositorio git (da qui il nome). Queste modifiche sono solitamente
rilasciate giornalmente e rappresentano l'attuale stato dei sorgenti di
Linus. Queste sono da considerarsi più sperimentali di un -rc in quanto
generate automaticamente senza nemmeno aver dato una rapida occhiata
per verificarne lo stato.
Sorgenti dei sottosistemi del kernel e le loro patch
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
I manutentori dei diversi sottosistemi del kernel --- ed anche molti
sviluppatori di sottosistemi --- mostrano il loro attuale stato di sviluppo
nei loro repositori. In questo modo, altri possono vedere cosa succede nelle
diverse parti del kernel. In aree dove lo sviluppo è rapido, potrebbe essere
chiesto ad uno sviluppatore di basare le proprie modifiche su questi repositori
in modo da evitare i conflitti fra le sottomissioni ed altri lavori in corso
La maggior parte di questi repositori sono git, ma esistono anche altri SCM
in uso, o file di patch pubblicate come una serie quilt.
Gli indirizzi dei repositori di sottosistema sono indicati nel file
MAINTAINERS. Molti di questi posso essere trovati su https://git.kernel.org/.
Prima che una modifica venga inclusa in questi sottosistemi, sarà soggetta ad
una revisione che inizialmente avviene tramite liste di discussione (vedere la
sezione dedicata qui sotto). Per molti sottosistemi del kernel, tale processo
di revisione è monitorato con lo strumento patchwork.
Patchwork offre un'interfaccia web che mostra le patch pubblicate, inclusi i
commenti o le revisioni fatte, e gli amministratori possono indicare le patch
come "in revisione", "accettate", o "rifiutate". Diversi siti Patchwork sono
elencati al sito https://patchwork.kernel.org/.
Il kernel 4.x -next per test d'integrazione
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Prima che gli aggiornamenti dei sottosistemi siano accorpati nel ramo
principale 4.x, sarà necessario un test d'integrazione.
A tale scopo, esiste un repositorio speciale di test nel quale virtualmente
tutti i rami dei sottosistemi vengono inclusi su base quotidiana:
https://git.kernel.org/?p=linux/kernel/git/next/linux-next.git
In questo modo, i kernel -next offrono uno sguardo riassuntivo su quello che
ci si aspetterà essere nel kernel principale nel successivo periodo
d'incorporazione.
Coloro che vorranno fare dei test d'esecuzione del kernel -next sono più che
benvenuti.
Riportare Bug
-------------
https://bugzilla.kernel.org è dove gli sviluppatori del kernel Linux tracciano
i bachi del kernel. Gli utenti sono incoraggiati nel riportare tutti i bachi
che trovano utilizzando questo strumento.
Per maggiori dettagli su come usare il bugzilla del kernel, guardare:
https://bugzilla.kernel.org/page.cgi?id=faq.html
Il file admin-guide/reporting-bugs.rst nella cartella principale del kernel
fornisce un buon modello sul come segnalare un baco nel kernel, e spiega quali
informazioni sono necessarie agli sviluppatori per poter aiutare il
rintracciamento del problema.
Gestire i rapporti sui bug
--------------------------
Uno dei modi migliori per mettere in pratica le vostre capacità di hacking è
quello di riparare bachi riportati da altre persone. Non solo aiuterete a far
diventare il kernel più stabile, ma imparerete a riparare problemi veri dal
mondo ed accrescerete le vostre competenze, e gli altri sviluppatori saranno
al corrente della vostra presenza. Riparare bachi è una delle migliori vie per
acquisire meriti tra gli altri sviluppatori, perchè non a molte persone piace
perdere tempo a sistemare i bachi di altri.
Per lavorare sui rapporti di bachi già riportati, andate su
https://bugzilla.kernel.org.
Liste di discussione
--------------------
Come descritto in molti dei documenti qui sopra, la maggior parte degli
sviluppatori del kernel partecipano alla lista di discussione Linux Kernel.
I dettagli su come iscriversi e disiscriversi dalla lista possono essere
trovati al sito:
http://vger.kernel.org/vger-lists.html#linux-kernel
Ci sono diversi archivi della lista di discussione. Usate un qualsiasi motore
di ricerca per trovarli. Per esempio:
http://dir.gmane.org/gmane.linux.kernel
É caldamente consigliata una ricerca in questi archivi sul tema che volete
sollevare, prima di pubblicarlo sulla lista. Molte cose sono già state
discusse in dettaglio e registrate negli archivi della lista di discussione.
Molti dei sottosistemi del kernel hanno anche una loro lista di discussione
dedicata. Guardate nel file MAINTAINERS per avere una lista delle liste di
discussione e il loro uso.
Molte di queste liste sono gestite su kernel.org. Per informazioni consultate
la seguente pagina:
http://vger.kernel.org/vger-lists.html
Per favore ricordatevi della buona educazione quando utilizzate queste liste.
Sebbene sia un pò dozzinale, il seguente URL contiene alcune semplici linee
guida per interagire con la lista (o con qualsiasi altra lista):
http://www.albion.com/netiquette/
Se diverse persone rispondo alla vostra mail, la lista dei riceventi (copia
conoscenza) potrebbe diventare abbastanza lunga. Non cancellate nessuno dalla
lista di CC: senza un buon motivo, e non rispondete solo all'indirizzo
della lista di discussione. Fateci l'abitudine perché capita spesso di
ricevere la stessa email due volte: una dal mittente ed una dalla lista; e non
cercate di modificarla aggiungendo intestazioni stravaganti, agli altri non
piacerà.
Ricordate di rimanere sempre in argomento e di mantenere le attribuzioni
delle vostre risposte invariate; mantenete il "John Kernelhacker wrote ...:"
in cima alla vostra replica e aggiungete le vostre risposte fra i singoli
blocchi citati, non scrivete all'inizio dell'email.
Se aggiungete patch alla vostra mail, assicuratevi che siano del tutto
leggibili come indicato in Documentation/process/submitting-patches.rst.
Gli sviluppatori kernel non vogliono avere a che fare con allegati o patch
compresse; vogliono invece poter commentare le righe dei vostri cambiamenti,
il che può funzionare solo in questo modo.
Assicuratevi di utilizzare un gestore di mail che non alterì gli spazi ed i
caratteri. Un ottimo primo test è quello di inviare a voi stessi una mail e
cercare di sottoporre la vostra stessa patch. Se non funziona, sistemate il
vostro programma di posta, o cambiatelo, finché non funziona.
Ed infine, per favore ricordatevi di mostrare rispetto per gli altri
sottoscriventi.
Lavorare con la comunità
------------------------
L'obiettivo di questa comunità è quello di fornire il miglior kernel possibile.
Quando inviate una modifica che volete integrare, sarà valutata esclusivamente
dal punto di vista tecnico. Quindi, cosa dovreste aspettarvi?
- critiche
- commenti
- richieste di cambiamento
- richieste di spiegazioni
- nulla
Ricordatevi che questo fa parte dell'integrazione della vostra modifica
all'interno del kernel. Dovete essere in grado di accettare le critiche,
valutarle a livello tecnico ed eventualmente rielaborare nuovamente le vostre
modifiche o fornire delle chiare e concise motivazioni per le quali le
modifiche suggerite non dovrebbero essere fatte.
Se non riceverete risposte, aspettate qualche giorno e riprovate ancora,
qualche volta le cose si perdono nell'enorme mucchio di email.
Cosa non dovreste fare?
- aspettarvi che la vostra modifica venga accettata senza problemi
- mettervi sulla difensiva
- ignorare i commenti
- sottomettere nuovamente la modifica senza fare nessuno dei cambiamenti
richiesti
In una comunità che è alla ricerca delle migliori soluzioni tecniche possibili,
ci saranno sempre opinioni differenti sull'utilità di una modifica.
Siate cooperativi e vogliate adattare la vostra idea in modo che sia inserita
nel kernel. O almeno vogliate dimostrare che la vostra idea vale.
Ricordatevi, sbagliare è accettato fintanto che siate disposti a lavorare verso
una soluzione che è corretta.
È normale che le risposte alla vostra prima modifica possa essere
semplicemente una lista con dozzine di cose che dovreste correggere.
Questo **non** implica che la vostra patch non sarà accettata, e questo
**non** è contro di voi personalmente.
Semplicemente correggete tutte le questioni sollevate contro la vostra modifica
ed inviatela nuovamente.
Differenze tra la comunità del kernel e le strutture aziendali
--------------------------------------------------------------
La comunità del kernel funziona diversamente rispetto a molti ambienti di
sviluppo aziendali. Qui di seguito una lista di cose che potete provare a
fare per evitare problemi:
Cose da dire riguardanti le modifiche da voi proposte:
- "Questo risolve più problematiche."
- "Questo elimina 2000 stringhe di codice."
- "Qui una modifica che spiega cosa sto cercando di fare."
- "L'ho testato su 5 diverse architetture.."
- "Qui una serie di piccole modifiche che.."
- "Questo aumenta le prestazioni di macchine standard..."
Cose che dovreste evitare di dire:
- "Lo abbiamo fatto in questo modo in AIX/ptx/Solaris, di conseguenza
deve per forza essere giusto..."
- "Ho fatto questo per 20 anni, quindi.."
- "Questo è richiesto dalla mia Azienda per far soldi"
- "Questo è per la linea di prodotti della nostra Azienda"
- "Ecco il mio documento di design di 1000 pagine che descrive ciò che ho
in mente"
- "Ci ho lavorato per 6 mesi..."
- "Ecco una patch da 5000 righe che.."
- "Ho riscritto il pasticcio attuale, ed ecco qua.."
- "Ho una scadenza, e questa modifica ha bisogno di essere approvata ora"
Un'altra cosa nella quale la comunità del kernel si differenzia dai più
classici ambienti di ingegneria del software è la natura "senza volto" delle
interazioni umane. Uno dei benefici dell'uso delle email e di irc come forma
primordiale di comunicazione è l'assenza di discriminazione basata su genere e
razza. L'ambienti di lavoro Linux accetta donne e minoranze perchè tutto quello
che sei è un indirizzo email. Aiuta anche l'aspetto internazionale nel
livellare il terreno di gioco perchè non è possibile indovinare il genere
basandosi sul nome di una persona. Un uomo può chiamarsi Andrea ed una donna
potrebbe chiamarsi Pat. Gran parte delle donne che hanno lavorato al kernel
Linux e che hanno espresso una personale opinione hanno avuto esperienze
positive.
La lingua potrebbe essere un ostacolo per quelle persone che non si trovano
a loro agio con l'inglese. Una buona padronanza del linguaggio può essere
necessaria per esporre le proprie idee in maniera appropiata all'interno
delle liste di discussione, quindi è consigliabile che rileggiate le vostre
email prima di inviarle in modo da essere certi che abbiano senso in inglese.
Spezzare le vostre modifiche
----------------------------
La comunità del kernel Linux non accetta con piacere grossi pezzi di codice
buttati lì tutti in una volta. Le modifiche necessitano di essere
adeguatamente presentate, discusse, e suddivise in parti più piccole ed
indipendenti. Questo è praticamente l'esatto opposto di quello che le
aziende fanno solitamente. La vostra proposta dovrebbe, inoltre, essere
presentata prestissimo nel processo di sviluppo, così che possiate ricevere
un riscontro su quello che state facendo. Lasciate che la comunità
senta che state lavorando con loro, e che non li stiate sfruttando come
discarica per le vostre aggiunte. In ogni caso, non inviate 50 email nello
stesso momento in una lista di discussione, il più delle volte la vostra serie
di modifiche dovrebbe essere più piccola.
I motivi per i quali dovreste frammentare le cose sono i seguenti:
1) Piccole modifiche aumentano le probabilità che vengano accettate,
altrimenti richiederebbe troppo tempo o sforzo nel verificarne
la correttezza. Una modifica di 5 righe può essere accettata da un
manutentore con a mala pena una seconda occhiata. Invece, una modifica da
500 linee può richiedere ore di rilettura per verificarne la correttezza
(il tempo necessario è esponenzialmente proporzionale alla dimensione della
modifica, o giù di lì)
Piccole modifiche sono inoltre molto facili da debuggare quando qualcosa
non va. È molto più facile annullare le modifiche una per una che
dissezionare una patch molto grande dopo la sua sottomissione (e rompere
qualcosa).
2) È importante non solo inviare piccole modifiche, ma anche riscriverle e
semplificarle (o più semplicemente ordinarle) prima di sottoporle.
Qui un'analogia dello sviluppatore kernel Al Viro:
*"Pensate ad un insegnante di matematica che corregge il compito
di uno studente (di matematica). L'insegnante non vuole vedere le
prove e gli errori commessi dallo studente prima che arrivi alla
soluzione. Vuole vedere la risposta più pulita ed elegante
possibile. Un buono studente lo sa, e non presenterebbe mai le
proprie bozze prima prima della soluzione finale"*
*"Lo stesso vale per lo sviluppo del kernel. I manutentori ed i
revisori non vogliono vedere il procedimento che sta dietro al
problema che uno sta risolvendo. Vogliono vedere una soluzione
semplice ed elegante."*
Può essere una vera sfida il saper mantenere l'equilibrio fra una presentazione
elegante della vostra soluzione, lavorare insieme ad una comunità e dibattere
su un lavoro incompleto. Pertanto è bene entrare presto nel processo di
revisione per migliorare il vostro lavoro, ma anche per riuscire a tenere le
vostre modifiche in pezzettini che potrebbero essere già accettate, nonostante
la vostra intera attività non lo sia ancora.
In fine, rendetevi conto che non è accettabile inviare delle modifiche
incomplete con la promessa che saranno "sistemate dopo".
Giustificare le vostre modifiche
--------------------------------
Insieme alla frammentazione delle vostre modifiche, è altrettanto importante
permettere alla comunità Linux di capire perché dovrebbero accettarle.
Nuove funzionalità devono essere motivate come necessarie ed utili.
Documentare le vostre modifiche
-------------------------------
Quando inviate le vostre modifiche, fate particolare attenzione a quello che
scrivete nella vostra email. Questa diventerà il *ChangeLog* per la modifica,
e sarà visibile a tutti per sempre. Dovrebbe descrivere la modifica nella sua
interezza, contenendo:
- perchè la modifica è necessaria
- l'approccio d'insieme alla patch
- dettagli supplementari
- risultati dei test
Per maggiori dettagli su come tutto ciò dovrebbe apparire, riferitevi alla
sezione ChangeLog del documento:
"The Perfect Patch"
http://www.ozlabs.org/~akpm/stuff/tpp.txt
A volte tutto questo è difficile da realizzare. Il perfezionamento di queste
pratiche può richiedere anni (eventualmente). È un processo continuo di
miglioramento che richiede molta pazienza e determinazione. Ma non mollate,
si può fare. Molti lo hanno fatto prima, ed ognuno ha dovuto iniziare dove
siete voi ora.
----------
Grazie a Paolo Ciarrocchi che ha permesso che la sezione "Development Process"
(https://lwn.net/Articles/94386/) fosse basata sui testi da lui scritti, ed a
Randy Dunlap e Gerrit Huizenga per la lista di cose che dovreste e non
dovreste dire. Grazie anche a Pat Mochel, Hanna Linder, Randy Dunlap,
Kay Sievers, Vojtech Pavlik, Jan Kara, Josh Boyer, Kees Cook, Andrew Morton,
Andi Kleen, Vadim Lobanov, Jesper Juhl, Adrian Bunk, Keri Harris, Frans Pop,
David A. Wheeler, Junio Hamano, Michael Kerrisk, e Alex Shepard per le
loro revisioni, commenti e contributi. Senza il loro aiuto, questo documento
non sarebbe stato possibile.
Manutentore: Greg Kroah-Hartman <greg@kroah.com>
.. raw:: latex
\renewcommand\thesection*
\renewcommand\thesubsection*
.. include:: ../disclaimer-ita.rst
:Original: :ref:`Documentation/process/index.rst <process_index>`
:Translator: Federico Vaga <federico.vaga@vaga.pv.it>
.. _it_process_index:
Lavorare con la comunità di sviluppo del kernel
===============================================
Quindi volete diventare sviluppatori del kernel? Benvenuti! C'è molto da
imparare sul lato tecnico del kernel, ma è anche importante capire come
funziona la nostra comunità. Leggere questi documenti renderà più facile
l'accettazione delle vostre modifiche con il minimo sforzo.
Di seguito le guide che ogni sviluppatore dovrebbe leggere.
.. toctree::
:maxdepth: 1
howto
code-of-conduct
development-process
submitting-patches
coding-style
maintainer-pgp-guide
email-clients
kernel-enforcement-statement
kernel-driver-statement
Poi ci sono altre guide sulla comunità che sono di interesse per molti
degli sviluppatori:
.. toctree::
:maxdepth: 1
changes
submitting-drivers
stable-api-nonsense
management-style
stable-kernel-rules
submit-checklist
kernel-docs
Ed infine, qui ci sono alcune guide più tecniche che son state messe qua solo
perché non si è trovato un posto migliore.
.. toctree::
:maxdepth: 1
applying-patches
adding-syscalls
magic-number
volatile-considered-harmful
clang-format
.. only:: subproject and html
Indices
=======
* :ref:`genindex`
.. include:: ../disclaimer-ita.rst
:Original: :ref:`Documentation/process/kernel-docs.rst <kernel_docs>`
.. _it_kernel_docs:
Indice di documenti per le persone interessate a capire e/o scrivere per il kernel Linux
========================================================================================
.. warning::
TODO ancora da tradurre
.. include:: ../disclaimer-ita.rst
:Original: :ref:`Documentation/process/kernel-driver-statement.rst <process_statement_driver>`
:Translator: Federico Vaga <federico.vaga@vaga.pv.it>
.. _it_process_statement_driver:
Dichiarazioni sui driver per il kernel
======================================
Presa di posizione sui moduli per il kernel Linux
-------------------------------------------------
Noi, i sottoscritti sviluppatori del kernel, consideriamo pericoloso
o indesiderato qualsiasi modulo o driver per il kernel Linux di tipo
*a sorgenti chiusi* (*closed-source*). Ripetutamente, li abbiamo
trovati deleteri per gli utenti Linux, le aziende, ed in generale
l'ecosistema Linux. Questi moduli impediscono l'apertura, la stabilità,
la flessibilità, e la manutenibilità del modello di sviluppo di Linux
e impediscono ai loro utenti di beneficiare dell'esperienza dalla
comunità Linux. I fornitori che distribuiscono codice a sorgenti chiusi
obbligano i propri utenti a rinunciare ai principali vantaggi di Linux
o a cercarsi nuovi fornitori.
Perciò, al fine di sfruttare i vantaggi che codice aperto ha da offrire,
come l'abbattimento dei costi e un supporto condiviso, spingiamo i
fornitori ad adottare una politica di supporto ai loro clienti Linux
che preveda il rilascio dei sorgenti per il kernel.
Parliamo solo per noi stessi, e non per una qualsiasi azienda per la
quale lavoriamo oggi, o abbiamo lavorato in passato, o lavoreremo in
futuro.
- Dave Airlie
- Nick Andrew
- Jens Axboe
- Ralf Baechle
- Felipe Balbi
- Ohad Ben-Cohen
- Muli Ben-Yehuda
- Jiri Benc
- Arnd Bergmann
- Thomas Bogendoerfer
- Vitaly Bordug
- James Bottomley
- Josh Boyer
- Neil Brown
- Mark Brown
- David Brownell
- Michael Buesch
- Franck Bui-Huu
- Adrian Bunk
- François Cami
- Ralph Campbell
- Luiz Fernando N. Capitulino
- Mauro Carvalho Chehab
- Denis Cheng
- Jonathan Corbet
- Glauber Costa
- Alan Cox
- Magnus Damm
- Ahmed S. Darwish
- Robert P. J. Day
- Hans de Goede
- Arnaldo Carvalho de Melo
- Helge Deller
- Jean Delvare
- Mathieu Desnoyers
- Sven-Thorsten Dietrich
- Alexey Dobriyan
- Daniel Drake
- Alex Dubov
- Randy Dunlap
- Michael Ellerman
- Pekka Enberg
- Jan Engelhardt
- Mark Fasheh
- J. Bruce Fields
- Larry Finger
- Jeremy Fitzhardinge
- Mike Frysinger
- Kumar Gala
- Robin Getz
- Liam Girdwood
- Jan-Benedict Glaw
- Thomas Gleixner
- Brice Goglin
- Cyrill Gorcunov
- Andy Gospodarek
- Thomas Graf
- Krzysztof Halasa
- Harvey Harrison
- Stephen Hemminger
- Michael Hennerich
- Tejun Heo
- Benjamin Herrenschmidt
- Kristian Høgsberg
- Henrique de Moraes Holschuh
- Marcel Holtmann
- Mike Isely
- Takashi Iwai
- Olof Johansson
- Dave Jones
- Jesper Juhl
- Matthias Kaehlcke
- Kenji Kaneshige
- Jan Kara
- Jeremy Kerr
- Russell King
- Olaf Kirch
- Roel Kluin
- Hans-Jürgen Koch
- Auke Kok
- Peter Korsgaard
- Jiri Kosina
- Aaro Koskinen
- Mariusz Kozlowski
- Greg Kroah-Hartman
- Michael Krufky
- Aneesh Kumar
- Clemens Ladisch
- Christoph Lameter
- Gunnar Larisch
- Anders Larsen
- Grant Likely
- John W. Linville
- Yinghai Lu
- Tony Luck
- Pavel Machek
- Matt Mackall
- Paul Mackerras
- Roland McGrath
- Patrick McHardy
- Kyle McMartin
- Paul Menage
- Thierry Merle
- Eric Miao
- Akinobu Mita
- Ingo Molnar
- James Morris
- Andrew Morton
- Paul Mundt
- Oleg Nesterov
- Luca Olivetti
- S.Çağlar Onur
- Pierre Ossman
- Keith Owens
- Venkatesh Pallipadi
- Nick Piggin
- Nicolas Pitre
- Evgeniy Polyakov
- Richard Purdie
- Mike Rapoport
- Sam Ravnborg
- Gerrit Renker
- Stefan Richter
- David Rientjes
- Luis R. Rodriguez
- Stefan Roese
- Francois Romieu
- Rami Rosen
- Stephen Rothwell
- Maciej W. Rozycki
- Mark Salyzyn
- Yoshinori Sato
- Deepak Saxena
- Holger Schurig
- Amit Shah
- Yoshihiro Shimoda
- Sergei Shtylyov
- Kay Sievers
- Sebastian Siewior
- Rik Snel
- Jes Sorensen
- Alexey Starikovskiy
- Alan Stern
- Timur Tabi
- Hirokazu Takata
- Eliezer Tamir
- Eugene Teo
- Doug Thompson
- FUJITA Tomonori
- Dmitry Torokhov
- Marcelo Tosatti
- Steven Toth
- Theodore Tso
- Matthias Urlichs
- Geert Uytterhoeven
- Arjan van de Ven
- Ivo van Doorn
- Rik van Riel
- Wim Van Sebroeck
- Hans Verkuil
- Horst H. von Brand
- Dmitri Vorobiev
- Anton Vorontsov
- Daniel Walker
- Johannes Weiner
- Harald Welte
- Matthew Wilcox
- Dan J. Williams
- Darrick J. Wong
- David Woodhouse
- Chris Wright
- Bryan Wu
- Rafael J. Wysocki
- Herbert Xu
- Vlad Yasevich
- Peter Zijlstra
- Bartlomiej Zolnierkiewicz
.. include:: ../disclaimer-ita.rst
:Original: :ref:`Documentation/process/kernel-enforcement-statement.rst <process_statement_kernel>`
.. _it_process_statement_kernel:
Applicazione della licenza sul kernel Linux
===========================================
.. warning::
TODO ancora da tradurre
.. include:: ../disclaimer-ita.rst
:Original: :ref:`Documentation/process/magic-numbers.rst <magicnumbers>`
:Translator: Federico Vaga <federico.vaga@vaga.pv.it>
.. _it_magicnumbers:
I numeri magici di Linux
========================
Questo documento è un registro dei numeri magici in uso. Quando
aggiungete un numero magico ad una struttura, dovreste aggiungerlo anche
a questo documento; la cosa migliore è che tutti i numeri magici usati
dalle varie strutture siano unici.
È **davvero** un'ottima idea proteggere le strutture dati del kernel con
dei numeri magici. Questo vi permette in fase d'esecuzione di (a) verificare
se una struttura è stata malmenata, o (b) avete passato a una procedura la
struttura errata. Quest'ultimo è molto utile - particolarmente quando si passa
una struttura dati tramite un puntatore void \*. Il codice tty, per esempio,
effettua questa operazione con regolarità passando avanti e indietro le
strutture specifiche per driver e discipline.
Per utilizzare un numero magico, dovete dichiararlo all'inizio della struttura
dati, come di seguito::
struct tty_ldisc {
int magic;
...
};
Per favore, seguite questa direttiva quando aggiungerete migliorie al kernel!
Mi ha risparmiato un numero illimitato di ore di debug, specialmente nei casi
più ostici dove si è andati oltre la dimensione di un vettore e la struttura
dati che lo seguiva in memoria è stata sovrascritta. Seguendo questa
direttiva, questi casi vengono identificati velocemente e in sicurezza.
Registro dei cambiamenti::
Theodore Ts'o
31 Mar 94
La tabella magica è aggiornata a Linux 2.1.55.
Michael Chastain
<mailto:mec@shout.net>
22 Sep 1997
Ora dovrebbe essere aggiornata a Linux 2.1.112. Dato che
siamo in un momento di congelamento delle funzionalità
(*feature freeze*) è improbabile che qualcosa cambi prima
della versione 2.2.x. Le righe sono ordinate secondo il
campo numero.
Krzysztof G. Baranowski
<mailto: kgb@knm.org.pl>
29 Jul 1998
Aggiornamento della tabella a Linux 2.5.45. Giusti nel congelamento
delle funzionalità ma è comunque possibile che qualche nuovo
numero magico s'intrufoli prima del kernel 2.6.x.
Petr Baudis
<pasky@ucw.cz>
03 Nov 2002
Aggiornamento della tabella magica a Linux 2.5.74.
Fabian Frederick
<ffrederick@users.sourceforge.net>
09 Jul 2003
===================== ================ ======================== ==========================================
Nome magico Numero Struttura File
===================== ================ ======================== ==========================================
PG_MAGIC 'P' pg_{read,write}_hdr ``include/linux/pg.h``
CMAGIC 0x0111 user ``include/linux/a.out.h``
MKISS_DRIVER_MAGIC 0x04bf mkiss_channel ``drivers/net/mkiss.h``
HDLC_MAGIC 0x239e n_hdlc ``drivers/char/n_hdlc.c``
APM_BIOS_MAGIC 0x4101 apm_user ``arch/x86/kernel/apm_32.c``
CYCLADES_MAGIC 0x4359 cyclades_port ``include/linux/cyclades.h``
DB_MAGIC 0x4442 fc_info ``drivers/net/iph5526_novram.c``
DL_MAGIC 0x444d fc_info ``drivers/net/iph5526_novram.c``
FASYNC_MAGIC 0x4601 fasync_struct ``include/linux/fs.h``
FF_MAGIC 0x4646 fc_info ``drivers/net/iph5526_novram.c``
ISICOM_MAGIC 0x4d54 isi_port ``include/linux/isicom.h``
PTY_MAGIC 0x5001 ``drivers/char/pty.c``
PPP_MAGIC 0x5002 ppp ``include/linux/if_pppvar.h``
SERIAL_MAGIC 0x5301 async_struct ``include/linux/serial.h``
SSTATE_MAGIC 0x5302 serial_state ``include/linux/serial.h``
SLIP_MAGIC 0x5302 slip ``drivers/net/slip.h``
STRIP_MAGIC 0x5303 strip ``drivers/net/strip.c``
X25_ASY_MAGIC 0x5303 x25_asy ``drivers/net/x25_asy.h``
SIXPACK_MAGIC 0x5304 sixpack ``drivers/net/hamradio/6pack.h``
AX25_MAGIC 0x5316 ax_disp ``drivers/net/mkiss.h``
TTY_MAGIC 0x5401 tty_struct ``include/linux/tty.h``
MGSL_MAGIC 0x5401 mgsl_info ``drivers/char/synclink.c``
TTY_DRIVER_MAGIC 0x5402 tty_driver ``include/linux/tty_driver.h``
MGSLPC_MAGIC 0x5402 mgslpc_info ``drivers/char/pcmcia/synclink_cs.c``
TTY_LDISC_MAGIC 0x5403 tty_ldisc ``include/linux/tty_ldisc.h``
USB_SERIAL_MAGIC 0x6702 usb_serial ``drivers/usb/serial/usb-serial.h``
FULL_DUPLEX_MAGIC 0x6969 ``drivers/net/ethernet/dec/tulip/de2104x.c``
USB_BLUETOOTH_MAGIC 0x6d02 usb_bluetooth ``drivers/usb/class/bluetty.c``
RFCOMM_TTY_MAGIC 0x6d02 ``net/bluetooth/rfcomm/tty.c``
USB_SERIAL_PORT_MAGIC 0x7301 usb_serial_port ``drivers/usb/serial/usb-serial.h``
CG_MAGIC 0x00090255 ufs_cylinder_group ``include/linux/ufs_fs.h``
RPORT_MAGIC 0x00525001 r_port ``drivers/char/rocket_int.h``
LSEMAGIC 0x05091998 lse ``drivers/fc4/fc.c``
GDTIOCTL_MAGIC 0x06030f07 gdth_iowr_str ``drivers/scsi/gdth_ioctl.h``
RIEBL_MAGIC 0x09051990 ``drivers/net/atarilance.c``
NBD_REQUEST_MAGIC 0x12560953 nbd_request ``include/linux/nbd.h``
RED_MAGIC2 0x170fc2a5 (any) ``mm/slab.c``
BAYCOM_MAGIC 0x19730510 baycom_state ``drivers/net/baycom_epp.c``
ISDN_X25IFACE_MAGIC 0x1e75a2b9 isdn_x25iface_proto_data ``drivers/isdn/isdn_x25iface.h``
ECP_MAGIC 0x21504345 cdkecpsig ``include/linux/cdk.h``
LSOMAGIC 0x27091997 lso ``drivers/fc4/fc.c``
LSMAGIC 0x2a3b4d2a ls ``drivers/fc4/fc.c``
WANPIPE_MAGIC 0x414C4453 sdla_{dump,exec} ``include/linux/wanpipe.h``
CS_CARD_MAGIC 0x43525553 cs_card ``sound/oss/cs46xx.c``
LABELCL_MAGIC 0x4857434c labelcl_info_s ``include/asm/ia64/sn/labelcl.h``
ISDN_ASYNC_MAGIC 0x49344C01 modem_info ``include/linux/isdn.h``
CTC_ASYNC_MAGIC 0x49344C01 ctc_tty_info ``drivers/s390/net/ctctty.c``
ISDN_NET_MAGIC 0x49344C02 isdn_net_local_s ``drivers/isdn/i4l/isdn_net_lib.h``
SAVEKMSG_MAGIC2 0x4B4D5347 savekmsg ``arch/*/amiga/config.c``
CS_STATE_MAGIC 0x4c4f4749 cs_state ``sound/oss/cs46xx.c``
SLAB_C_MAGIC 0x4f17a36d kmem_cache ``mm/slab.c``
COW_MAGIC 0x4f4f4f4d cow_header_v1 ``arch/um/drivers/ubd_user.c``
I810_CARD_MAGIC 0x5072696E i810_card ``sound/oss/i810_audio.c``
TRIDENT_CARD_MAGIC 0x5072696E trident_card ``sound/oss/trident.c``
ROUTER_MAGIC 0x524d4157 wan_device [in ``wanrouter.h`` pre 3.9]
SAVEKMSG_MAGIC1 0x53415645 savekmsg ``arch/*/amiga/config.c``
GDA_MAGIC 0x58464552 gda ``arch/mips/include/asm/sn/gda.h``
RED_MAGIC1 0x5a2cf071 (any) ``mm/slab.c``
EEPROM_MAGIC_VALUE 0x5ab478d2 lanai_dev ``drivers/atm/lanai.c``
HDLCDRV_MAGIC 0x5ac6e778 hdlcdrv_state ``include/linux/hdlcdrv.h``
PCXX_MAGIC 0x5c6df104 channel ``drivers/char/pcxx.h``
KV_MAGIC 0x5f4b565f kernel_vars_s ``arch/mips/include/asm/sn/klkernvars.h``
I810_STATE_MAGIC 0x63657373 i810_state ``sound/oss/i810_audio.c``
TRIDENT_STATE_MAGIC 0x63657373 trient_state ``sound/oss/trident.c``
M3_CARD_MAGIC 0x646e6f50 m3_card ``sound/oss/maestro3.c``
FW_HEADER_MAGIC 0x65726F66 fw_header ``drivers/atm/fore200e.h``
SLOT_MAGIC 0x67267321 slot ``drivers/hotplug/cpqphp.h``
SLOT_MAGIC 0x67267322 slot ``drivers/hotplug/acpiphp.h``
LO_MAGIC 0x68797548 nbd_device ``include/linux/nbd.h``
OPROFILE_MAGIC 0x6f70726f super_block ``drivers/oprofile/oprofilefs.h``
M3_STATE_MAGIC 0x734d724d m3_state ``sound/oss/maestro3.c``
VMALLOC_MAGIC 0x87654320 snd_alloc_track ``sound/core/memory.c``
KMALLOC_MAGIC 0x87654321 snd_alloc_track ``sound/core/memory.c``
PWC_MAGIC 0x89DC10AB pwc_device ``drivers/usb/media/pwc.h``
NBD_REPLY_MAGIC 0x96744668 nbd_reply ``include/linux/nbd.h``
ENI155_MAGIC 0xa54b872d midway_eprom ``drivers/atm/eni.h``
CODA_MAGIC 0xC0DAC0DA coda_file_info ``fs/coda/coda_fs_i.h``
DPMEM_MAGIC 0xc0ffee11 gdt_pci_sram ``drivers/scsi/gdth.h``
YAM_MAGIC 0xF10A7654 yam_port ``drivers/net/hamradio/yam.c``
CCB_MAGIC 0xf2691ad2 ccb ``drivers/scsi/ncr53c8xx.c``
QUEUE_MAGIC_FREE 0xf7e1c9a3 queue_entry ``drivers/scsi/arm/queue.c``
QUEUE_MAGIC_USED 0xf7e1cc33 queue_entry ``drivers/scsi/arm/queue.c``
HTB_CMAGIC 0xFEFAFEF1 htb_class ``net/sched/sch_htb.c``
NMI_MAGIC 0x48414d4d455201 nmi_s ``arch/mips/include/asm/sn/nmi.h``
===================== ================ ======================== ==========================================
Da notare che ci sono anche dei numeri magici specifici per driver nel
*sound memory management*. Consultate ``include/sound/sndmagic.h`` per una
lista completa. Molti driver audio OSS hanno i loro numeri magici costruiti a
partire dall'identificativo PCI della scheda audio - nemmeno questi sono
elencati in questo file.
Il file-system HFS è un altro grande utilizzatore di numeri magici - potete
trovarli qui ``fs/hfs/hfs.h``.
.. include:: ../disclaimer-ita.rst
:Original: :ref:`Documentation/process/maintainer-pgp-guide.rst <pgpguide>`
.. _it_pgpguide:
========================================
Guida a PGP per i manutentori del kernel
========================================
.. warning::
TODO ancora da tradurre
.. include:: ../disclaimer-ita.rst
:Original: :ref:`Documentation/process/management-style.rst <managementstyle>`
.. _it_managementstyle:
Tipo di gestione del kernel Linux
=================================
.. warning::
TODO ancora da tradurre
.. include:: ../disclaimer-ita.rst
:Original: :ref:`Documentation/process/stable-api-nonsense.rst <stable_api_nonsense>`
.. _it_stable_api_nonsense:
L'interfaccia dei driver per il kernel Linux
============================================
.. warning::
TODO ancora da tradurre
.. include:: ../disclaimer-ita.rst
:Original: :ref:`Documentation/process/stable-kernel-rules.rst <stable_kernel_rules>`
.. _it_stable_kernel_rules:
Tutto quello che volevate sapere sui rilasci -stable di Linux
==============================================================
.. warning::
TODO ancora da tradurre
.. include:: ../disclaimer-ita.rst
:Original: :ref:`Documentation/process/submit-checklist.rst <submitchecklist>`
.. _it_submitchecklist:
Lista delle cose da fare per inviare una modifica al kernel Linux
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. warning::
TODO ancora da tradurre
.. include:: ../disclaimer-ita.rst
:Original: :ref:`Documentation/process/submitting-drivers.rst <submittingdrivers>`
.. _it_submittingdrivers:
Sottomettere driver per il kernel Linux
=======================================
.. warning::
TODO ancora da tradurre
.. include:: ../disclaimer-ita.rst
:Original: :ref:`Documentation/process/submitting-patches.rst <submittingpatches>`
.. _it_submittingpatches:
Sottomettere modifiche: la guida essenziale per vedere il vostro codice nel kernel
==================================================================================
.. warning::
TODO ancora da tradurre
.. include:: ../disclaimer-ita.rst
:Original: :ref:`Documentation/process/volatile-considered-harmful.rst <volatile_considered_harmful>`
:Translator: Federico Vaga <federico.vaga@vaga.pv.it>
.. _it_volatile_considered_harmful:
Perché la parola chiave "volatile" non dovrebbe essere usata
------------------------------------------------------------
Spesso i programmatori C considerano volatili quelle variabili che potrebbero
essere cambiate al di fuori dal thread di esecuzione corrente; come risultato,
a volte saranno tentati dall'utilizzare *volatile* nel kernel per le
strutture dati condivise. In altre parole, gli è stato insegnato ad usare
*volatile* come una variabile atomica di facile utilizzo, ma non è così.
L'uso di *volatile* nel kernel non è quasi mai corretto; questo documento ne
descrive le ragioni.
Il punto chiave da capire su *volatile* è che il suo scopo è quello di
sopprimere le ottimizzazioni, che non è quasi mai quello che si vuole.
Nel kernel si devono proteggere le strutture dati condivise contro accessi
concorrenti e indesiderati: questa è un'attività completamente diversa.
Il processo di protezione contro gli accessi concorrenti indesiderati eviterà
anche la maggior parte dei problemi relativi all'ottimizzazione in modo più
efficiente.
Come *volatile*, le primitive del kernel che rendono sicuro l'accesso ai dati
(spinlock, mutex, barriere di sincronizzazione, ecc) sono progettate per
prevenire le ottimizzazioni indesiderate. Se vengono usate opportunamente,
non ci sarà bisogno di utilizzare *volatile*. Se vi sembra che *volatile* sia
comunque necessario, ci dev'essere quasi sicuramente un baco da qualche parte.
In un pezzo di codice kernel scritto a dovere, *volatile* può solo servire a
rallentare le cose.
Considerate questo tipico blocco di codice kernel::
spin_lock(&the_lock);
do_something_on(&shared_data);
do_something_else_with(&shared_data);
spin_unlock(&the_lock);
Se tutto il codice seguisse le regole di sincronizzazione, il valore di un
dato condiviso non potrebbe cambiare inaspettatamente mentre si trattiene un
lock. Un qualsiasi altro blocco di codice che vorrà usare quel dato rimarrà
in attesa del lock. Gli spinlock agiscono come barriere di sincronizzazione
- sono stati esplicitamente scritti per agire così - il che significa che gli
accessi al dato condiviso non saranno ottimizzati. Quindi il compilatore
potrebbe pensare di sapere cosa ci sarà nel dato condiviso ma la chiamata
spin_lock(), che agisce come una barriera di sincronizzazione, gli imporrà di
dimenticarsi tutto ciò che sapeva su di esso.
Se il dato condiviso fosse stato dichiarato come *volatile*, la
sincronizzazione rimarrebbe comunque necessaria. Ma verrà impedito al
compilatore di ottimizzare gli accessi al dato anche _dentro_ alla sezione
critica, dove sappiamo che in realtà nessun altro può accedervi. Mentre si
trattiene un lock, il dato condiviso non è *volatile*. Quando si ha a che
fare con dei dati condivisi, un'opportuna sincronizzazione rende inutile
l'uso di *volatile* - anzi potenzialmente dannoso.
L'uso di *volatile* fu originalmente pensato per l'accesso ai registri di I/O
mappati in memoria. All'interno del kernel, l'accesso ai registri, dovrebbe
essere protetto dai lock, ma si potrebbe anche desiderare che il compilatore
non "ottimizzi" l'accesso ai registri all'interno di una sezione critica.
Ma, all'interno del kernel, l'accesso alla memoria di I/O viene sempre fatto
attraverso funzioni d'accesso; accedere alla memoria di I/O direttamente
con i puntatori è sconsigliato e non funziona su tutte le architetture.
Queste funzioni d'accesso sono scritte per evitare ottimizzazioni indesiderate,
quindi, di nuovo, *volatile* è inutile.
Un'altra situazione dove qualcuno potrebbe essere tentato dall'uso di
*volatile*, è nel caso in cui il processore è in un'attesa attiva sul valore
di una variabile. Il modo giusto di fare questo tipo di attesa è il seguente::
while (my_variable != what_i_want)
cpu_relax();
La chiamata cpu_relax() può ridurre il consumo di energia del processore
o cedere il passo ad un processore hyperthreaded gemello; funziona anche come
una barriera per il compilatore, quindi, ancora una volta, *volatile* non è
necessario. Ovviamente, tanto per puntualizzare, le attese attive sono
generalmente un atto antisociale.
Ci sono comunque alcune rare situazioni dove l'uso di *volatile* nel kernel
ha senso:
- Le funzioni d'accesso sopracitate potrebbero usare *volatile* su quelle
architetture che supportano l'accesso diretto alla memoria di I/O.
In pratica, ogni chiamata ad una funzione d'accesso diventa una piccola
sezione critica a se stante, e garantisce che l'accesso avvenga secondo
le aspettative del programmatore.
- I codice *inline assembly* che fa cambiamenti nella memoria, ma che non
ha altri effetti espliciti, rischia di essere rimosso da GCC. Aggiungere
la parola chiave *volatile* a questo codice ne previene la rimozione.
- La variabile jiffies è speciale in quanto assume un valore diverso ogni
volta che viene letta ma può essere lette senza alcuna sincronizzazione.
Quindi jiffies può essere *volatile*, ma l'aggiunta ad altre variabili di
questo è sconsigliata. Jiffies è considerata uno "stupido retaggio"
(parole di Linus) in questo contesto; correggerla non ne varrebbe la pena e
causerebbe più problemi.
- I puntatori a delle strutture dati in una memoria coerente che potrebbe
essere modificata da dispositivi di I/O può, a volte, essere legittimamente
*volatile*. Un esempio pratico può essere quello di un adattatore di rete
che utilizza un puntatore ad un buffer circolare, questo viene cambiato
dall'adattatore per indicare quali descrittori sono stati processati.
Per la maggior parte del codice, nessuna delle giustificazioni sopracitate può
essere considerata. Di conseguenza, l'uso di *volatile* è probabile che venga
visto come un baco e porterà a verifiche aggiuntive. Gli sviluppatori tentati
dall'uso di *volatile* dovrebbero fermarsi e pensare a cosa vogliono davvero
ottenere.
Le modifiche che rimuovono variabili *volatile* sono generalmente ben accette
- purché accompagnate da una giustificazione che dimostri che i problemi di
concorrenza siano stati opportunamente considerati.
Riferimenti
===========
[1] http://lwn.net/Articles/233481/
[2] http://lwn.net/Articles/233482/
Crediti
=======
Impulso e ricerca originale di Randy Dunlap
Scritto da Jonathan Corbet
Migliorato dai commenti di Satyam Sharma, Johannes Stezenbach, Jesper
Juhl, Heikki Orsila, H. Peter Anvin, Philipp Hahn, e Stefan Richter.
...@@ -2,7 +2,9 @@ ...@@ -2,7 +2,9 @@
Linux Memory Management Documentation Linux Memory Management Documentation
===================================== =====================================
This is a collection of documents about Linux memory management (mm) subsystem. This is a collection of documents about the Linux memory management (mm)
subsystem. If you are looking for advice on simply allocating memory,
see the :ref:`memory-allocation`.
User guides for MM features User guides for MM features
=========================== ===========================
......
...@@ -486,48 +486,47 @@ static __always_inline void *kmalloc_large(size_t size, gfp_t flags) ...@@ -486,48 +486,47 @@ static __always_inline void *kmalloc_large(size_t size, gfp_t flags)
* kmalloc is the normal method of allocating memory * kmalloc is the normal method of allocating memory
* for objects smaller than page size in the kernel. * for objects smaller than page size in the kernel.
* *
* The @flags argument may be one of: * The @flags argument may be one of the GFP flags defined at
* include/linux/gfp.h and described at
* :ref:`Documentation/core-api/mm-api.rst <mm-api-gfp-flags>`
* *
* %GFP_USER - Allocate memory on behalf of user. May sleep. * The recommended usage of the @flags is described at
* :ref:`Documentation/core-api/memory-allocation.rst <memory-allocation>`
* *
* %GFP_KERNEL - Allocate normal kernel ram. May sleep. * Below is a brief outline of the most useful GFP flags
* *
* %GFP_ATOMIC - Allocation will not sleep. May use emergency pools. * %GFP_KERNEL
* For example, use this inside interrupt handlers. * Allocate normal kernel ram. May sleep.
* *
* %GFP_HIGHUSER - Allocate pages from high memory. * %GFP_NOWAIT
* Allocation will not sleep.
* *
* %GFP_NOIO - Do not do any I/O at all while trying to get memory. * %GFP_ATOMIC
* Allocation will not sleep. May use emergency pools.
* *
* %GFP_NOFS - Do not make any fs calls while trying to get memory. * %GFP_HIGHUSER
* * Allocate memory from high memory on behalf of user.
* %GFP_NOWAIT - Allocation will not sleep.
*
* %__GFP_THISNODE - Allocate node-local memory only.
*
* %GFP_DMA - Allocation suitable for DMA.
* Should only be used for kmalloc() caches. Otherwise, use a
* slab created with SLAB_DMA.
* *
* Also it is possible to set different flags by OR'ing * Also it is possible to set different flags by OR'ing
* in one or more of the following additional @flags: * in one or more of the following additional @flags:
* *
* %__GFP_HIGH - This allocation has high priority and may use emergency pools. * %__GFP_HIGH
* This allocation has high priority and may use emergency pools.
* *
* %__GFP_NOFAIL - Indicate that this allocation is in no way allowed to fail * %__GFP_NOFAIL
* Indicate that this allocation is in no way allowed to fail
* (think twice before using). * (think twice before using).
* *
* %__GFP_NORETRY - If memory is not immediately available, * %__GFP_NORETRY
* If memory is not immediately available,
* then give up at once. * then give up at once.
* *
* %__GFP_NOWARN - If allocation fails, don't issue any warnings. * %__GFP_NOWARN
* If allocation fails, don't issue any warnings.
* *
* %__GFP_RETRY_MAYFAIL - Try really hard to succeed the allocation but fail * %__GFP_RETRY_MAYFAIL
* Try really hard to succeed the allocation but fail
* eventually. * eventually.
*
* There are other flags available as well, but these are not intended
* for general use, and so are not documented here. For a full list of
* potential flags, always refer to linux/gfp.h.
*/ */
static __always_inline void *kmalloc(size_t size, gfp_t flags) static __always_inline void *kmalloc(size_t size, gfp_t flags)
{ {
......
...@@ -406,8 +406,9 @@ static struct kmem_cache *create_cache(const char *name, ...@@ -406,8 +406,9 @@ static struct kmem_cache *create_cache(const char *name,
goto out; goto out;
} }
/* /**
* kmem_cache_create_usercopy - Create a cache. * kmem_cache_create_usercopy - Create a cache with a region suitable
* for copying to userspace
* @name: A string which is used in /proc/slabinfo to identify this cache. * @name: A string which is used in /proc/slabinfo to identify this cache.
* @size: The size of objects to be created in this cache. * @size: The size of objects to be created in this cache.
* @align: The required alignment for the objects. * @align: The required alignment for the objects.
...@@ -416,7 +417,6 @@ static struct kmem_cache *create_cache(const char *name, ...@@ -416,7 +417,6 @@ static struct kmem_cache *create_cache(const char *name,
* @usersize: Usercopy region size * @usersize: Usercopy region size
* @ctor: A constructor for the objects. * @ctor: A constructor for the objects.
* *
* Returns a ptr to the cache on success, NULL on failure.
* Cannot be called within a interrupt, but can be interrupted. * Cannot be called within a interrupt, but can be interrupted.
* The @ctor is run when new pages are allocated by the cache. * The @ctor is run when new pages are allocated by the cache.
* *
...@@ -425,12 +425,14 @@ static struct kmem_cache *create_cache(const char *name, ...@@ -425,12 +425,14 @@ static struct kmem_cache *create_cache(const char *name,
* %SLAB_POISON - Poison the slab with a known test pattern (a5a5a5a5) * %SLAB_POISON - Poison the slab with a known test pattern (a5a5a5a5)
* to catch references to uninitialised memory. * to catch references to uninitialised memory.
* *
* %SLAB_RED_ZONE - Insert `Red' zones around the allocated memory to check * %SLAB_RED_ZONE - Insert `Red` zones around the allocated memory to check
* for buffer overruns. * for buffer overruns.
* *
* %SLAB_HWCACHE_ALIGN - Align the objects in this cache to a hardware * %SLAB_HWCACHE_ALIGN - Align the objects in this cache to a hardware
* cacheline. This can be beneficial if you're counting cycles as closely * cacheline. This can be beneficial if you're counting cycles as closely
* as davem. * as davem.
*
* Return: a pointer to the cache on success, NULL on failure.
*/ */
struct kmem_cache * struct kmem_cache *
kmem_cache_create_usercopy(const char *name, kmem_cache_create_usercopy(const char *name,
...@@ -514,6 +516,31 @@ kmem_cache_create_usercopy(const char *name, ...@@ -514,6 +516,31 @@ kmem_cache_create_usercopy(const char *name,
} }
EXPORT_SYMBOL(kmem_cache_create_usercopy); EXPORT_SYMBOL(kmem_cache_create_usercopy);
/**
* kmem_cache_create - Create a cache.
* @name: A string which is used in /proc/slabinfo to identify this cache.
* @size: The size of objects to be created in this cache.
* @align: The required alignment for the objects.
* @flags: SLAB flags
* @ctor: A constructor for the objects.
*
* Cannot be called within a interrupt, but can be interrupted.
* The @ctor is run when new pages are allocated by the cache.
*
* The flags are
*
* %SLAB_POISON - Poison the slab with a known test pattern (a5a5a5a5)
* to catch references to uninitialised memory.
*
* %SLAB_RED_ZONE - Insert `Red` zones around the allocated memory to check
* for buffer overruns.
*
* %SLAB_HWCACHE_ALIGN - Align the objects in this cache to a hardware
* cacheline. This can be beneficial if you're counting cycles as closely
* as davem.
*
* Return: a pointer to the cache on success, NULL on failure.
*/
struct kmem_cache * struct kmem_cache *
kmem_cache_create(const char *name, unsigned int size, unsigned int align, kmem_cache_create(const char *name, unsigned int size, unsigned int align,
slab_flags_t flags, void (*ctor)(void *)) slab_flags_t flags, void (*ctor)(void *))
......
...@@ -212,7 +212,7 @@ my $anon_struct_union = 0; ...@@ -212,7 +212,7 @@ my $anon_struct_union = 0;
my $type_constant = '\b``([^\`]+)``\b'; my $type_constant = '\b``([^\`]+)``\b';
my $type_constant2 = '\%([-_\w]+)'; my $type_constant2 = '\%([-_\w]+)';
my $type_func = '(\w+)\(\)'; my $type_func = '(\w+)\(\)';
my $type_param = '\@(\w*(\.\w+)*(\.\.\.)?)'; my $type_param = '\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)';
my $type_fp_param = '\@(\w+)\(\)'; # Special RST handling for func ptr params my $type_fp_param = '\@(\w+)\(\)'; # Special RST handling for func ptr params
my $type_env = '(\$\w+)'; my $type_env = '(\$\w+)';
my $type_enum = '\&(enum\s*([_\w]+))'; my $type_enum = '\&(enum\s*([_\w]+))';
...@@ -1062,7 +1062,7 @@ sub dump_struct($$) { ...@@ -1062,7 +1062,7 @@ sub dump_struct($$) {
my $x = shift; my $x = shift;
my $file = shift; my $file = shift;
if ($x =~ /(struct|union)\s+(\w+)\s*\{(.*)\}/) { if ($x =~ /(struct|union)\s+(\w+)\s*\{(.*)\}(\s*(__packed|__aligned|__attribute__\s*\(\([a-z0-9,_\s\(\)]*\)\)))*/) {
my $decl_type = $1; my $decl_type = $1;
$declaration_name = $2; $declaration_name = $2;
my $members = $3; my $members = $3;
...@@ -1073,8 +1073,9 @@ sub dump_struct($$) { ...@@ -1073,8 +1073,9 @@ sub dump_struct($$) {
# strip comments: # strip comments:
$members =~ s/\/\*.*?\*\///gos; $members =~ s/\/\*.*?\*\///gos;
# strip attributes # strip attributes
$members =~ s/__attribute__\s*\(\([a-z,_\*\s\(\)]*\)\)//i; $members =~ s/\s*__attribute__\s*\(\([a-z0-9,_\*\s\(\)]*\)\)//gi;
$members =~ s/__aligned\s*\([^;]*\)//gos; $members =~ s/\s*__aligned\s*\([^;]*\)//gos;
$members =~ s/\s*__packed\s*//gos;
$members =~ s/\s*CRYPTO_MINALIGN_ATTR//gos; $members =~ s/\s*CRYPTO_MINALIGN_ATTR//gos;
# replace DECLARE_BITMAP # replace DECLARE_BITMAP
$members =~ s/DECLARE_BITMAP\s*\(([^,)]+),\s*([^,)]+)\)/unsigned long $1\[BITS_TO_LONGS($2)\]/gos; $members =~ s/DECLARE_BITMAP\s*\(([^,)]+),\s*([^,)]+)\)/unsigned long $1\[BITS_TO_LONGS($2)\]/gos;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment