mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-09-02 18:52:59 -04:00
Compare commits
99 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
89a312991d | ||
|
|
9a58da8005 | ||
|
|
786262be60 | ||
|
|
68c90773f4 | ||
|
|
abdf623ddb | ||
|
|
c3b510de42 | ||
|
|
93e257938a | ||
|
|
bf1079577a | ||
|
|
a7f25dc23f | ||
|
|
068c35b5d0 | ||
|
|
068e5a0bc5 | ||
|
|
4881a13521 | ||
|
|
84590dbb9f | ||
|
|
b6ee92d7f7 | ||
|
|
2376135986 | ||
|
|
3f4b7d1a49 | ||
|
|
8e35992021 | ||
|
|
4aa2c106ae | ||
|
|
a8603b52b3 | ||
|
|
ba9572bc43 | ||
|
|
a506290f59 | ||
|
|
f25e93768f | ||
|
|
feca5e70fc | ||
|
|
c61dc7b1b4 | ||
|
|
73f860489e | ||
|
|
d12168084c | ||
|
|
5c944895a9 | ||
|
|
edcd92df5e | ||
|
|
db2267b27c | ||
|
|
c0cd3fc682 | ||
|
|
399aa12450 | ||
|
|
fe39cd9d48 | ||
|
|
1dac61e2c2 | ||
|
|
6949939586 | ||
|
|
d83a21bb26 | ||
|
|
448ba0ae65 | ||
|
|
01261a6fa4 | ||
|
|
7811701d6a | ||
|
|
0923ae9f23 | ||
|
|
cd03ce4950 | ||
|
|
88972e3575 | ||
|
|
1519dc88c8 | ||
|
|
53676a5e28 | ||
|
|
cf9c8aaea0 | ||
|
|
6586705bc2 | ||
|
|
87d347a8c8 | ||
|
|
6c37d7e074 | ||
|
|
2bf404b1bd | ||
|
|
909a3f0e9d | ||
|
|
cca061dccf | ||
|
|
9e8581a090 | ||
|
|
e10b8b4931 | ||
|
|
412f89fb39 | ||
|
|
be9c45bdb1 | ||
|
|
4642259374 | ||
|
|
1b91724d0b | ||
|
|
e2f62a9744 | ||
|
|
885435535b | ||
|
|
6b855256eb | ||
|
|
0510346e8e | ||
|
|
4bc67fc800 | ||
|
|
2d829cc767 | ||
|
|
ae28561189 | ||
|
|
4e07cd78e1 | ||
|
|
750a361bfc | ||
|
|
7fc296b379 | ||
|
|
b9b541e70d | ||
|
|
41a28c865d | ||
|
|
a8c6daab4b | ||
|
|
72c5ae18eb | ||
|
|
7e2f2a377a | ||
|
|
0c893d170f | ||
|
|
4fb8d6379d | ||
|
|
6eca8f94d8 | ||
|
|
5f01293930 | ||
|
|
d6eac38681 | ||
|
|
9987979189 | ||
|
|
66cc9dec91 | ||
|
|
1713cc6b0e | ||
|
|
377c68b988 | ||
|
|
1f43c17ce5 | ||
|
|
e492449e39 | ||
|
|
8ac9136d79 | ||
|
|
a118a5e2f1 | ||
|
|
0361f576ec | ||
|
|
7b348d0d40 | ||
|
|
540b79536f | ||
|
|
f4008169bd | ||
|
|
141556543c | ||
|
|
0c4775d3a7 | ||
|
|
36a6518e74 | ||
|
|
e09afa69e3 | ||
|
|
11f5fd3607 | ||
|
|
d4486fc309 | ||
|
|
4c3da04827 | ||
|
|
97dfcb871b | ||
|
|
07897bdf7a | ||
|
|
90cfd27df4 | ||
|
|
749c510248 |
4
CREDITS
4
CREDITS
@@ -1512,6 +1512,10 @@ N: Andy Gross
|
||||
E: agross@kernel.org
|
||||
D: Qualcomm SoC subsystem and drivers
|
||||
|
||||
N: Mark Gross
|
||||
E: markgross@kernel.org
|
||||
D: x86/mellanox platform maintenance and various x86 specific drivers
|
||||
|
||||
N: Grant Grundler
|
||||
E: grantgrundler@gmail.com
|
||||
W: http://obmouse.sourceforge.net/
|
||||
|
||||
@@ -1130,9 +1130,9 @@ policy and the underlying scheduler. From the point of view of the cpu controlle
|
||||
processes can be categorized as follows:
|
||||
|
||||
* Processes under the fair-class scheduler
|
||||
* Processes under a BPF scheduler with the ``cgroup_set_weight`` callback
|
||||
* Processes under a BPF scheduler with the corresponding ``cgroup_set_*`` callback
|
||||
* Everything else: ``SCHED_{FIFO,RR,DEADLINE}`` and processes under a BPF scheduler
|
||||
without the ``cgroup_set_weight`` callback
|
||||
without the corresponding ``cgroup_set_*`` callback
|
||||
|
||||
For details on when a process is under the fair-class scheduler or a BPF scheduler,
|
||||
check out :ref:`Documentation/scheduler/sched-ext.rst <sched-ext>`.
|
||||
@@ -1223,7 +1223,9 @@ will be referred to. All time durations are in microseconds.
|
||||
$PERIOD duration. "max" for $MAX indicates no limit. If only
|
||||
one number is written, $MAX is updated.
|
||||
|
||||
This file affects only processes under the fair-class scheduler.
|
||||
This file affects only processes under the fair-class scheduler and a BPF
|
||||
scheduler with the ``cgroup_set_bandwidth`` callback depending on what
|
||||
the callback actually does.
|
||||
|
||||
cpu.max.burst
|
||||
A read-write single value file which exists on non-root
|
||||
@@ -1231,7 +1233,9 @@ will be referred to. All time durations are in microseconds.
|
||||
|
||||
The burst in the range [0, $MAX].
|
||||
|
||||
This file affects only processes under the fair-class scheduler.
|
||||
This file affects only processes under the fair-class scheduler and a BPF
|
||||
scheduler with the ``cgroup_set_bandwidth`` callback depending on what
|
||||
the callback actually does.
|
||||
|
||||
cpu.pressure
|
||||
A read-write nested-keyed file.
|
||||
@@ -1283,7 +1287,9 @@ will be referred to. All time durations are in microseconds.
|
||||
own relative priorities, but the cgroup itself will be treated as
|
||||
very low priority relative to its peers.
|
||||
|
||||
This file affects only processes under the fair-class scheduler.
|
||||
This file affects only processes under the fair-class scheduler and a BPF
|
||||
scheduler with the ``cgroup_set_idle`` callback depending on what the
|
||||
callback actually does.
|
||||
|
||||
Memory
|
||||
------
|
||||
@@ -3064,7 +3070,7 @@ resources (res_a and res_b) are registered then:
|
||||
change in this file generates a file modified event. All fields in
|
||||
this file are hierarchical.
|
||||
|
||||
max
|
||||
<res>.max
|
||||
The number of times the cgroup's resource usage was
|
||||
about to go over the max boundary.
|
||||
|
||||
|
||||
@@ -230,7 +230,7 @@ optional. The following modified excerpt is from
|
||||
|
||||
void BPF_STRUCT_OPS(simple_exit, struct scx_exit_info *ei)
|
||||
{
|
||||
exit_type = ei->type;
|
||||
exit_type = ei->kind;
|
||||
}
|
||||
|
||||
SEC(".struct_ops")
|
||||
@@ -242,6 +242,21 @@ optional. The following modified excerpt is from
|
||||
.name = "simple",
|
||||
};
|
||||
|
||||
Scheduler-Dependent Knobs
|
||||
-------------------------
|
||||
|
||||
The fair-class scheduler enforces CPU controller settings such as
|
||||
``cpu.max``, ``cpu.weight`` and ``cpu.idle``. For sched_ext tasks, the
|
||||
scheduler core communicates these settings to the BPF scheduler
|
||||
through ``ops.cgroup_init()`` and reports subsequent changes through
|
||||
the corresponding ``ops.cgroup_set_*()`` callbacks. Similarly, per-task
|
||||
nice changes are converted to weights and reported through
|
||||
``ops.set_weight()``.
|
||||
|
||||
Each BPF scheduler is responsible for implementing the scheduling
|
||||
semantics of these settings and may choose to ignore them. Consult the
|
||||
loaded scheduler's documentation before relying on these controls.
|
||||
|
||||
Dispatch Queues
|
||||
---------------
|
||||
|
||||
|
||||
21
MAINTAINERS
21
MAINTAINERS
@@ -9387,9 +9387,8 @@ S: Maintained
|
||||
F: drivers/edac/octeon_edac*
|
||||
|
||||
EDAC-CAVIUM THUNDERX
|
||||
M: Robert Richter <rric@kernel.org>
|
||||
L: linux-edac@vger.kernel.org
|
||||
S: Odd Fixes
|
||||
S: Orphan
|
||||
F: drivers/edac/thunderx_edac*
|
||||
|
||||
EDAC-CORE
|
||||
@@ -9416,9 +9415,8 @@ S: Supported
|
||||
F: drivers/edac/dmc520_edac.c
|
||||
|
||||
EDAC-E752X
|
||||
M: Mark Gross <markgross@kernel.org>
|
||||
L: linux-edac@vger.kernel.org
|
||||
S: Maintained
|
||||
S: Orphan
|
||||
F: drivers/edac/e752x_edac.c
|
||||
|
||||
EDAC-E7XXX
|
||||
@@ -9493,9 +9491,8 @@ S: Maintained
|
||||
F: drivers/edac/igen6_edac.c
|
||||
|
||||
EDAC-MPC85XX
|
||||
M: Johannes Thumshirn <morbidrsa@gmail.com>
|
||||
L: linux-edac@vger.kernel.org
|
||||
S: Maintained
|
||||
S: Orphan
|
||||
F: drivers/edac/mpc85xx_edac.[ch]
|
||||
|
||||
EDAC-NPCM
|
||||
@@ -13283,7 +13280,6 @@ F: drivers/crypto/intel/keembay/ocs-aes.h
|
||||
|
||||
INTEL KEEM BAY OCS ECC CRYPTO DRIVER
|
||||
M: Prabhjot Khurana <prabhjot.khurana@intel.com>
|
||||
M: Mark Gross <mgross@linux.intel.com>
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/crypto/intel,keembay-ocs-ecc.yaml
|
||||
F: drivers/crypto/intel/keembay/Kconfig
|
||||
@@ -14240,8 +14236,6 @@ F: tools/testing/selftests/
|
||||
KERNEL SMB3 SERVER (KSMBD)
|
||||
M: Namjae Jeon <linkinjeon@kernel.org>
|
||||
M: Namjae Jeon <linkinjeon@samba.org>
|
||||
M: Steve French <smfrench@gmail.com>
|
||||
M: Steve French <sfrench@samba.org>
|
||||
R: Sergey Senozhatsky <senozhatsky@chromium.org>
|
||||
R: Tom Talpey <tom@talpey.com>
|
||||
R: ChenXiaoSong <chenxiaosong@chenxiaosong.com>
|
||||
@@ -25236,10 +25230,9 @@ F: Documentation/admin-guide/LSM/Smack.rst
|
||||
F: security/smack/
|
||||
|
||||
SMBDIRECT (RDMA Stream Transport with Read/Write-Offload, MS-SMBD)
|
||||
M: Steve French <smfrench@gmail.com>
|
||||
M: Steve French <sfrench@samba.org>
|
||||
M: Namjae Jeon <linkinjeon@kernel.org>
|
||||
M: Namjae Jeon <linkinjeon@samba.org>
|
||||
M: Paulo Alcantara <pc@manguebit.org>
|
||||
R: Stefan Metzmacher <metze@samba.org>
|
||||
R: Tom Talpey <tom@talpey.com>
|
||||
L: linux-cifs@vger.kernel.org
|
||||
@@ -26954,8 +26947,7 @@ S: Maintained
|
||||
F: drivers/net/ethernet/tehuti/tn40*
|
||||
|
||||
TELECOM CLOCK DRIVER FOR MCPL0010
|
||||
M: Mark Gross <markgross@kernel.org>
|
||||
S: Supported
|
||||
S: Orphan
|
||||
F: drivers/char/tlclk.c
|
||||
|
||||
TEMPO SEMICONDUCTOR DRIVERS
|
||||
@@ -29937,12 +29929,14 @@ F: include/uapi/linux/xilinx-v4l2-controls.h
|
||||
XILINX VERSAL EDAC DRIVER
|
||||
M: Shubhrajyoti Datta <shubhrajyoti.datta@amd.com>
|
||||
M: Sai Krishna Potthuri <sai.krishna.potthuri@amd.com>
|
||||
R: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/memory-controllers/xlnx,versal-ddrmc-edac.yaml
|
||||
F: drivers/edac/versal_edac.c
|
||||
|
||||
XILINX VERSALNET EDAC DRIVER
|
||||
M: Shubhrajyoti Datta <shubhrajyoti.datta@amd.com>
|
||||
R: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/memory-controllers/xlnx,versal-net-ddrmc5.yaml
|
||||
F: drivers/edac/versalnet_edac.c
|
||||
@@ -29988,6 +29982,7 @@ F: include/dt-bindings/dma/xlnx-zynqmp-dpdma.h
|
||||
XILINX ZYNQMP OCM EDAC DRIVER
|
||||
M: Shubhrajyoti Datta <shubhrajyoti.datta@amd.com>
|
||||
M: Sai Krishna Potthuri <sai.krishna.potthuri@amd.com>
|
||||
R: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/memory-controllers/xlnx,zynqmp-ocmc-1.0.yaml
|
||||
F: drivers/edac/zynqmp_edac.c
|
||||
|
||||
@@ -302,12 +302,8 @@ static int al_mc_edac_probe(struct platform_device *pdev)
|
||||
IRQF_SHARED,
|
||||
pdev->name,
|
||||
pdev);
|
||||
if (ret != 0) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed to request UE IRQ %d (%d)\n",
|
||||
al_mc->irq_ue, ret);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (al_mc->irq_ce > 0) {
|
||||
@@ -317,12 +313,8 @@ static int al_mc_edac_probe(struct platform_device *pdev)
|
||||
IRQF_SHARED,
|
||||
pdev->name,
|
||||
pdev);
|
||||
if (ret != 0) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed to request CE IRQ %d (%d)\n",
|
||||
al_mc->irq_ce, ret);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -1507,6 +1507,7 @@ static int altr_portb_setup(struct altr_edac_device_dev *device)
|
||||
int edac_idx, rc;
|
||||
struct device_node *np;
|
||||
const struct edac_device_prv_data *prv = &a10_sdmmceccb_data;
|
||||
bool is_s10 = device->edac->is_s10;
|
||||
|
||||
rc = altr_check_ecc_deps(device);
|
||||
if (rc)
|
||||
@@ -1548,15 +1549,14 @@ static int altr_portb_setup(struct altr_edac_device_dev *device)
|
||||
|
||||
/*
|
||||
* Update the PortB IRQs - A10 has 4, S10 has 2, Index accordingly
|
||||
*
|
||||
* FIXME: Instead of ifdefs with different architectures the driver
|
||||
* should properly use compatibles.
|
||||
*/
|
||||
#ifdef CONFIG_64BIT
|
||||
altdev->sb_irq = irq_of_parse_and_map(np, 1);
|
||||
#else
|
||||
altdev->sb_irq = irq_of_parse_and_map(np, 2);
|
||||
#endif
|
||||
|
||||
/* Using compatibles to determine the IRQ Index */
|
||||
if (is_s10)
|
||||
altdev->sb_irq = irq_of_parse_and_map(np, 1);
|
||||
else
|
||||
altdev->sb_irq = irq_of_parse_and_map(np, 2);
|
||||
|
||||
if (!altdev->sb_irq) {
|
||||
edac_printk(KERN_ERR, EDAC_DEVICE, "Error PortB SBIRQ alloc\n");
|
||||
rc = -ENODEV;
|
||||
@@ -1570,29 +1570,28 @@ static int altr_portb_setup(struct altr_edac_device_dev *device)
|
||||
goto err_release_group_1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
/* Use IRQ to determine SError origin instead of assigning IRQ */
|
||||
rc = of_property_read_u32_index(np, "interrupts", 1, &altdev->db_irq);
|
||||
if (rc) {
|
||||
edac_printk(KERN_ERR, EDAC_DEVICE,
|
||||
"Error PortB DBIRQ alloc\n");
|
||||
goto err_release_group_1;
|
||||
if (is_s10) {
|
||||
/* Use IRQ to determine SError origin instead of assigning IRQ */
|
||||
rc = of_property_read_u32_index(np, "interrupts", 1, &altdev->db_irq);
|
||||
if (rc) {
|
||||
edac_printk(KERN_ERR, EDAC_DEVICE, "Error PortB DBIRQ alloc\n");
|
||||
goto err_release_group_1;
|
||||
}
|
||||
} else {
|
||||
altdev->db_irq = irq_of_parse_and_map(np, 3);
|
||||
if (!altdev->db_irq) {
|
||||
edac_printk(KERN_ERR, EDAC_DEVICE, "Error PortB DBIRQ alloc\n");
|
||||
rc = -ENODEV;
|
||||
goto err_release_group_1;
|
||||
}
|
||||
rc = devm_request_irq(&altdev->ddev, altdev->db_irq,
|
||||
prv->ecc_irq_handler, IRQF_TRIGGER_HIGH,
|
||||
ecc_name, altdev);
|
||||
if (rc) {
|
||||
edac_printk(KERN_ERR, EDAC_DEVICE, "PortB DBERR IRQ error\n");
|
||||
goto err_release_group_1;
|
||||
}
|
||||
}
|
||||
#else
|
||||
altdev->db_irq = irq_of_parse_and_map(np, 3);
|
||||
if (!altdev->db_irq) {
|
||||
edac_printk(KERN_ERR, EDAC_DEVICE, "Error PortB DBIRQ alloc\n");
|
||||
rc = -ENODEV;
|
||||
goto err_release_group_1;
|
||||
}
|
||||
rc = devm_request_irq(&altdev->ddev, altdev->db_irq,
|
||||
prv->ecc_irq_handler, IRQF_TRIGGER_HIGH,
|
||||
ecc_name, altdev);
|
||||
if (rc) {
|
||||
edac_printk(KERN_ERR, EDAC_DEVICE, "PortB DBERR IRQ error\n");
|
||||
goto err_release_group_1;
|
||||
}
|
||||
#endif
|
||||
|
||||
rc = edac_device_add_device(dci);
|
||||
if (rc) {
|
||||
@@ -1974,29 +1973,29 @@ static int altr_edac_a10_device_add(struct altr_arria10_edac *edac,
|
||||
goto err_release_group1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
/* Use IRQ to determine SError origin instead of assigning IRQ */
|
||||
rc = of_property_read_u32_index(np, "interrupts", 0, &altdev->db_irq);
|
||||
if (rc) {
|
||||
edac_printk(KERN_ERR, EDAC_DEVICE,
|
||||
"Unable to parse DB IRQ index\n");
|
||||
goto err_release_group1;
|
||||
if (edac->is_s10) {
|
||||
/* Use IRQ to determine SError origin instead of assigning IRQ */
|
||||
rc = of_property_read_u32_index(np, "interrupts", 0, &altdev->db_irq);
|
||||
if (rc) {
|
||||
edac_printk(KERN_ERR, EDAC_DEVICE,
|
||||
"Unable to parse DB IRQ index\n");
|
||||
goto err_release_group1;
|
||||
}
|
||||
} else {
|
||||
altdev->db_irq = irq_of_parse_and_map(np, 1);
|
||||
if (!altdev->db_irq) {
|
||||
edac_printk(KERN_ERR, EDAC_DEVICE, "Error allocating DBIRQ\n");
|
||||
rc = -ENODEV;
|
||||
goto err_release_group1;
|
||||
}
|
||||
rc = devm_request_irq(edac->dev, altdev->db_irq, prv->ecc_irq_handler,
|
||||
IRQF_TRIGGER_HIGH,
|
||||
ecc_name, altdev);
|
||||
if (rc) {
|
||||
edac_printk(KERN_ERR, EDAC_DEVICE, "No DBERR IRQ resource\n");
|
||||
goto err_release_group1;
|
||||
}
|
||||
}
|
||||
#else
|
||||
altdev->db_irq = irq_of_parse_and_map(np, 1);
|
||||
if (!altdev->db_irq) {
|
||||
edac_printk(KERN_ERR, EDAC_DEVICE, "Error allocating DBIRQ\n");
|
||||
rc = -ENODEV;
|
||||
goto err_release_group1;
|
||||
}
|
||||
rc = devm_request_irq(edac->dev, altdev->db_irq, prv->ecc_irq_handler,
|
||||
IRQF_TRIGGER_HIGH,
|
||||
ecc_name, altdev);
|
||||
if (rc) {
|
||||
edac_printk(KERN_ERR, EDAC_DEVICE, "No DBERR IRQ resource\n");
|
||||
goto err_release_group1;
|
||||
}
|
||||
#endif
|
||||
|
||||
rc = edac_device_add_device(dci);
|
||||
if (rc) {
|
||||
@@ -2059,7 +2058,6 @@ static const struct irq_domain_ops a10_eccmgr_ic_ops = {
|
||||
/************** Stratix 10 EDAC Double Bit Error Handler ************/
|
||||
#define to_a10edac(p, m) container_of(p, struct altr_arria10_edac, m)
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
/* panic routine issues reboot on non-zero panic_timeout */
|
||||
extern int panic_timeout;
|
||||
|
||||
@@ -2106,7 +2104,6 @@ static int s10_edac_dberr_handler(struct notifier_block *this,
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************** Arria 10 EDAC Probe Function *********************/
|
||||
static int altr_edac_a10_probe(struct platform_device *pdev)
|
||||
@@ -2122,6 +2119,8 @@ static int altr_edac_a10_probe(struct platform_device *pdev)
|
||||
platform_set_drvdata(pdev, edac);
|
||||
INIT_LIST_HEAD(&edac->a10_ecc_devices);
|
||||
|
||||
edac->is_s10 = !!device_get_match_data(&pdev->dev);
|
||||
|
||||
edac->ecc_mgr_map =
|
||||
altr_sysmgr_regmap_lookup_by_phandle(pdev->dev.of_node,
|
||||
"altr,sysmgr-syscon");
|
||||
@@ -2153,8 +2152,7 @@ static int altr_edac_a10_probe(struct platform_device *pdev)
|
||||
irq_set_chained_handler_and_data(edac->sb_irq,
|
||||
altr_edac_a10_irq_handler,
|
||||
edac);
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
if (edac->is_s10)
|
||||
{
|
||||
int dberror, err_addr;
|
||||
|
||||
@@ -2177,15 +2175,14 @@ static int altr_edac_a10_probe(struct platform_device *pdev)
|
||||
regmap_write(edac->ecc_mgr_map,
|
||||
S10_SYSMGR_UE_ADDR_OFST, 0);
|
||||
}
|
||||
}
|
||||
#else
|
||||
edac->db_irq = platform_get_irq(pdev, 1);
|
||||
if (edac->db_irq < 0)
|
||||
return edac->db_irq;
|
||||
} else {
|
||||
edac->db_irq = platform_get_irq(pdev, 1);
|
||||
if (edac->db_irq < 0)
|
||||
return edac->db_irq;
|
||||
|
||||
irq_set_chained_handler_and_data(edac->db_irq,
|
||||
altr_edac_a10_irq_handler, edac);
|
||||
#endif
|
||||
irq_set_chained_handler_and_data(edac->db_irq,
|
||||
altr_edac_a10_irq_handler, edac);
|
||||
}
|
||||
|
||||
for_each_child_of_node(pdev->dev.of_node, child) {
|
||||
if (!of_device_is_available(child))
|
||||
@@ -2207,7 +2204,7 @@ static int altr_edac_a10_probe(struct platform_device *pdev)
|
||||
|
||||
static const struct of_device_id altr_edac_a10_of_match[] = {
|
||||
{ .compatible = "altr,socfpga-a10-ecc-manager" },
|
||||
{ .compatible = "altr,socfpga-s10-ecc-manager" },
|
||||
{ .compatible = "altr,socfpga-s10-ecc-manager", .data = (void *)1 },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, altr_edac_a10_of_match);
|
||||
|
||||
@@ -394,6 +394,7 @@ struct altr_arria10_edac {
|
||||
struct irq_chip irq_chip;
|
||||
struct list_head a10_ecc_devices;
|
||||
struct notifier_block panic_notifier;
|
||||
bool is_s10;
|
||||
};
|
||||
|
||||
#endif /* #ifndef _ALTERA_EDAC_H */
|
||||
|
||||
@@ -4173,6 +4173,8 @@ static int __init amd64_edac_init(void)
|
||||
goto err_pci;
|
||||
}
|
||||
|
||||
request_module_nowait("amd_atl");
|
||||
|
||||
/* register stuff with EDAC MCE */
|
||||
if (boot_cpu_data.x86 >= 0x17) {
|
||||
amd_register_ecc_decoder(decode_umc_error);
|
||||
|
||||
@@ -214,10 +214,8 @@ static int config_irq(void *ctx, struct platform_device *pdev)
|
||||
|
||||
rc = devm_request_irq(&pdev->dev, irq, mcr_isr, IRQF_TRIGGER_HIGH,
|
||||
DRV_NAME, ctx);
|
||||
if (rc) {
|
||||
dev_err(&pdev->dev, "unable to request irq %d\n", irq);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* enable interrupts */
|
||||
regmap_update_bits(aspeed_regmap, ASPEED_MCR_INTR_CTRL,
|
||||
|
||||
@@ -1,50 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include <linux/string_choices.h>
|
||||
|
||||
#include "edac_module.h"
|
||||
|
||||
static struct dentry *edac_debugfs;
|
||||
|
||||
static ssize_t edac_fake_inject_write(struct file *file,
|
||||
const char __user *data,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct device *dev = file->private_data;
|
||||
struct mem_ctl_info *mci = to_mci(dev);
|
||||
static enum hw_event_mc_err_type type;
|
||||
u16 errcount = mci->fake_inject_count;
|
||||
|
||||
if (!errcount)
|
||||
errcount = 1;
|
||||
|
||||
type = mci->fake_inject_ue ? HW_EVENT_ERR_UNCORRECTED
|
||||
: HW_EVENT_ERR_CORRECTED;
|
||||
|
||||
printk(KERN_DEBUG
|
||||
"Generating %d %s fake error%s to %d.%d.%d to test core handling. NOTE: this won't test the driver-specific decoding logic.\n",
|
||||
errcount,
|
||||
(type == HW_EVENT_ERR_UNCORRECTED) ? "UE" : "CE",
|
||||
str_plural(errcount),
|
||||
mci->fake_inject_layer[0],
|
||||
mci->fake_inject_layer[1],
|
||||
mci->fake_inject_layer[2]
|
||||
);
|
||||
edac_mc_handle_error(type, mci, errcount, 0, 0, 0,
|
||||
mci->fake_inject_layer[0],
|
||||
mci->fake_inject_layer[1],
|
||||
mci->fake_inject_layer[2],
|
||||
"FAKE ERROR", "for EDAC testing only");
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations debug_fake_inject_fops = {
|
||||
.open = simple_open,
|
||||
.write = edac_fake_inject_write,
|
||||
.llseek = generic_file_llseek,
|
||||
};
|
||||
|
||||
void __init edac_debugfs_init(void)
|
||||
{
|
||||
edac_debugfs = debugfs_create_dir("edac", NULL);
|
||||
@@ -57,29 +16,7 @@ void edac_debugfs_exit(void)
|
||||
|
||||
void edac_create_debugfs_nodes(struct mem_ctl_info *mci)
|
||||
{
|
||||
struct dentry *parent;
|
||||
char name[80];
|
||||
int i;
|
||||
|
||||
parent = debugfs_create_dir(mci->dev.kobj.name, edac_debugfs);
|
||||
|
||||
for (i = 0; i < mci->n_layers; i++) {
|
||||
sprintf(name, "fake_inject_%s",
|
||||
edac_layer_name[mci->layers[i].type]);
|
||||
debugfs_create_u8(name, S_IRUGO | S_IWUSR, parent,
|
||||
&mci->fake_inject_layer[i]);
|
||||
}
|
||||
|
||||
debugfs_create_bool("fake_inject_ue", S_IRUGO | S_IWUSR, parent,
|
||||
&mci->fake_inject_ue);
|
||||
|
||||
debugfs_create_u16("fake_inject_count", S_IRUGO | S_IWUSR, parent,
|
||||
&mci->fake_inject_count);
|
||||
|
||||
debugfs_create_file("fake_inject", S_IWUSR, parent, &mci->dev,
|
||||
&debug_fake_inject_fops);
|
||||
|
||||
mci->debugfs = parent;
|
||||
mci->debugfs = debugfs_create_dir(mci->dev.kobj.name, edac_debugfs);
|
||||
}
|
||||
|
||||
/* Create a toplevel dir under EDAC's debugfs hierarchy */
|
||||
|
||||
@@ -342,14 +342,10 @@ static void edac_device_workq_teardown(struct edac_device_ctl_info *edac_dev)
|
||||
}
|
||||
|
||||
/*
|
||||
* edac_device_reset_delay_period
|
||||
*
|
||||
* need to stop any outstanding workq queued up at this time
|
||||
* because we will be resetting the sleep time.
|
||||
* Then restart the workq on the new delay
|
||||
* Stop any outstanding workq queued up at this time because sleep time will
|
||||
* be reset. Then restart the workq on the new delay.
|
||||
*/
|
||||
void edac_device_reset_delay_period(struct edac_device_ctl_info *edac_dev,
|
||||
unsigned long msec)
|
||||
void edac_device_reset_delay_period(struct edac_device_ctl_info *edac_dev, unsigned int msec)
|
||||
{
|
||||
edac_dev->poll_msec = msec;
|
||||
edac_dev->delay = msecs_to_jiffies(msec);
|
||||
|
||||
@@ -84,18 +84,23 @@ static ssize_t edac_device_ctl_poll_msec_show(struct edac_device_ctl_info
|
||||
return sprintf(data, "%u\n", ctl_info->poll_msec);
|
||||
}
|
||||
|
||||
static ssize_t edac_device_ctl_poll_msec_store(struct edac_device_ctl_info
|
||||
*ctl_info, const char *data,
|
||||
size_t count)
|
||||
static ssize_t edac_device_ctl_poll_msec_store(struct edac_device_ctl_info *ctl_info,
|
||||
const char *data, size_t count)
|
||||
{
|
||||
unsigned long value;
|
||||
unsigned int value;
|
||||
int ret;
|
||||
|
||||
/* get the value and enforce that it is non-zero, must be at least
|
||||
* one millisecond for the delay period, between scans
|
||||
* Then cancel last outstanding delay for the work request
|
||||
* and set a new one.
|
||||
/*
|
||||
* Get the value, make sure it is non-zero, must be at least one millisecond
|
||||
* for the delay period between scans.
|
||||
*/
|
||||
value = simple_strtoul(data, NULL, 0);
|
||||
ret = kstrtouint(data, 0, &value);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (value < 1)
|
||||
return -EINVAL;
|
||||
|
||||
edac_device_reset_delay_period(ctl_info, value);
|
||||
|
||||
return count;
|
||||
|
||||
@@ -129,7 +129,7 @@ static ssize_t dimmdev_location_show(struct device *dev,
|
||||
ssize_t count;
|
||||
|
||||
count = edac_dimm_info_location(dimm, data, PAGE_SIZE);
|
||||
count += scnprintf(data + count, PAGE_SIZE - count, "\n");
|
||||
count += sysfs_emit_at(data, count, "\n");
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -52,8 +52,7 @@ bool edac_queue_work(struct delayed_work *work, unsigned long delay);
|
||||
bool edac_stop_work(struct delayed_work *work);
|
||||
bool edac_mod_work(struct delayed_work *work, unsigned long delay);
|
||||
|
||||
extern void edac_device_reset_delay_period(struct edac_device_ctl_info
|
||||
*edac_dev, unsigned long msec);
|
||||
extern void edac_device_reset_delay_period(struct edac_device_ctl_info *edac_dev, unsigned int msec);
|
||||
extern void edac_mc_reset_delay_period(unsigned long value);
|
||||
|
||||
/*
|
||||
|
||||
@@ -235,10 +235,8 @@ static int highbank_mc_probe(struct platform_device *pdev)
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
res = devm_request_irq(&pdev->dev, irq, highbank_mc_err_handler,
|
||||
0, dev_name(&pdev->dev), mci);
|
||||
if (res < 0) {
|
||||
dev_err(&pdev->dev, "Unable to request irq %d\n", irq);
|
||||
if (res < 0)
|
||||
goto err2;
|
||||
}
|
||||
|
||||
devres_close_group(&pdev->dev, NULL);
|
||||
return 0;
|
||||
|
||||
@@ -416,7 +416,23 @@ static void populate_dimm_info(struct dimm_data *dd, u32 addr_decode, int dimm,
|
||||
{
|
||||
dd->size = field_get(cfg->reg_mad_dimm_size_mask[dimm], addr_decode) * cfg->reg_mad_dimm_size_granularity;
|
||||
dd->ranks = field_get(cfg->reg_mad_dimm_rank_mask[dimm], addr_decode) + 1;
|
||||
dd->dtype = field_get(cfg->reg_mad_dimm_width_mask[dimm], addr_decode) + DEV_X8;
|
||||
|
||||
switch (field_get(cfg->reg_mad_dimm_width_mask[dimm], addr_decode)) {
|
||||
case 0:
|
||||
dd->dtype = DEV_X8;
|
||||
break;
|
||||
case 1:
|
||||
dd->dtype = DEV_X16;
|
||||
break;
|
||||
case 2:
|
||||
dd->dtype = DEV_X32;
|
||||
break;
|
||||
case 3:
|
||||
dd->dtype = DEV_X64;
|
||||
break;
|
||||
default:
|
||||
dd->dtype = DEV_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
static void ie31200_get_dimm_config(struct mem_ctl_info *mci, void __iomem *window,
|
||||
|
||||
@@ -42,7 +42,8 @@
|
||||
|
||||
#define GET_BITFIELD(v, lo, hi) (((v) & GENMASK_ULL(hi, lo)) >> (lo))
|
||||
|
||||
#define NUM_IMC 2 /* Max memory controllers */
|
||||
/* Probing upper bound, not a hardware capability limit. */
|
||||
#define MAX_IMC_TO_PROBE 8
|
||||
#define NUM_CHANNELS 2 /* Max channels */
|
||||
#define NUM_DIMMS 2 /* Max DIMMs per channel */
|
||||
|
||||
@@ -122,6 +123,43 @@
|
||||
#define MEM_SLICE_HASH_MASK(v) (GET_BITFIELD(v, 6, 19) << 6)
|
||||
#define MEM_SLICE_HASH_LSB_MASK_BIT(v) GET_BITFIELD(v, 24, 26)
|
||||
|
||||
/*
|
||||
* A slice represents a portion of memory space participating in an
|
||||
* interleave relationship within the memory hierarchy.
|
||||
*
|
||||
* It can represent in different levels such as:
|
||||
*
|
||||
* - a pair of memory controllers
|
||||
* - a memory controller
|
||||
* - a memory channel
|
||||
* - a memory sub-channel / DIMM
|
||||
*
|
||||
* +--------+
|
||||
* | |
|
||||
* | Zone 1 |
|
||||
* | |
|
||||
* +--------+ +--------+
|
||||
* | | | |
|
||||
* | | | |
|
||||
* | Zone 0 | | Zone 0 |
|
||||
* | | | |
|
||||
* | | | |
|
||||
* +--------+ +--------+
|
||||
*
|
||||
* Slice L Slice S
|
||||
*
|
||||
* Memory space is divided into:
|
||||
*
|
||||
* - Zone 0 : Interleaved region
|
||||
* - Zone 1 : Non-interleaved region (upper part of the large slice).
|
||||
*/
|
||||
struct slice {
|
||||
/* Slice address. */
|
||||
u64 addr;
|
||||
/* Slice that @addr belongs to. */
|
||||
int id;
|
||||
};
|
||||
|
||||
struct igen6_imc {
|
||||
int mc;
|
||||
struct mem_ctl_info *mci;
|
||||
@@ -175,8 +213,6 @@ static struct res_config {
|
||||
/* Set imc->dimm_{l_size,s_size,l_map}[chan]. */
|
||||
void (*set_dimm_params)(struct igen6_imc *imc, int chan);
|
||||
bool (*ibecc_available)(struct pci_dev *pdev);
|
||||
/* Extract error address logged in IBECC */
|
||||
u64 (*err_addr)(u64 ecclog);
|
||||
/* Convert error address logged in IBECC to system physical address */
|
||||
u64 (*err_addr_to_sys_addr)(u64 eaddr, int mc);
|
||||
/* Convert error address logged in IBECC to integrated memory controller address */
|
||||
@@ -184,11 +220,11 @@ static struct res_config {
|
||||
} *res_cfg;
|
||||
|
||||
static struct igen6_pvt {
|
||||
struct igen6_imc imc[NUM_IMC];
|
||||
void __iomem *memss_pma_cr;
|
||||
u64 ms_hash;
|
||||
u64 ms_s_size;
|
||||
int ms_l_map;
|
||||
struct igen6_imc imc[];
|
||||
} *igen6_pvt;
|
||||
|
||||
/* The top of low usable DRAM */
|
||||
@@ -225,7 +261,8 @@ static char ecclog_buf[ECCLOG_POOL_SIZE];
|
||||
static struct irq_work ecclog_irq_work;
|
||||
static struct work_struct ecclog_work;
|
||||
|
||||
/* Compute die IDs for Elkhart Lake with IBECC */
|
||||
/* SoC compute die IDs with IBECC capability. */
|
||||
/* Elkhart Lake */
|
||||
#define DID_EHL_SKU5 0x4514
|
||||
#define DID_EHL_SKU6 0x4528
|
||||
#define DID_EHL_SKU7 0x452a
|
||||
@@ -238,22 +275,22 @@ static struct work_struct ecclog_work;
|
||||
#define DID_EHL_SKU14 0x4534
|
||||
#define DID_EHL_SKU15 0x4536
|
||||
|
||||
/* Compute die IDs for ICL-NNPI with IBECC */
|
||||
/* ICL-NNPI */
|
||||
#define DID_ICL_SKU8 0x4581
|
||||
#define DID_ICL_SKU10 0x4585
|
||||
#define DID_ICL_SKU11 0x4589
|
||||
#define DID_ICL_SKU12 0x458d
|
||||
|
||||
/* Compute die IDs for Tiger Lake with IBECC */
|
||||
/* Tiger Lake */
|
||||
#define DID_TGL_SKU 0x9a14
|
||||
|
||||
/* Compute die IDs for Alder Lake with IBECC */
|
||||
/* Alder Lake */
|
||||
#define DID_ADL_SKU1 0x4601
|
||||
#define DID_ADL_SKU2 0x4602
|
||||
#define DID_ADL_SKU3 0x4621
|
||||
#define DID_ADL_SKU4 0x4641
|
||||
|
||||
/* Compute die IDs for Alder Lake-N with IBECC */
|
||||
/* Alder Lake-N */
|
||||
#define DID_ADL_N_SKU1 0x4614
|
||||
#define DID_ADL_N_SKU2 0x4617
|
||||
#define DID_ADL_N_SKU3 0x461b
|
||||
@@ -267,38 +304,38 @@ static struct work_struct ecclog_work;
|
||||
#define DID_ADL_N_SKU11 0x467c
|
||||
#define DID_ADL_N_SKU12 0x4632
|
||||
|
||||
/* Compute die IDs for Arizona Beach with IBECC */
|
||||
/* Arizona Beach */
|
||||
#define DID_AZB_SKU1 0x4676
|
||||
|
||||
/* Compute did IDs for Amston Lake with IBECC */
|
||||
/* Amston Lake */
|
||||
#define DID_ASL_SKU1 0x464a
|
||||
#define DID_ASL_SKU2 0x4646
|
||||
#define DID_ASL_SKU3 0x4652
|
||||
|
||||
/* Compute die IDs for Raptor Lake-P with IBECC */
|
||||
/* Raptor Lake-P */
|
||||
#define DID_RPL_P_SKU1 0xa706
|
||||
#define DID_RPL_P_SKU2 0xa707
|
||||
#define DID_RPL_P_SKU3 0xa708
|
||||
#define DID_RPL_P_SKU4 0xa716
|
||||
#define DID_RPL_P_SKU5 0xa718
|
||||
|
||||
/* Compute die IDs for Meteor Lake-PS with IBECC */
|
||||
/* Meteor Lake-PS */
|
||||
#define DID_MTL_PS_SKU1 0x7d21
|
||||
#define DID_MTL_PS_SKU2 0x7d22
|
||||
#define DID_MTL_PS_SKU3 0x7d23
|
||||
#define DID_MTL_PS_SKU4 0x7d24
|
||||
|
||||
/* Compute die IDs for Meteor Lake-P with IBECC */
|
||||
/* Meteor Lake-P */
|
||||
#define DID_MTL_P_SKU1 0x7d01
|
||||
#define DID_MTL_P_SKU2 0x7d02
|
||||
#define DID_MTL_P_SKU3 0x7d14
|
||||
|
||||
/* Compute die IDs for Arrow Lake-UH with IBECC */
|
||||
/* Arrow Lake-UH */
|
||||
#define DID_ARL_UH_SKU1 0x7d06
|
||||
#define DID_ARL_UH_SKU2 0x7d20
|
||||
#define DID_ARL_UH_SKU3 0x7d30
|
||||
|
||||
/* Compute die IDs for Panther Lake-H with IBECC */
|
||||
/* Panther Lake-H */
|
||||
#define DID_PTL_H_SKU1 0xb000
|
||||
#define DID_PTL_H_SKU2 0xb001
|
||||
#define DID_PTL_H_SKU3 0xb002
|
||||
@@ -314,15 +351,114 @@ static struct work_struct ecclog_work;
|
||||
#define DID_PTL_H_SKU13 0xb02a
|
||||
#define DID_PTL_H_SKU14 0xb00a
|
||||
|
||||
/* Compute die IDs for Wildcat Lake with IBECC */
|
||||
/* Starfire */
|
||||
#define DID_STF_SKU1 0xb02b
|
||||
|
||||
/* Wildcat Lake */
|
||||
#define DID_WCL_SKU1 0xfd00
|
||||
|
||||
/* Compute die IDs for Nova Lake-H/HX with IBECC */
|
||||
/* Nova Lake-H/HX */
|
||||
#define DID_NVL_H_SKU1 0xd701
|
||||
#define DID_NVL_H_SKU2 0xd702
|
||||
#define DID_NVL_H_SKU3 0xd704
|
||||
#define DID_NVL_H_SKU4 0xd705
|
||||
|
||||
/* Remove the interleave bit and shift upper part down to fill gap. */
|
||||
static u64 squeeze_addr(u64 addr, int intlv_bit)
|
||||
{
|
||||
u64 slice_addr;
|
||||
|
||||
slice_addr = GET_BITFIELD(addr, intlv_bit + 1, 63) << intlv_bit;
|
||||
slice_addr |= GET_BITFIELD(addr, 0, intlv_bit - 1);
|
||||
|
||||
return slice_addr;
|
||||
}
|
||||
|
||||
/* Shift the upper bits up and insert a zero at the @intlv_bit bit position. */
|
||||
static u64 inflate_addr(u64 addr, int intlv_bit)
|
||||
{
|
||||
u64 inflated_addr;
|
||||
|
||||
/* Insert a zero at @intlv_bit position. */
|
||||
inflated_addr = GET_BITFIELD(addr, intlv_bit, 63) << (intlv_bit + 1);
|
||||
inflated_addr |= GET_BITFIELD(addr, 0, intlv_bit - 1);
|
||||
|
||||
return inflated_addr;
|
||||
}
|
||||
|
||||
static u64 compute_hash(u64 addr, u64 hash_mask, u64 hash_base, int intlv_bit)
|
||||
{
|
||||
u64 hash_addr;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* In hash mode, @intlv_bit is the lowest selected bit of @addr
|
||||
* to be XORed. While @mask may or may not include this @intlv_bit,
|
||||
* we enforce that @mask includes @intlv_bit to ensure @intlv_bit is
|
||||
* XORed exactly once.
|
||||
*/
|
||||
hash_mask |= BIT_ULL(intlv_bit);
|
||||
hash_addr = addr & hash_mask;
|
||||
|
||||
for (i = 6; i < 20; i++)
|
||||
hash_base ^= (hash_addr >> i) & 1;
|
||||
|
||||
return hash_base;
|
||||
}
|
||||
|
||||
/*
|
||||
* Converts a higher-level address (system / IMC / channel) into a lower-level
|
||||
* slice address and identifier.
|
||||
*/
|
||||
static void translate_to_lower_level(u64 addr, u64 hash_mask, u64 hash_base,
|
||||
int intlv_bit, u64 s_size, int l_map,
|
||||
struct slice *slice)
|
||||
{
|
||||
/* In non-interleave zone. */
|
||||
if (addr >= 2 * s_size) {
|
||||
slice->addr = addr - s_size;
|
||||
slice->id = l_map;
|
||||
return;
|
||||
}
|
||||
|
||||
/* In interleave zone. */
|
||||
slice->addr = squeeze_addr(addr, intlv_bit);
|
||||
|
||||
/* Non-hash mode. */
|
||||
if (!hash_mask) {
|
||||
slice->id = GET_BITFIELD(addr, intlv_bit, intlv_bit);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Hash mode. */
|
||||
slice->id = compute_hash(addr, hash_mask, hash_base, intlv_bit);
|
||||
}
|
||||
|
||||
/* Reconstruct address for upper memory hierarchy level. */
|
||||
static u64 translate_to_upper_level(u64 addr, u64 hash_mask, u64 hash_base,
|
||||
int intlv_bit, u64 s_size)
|
||||
{
|
||||
u64 inflated_addr, hash_val;
|
||||
|
||||
/* In non-interleave zone. */
|
||||
if (addr >= s_size)
|
||||
return addr + s_size;
|
||||
|
||||
/*
|
||||
* In interleave zone.
|
||||
*
|
||||
* Insert a zero at @intlv_bit position.
|
||||
*/
|
||||
inflated_addr = inflate_addr(addr, intlv_bit);
|
||||
|
||||
/*
|
||||
* Reconstruct the removed interleave bit and use it to replace
|
||||
* the zero at @intlv_bit position.
|
||||
*/
|
||||
hash_val = compute_hash(inflated_addr, hash_mask, hash_base, intlv_bit);
|
||||
return inflated_addr | (hash_val << intlv_bit);
|
||||
}
|
||||
|
||||
static int get_mchbar(struct pci_dev *pdev, u64 *mchbar)
|
||||
{
|
||||
union {
|
||||
@@ -354,6 +490,46 @@ static int get_mchbar(struct pci_dev *pdev, u64 *mchbar)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check whether the memory controller is absent. */
|
||||
static bool imc_absent(void __iomem *window)
|
||||
{
|
||||
return readl(window + MAD_INTER_CHANNEL_OFFSET) == ~0;
|
||||
}
|
||||
|
||||
/* Return MMIO base address of the memory controller if it's present, otherwise return NULL. */
|
||||
static void __iomem *map_imc_window(u64 mchbar, int pmc)
|
||||
{
|
||||
void __iomem *window;
|
||||
|
||||
window = ioremap(mchbar + pmc * MCHBAR_SIZE, MCHBAR_SIZE);
|
||||
if (!window)
|
||||
return NULL;
|
||||
|
||||
if (imc_absent(window)) {
|
||||
iounmap(window);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
/* Return the number of present memory controllers. */
|
||||
static int get_imc_num(u64 mchbar)
|
||||
{
|
||||
void __iomem *window;
|
||||
int lmc, pmc;
|
||||
|
||||
for (lmc = 0, pmc = 0; pmc < MAX_IMC_TO_PROBE; pmc++) {
|
||||
window = map_imc_window(mchbar, pmc);
|
||||
if (window) {
|
||||
iounmap(window);
|
||||
lmc++;
|
||||
}
|
||||
}
|
||||
|
||||
return lmc;
|
||||
}
|
||||
|
||||
static bool ehl_ibecc_available(struct pci_dev *pdev)
|
||||
{
|
||||
u32 v;
|
||||
@@ -450,21 +626,9 @@ static u64 mem_addr_to_sys_addr(u64 maddr)
|
||||
return maddr;
|
||||
}
|
||||
|
||||
static u64 mem_slice_hash(u64 addr, u64 mask, u64 hash_init, int intlv_bit)
|
||||
{
|
||||
u64 hash_addr = addr & mask, hash = hash_init;
|
||||
u64 intlv = (addr >> intlv_bit) & 1;
|
||||
int i;
|
||||
|
||||
for (i = 6; i < 20; i++)
|
||||
hash ^= (hash_addr >> i) & 1;
|
||||
|
||||
return hash ^ intlv;
|
||||
}
|
||||
|
||||
static u64 tgl_err_addr_to_mem_addr(u64 eaddr, int mc)
|
||||
{
|
||||
u64 maddr, hash, mask, ms_s_size;
|
||||
u64 mask, ms_s_size;
|
||||
int intlv_bit;
|
||||
u32 ms_hash;
|
||||
|
||||
@@ -477,12 +641,7 @@ static u64 tgl_err_addr_to_mem_addr(u64 eaddr, int mc)
|
||||
mask = MEM_SLICE_HASH_MASK(ms_hash);
|
||||
intlv_bit = MEM_SLICE_HASH_LSB_MASK_BIT(ms_hash) + 6;
|
||||
|
||||
maddr = GET_BITFIELD(eaddr, intlv_bit, 63) << (intlv_bit + 1) |
|
||||
GET_BITFIELD(eaddr, 0, intlv_bit - 1);
|
||||
|
||||
hash = mem_slice_hash(maddr, mask, mc, intlv_bit);
|
||||
|
||||
return maddr | (hash << intlv_bit);
|
||||
return translate_to_upper_level(eaddr, mask, mc, intlv_bit, ms_s_size);
|
||||
}
|
||||
|
||||
static u64 tgl_err_addr_to_sys_addr(u64 eaddr, int mc)
|
||||
@@ -504,8 +663,9 @@ static u64 adl_err_addr_to_sys_addr(u64 eaddr, int mc)
|
||||
|
||||
static u64 adl_err_addr_to_imc_addr(u64 eaddr, int mc)
|
||||
{
|
||||
u64 imc_addr, ms_s_size = igen6_pvt->ms_s_size;
|
||||
u64 ms_s_size = igen6_pvt->ms_s_size;
|
||||
struct igen6_imc *imc = &igen6_pvt->imc[mc];
|
||||
struct slice slice;
|
||||
int intlv_bit;
|
||||
u32 mc_hash;
|
||||
|
||||
@@ -516,15 +676,8 @@ static u64 adl_err_addr_to_imc_addr(u64 eaddr, int mc)
|
||||
|
||||
intlv_bit = MAC_MC_HASH_LSB(mc_hash) + 6;
|
||||
|
||||
imc_addr = GET_BITFIELD(eaddr, intlv_bit + 1, 63) << intlv_bit |
|
||||
GET_BITFIELD(eaddr, 0, intlv_bit - 1);
|
||||
|
||||
return imc_addr;
|
||||
}
|
||||
|
||||
static u64 rpl_p_err_addr(u64 ecclog)
|
||||
{
|
||||
return field_get(res_cfg->reg_eccerrlog_addr_mask, ecclog);
|
||||
translate_to_lower_level(eaddr, 0, 0, intlv_bit, ms_s_size, 0, &slice);
|
||||
return slice.addr;
|
||||
}
|
||||
|
||||
static enum mem_type ptl_h_get_mem_type(struct igen6_imc *imc)
|
||||
@@ -686,6 +839,7 @@ static struct res_config tgl_cfg = {
|
||||
.err_addr_to_imc_addr = tgl_err_addr_to_imc_addr,
|
||||
};
|
||||
|
||||
/* Shared by Alder Lake, Alder Lake-N, Arizona Beach, Amston Lake, and Raptor Lake-P */
|
||||
static struct res_config adl_cfg = {
|
||||
.machine_check = true,
|
||||
.num_imc = 2,
|
||||
@@ -701,37 +855,6 @@ static struct res_config adl_cfg = {
|
||||
.err_addr_to_imc_addr = adl_err_addr_to_imc_addr,
|
||||
};
|
||||
|
||||
static struct res_config adl_n_cfg = {
|
||||
.machine_check = true,
|
||||
.num_imc = 1,
|
||||
.reg_mchbar_mask = GENMASK_ULL(41, 17),
|
||||
.reg_tom_mask = GENMASK_ULL(41, 20),
|
||||
.reg_touud_mask = GENMASK_ULL(41, 20),
|
||||
.reg_eccerrlog_addr_mask = GENMASK_ULL(45, 5),
|
||||
.imc_base = 0xd800,
|
||||
.ibecc_base = 0xd400,
|
||||
.ibecc_error_log_offset = 0x68,
|
||||
.ibecc_available = tgl_ibecc_available,
|
||||
.err_addr_to_sys_addr = adl_err_addr_to_sys_addr,
|
||||
.err_addr_to_imc_addr = adl_err_addr_to_imc_addr,
|
||||
};
|
||||
|
||||
static struct res_config rpl_p_cfg = {
|
||||
.machine_check = true,
|
||||
.num_imc = 2,
|
||||
.reg_mchbar_mask = GENMASK_ULL(41, 17),
|
||||
.reg_tom_mask = GENMASK_ULL(41, 20),
|
||||
.reg_touud_mask = GENMASK_ULL(41, 20),
|
||||
.reg_eccerrlog_addr_mask = GENMASK_ULL(45, 5),
|
||||
.imc_base = 0xd800,
|
||||
.ibecc_base = 0xd400,
|
||||
.ibecc_error_log_offset = 0x68,
|
||||
.ibecc_available = tgl_ibecc_available,
|
||||
.err_addr = rpl_p_err_addr,
|
||||
.err_addr_to_sys_addr = adl_err_addr_to_sys_addr,
|
||||
.err_addr_to_imc_addr = adl_err_addr_to_imc_addr,
|
||||
};
|
||||
|
||||
static struct res_config mtl_ps_cfg = {
|
||||
.machine_check = true,
|
||||
.num_imc = 2,
|
||||
@@ -749,6 +872,7 @@ static struct res_config mtl_ps_cfg = {
|
||||
.err_addr_to_imc_addr = adl_err_addr_to_imc_addr,
|
||||
};
|
||||
|
||||
/* Shared by Meteor Lake-P, Arrow Lake-UH, and Wildcat Lake */
|
||||
static struct res_config mtl_p_cfg = {
|
||||
.machine_check = true,
|
||||
.num_imc = 2,
|
||||
@@ -764,6 +888,7 @@ static struct res_config mtl_p_cfg = {
|
||||
.err_addr_to_imc_addr = adl_err_addr_to_imc_addr,
|
||||
};
|
||||
|
||||
/* Shared by Panther Lake-H and Starfire */
|
||||
static struct res_config ptl_h_cfg = {
|
||||
.machine_check = true,
|
||||
.num_imc = 2,
|
||||
@@ -794,21 +919,6 @@ static struct res_config ptl_h_cfg = {
|
||||
.err_addr_to_imc_addr = adl_err_addr_to_imc_addr,
|
||||
};
|
||||
|
||||
static struct res_config wcl_cfg = {
|
||||
.machine_check = true,
|
||||
.num_imc = 1,
|
||||
.reg_mchbar_mask = GENMASK_ULL(41, 17),
|
||||
.reg_tom_mask = GENMASK_ULL(41, 20),
|
||||
.reg_touud_mask = GENMASK_ULL(41, 20),
|
||||
.reg_eccerrlog_addr_mask = GENMASK_ULL(38, 5),
|
||||
.imc_base = 0xd800,
|
||||
.ibecc_base = 0xd400,
|
||||
.ibecc_error_log_offset = 0x170,
|
||||
.ibecc_available = mtl_p_ibecc_available,
|
||||
.err_addr_to_sys_addr = adl_err_addr_to_sys_addr,
|
||||
.err_addr_to_imc_addr = adl_err_addr_to_imc_addr,
|
||||
};
|
||||
|
||||
static struct res_config nvl_h_cfg = {
|
||||
.machine_check = true,
|
||||
.num_imc = 2,
|
||||
@@ -861,27 +971,27 @@ static struct pci_device_id igen6_pci_tbl[] = {
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_SKU2), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_SKU3), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_SKU4), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU1), .driver_data = (kernel_ulong_t)&adl_n_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU2), .driver_data = (kernel_ulong_t)&adl_n_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU3), .driver_data = (kernel_ulong_t)&adl_n_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU4), .driver_data = (kernel_ulong_t)&adl_n_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU5), .driver_data = (kernel_ulong_t)&adl_n_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU6), .driver_data = (kernel_ulong_t)&adl_n_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU7), .driver_data = (kernel_ulong_t)&adl_n_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU8), .driver_data = (kernel_ulong_t)&adl_n_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU9), .driver_data = (kernel_ulong_t)&adl_n_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU10), .driver_data = (kernel_ulong_t)&adl_n_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU11), .driver_data = (kernel_ulong_t)&adl_n_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU12), .driver_data = (kernel_ulong_t)&adl_n_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_AZB_SKU1), .driver_data = (kernel_ulong_t)&adl_n_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ASL_SKU1), .driver_data = (kernel_ulong_t)&adl_n_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ASL_SKU2), .driver_data = (kernel_ulong_t)&adl_n_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ASL_SKU3), .driver_data = (kernel_ulong_t)&adl_n_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_RPL_P_SKU1), .driver_data = (kernel_ulong_t)&rpl_p_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_RPL_P_SKU2), .driver_data = (kernel_ulong_t)&rpl_p_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_RPL_P_SKU3), .driver_data = (kernel_ulong_t)&rpl_p_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_RPL_P_SKU4), .driver_data = (kernel_ulong_t)&rpl_p_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_RPL_P_SKU5), .driver_data = (kernel_ulong_t)&rpl_p_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU1), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU2), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU3), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU4), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU5), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU6), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU7), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU8), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU9), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU10), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU11), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU12), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_AZB_SKU1), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ASL_SKU1), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ASL_SKU2), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ASL_SKU3), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_RPL_P_SKU1), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_RPL_P_SKU2), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_RPL_P_SKU3), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_RPL_P_SKU4), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_RPL_P_SKU5), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_MTL_PS_SKU1), .driver_data = (kernel_ulong_t)&mtl_ps_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_MTL_PS_SKU2), .driver_data = (kernel_ulong_t)&mtl_ps_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_MTL_PS_SKU3), .driver_data = (kernel_ulong_t)&mtl_ps_cfg },
|
||||
@@ -892,6 +1002,7 @@ static struct pci_device_id igen6_pci_tbl[] = {
|
||||
{ PCI_VDEVICE(INTEL, DID_ARL_UH_SKU1), .driver_data = (kernel_ulong_t)&mtl_p_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ARL_UH_SKU2), .driver_data = (kernel_ulong_t)&mtl_p_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ARL_UH_SKU3), .driver_data = (kernel_ulong_t)&mtl_p_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_WCL_SKU1), .driver_data = (kernel_ulong_t)&mtl_p_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_PTL_H_SKU1), .driver_data = (kernel_ulong_t)&ptl_h_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_PTL_H_SKU2), .driver_data = (kernel_ulong_t)&ptl_h_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_PTL_H_SKU3), .driver_data = (kernel_ulong_t)&ptl_h_cfg },
|
||||
@@ -906,7 +1017,7 @@ static struct pci_device_id igen6_pci_tbl[] = {
|
||||
{ PCI_VDEVICE(INTEL, DID_PTL_H_SKU12), .driver_data = (kernel_ulong_t)&ptl_h_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_PTL_H_SKU13), .driver_data = (kernel_ulong_t)&ptl_h_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_PTL_H_SKU14), .driver_data = (kernel_ulong_t)&ptl_h_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_WCL_SKU1), .driver_data = (kernel_ulong_t)&wcl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_STF_SKU1), .driver_data = (kernel_ulong_t)&ptl_h_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_NVL_H_SKU1), .driver_data = (kernel_ulong_t)&nvl_h_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_NVL_H_SKU2), .driver_data = (kernel_ulong_t)&nvl_h_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_NVL_H_SKU3), .driver_data = (kernel_ulong_t)&nvl_h_cfg },
|
||||
@@ -1007,55 +1118,13 @@ static void set_dimm_params(struct igen6_imc *imc, int chan)
|
||||
imc->dimm_s_size[chan] = MAD_DIMM_CH_DIMM_S_SIZE(val);
|
||||
}
|
||||
|
||||
static int decode_chan_idx(u64 addr, u64 mask, int intlv_bit)
|
||||
{
|
||||
u64 hash_addr = addr & mask, hash = 0;
|
||||
u64 intlv = (addr >> intlv_bit) & 1;
|
||||
int i;
|
||||
|
||||
for (i = 6; i < 20; i++)
|
||||
hash ^= (hash_addr >> i) & 1;
|
||||
|
||||
return (int)hash ^ intlv;
|
||||
}
|
||||
|
||||
static u64 decode_channel_addr(u64 addr, int intlv_bit)
|
||||
{
|
||||
u64 channel_addr;
|
||||
|
||||
/* Remove the interleave bit and shift upper part down to fill gap */
|
||||
channel_addr = GET_BITFIELD(addr, intlv_bit + 1, 63) << intlv_bit;
|
||||
channel_addr |= GET_BITFIELD(addr, 0, intlv_bit - 1);
|
||||
|
||||
return channel_addr;
|
||||
}
|
||||
|
||||
static void decode_addr(u64 addr, u32 hash, u64 s_size, int l_map,
|
||||
int *idx, u64 *sub_addr)
|
||||
{
|
||||
int intlv_bit = CHANNEL_HASH_LSB_MASK_BIT(hash) + 6;
|
||||
|
||||
if (addr > 2 * s_size) {
|
||||
*sub_addr = addr - s_size;
|
||||
*idx = l_map;
|
||||
return;
|
||||
}
|
||||
|
||||
if (CHANNEL_HASH_MODE(hash)) {
|
||||
*sub_addr = decode_channel_addr(addr, intlv_bit);
|
||||
*idx = decode_chan_idx(addr, CHANNEL_HASH_MASK(hash), intlv_bit);
|
||||
} else {
|
||||
*sub_addr = decode_channel_addr(addr, 6);
|
||||
*idx = GET_BITFIELD(addr, 6, 6);
|
||||
}
|
||||
}
|
||||
|
||||
static int igen6_decode(struct decoded_addr *res)
|
||||
{
|
||||
struct igen6_imc *imc = &igen6_pvt->imc[res->mc];
|
||||
u64 addr = res->imc_addr, sub_addr, s_size;
|
||||
int idx, l_map;
|
||||
u32 hash;
|
||||
u64 addr = res->imc_addr, s_size;
|
||||
int intlv_bit, l_map;
|
||||
u32 hash, hash_mask;
|
||||
struct slice slice;
|
||||
|
||||
if (addr >= igen6_tom) {
|
||||
edac_dbg(0, "Address 0x%llx out of range\n", addr);
|
||||
@@ -1066,17 +1135,25 @@ static int igen6_decode(struct decoded_addr *res)
|
||||
hash = readl(imc->window + CHANNEL_HASH_OFFSET);
|
||||
s_size = imc->ch_s_size;
|
||||
l_map = imc->ch_l_map;
|
||||
decode_addr(addr, hash, s_size, l_map, &idx, &sub_addr);
|
||||
res->channel_idx = idx;
|
||||
res->channel_addr = sub_addr;
|
||||
hash_mask = CHANNEL_HASH_MODE(hash) ? CHANNEL_HASH_MASK(hash) : 0;
|
||||
intlv_bit = CHANNEL_HASH_LSB_MASK_BIT(hash) + 6;
|
||||
|
||||
translate_to_lower_level(addr, hash_mask, 0, intlv_bit, s_size, l_map, &slice);
|
||||
|
||||
res->channel_idx = slice.id;
|
||||
res->channel_addr = slice.addr;
|
||||
|
||||
/* Decode sub-channel/DIMM */
|
||||
hash = readl(imc->window + CHANNEL_EHASH_OFFSET);
|
||||
s_size = imc->dimm_s_size[idx];
|
||||
l_map = imc->dimm_l_map[idx];
|
||||
decode_addr(res->channel_addr, hash, s_size, l_map, &idx, &sub_addr);
|
||||
res->sub_channel_idx = idx;
|
||||
res->sub_channel_addr = sub_addr;
|
||||
s_size = imc->dimm_s_size[res->channel_idx];
|
||||
l_map = imc->dimm_l_map[res->channel_idx];
|
||||
hash_mask = CHANNEL_HASH_MODE(hash) ? CHANNEL_HASH_MASK(hash) : 0;
|
||||
intlv_bit = CHANNEL_HASH_LSB_MASK_BIT(hash) + 6;
|
||||
|
||||
translate_to_lower_level(res->channel_addr, hash_mask, 0, intlv_bit, s_size, l_map, &slice);
|
||||
|
||||
res->sub_channel_idx = slice.id;
|
||||
res->sub_channel_addr = slice.addr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1230,11 +1307,7 @@ static void ecclog_work_cb(struct work_struct *work)
|
||||
|
||||
llist_for_each_entry_safe(node, tmp, head, llnode) {
|
||||
memset(&res, 0, sizeof(res));
|
||||
if (res_cfg->err_addr)
|
||||
eaddr = res_cfg->err_addr(node->ecclog);
|
||||
else
|
||||
eaddr = node->ecclog & res_cfg->reg_eccerrlog_addr_mask;
|
||||
|
||||
eaddr = node->ecclog & res_cfg->reg_eccerrlog_addr_mask;
|
||||
res.mc = node->mc;
|
||||
res.sys_addr = res_cfg->err_addr_to_sys_addr(eaddr, res.mc);
|
||||
res.imc_addr = res_cfg->err_addr_to_imc_addr(eaddr, res.mc);
|
||||
@@ -1476,18 +1549,27 @@ static struct igen6_pvt *igen6_pvt_setup(struct pci_dev *pdev)
|
||||
{
|
||||
void __iomem *memss_pma_cr;
|
||||
struct igen6_pvt *pvt;
|
||||
int imc_num, rc;
|
||||
u64 mchbar;
|
||||
int rc;
|
||||
|
||||
pvt = kzalloc_obj(*igen6_pvt);
|
||||
if (!pvt)
|
||||
return NULL;
|
||||
|
||||
rc = get_mchbar(pdev, &mchbar);
|
||||
if (rc) {
|
||||
kfree(pvt);
|
||||
if (rc)
|
||||
return NULL;
|
||||
|
||||
imc_num = get_imc_num(mchbar);
|
||||
if (!imc_num) {
|
||||
igen6_printk(KERN_ERR, "No mc found.\n");
|
||||
return NULL;
|
||||
}
|
||||
edac_dbg(2, "%d mcs found.\n", imc_num);
|
||||
|
||||
/* Use the runtime detected IMC count. */
|
||||
if (res_cfg->num_imc != imc_num)
|
||||
res_cfg->num_imc = imc_num;
|
||||
|
||||
pvt = kzalloc_flex(*pvt, imc, imc_num);
|
||||
if (!pvt)
|
||||
return NULL;
|
||||
|
||||
memss_pma_cr = ioremap(mchbar, MCHBAR_SIZE * 2);
|
||||
if (!memss_pma_cr) {
|
||||
@@ -1572,12 +1654,6 @@ static void igen6_check(struct mem_ctl_info *mci)
|
||||
irq_work_queue(&ecclog_irq_work);
|
||||
}
|
||||
|
||||
/* Check whether the memory controller is absent. */
|
||||
static bool igen6_imc_absent(void __iomem *window)
|
||||
{
|
||||
return readl(window + MAD_INTER_CHANNEL_OFFSET) == ~0;
|
||||
}
|
||||
|
||||
static void imc_release(struct device *dev)
|
||||
{
|
||||
/* Nothing to do, the 'imc' owns the 'dev' and will also release it. */
|
||||
@@ -1689,26 +1765,15 @@ static int igen6_register_mcis(struct pci_dev *pdev, u64 mchbar)
|
||||
{
|
||||
void __iomem *window;
|
||||
int lmc, pmc, rc;
|
||||
u64 base;
|
||||
|
||||
for (lmc = 0, pmc = 0; pmc < NUM_IMC; pmc++) {
|
||||
base = mchbar + pmc * MCHBAR_SIZE;
|
||||
window = ioremap(base, MCHBAR_SIZE);
|
||||
if (!window) {
|
||||
igen6_printk(KERN_ERR, "Failed to ioremap 0x%llx for mc%d\n", base, pmc);
|
||||
rc = -ENOMEM;
|
||||
goto out_unregister_mcis;
|
||||
}
|
||||
|
||||
if (igen6_imc_absent(window)) {
|
||||
iounmap(window);
|
||||
edac_dbg(2, "Skip absent mc%d\n", pmc);
|
||||
for (lmc = 0, pmc = 0; pmc < MAX_IMC_TO_PROBE; pmc++) {
|
||||
window = map_imc_window(mchbar, pmc);
|
||||
if (!window)
|
||||
continue;
|
||||
}
|
||||
|
||||
rc = igen6_register_mci(lmc, window, pdev);
|
||||
if (rc)
|
||||
goto out_iounmap;
|
||||
goto err_unregister;
|
||||
|
||||
/* Done, if all present MCs are detected and registered. */
|
||||
if (++lmc >= res_cfg->num_imc)
|
||||
@@ -1728,10 +1793,8 @@ static int igen6_register_mcis(struct pci_dev *pdev, u64 mchbar)
|
||||
|
||||
return 0;
|
||||
|
||||
out_iounmap:
|
||||
err_unregister:
|
||||
iounmap(window);
|
||||
|
||||
out_unregister_mcis:
|
||||
igen6_unregister_mcis();
|
||||
|
||||
return rc;
|
||||
|
||||
@@ -729,10 +729,8 @@ static int thunderx_lmc_probe(struct pci_dev *pdev,
|
||||
thunderx_lmc_err_isr,
|
||||
thunderx_lmc_threaded_isr, 0,
|
||||
"[EDAC] ThunderX LMC", mci);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Cannot set ISR: %d\n", ret);
|
||||
if (ret)
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
lmc->node = FIELD_GET(THUNDERX_NODE, pci_resource_start(pdev, 0));
|
||||
|
||||
|
||||
@@ -1924,11 +1924,8 @@ static int xgene_edac_probe(struct platform_device *pdev)
|
||||
rc = devm_request_irq(&pdev->dev, irq,
|
||||
xgene_edac_isr, IRQF_SHARED,
|
||||
dev_name(&pdev->dev), edac);
|
||||
if (rc) {
|
||||
dev_err(&pdev->dev,
|
||||
"Could not request IRQ %d\n", irq);
|
||||
if (rc)
|
||||
goto out_err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -190,7 +190,6 @@ static const struct x86_cpu_id amd_atl_cpuids[] = {
|
||||
X86_MATCH_FEATURE(X86_FEATURE_ZEN, NULL),
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(x86cpu, amd_atl_cpuids);
|
||||
|
||||
static int __init amd_atl_init(void)
|
||||
{
|
||||
|
||||
@@ -771,9 +771,5 @@ int get_address_map(struct addr_ctx *ctx)
|
||||
|
||||
dump_address_map(&ctx->map);
|
||||
|
||||
ret = validate_address_map(ctx);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ret;
|
||||
return validate_address_map(ctx);
|
||||
}
|
||||
|
||||
@@ -1240,7 +1240,7 @@ static int ignore_hardlimit(struct dquot *dquot)
|
||||
{
|
||||
struct mem_dqinfo *info = &sb_dqopt(dquot->dq_sb)->info[dquot->dq_id.type];
|
||||
|
||||
return capable(CAP_SYS_RESOURCE) &&
|
||||
return capable_noaudit(CAP_SYS_RESOURCE) &&
|
||||
(info->dqi_format->qf_fmt_id != QFMT_VFS_OLD ||
|
||||
!(info->dqi_flags & DQF_ROOT_SQUASH));
|
||||
}
|
||||
|
||||
@@ -3555,6 +3555,7 @@ int cifs_do_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
int rc = 0;
|
||||
int bytes_returned = 0;
|
||||
__u16 params, byte_count, data_count, param_offset, offset;
|
||||
size_t cifs_acl_size, bytes_available;
|
||||
|
||||
cifs_dbg(FYI, "In SetPosixACL (Unix) for path %s\n", fileName);
|
||||
setAclRetry:
|
||||
@@ -3574,8 +3575,7 @@ int cifs_do_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
}
|
||||
params = 6 + name_len;
|
||||
pSMB->MaxParameterCount = cpu_to_le16(2);
|
||||
/* BB find max SMB size from sess */
|
||||
pSMB->MaxDataCount = cpu_to_le16(1000);
|
||||
pSMB->MaxDataCount = cpu_to_le16(min_t(unsigned int, CIFSMaxBufSize, USHRT_MAX));
|
||||
pSMB->MaxSetupCount = 0;
|
||||
pSMB->Reserved = 0;
|
||||
pSMB->Flags = 0;
|
||||
@@ -3587,6 +3587,15 @@ int cifs_do_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
parm_data = ((char *)pSMB) + offset;
|
||||
pSMB->ParameterOffset = cpu_to_le16(param_offset);
|
||||
|
||||
/* make sure we can fit the larger cifs_posix_aces in the buffer */
|
||||
cifs_acl_size = sizeof(struct cifs_posix_acl) +
|
||||
(acl->a_count * sizeof(struct cifs_posix_ace));
|
||||
bytes_available = (CIFSMaxBufSize + MAX_HEADER_SIZE(tcon->ses->server)) - offset;
|
||||
if (cifs_acl_size > bytes_available || cifs_acl_size > USHRT_MAX) {
|
||||
rc = -E2BIG;
|
||||
goto setACLerrorExit;
|
||||
}
|
||||
|
||||
/* convert to on the wire format for POSIX ACL */
|
||||
data_count = posix_acl_to_cifs(parm_data, acl, acl_type);
|
||||
|
||||
@@ -6325,8 +6334,10 @@ CIFSSMBSetEA(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
int name_len;
|
||||
int rc = 0;
|
||||
int bytes_returned = 0;
|
||||
__u16 params, param_offset, byte_count, offset, count;
|
||||
__u16 params, param_offset;
|
||||
unsigned int byte_count, offset, count;
|
||||
int remap = cifs_remap(cifs_sb);
|
||||
unsigned int total_len;
|
||||
|
||||
cifs_dbg(FYI, "In SetEA\n");
|
||||
SetEARetry:
|
||||
@@ -6378,6 +6389,13 @@ CIFSSMBSetEA(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
pSMB->Reserved3 = 0;
|
||||
pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
|
||||
byte_count = 3 /* pad */ + params + count;
|
||||
if (check_add_overflow(in_len, byte_count, &total_len) ||
|
||||
byte_count > U16_MAX ||
|
||||
total_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
|
||||
cifs_dbg(VFS, "EA request too large: %u bytes\n", total_len);
|
||||
cifs_buf_release(pSMB);
|
||||
return -E2BIG;
|
||||
}
|
||||
pSMB->DataCount = cpu_to_le16(count);
|
||||
parm_data->list_len = cpu_to_le32(count);
|
||||
parm_data->list.EA_flags = 0;
|
||||
|
||||
@@ -4189,14 +4189,25 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
cifs_set_vol_auth(struct smb3_fs_context *ctx, struct cifs_ses *ses)
|
||||
static int set_fs_context_auth(struct smb3_fs_context *ctx,
|
||||
struct cifs_ses *ses)
|
||||
{
|
||||
ctx->sectype = ses->sectype;
|
||||
|
||||
/* krb5 is special, since we don't need username or pw */
|
||||
if (ctx->sectype == Kerberos)
|
||||
/*
|
||||
* krb5 is special as we might need to pass username (passwordless) down
|
||||
* to cifs.upcall(8) for keytab.
|
||||
*/
|
||||
if (ctx->sectype == Kerberos) {
|
||||
if (ses->user_name && ses->user_name[0]) {
|
||||
ctx->username = kstrndup(ses->user_name,
|
||||
CIFS_MAX_USERNAME_LEN,
|
||||
GFP_KERNEL);
|
||||
if (!ctx->username)
|
||||
return -ENOMEM;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return cifs_set_cifscreds(ctx, ses);
|
||||
}
|
||||
@@ -4236,7 +4247,7 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid)
|
||||
ctx->dfs_root_ses = master_tcon->ses->dfs_root_ses;
|
||||
ctx->unicode = master_tcon->ses->unicode;
|
||||
|
||||
rc = cifs_set_vol_auth(ctx, master_tcon->ses);
|
||||
rc = set_fs_context_auth(ctx, master_tcon->ses);
|
||||
if (rc) {
|
||||
tcon = ERR_PTR(rc);
|
||||
goto out;
|
||||
|
||||
@@ -999,26 +999,50 @@ static int cifs_do_truncate(const unsigned int xid, struct dentry *dentry)
|
||||
struct cifs_tcon *tcon;
|
||||
int rc;
|
||||
|
||||
rc = filemap_write_and_wait(inode->i_mapping);
|
||||
if (is_interrupt_error(rc))
|
||||
rc = inode_lock_killable(inode);
|
||||
if (rc)
|
||||
return -ERESTARTSYS;
|
||||
|
||||
filemap_invalidate_lock(inode->i_mapping);
|
||||
|
||||
rc = filemap_write_and_wait(inode->i_mapping);
|
||||
if (is_interrupt_error(rc)) {
|
||||
rc = -ERESTARTSYS;
|
||||
goto out;
|
||||
}
|
||||
mapping_set_error(inode->i_mapping, rc);
|
||||
|
||||
cfile = find_writable_file(cinode, FIND_FSUID_ONLY);
|
||||
rc = cifs_file_flush(xid, inode, cfile);
|
||||
if (!rc) {
|
||||
if (cfile) {
|
||||
struct netfs_inode *ictx = netfs_inode(inode);
|
||||
|
||||
tcon = tlink_tcon(cfile->tlink);
|
||||
server = tcon->ses->server;
|
||||
netfs_wb_begin(ictx, false);
|
||||
rc = server->ops->set_file_size(xid, tcon,
|
||||
cfile, 0, false);
|
||||
}
|
||||
if (!rc) {
|
||||
netfs_resize_file(&cinode->netfs, 0, true);
|
||||
cifs_setsize(inode, 0);
|
||||
if (!rc) {
|
||||
netfs_resize_file(&cinode->netfs, 0, true);
|
||||
cifs_setsize(inode, 0);
|
||||
cifs_invalidate_cache(inode, 0);
|
||||
}
|
||||
netfs_wb_end(ictx);
|
||||
} else {
|
||||
/*
|
||||
* No cached handle; evict stale pages so they can't
|
||||
* be served after the file is later extended; let
|
||||
* the server's O_TRUNC open response set the i_size
|
||||
*/
|
||||
truncate_inode_pages(inode->i_mapping, 0);
|
||||
cifs_invalidate_cache(inode, 0);
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
filemap_invalidate_unlock(inode->i_mapping);
|
||||
inode_unlock(inode);
|
||||
if (cfile)
|
||||
cifsFileInfo_put(cfile);
|
||||
return rc;
|
||||
|
||||
@@ -1839,31 +1839,31 @@ smb2_ioctl_query_info(const unsigned int xid,
|
||||
*
|
||||
* @tcon: destination file tcon
|
||||
* @bytes_left: how many bytes are left to copy
|
||||
* @chunk_size: maximum size of a single chunk
|
||||
*
|
||||
* Return: maximum number of chunks with which Chunks[] can be filled.
|
||||
*/
|
||||
static inline u32
|
||||
calc_chunk_count(struct cifs_tcon *tcon, u64 bytes_left)
|
||||
calc_chunk_count(struct cifs_tcon *tcon, u64 bytes_left, u32 chunk_size)
|
||||
{
|
||||
u32 max_chunks = READ_ONCE(tcon->max_chunks);
|
||||
u32 max_bytes_copy = READ_ONCE(tcon->max_bytes_copy);
|
||||
u32 max_bytes_chunk = READ_ONCE(tcon->max_bytes_chunk);
|
||||
u64 need;
|
||||
u32 allowed;
|
||||
|
||||
if (!max_bytes_chunk || !max_bytes_copy || !max_chunks)
|
||||
if (!chunk_size || !max_bytes_copy || !max_chunks)
|
||||
return 0;
|
||||
|
||||
/* chunks needed for the remaining bytes */
|
||||
need = DIV_ROUND_UP_ULL(bytes_left, max_bytes_chunk);
|
||||
need = DIV_ROUND_UP_ULL(bytes_left, chunk_size);
|
||||
/* chunks allowed per cc request */
|
||||
allowed = DIV_ROUND_UP(max_bytes_copy, max_bytes_chunk);
|
||||
allowed = DIV_ROUND_UP(max_bytes_copy, chunk_size);
|
||||
|
||||
return (u32)umin(need, umin(max_chunks, allowed));
|
||||
}
|
||||
|
||||
/**
|
||||
* smb2_copychunk_range - server-side copy of data range
|
||||
* __smb2_copychunk_range - server-side copy of data range
|
||||
*
|
||||
* @xid: transaction id
|
||||
* @src_file: source file
|
||||
@@ -1875,15 +1875,15 @@ calc_chunk_count(struct cifs_tcon *tcon, u64 bytes_left)
|
||||
* Obtains a resume key for @src_file and issues FSCTL_SRV_COPYCHUNK_WRITE
|
||||
* IOCTLs, splitting the request into chunks limited by tcon->max_*.
|
||||
*
|
||||
* Return: @len on success; negative errno on failure.
|
||||
* Return: 0 on success; negative errno on failure.
|
||||
*/
|
||||
static ssize_t
|
||||
smb2_copychunk_range(const unsigned int xid,
|
||||
struct cifsFileInfo *src_file,
|
||||
struct cifsFileInfo *dst_file,
|
||||
u64 src_off,
|
||||
u64 len,
|
||||
u64 dst_off)
|
||||
static int
|
||||
__smb2_copychunk_range(const unsigned int xid,
|
||||
struct cifsFileInfo *src_file,
|
||||
struct cifsFileInfo *dst_file,
|
||||
u64 src_off,
|
||||
u64 len,
|
||||
u64 dst_off)
|
||||
{
|
||||
int rc = 0;
|
||||
unsigned int ret_data_len = 0;
|
||||
@@ -1891,12 +1891,14 @@ smb2_copychunk_range(const unsigned int xid,
|
||||
struct copychunk_ioctl_rsp *cc_rsp = NULL;
|
||||
struct cifs_tcon *tcon;
|
||||
struct srv_copychunk *chunk;
|
||||
u32 chunks, chunk_count, chunk_bytes;
|
||||
u32 chunks, chunk_count, chunk_bytes, chunk_size;
|
||||
u32 copy_bytes, copy_bytes_left;
|
||||
u32 chunks_written, bytes_written;
|
||||
u64 total_bytes_left = len;
|
||||
u64 src_off_prev, dst_off_prev;
|
||||
u64 max_chunk = 0;
|
||||
u32 retries = 0;
|
||||
bool reverse = false;
|
||||
|
||||
tcon = tlink_tcon(dst_file->tlink);
|
||||
|
||||
@@ -1904,8 +1906,50 @@ smb2_copychunk_range(const unsigned int xid,
|
||||
dst_file->fid.volatile_fid, tcon->tid,
|
||||
tcon->ses->Suid, src_off, dst_off, len);
|
||||
|
||||
/*
|
||||
* Same-file left shifts are safe in forward order. For a right shift,
|
||||
* let L be the copy length, delta the distance between the source and
|
||||
* destination, and C the normal chunk size:
|
||||
*
|
||||
* delta >= L: copy forwards using C
|
||||
* delta < L:
|
||||
* delta >= C: copy backwards using C
|
||||
* delta < C: copy backwards with chunks limited to delta
|
||||
*
|
||||
* Copying backwards prevents one chunk from overwriting data needed by
|
||||
* a later chunk. Limiting the chunk size to delta prevents an individual
|
||||
* chunk from overlapping itself.
|
||||
* This limit can be removed once all supported servers handle overlapping
|
||||
* descriptors safely.
|
||||
*
|
||||
* A small right shift over a large range may therefore require many
|
||||
* chunks.
|
||||
*/
|
||||
if (src_file == dst_file && dst_off > src_off) {
|
||||
u64 delta = dst_off - src_off;
|
||||
|
||||
if (delta < len) {
|
||||
reverse = true;
|
||||
max_chunk = delta;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* A backward copy walks the offsets down from the end of the range.
|
||||
* Do this once, outside the retry loop, so a retry does not move the
|
||||
* offsets again.
|
||||
*/
|
||||
if (reverse) {
|
||||
src_off += len;
|
||||
dst_off += len;
|
||||
}
|
||||
|
||||
retry:
|
||||
chunk_count = calc_chunk_count(tcon, total_bytes_left);
|
||||
chunk_size = READ_ONCE(tcon->max_bytes_chunk);
|
||||
if (max_chunk && max_chunk < chunk_size)
|
||||
chunk_size = (u32)max_chunk;
|
||||
|
||||
chunk_count = calc_chunk_count(tcon, total_bytes_left, chunk_size);
|
||||
if (!chunk_count) {
|
||||
rc = -EOPNOTSUPP;
|
||||
goto out;
|
||||
@@ -1946,16 +1990,21 @@ smb2_copychunk_range(const unsigned int xid,
|
||||
while (copy_bytes_left > 0 && chunks < chunk_count) {
|
||||
chunk = &cc_req->Chunks[chunks++];
|
||||
|
||||
chunk_bytes = umin(copy_bytes_left, chunk_size);
|
||||
if (reverse) {
|
||||
src_off -= chunk_bytes;
|
||||
dst_off -= chunk_bytes;
|
||||
}
|
||||
|
||||
chunk->SourceOffset = cpu_to_le64(src_off);
|
||||
chunk->TargetOffset = cpu_to_le64(dst_off);
|
||||
|
||||
chunk_bytes = umin(copy_bytes_left, tcon->max_bytes_chunk);
|
||||
|
||||
chunk->Length = cpu_to_le32(chunk_bytes);
|
||||
/* Buffer is zeroed, no need to set chunk->Reserved = 0 */
|
||||
|
||||
src_off += chunk_bytes;
|
||||
dst_off += chunk_bytes;
|
||||
if (!reverse) {
|
||||
src_off += chunk_bytes;
|
||||
dst_off += chunk_bytes;
|
||||
}
|
||||
|
||||
copy_bytes_left -= chunk_bytes;
|
||||
copy_bytes += chunk_bytes;
|
||||
@@ -2003,6 +2052,18 @@ smb2_copychunk_range(const unsigned int xid,
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* A successful COPYCHUNK should copy every descriptor (MS-SMB2
|
||||
* 3.3.5.15.6). Reject a short backward copy because the rewind
|
||||
* below only supports forward copying.
|
||||
*/
|
||||
if (unlikely(reverse && bytes_written < copy_bytes)) {
|
||||
cifs_tcon_dbg(VFS, "Copychunk short write %u/%u (reverse)\n",
|
||||
bytes_written, copy_bytes);
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Partial write: rewind */
|
||||
if (bytes_written < copy_bytes) {
|
||||
u32 delta = copy_bytes - bytes_written;
|
||||
@@ -2064,10 +2125,27 @@ smb2_copychunk_range(const unsigned int xid,
|
||||
trace_smb3_copychunk_done(xid, src_file->fid.volatile_fid,
|
||||
dst_file->fid.volatile_fid, tcon->tid,
|
||||
tcon->ses->Suid, src_off, dst_off, len);
|
||||
return len;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
smb2_copychunk_range(const unsigned int xid,
|
||||
struct cifsFileInfo *src_file,
|
||||
struct cifsFileInfo *dst_file,
|
||||
u64 src_off,
|
||||
u64 len,
|
||||
u64 dst_off)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = __smb2_copychunk_range(xid, src_file, dst_file, src_off, len,
|
||||
dst_off);
|
||||
if (rc)
|
||||
return rc;
|
||||
return len;
|
||||
}
|
||||
|
||||
static int
|
||||
smb2_flush_file(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_fid *fid)
|
||||
@@ -2218,7 +2296,7 @@ smb2_duplicate_extents(const unsigned int xid,
|
||||
trgtfile->fid.volatile_fid, tcon->tid,
|
||||
tcon->ses->Suid, src_off, dest_off, len);
|
||||
inode = d_inode(trgtfile->dentry);
|
||||
if (inode->i_size < dest_off + len) {
|
||||
if (i_size_read(inode) < dest_off + len) {
|
||||
rc = smb2_set_file_size(xid, tcon, trgtfile, dest_off + len, false);
|
||||
if (rc)
|
||||
goto duplicate_extents_out;
|
||||
@@ -2235,7 +2313,10 @@ smb2_duplicate_extents(const unsigned int xid,
|
||||
if (ret_data_len > 0)
|
||||
cifs_dbg(FYI, "Non-zero response length in duplicate extents\n");
|
||||
|
||||
if (rc == 0) {
|
||||
if (rc) {
|
||||
CIFS_I(inode)->time = 0; /* force reval */
|
||||
cifs_invalidate_cache(inode, 0);
|
||||
} else {
|
||||
qrc = SMB2_query_info(xid, tcon, trgtfile->fid.persistent_fid,
|
||||
trgtfile->fid.volatile_fid, &file_inf);
|
||||
spin_lock(&inode->i_lock);
|
||||
@@ -3441,6 +3522,13 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
|
||||
trace_smb3_zero_enter(xid, cfile->fid.persistent_fid, tcon->tid,
|
||||
ses->Suid, offset, len);
|
||||
|
||||
new_size = offset + len;
|
||||
if (!keep_size && i_size_read(inode) < new_size) {
|
||||
rc = inode_newsize_ok(inode, new_size);
|
||||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
|
||||
filemap_invalidate_lock(inode->i_mapping);
|
||||
|
||||
netfs_read_sizes(inode, &i_size, &remote_i_size, &zero_point);
|
||||
@@ -3464,6 +3552,9 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
|
||||
if (keep_size == false && !CIFS_CACHE_READ(cifsi))
|
||||
goto zero_range_exit;
|
||||
|
||||
fscache_invalidate(cifs_inode_cookie(inode), NULL,
|
||||
i_size_read(inode), 0);
|
||||
|
||||
rc = smb3_zero_data(file, tcon, offset, len, xid);
|
||||
if (rc < 0)
|
||||
goto zero_range_exit;
|
||||
@@ -3471,7 +3562,6 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
|
||||
/*
|
||||
* do we also need to change the size of the file?
|
||||
*/
|
||||
new_size = offset + len;
|
||||
if (keep_size == false && (unsigned long long)i_size_read(inode) < new_size) {
|
||||
rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
|
||||
cfile->fid.volatile_fid, cfile->pid, new_size);
|
||||
@@ -3488,6 +3578,7 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
|
||||
|
||||
zero_range_exit:
|
||||
filemap_invalidate_unlock(inode->i_mapping);
|
||||
out:
|
||||
free_xid(xid);
|
||||
if (rc)
|
||||
trace_smb3_zero_err(xid, cfile->fid.persistent_fid, tcon->tid,
|
||||
@@ -3533,6 +3624,8 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon,
|
||||
*/
|
||||
truncate_pagecache_range(inode, offset, offset + len - 1);
|
||||
netfs_wait_for_outstanding_io(inode);
|
||||
fscache_invalidate(cifs_inode_cookie(inode), NULL,
|
||||
i_size_read(inode), 0);
|
||||
|
||||
cifs_dbg(FYI, "Offset %lld len %lld\n", offset, len);
|
||||
|
||||
@@ -3938,18 +4031,26 @@ static long smb3_collapse_range(struct file *file, struct cifs_tcon *tcon,
|
||||
}
|
||||
|
||||
filemap_invalidate_lock(inode->i_mapping);
|
||||
rc = filemap_write_and_wait_range(inode->i_mapping, off, old_eof - 1);
|
||||
rc = filemap_write_and_wait_range(inode->i_mapping,
|
||||
round_down(off, PAGE_SIZE),
|
||||
old_eof - 1);
|
||||
if (rc < 0)
|
||||
goto out_2;
|
||||
|
||||
truncate_pagecache_range(inode, off, old_eof);
|
||||
netfs_wait_for_outstanding_io(inode);
|
||||
/*
|
||||
* Invalidate cached folios from the page containing off to EOF before
|
||||
* moving data on the server, so subsequent reads do not see stale data.
|
||||
*/
|
||||
truncate_pagecache_range(inode, round_down(off, PAGE_SIZE), -1);
|
||||
fscache_invalidate(cifs_inode_cookie(inode), NULL, old_eof, 0);
|
||||
|
||||
spin_lock(&inode->i_lock);
|
||||
netfs_write_zero_point(inode, old_eof);
|
||||
spin_unlock(&inode->i_lock);
|
||||
netfs_wait_for_outstanding_io(inode);
|
||||
|
||||
rc = smb2_copychunk_range(xid, cfile, cfile, off + len,
|
||||
old_eof - off - len, off);
|
||||
rc = __smb2_copychunk_range(xid, cfile, cfile, off + len,
|
||||
old_eof - off - len, off);
|
||||
if (rc < 0)
|
||||
goto out_2;
|
||||
|
||||
@@ -3982,7 +4083,7 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon,
|
||||
struct cifsFileInfo *cfile = file->private_data;
|
||||
struct inode *inode = file_inode(file);
|
||||
struct cifsInodeInfo *cifsi = CIFS_I(inode);
|
||||
__u64 count, old_eof, new_eof;
|
||||
loff_t old_eof, new_eof;
|
||||
|
||||
xid = get_xid();
|
||||
|
||||
@@ -3992,15 +4093,32 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon,
|
||||
goto out;
|
||||
}
|
||||
|
||||
count = old_eof - off;
|
||||
new_eof = old_eof + len;
|
||||
if (check_add_overflow(old_eof, len, &new_eof)) {
|
||||
rc = -EFBIG;
|
||||
goto out;
|
||||
}
|
||||
rc = inode_newsize_ok(inode, new_eof);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
/* SET_ZERO_DATA creates a hole only in a sparse file. */
|
||||
rc = smb2_set_sparse(xid, tcon, cfile, inode, true);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
filemap_invalidate_lock(inode->i_mapping);
|
||||
rc = filemap_write_and_wait_range(inode->i_mapping, off, new_eof - 1);
|
||||
rc = filemap_write_and_wait_range(inode->i_mapping,
|
||||
round_down(off, PAGE_SIZE),
|
||||
old_eof - 1);
|
||||
if (rc < 0)
|
||||
goto out_2;
|
||||
truncate_pagecache_range(inode, off, old_eof);
|
||||
netfs_wait_for_outstanding_io(inode);
|
||||
/*
|
||||
* Invalidate cached folios from the page containing off to EOF before
|
||||
* moving data on the server, so subsequent reads do not see stale data.
|
||||
*/
|
||||
truncate_pagecache_range(inode, round_down(off, PAGE_SIZE), -1);
|
||||
fscache_invalidate(cifs_inode_cookie(inode), NULL, old_eof, 0);
|
||||
|
||||
rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
|
||||
cfile->fid.volatile_fid, cfile->pid, new_eof);
|
||||
@@ -4013,7 +4131,12 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon,
|
||||
spin_unlock(&inode->i_lock);
|
||||
fscache_resize_cookie(cifs_inode_cookie(inode), i_size_read(inode));
|
||||
|
||||
rc = smb2_copychunk_range(xid, cfile, cfile, off, count, off + len);
|
||||
/*
|
||||
* Move [off, old_eof) right by len. The helper copies backwards if the
|
||||
* source and destination ranges overlap.
|
||||
*/
|
||||
rc = __smb2_copychunk_range(xid, cfile, cfile, off, old_eof - off,
|
||||
off + len);
|
||||
if (rc < 0)
|
||||
goto out_2;
|
||||
spin_lock(&inode->i_lock);
|
||||
|
||||
@@ -101,12 +101,11 @@ void __release_mid(struct TCP_Server_Info *server, struct mid_q_entry *midEntry)
|
||||
trace_smb3_slow_rsp(smb_cmd, midEntry->mid, midEntry->pid,
|
||||
midEntry->when_sent, midEntry->when_received);
|
||||
if (cifsFYI & CIFS_TIMER) {
|
||||
pr_debug("slow rsp: cmd %d mid %llu",
|
||||
midEntry->command, midEntry->mid);
|
||||
cifs_info("A: 0x%lx S: 0x%lx R: 0x%lx\n",
|
||||
now - midEntry->when_alloc,
|
||||
now - midEntry->when_sent,
|
||||
now - midEntry->when_received);
|
||||
pr_debug("slow rsp: cmd %d mid %llu A: 0x%lx S: 0x%lx R: 0x%lx\n",
|
||||
midEntry->command, midEntry->mid,
|
||||
now - midEntry->when_alloc,
|
||||
now - midEntry->when_sent,
|
||||
now - midEntry->when_received);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "mgmt/ksmbd_ida.h"
|
||||
#include "mgmt/user_session.h"
|
||||
#include "connection.h"
|
||||
#include "vfs_cache.h"
|
||||
#include "compress.h"
|
||||
#include "transport_tcp.h"
|
||||
#include "transport_rdma.h"
|
||||
@@ -384,12 +385,12 @@ static void ksmbd_conn_cancel_async_requests(struct ksmbd_conn *conn)
|
||||
spin_lock(&conn->request_lock);
|
||||
list_for_each_entry_safe(work, tmp, &conn->async_requests,
|
||||
async_request_entry) {
|
||||
if (work->state != KSMBD_WORK_ACTIVE)
|
||||
if (cmpxchg(&work->state, KSMBD_WORK_ACTIVE,
|
||||
KSMBD_WORK_CANCELLED) != KSMBD_WORK_ACTIVE)
|
||||
continue;
|
||||
|
||||
ksmbd_debug(CONN, "Cancel async request id %d\n",
|
||||
work->async_id);
|
||||
work->state = KSMBD_WORK_CANCELLED;
|
||||
if (work->cancel_fn)
|
||||
work->cancel_fn(work->cancel_argv);
|
||||
}
|
||||
@@ -473,6 +474,9 @@ int ksmbd_conn_wait_idle_sess(struct ksmbd_conn *curr_conn,
|
||||
if (retry_count >= max_timeout)
|
||||
return -EIO;
|
||||
|
||||
/* A blocked byte-range lock cannot drain until teardown wakes it. */
|
||||
ksmbd_wake_session_blocked_works(sess);
|
||||
|
||||
down_read(&conn_list_lock);
|
||||
hash_for_each(conn_list, bkt, conn, hlist) {
|
||||
if (ksmbd_session_is_bound_to_conn(sess, conn)) {
|
||||
|
||||
@@ -146,9 +146,9 @@ static struct ksmbd_share_config *__share_lookup(const char *name)
|
||||
|
||||
static int parse_veto_list(struct ksmbd_share_config *share,
|
||||
char *veto_list,
|
||||
int veto_list_sz)
|
||||
size_t veto_list_sz)
|
||||
{
|
||||
int sz = 0;
|
||||
size_t sz;
|
||||
|
||||
if (!veto_list_sz)
|
||||
return 0;
|
||||
@@ -156,7 +156,7 @@ static int parse_veto_list(struct ksmbd_share_config *share,
|
||||
while (veto_list_sz > 0) {
|
||||
struct ksmbd_veto_pattern *p;
|
||||
|
||||
sz = strlen(veto_list);
|
||||
sz = strnlen(veto_list, veto_list_sz);
|
||||
if (!sz)
|
||||
break;
|
||||
|
||||
@@ -164,7 +164,7 @@ static int parse_veto_list(struct ksmbd_share_config *share,
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
p->pattern = kstrdup(veto_list, KSMBD_DEFAULT_GFP);
|
||||
p->pattern = kstrndup(veto_list, sz, KSMBD_DEFAULT_GFP);
|
||||
if (!p->pattern) {
|
||||
kfree(p);
|
||||
return -ENOMEM;
|
||||
@@ -172,6 +172,9 @@ static int parse_veto_list(struct ksmbd_share_config *share,
|
||||
|
||||
list_add(&p->list, &share->veto_list);
|
||||
|
||||
if (sz == veto_list_sz)
|
||||
break;
|
||||
|
||||
veto_list += sz + 1;
|
||||
veto_list_sz -= (sz + 1);
|
||||
}
|
||||
@@ -224,17 +227,28 @@ static struct ksmbd_share_config *share_config_request(struct ksmbd_work *work,
|
||||
}
|
||||
|
||||
if (!test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) {
|
||||
int path_len = PATH_MAX;
|
||||
size_t path_len;
|
||||
|
||||
if (resp->payload_sz)
|
||||
path_len = resp->payload_sz - resp->veto_list_sz;
|
||||
|
||||
share->path = kstrndup(ksmbd_share_config_path(resp), path_len,
|
||||
KSMBD_DEFAULT_GFP);
|
||||
if (!share->path) {
|
||||
ret = -ENOMEM;
|
||||
if (resp->payload_sz <= resp->veto_list_sz) {
|
||||
ret = -EINVAL;
|
||||
} else {
|
||||
ret = 0;
|
||||
path_len = resp->payload_sz - resp->veto_list_sz;
|
||||
if (resp->veto_list_sz)
|
||||
path_len--;
|
||||
|
||||
if (!path_len) {
|
||||
ret = -EINVAL;
|
||||
} else {
|
||||
share->path = kstrndup(
|
||||
ksmbd_share_config_path(resp),
|
||||
path_len, KSMBD_DEFAULT_GFP);
|
||||
if (!share->path)
|
||||
ret = -ENOMEM;
|
||||
else
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
if (share->path) {
|
||||
share->path_sz = strlen(share->path);
|
||||
while (share->path_sz > 1 &&
|
||||
share->path[share->path_sz - 1] == '/')
|
||||
|
||||
@@ -666,10 +666,21 @@ void destroy_previous_session(struct ksmbd_conn *conn,
|
||||
memcmp(user->passkey, prev_user->passkey, user->passkey_sz))
|
||||
goto out;
|
||||
|
||||
down_write(&prev_sess->chann_lock);
|
||||
if (prev_sess->tearing_down) {
|
||||
up_write(&prev_sess->chann_lock);
|
||||
goto out;
|
||||
}
|
||||
prev_sess->tearing_down = true;
|
||||
up_write(&prev_sess->chann_lock);
|
||||
|
||||
ksmbd_all_conn_set_status(prev_sess, KSMBD_SESS_NEED_RECONNECT);
|
||||
err = ksmbd_conn_wait_idle_sess(conn, prev_sess);
|
||||
if (err) {
|
||||
ksmbd_all_conn_set_status(prev_sess, KSMBD_SESS_NEED_SETUP);
|
||||
down_write(&prev_sess->chann_lock);
|
||||
prev_sess->tearing_down = false;
|
||||
up_write(&prev_sess->chann_lock);
|
||||
ksmbd_all_conn_set_status(prev_sess, KSMBD_SESS_GOOD);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ struct ksmbd_session {
|
||||
|
||||
bool sign;
|
||||
bool enc;
|
||||
bool tearing_down;
|
||||
|
||||
int state;
|
||||
__u8 *Preauth_HashValue;
|
||||
|
||||
@@ -97,6 +97,11 @@ static int register_session_channel(struct ksmbd_session *sess,
|
||||
int rc = 0;
|
||||
|
||||
down_write(&sess->chann_lock);
|
||||
if (sess->tearing_down) {
|
||||
rc = -ESHUTDOWN;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (xa_load(&sess->ksmbd_chann_list, (long)conn))
|
||||
goto out;
|
||||
|
||||
@@ -873,7 +878,8 @@ int smb2_allocate_rsp_buf(struct ksmbd_work *work)
|
||||
req = smb_get_msg(work->request_buf);
|
||||
if ((req->InfoType == SMB2_O_INFO_FILE &&
|
||||
(req->FileInfoClass == FILE_FULL_EA_INFORMATION ||
|
||||
req->FileInfoClass == FILE_ALL_INFORMATION)) ||
|
||||
req->FileInfoClass == FILE_ALL_INFORMATION ||
|
||||
req->FileInfoClass == FILE_NORMALIZED_NAME_INFORMATION)) ||
|
||||
req->InfoType == SMB2_O_INFO_SECURITY)
|
||||
sz = large_sz;
|
||||
}
|
||||
@@ -3086,17 +3092,41 @@ int smb2_session_logoff(struct ksmbd_work *work)
|
||||
smb2_set_err_rsp(work);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
down_write(&sess->chann_lock);
|
||||
if (sess->tearing_down) {
|
||||
up_write(&sess->chann_lock);
|
||||
ksmbd_conn_unlock(conn);
|
||||
rsp->hdr.Status = STATUS_USER_SESSION_DELETED;
|
||||
smb2_set_err_rsp(work);
|
||||
return -ENOENT;
|
||||
}
|
||||
sess->tearing_down = true;
|
||||
up_write(&sess->chann_lock);
|
||||
|
||||
ksmbd_all_conn_set_status(sess, KSMBD_SESS_NEED_RECONNECT);
|
||||
ksmbd_conn_unlock(conn);
|
||||
|
||||
err = ksmbd_conn_wait_idle_sess(conn, sess);
|
||||
if (err) {
|
||||
down_write(&sess->chann_lock);
|
||||
sess->tearing_down = false;
|
||||
up_write(&sess->chann_lock);
|
||||
ksmbd_all_conn_set_status(sess, KSMBD_SESS_GOOD);
|
||||
rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR;
|
||||
smb2_set_err_rsp(work);
|
||||
return err;
|
||||
}
|
||||
|
||||
ksmbd_close_session_fds(work);
|
||||
ksmbd_conn_wait_idle(conn);
|
||||
|
||||
if (ksmbd_tree_conn_session_logoff(sess)) {
|
||||
ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId);
|
||||
rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED;
|
||||
smb2_set_err_rsp(work);
|
||||
return -ENOENT;
|
||||
err = -ENOENT;
|
||||
} else {
|
||||
err = 0;
|
||||
}
|
||||
|
||||
down_write(&conn->session_lock);
|
||||
@@ -3106,6 +3136,9 @@ int smb2_session_logoff(struct ksmbd_work *work)
|
||||
|
||||
ksmbd_all_conn_set_status(sess, KSMBD_SESS_NEED_SETUP);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
rsp->StructureSize = cpu_to_le16(4);
|
||||
err = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_logoff_rsp));
|
||||
if (err) {
|
||||
@@ -6757,7 +6790,7 @@ static int get_file_normalized_name_info(struct ksmbd_work *work,
|
||||
{
|
||||
struct smb2_file_alt_name_info *file_info;
|
||||
char *filename, *normalized, *stream_name;
|
||||
int conv_len, filename_len;
|
||||
int buf_free_len, conv_len, filename_len;
|
||||
|
||||
if (work->conn->dialect < SMB311_PROT_ID) {
|
||||
rsp->hdr.Status = STATUS_NOT_SUPPORTED;
|
||||
@@ -6781,6 +6814,14 @@ static int get_file_normalized_name_info(struct ksmbd_work *work,
|
||||
return -ENOMEM;
|
||||
|
||||
filename_len = strlen(normalized);
|
||||
buf_free_len = smb2_resp_buf_len(work, sizeof(*rsp) +
|
||||
sizeof(*file_info));
|
||||
if (buf_free_len < 0 ||
|
||||
(size_t)buf_free_len < (filename_len + 1) * sizeof(__le16)) {
|
||||
kfree(normalized);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
file_info = (struct smb2_file_alt_name_info *)rsp->Buffer;
|
||||
conv_len = smbConvertToUTF16((__le16 *)file_info->FileName,
|
||||
normalized, filename_len,
|
||||
@@ -7444,6 +7485,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
|
||||
struct object_id_info *info;
|
||||
|
||||
info = (struct object_id_info *)(rsp->Buffer);
|
||||
memset(info, 0, sizeof(*info));
|
||||
|
||||
if (path.mnt->mnt_sb->s_uuid_len == 16)
|
||||
memcpy(info->objid, path.mnt->mnt_sb->s_uuid.b,
|
||||
@@ -7499,6 +7541,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
|
||||
info->FreeSpaceStopFiltering = 0;
|
||||
info->DefaultQuotaThreshold = cpu_to_le64(SMB2_NO_FID);
|
||||
info->DefaultQuotaLimit = cpu_to_le64(SMB2_NO_FID);
|
||||
info->FileSystemControlFlags = 0;
|
||||
info->Padding = 0;
|
||||
rsp->OutputBufferLength = cpu_to_le32(48);
|
||||
fixed_len = 48;
|
||||
@@ -7521,6 +7564,9 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
|
||||
info->UserBlocksAvail = cpu_to_le64(stfs.f_bavail);
|
||||
info->TotalFileNodes = cpu_to_le64(stfs.f_files);
|
||||
info->FreeFileNodes = cpu_to_le64(stfs.f_ffree);
|
||||
info->FileSysIdentifier =
|
||||
cpu_to_le64((u64)(u32)stfs.f_fsid.val[1] << 32 |
|
||||
(u32)stfs.f_fsid.val[0]);
|
||||
rsp->OutputBufferLength = cpu_to_le32(56);
|
||||
fixed_len = 56;
|
||||
}
|
||||
@@ -8620,13 +8666,18 @@ static noinline int smb2_read_pipe(struct ksmbd_work *work)
|
||||
}
|
||||
|
||||
aux_payload_buf =
|
||||
kvmalloc(rpc_resp->payload_sz, KSMBD_DEFAULT_GFP);
|
||||
kvmalloc(ALIGN(rpc_resp->payload_sz, 8),
|
||||
KSMBD_DEFAULT_GFP);
|
||||
if (!aux_payload_buf) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(aux_payload_buf, rpc_resp->payload, rpc_resp->payload_sz);
|
||||
if (rpc_resp->payload_sz & 7)
|
||||
memset(aux_payload_buf + rpc_resp->payload_sz, 0,
|
||||
ALIGN(rpc_resp->payload_sz, 8) -
|
||||
rpc_resp->payload_sz);
|
||||
|
||||
nbytes = rpc_resp->payload_sz;
|
||||
err = ksmbd_iov_pin_rsp_read(work, (void *)rsp,
|
||||
@@ -9680,14 +9731,14 @@ int smb2_cancel(struct ksmbd_work *work)
|
||||
* still on conn->async_requests with a live cancel_fn
|
||||
* pointing at the freed file_lock.
|
||||
*/
|
||||
if (iter->state != KSMBD_WORK_ACTIVE)
|
||||
if (cmpxchg(&iter->state, KSMBD_WORK_ACTIVE,
|
||||
KSMBD_WORK_CANCELLED) != KSMBD_WORK_ACTIVE)
|
||||
break;
|
||||
|
||||
ksmbd_debug(SMB,
|
||||
"smb2 with AsyncId %llu cancelled command = 0x%x\n",
|
||||
le64_to_cpu(hdr->Id.AsyncId),
|
||||
le16_to_cpu(chdr->Command));
|
||||
iter->state = KSMBD_WORK_CANCELLED;
|
||||
if (iter->cancel_fn == smb2_notify_cancel_fn)
|
||||
cancelled_notify =
|
||||
smb2_notify_cancel_claim(iter->cancel_argv);
|
||||
@@ -9716,11 +9767,16 @@ int smb2_cancel(struct ksmbd_work *work)
|
||||
iter == work)
|
||||
continue;
|
||||
|
||||
if (cmpxchg(&iter->state, KSMBD_WORK_ACTIVE,
|
||||
KSMBD_WORK_CANCELLED) != KSMBD_WORK_ACTIVE)
|
||||
break;
|
||||
|
||||
ksmbd_debug(SMB,
|
||||
"smb2 with mid %llu cancelled command = 0x%x\n",
|
||||
le64_to_cpu(hdr->MessageId),
|
||||
le16_to_cpu(chdr->Command));
|
||||
iter->state = KSMBD_WORK_CANCELLED;
|
||||
if (iter->cancel_fn)
|
||||
iter->cancel_fn(iter->cancel_argv);
|
||||
break;
|
||||
}
|
||||
spin_unlock(&conn->request_lock);
|
||||
|
||||
@@ -383,10 +383,10 @@ void free_acl_state(struct posix_acl_state *state)
|
||||
kfree(state->groups);
|
||||
}
|
||||
|
||||
static void parse_dacl(struct mnt_idmap *idmap,
|
||||
struct smb_acl *pdacl, char *end_of_acl,
|
||||
struct smb_sid *pownersid, struct smb_sid *pgrpsid,
|
||||
struct smb_fattr *fattr)
|
||||
static int parse_dacl(struct mnt_idmap *idmap,
|
||||
struct smb_acl *pdacl, char *end_of_acl,
|
||||
struct smb_sid *pownersid, struct smb_sid *pgrpsid,
|
||||
struct smb_fattr *fattr)
|
||||
{
|
||||
int i, ret;
|
||||
u16 num_aces = 0;
|
||||
@@ -400,13 +400,13 @@ static void parse_dacl(struct mnt_idmap *idmap,
|
||||
bool owner_found = false, group_found = false, others_found = false;
|
||||
|
||||
if (!pdacl)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
/* validate that we do not go past end of acl */
|
||||
if (end_of_acl < (char *)pdacl + sizeof(struct smb_acl) ||
|
||||
end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) {
|
||||
pr_err("ACL too small to parse DACL\n");
|
||||
return;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ksmbd_debug(SMB, "DACL revision %d size %d num aces %d\n",
|
||||
@@ -418,31 +418,31 @@ static void parse_dacl(struct mnt_idmap *idmap,
|
||||
|
||||
num_aces = le16_to_cpu(pdacl->num_aces);
|
||||
if (num_aces <= 0)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
dacl_size = le16_to_cpu(pdacl->size);
|
||||
if (dacl_size < sizeof(struct smb_acl))
|
||||
return;
|
||||
return -EINVAL;
|
||||
|
||||
if (num_aces > (dacl_size - sizeof(struct smb_acl)) /
|
||||
(offsetof(struct smb_ace, sid) +
|
||||
offsetof(struct smb_sid, sub_auth) + sizeof(__le16)))
|
||||
return;
|
||||
return -EINVAL;
|
||||
|
||||
ret = init_acl_state(&acl_state, num_aces);
|
||||
if (ret)
|
||||
return;
|
||||
return ret;
|
||||
ret = init_acl_state(&default_acl_state, num_aces);
|
||||
if (ret) {
|
||||
free_acl_state(&acl_state);
|
||||
return;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ppace = kmalloc_objs(struct smb_ace *, num_aces, KSMBD_DEFAULT_GFP);
|
||||
if (!ppace) {
|
||||
free_acl_state(&default_acl_state);
|
||||
free_acl_state(&acl_state);
|
||||
return;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -451,8 +451,10 @@ static void parse_dacl(struct mnt_idmap *idmap,
|
||||
* user/group/other have no permissions
|
||||
*/
|
||||
for (i = 0; i < num_aces; ++i) {
|
||||
if (end_of_acl - acl_base < acl_size)
|
||||
break;
|
||||
if (end_of_acl - acl_base < acl_size) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ppace[i] = (struct smb_ace *)(acl_base + acl_size);
|
||||
acl_base = (char *)ppace[i];
|
||||
@@ -465,8 +467,10 @@ static void parse_dacl(struct mnt_idmap *idmap,
|
||||
(end_of_acl - acl_base <
|
||||
acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth) ||
|
||||
(le16_to_cpu(ppace[i]->size) <
|
||||
acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth))
|
||||
break;
|
||||
acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
acl_size = le16_to_cpu(ppace[i]->size);
|
||||
ppace[i]->access_req =
|
||||
@@ -524,8 +528,8 @@ static void parse_dacl(struct mnt_idmap *idmap,
|
||||
temp_fattr.cf_uid = INVALID_UID;
|
||||
ret = sid_to_id(idmap, &ppace[i]->sid, SIDOWNER, &temp_fattr);
|
||||
if (ret || uid_eq(temp_fattr.cf_uid, INVALID_UID)) {
|
||||
pr_err("%s: Error %d mapping Owner SID to uid\n",
|
||||
__func__, ret);
|
||||
pr_err_ratelimited("%s: Error %d mapping Owner SID to uid\n",
|
||||
__func__, ret);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -541,7 +545,6 @@ static void parse_dacl(struct mnt_idmap *idmap,
|
||||
((acl_mode & 0700) >> 6) | 0004;
|
||||
}
|
||||
}
|
||||
kfree(ppace);
|
||||
|
||||
if (owner_found) {
|
||||
/* The owner must be set to at least read-only. */
|
||||
@@ -584,10 +587,12 @@ static void parse_dacl(struct mnt_idmap *idmap,
|
||||
fattr->cf_acls =
|
||||
posix_acl_alloc(acl_state.users->n +
|
||||
acl_state.groups->n + 4, KSMBD_DEFAULT_GFP);
|
||||
if (fattr->cf_acls) {
|
||||
cf_pace = fattr->cf_acls->a_entries;
|
||||
posix_state_to_acl(&acl_state, cf_pace);
|
||||
if (!fattr->cf_acls) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
cf_pace = fattr->cf_acls->a_entries;
|
||||
posix_state_to_acl(&acl_state, cf_pace);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -598,14 +603,20 @@ static void parse_dacl(struct mnt_idmap *idmap,
|
||||
fattr->cf_dacls =
|
||||
posix_acl_alloc(default_acl_state.users->n +
|
||||
default_acl_state.groups->n + 4, KSMBD_DEFAULT_GFP);
|
||||
if (fattr->cf_dacls) {
|
||||
cf_pdace = fattr->cf_dacls->a_entries;
|
||||
posix_state_to_acl(&default_acl_state, cf_pdace);
|
||||
if (!fattr->cf_dacls) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
cf_pdace = fattr->cf_dacls->a_entries;
|
||||
posix_state_to_acl(&default_acl_state, cf_pdace);
|
||||
}
|
||||
}
|
||||
ret = 0;
|
||||
out:
|
||||
kfree(ppace);
|
||||
free_acl_state(&acl_state);
|
||||
free_acl_state(&default_acl_state);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap,
|
||||
@@ -966,8 +977,10 @@ int parse_sec_desc(struct mnt_idmap *idmap, struct smb_ntsd *pntsd,
|
||||
if (dacloffset < sizeof(struct smb_ntsd))
|
||||
return -EINVAL;
|
||||
|
||||
parse_dacl(idmap, dacl_ptr, end_of_acl,
|
||||
owner_sid_ptr, group_sid_ptr, fattr);
|
||||
rc = parse_dacl(idmap, dacl_ptr, end_of_acl,
|
||||
owner_sid_ptr, group_sid_ptr, fattr);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -532,14 +532,21 @@ static int ipc_validate_msg(struct ipc_msg_table_entry *entry)
|
||||
if (entry->msg_sz < sizeof(struct ksmbd_share_config_response))
|
||||
return -EINVAL;
|
||||
|
||||
if (resp->payload_sz) {
|
||||
if (resp->payload_sz < resp->veto_list_sz)
|
||||
return -EINVAL;
|
||||
if (strnlen(resp->share_name, sizeof(resp->share_name)) ==
|
||||
sizeof(resp->share_name))
|
||||
return -EINVAL;
|
||||
|
||||
if (check_add_overflow(sizeof(struct ksmbd_share_config_response),
|
||||
resp->payload_sz, &msg_sz))
|
||||
return -EINVAL;
|
||||
}
|
||||
if (resp->veto_list_sz > resp->payload_sz)
|
||||
return -EINVAL;
|
||||
|
||||
if (resp->flags != KSMBD_SHARE_FLAG_INVALID &&
|
||||
!(resp->flags & KSMBD_SHARE_FLAG_PIPE) &&
|
||||
resp->payload_sz <= resp->veto_list_sz)
|
||||
return -EINVAL;
|
||||
|
||||
if (check_add_overflow(sizeof(struct ksmbd_share_config_response),
|
||||
resp->payload_sz, &msg_sz))
|
||||
return -EINVAL;
|
||||
break;
|
||||
}
|
||||
case KSMBD_EVENT_LOGIN_REQUEST_EXT:
|
||||
|
||||
@@ -39,6 +39,7 @@ struct tcp_transport {
|
||||
static const struct ksmbd_transport_ops ksmbd_tcp_transport_ops;
|
||||
|
||||
static void tcp_stop_kthread(struct task_struct *kthread);
|
||||
static void ksmbd_tcp_stop_listener(struct interface *iface);
|
||||
static struct interface *alloc_iface(char *ifname);
|
||||
static void ksmbd_tcp_disconnect(struct ksmbd_transport *t);
|
||||
|
||||
@@ -321,13 +322,20 @@ static int ksmbd_tcp_run_kthread(struct interface *iface)
|
||||
int rc;
|
||||
struct task_struct *kthread;
|
||||
|
||||
kthread = kthread_run(ksmbd_kthread_fn, (void *)iface, "ksmbd-%s",
|
||||
iface->name);
|
||||
kthread = kthread_create(ksmbd_kthread_fn, (void *)iface, "ksmbd-%s",
|
||||
iface->name);
|
||||
if (IS_ERR(kthread)) {
|
||||
rc = PTR_ERR(kthread);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* The listener can exit after its socket is shutdown, so keep the
|
||||
* task_struct alive until the caller has stopped it.
|
||||
*/
|
||||
get_task_struct(kthread);
|
||||
iface->ksmbd_kthread = kthread;
|
||||
wake_up_process(kthread);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -598,12 +606,7 @@ static int ksmbd_netdev_event(struct notifier_block *nb, unsigned long event,
|
||||
if (iface && iface->state == IFACE_STATE_CONFIGURED) {
|
||||
ksmbd_debug(CONN, "netdev-down event: netdev(%s) is going down\n",
|
||||
iface->name);
|
||||
kernel_sock_shutdown(iface->ksmbd_socket, SHUT_RDWR);
|
||||
tcp_stop_kthread(iface->ksmbd_kthread);
|
||||
iface->ksmbd_kthread = NULL;
|
||||
sock_release(iface->ksmbd_socket);
|
||||
iface->ksmbd_socket = NULL;
|
||||
|
||||
ksmbd_tcp_stop_listener(iface);
|
||||
iface->state = IFACE_STATE_DOWN;
|
||||
break;
|
||||
}
|
||||
@@ -631,11 +634,25 @@ static void tcp_stop_kthread(struct task_struct *kthread)
|
||||
if (!kthread)
|
||||
return;
|
||||
|
||||
ret = kthread_stop(kthread);
|
||||
ret = kthread_stop_put(kthread);
|
||||
if (ret)
|
||||
pr_err("failed to stop forker thread\n");
|
||||
}
|
||||
|
||||
static void ksmbd_tcp_stop_listener(struct interface *iface)
|
||||
{
|
||||
if (iface->ksmbd_socket)
|
||||
kernel_sock_shutdown(iface->ksmbd_socket, SHUT_RDWR);
|
||||
|
||||
tcp_stop_kthread(iface->ksmbd_kthread);
|
||||
iface->ksmbd_kthread = NULL;
|
||||
|
||||
if (iface->ksmbd_socket) {
|
||||
sock_release(iface->ksmbd_socket);
|
||||
iface->ksmbd_socket = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void ksmbd_tcp_destroy(void)
|
||||
{
|
||||
struct interface *iface, *tmp;
|
||||
@@ -643,6 +660,7 @@ void ksmbd_tcp_destroy(void)
|
||||
unregister_netdevice_notifier(&ksmbd_netdev_notifier);
|
||||
|
||||
list_for_each_entry_safe(iface, tmp, &iface_list, entry) {
|
||||
ksmbd_tcp_stop_listener(iface);
|
||||
list_del(&iface->entry);
|
||||
kfree(iface->name);
|
||||
kfree(iface);
|
||||
|
||||
@@ -846,12 +846,25 @@ static void set_close_state_blocked_works(struct ksmbd_file *fp)
|
||||
spin_lock(&fp->f_lock);
|
||||
list_for_each_entry(cancel_work, &fp->blocked_works,
|
||||
fp_entry) {
|
||||
cancel_work->state = KSMBD_WORK_CLOSED;
|
||||
cancel_work->cancel_fn(cancel_work->cancel_argv);
|
||||
if (xchg(&cancel_work->state, KSMBD_WORK_CLOSED) ==
|
||||
KSMBD_WORK_ACTIVE)
|
||||
cancel_work->cancel_fn(cancel_work->cancel_argv);
|
||||
}
|
||||
spin_unlock(&fp->f_lock);
|
||||
}
|
||||
|
||||
void ksmbd_wake_session_blocked_works(struct ksmbd_session *sess)
|
||||
{
|
||||
struct ksmbd_file_table *ft = &sess->file_table;
|
||||
struct ksmbd_file *fp;
|
||||
unsigned int id;
|
||||
|
||||
read_lock(&ft->lock);
|
||||
idr_for_each_entry(ft->idr, fp, id)
|
||||
set_close_state_blocked_works(fp);
|
||||
read_unlock(&ft->lock);
|
||||
}
|
||||
|
||||
int ksmbd_close_fd(struct ksmbd_work *work, u64 id)
|
||||
{
|
||||
struct ksmbd_file *fp;
|
||||
|
||||
@@ -226,6 +226,7 @@ void ksmbd_stop_durable_scavenger(void);
|
||||
bool ksmbd_durable_scavenger_active(void);
|
||||
void ksmbd_close_tree_conn_fds(struct ksmbd_work *work);
|
||||
void ksmbd_close_session_fds(struct ksmbd_work *work);
|
||||
void ksmbd_wake_session_blocked_works(struct ksmbd_session *sess);
|
||||
int ksmbd_close_inode_fds(struct ksmbd_work *work, struct inode *inode);
|
||||
int ksmbd_init_global_file_table(void);
|
||||
void ksmbd_free_global_file_table(void);
|
||||
|
||||
@@ -91,6 +91,7 @@ xfs-y += xfs_aops.o \
|
||||
xfs_healthmon.o \
|
||||
xfs_icache.o \
|
||||
xfs_ioctl.o \
|
||||
xfs_ioend.o \
|
||||
xfs_iomap.o \
|
||||
xfs_iops.o \
|
||||
xfs_inode.o \
|
||||
|
||||
@@ -359,7 +359,11 @@ static inline int xfs_initialize_rtgroups(struct xfs_mount *mp,
|
||||
# define xfs_rtgroup_unlock(rtg, gf) ((void)0)
|
||||
# define xfs_rtgroup_trans_join(tp, rtg, gf) ((void)0)
|
||||
# define xfs_update_rtsb(bp, sb_bp) ((void)0)
|
||||
# define xfs_log_rtsb(tp, sb_bp) (NULL)
|
||||
static inline struct xfs_buf *xfs_log_rtsb(struct xfs_trans *tp,
|
||||
const struct xfs_buf *sb_bp)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
# define xfs_rtgroup_get_geometry(rtg, rgeo) (-EOPNOTSUPP)
|
||||
#endif /* CONFIG_XFS_RT */
|
||||
|
||||
|
||||
@@ -1470,36 +1470,33 @@ xfs_sync_sb_buf(
|
||||
bool update_rtsb)
|
||||
{
|
||||
struct xfs_trans *tp;
|
||||
struct xfs_buf *bp;
|
||||
struct xfs_buf *rtsb_bp = NULL;
|
||||
int error;
|
||||
|
||||
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_sb, 0, 0, 0, &tp);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
bp = xfs_trans_getsb(tp);
|
||||
xfs_log_sb(tp);
|
||||
xfs_trans_bhold(tp, bp);
|
||||
if (update_rtsb) {
|
||||
rtsb_bp = xfs_log_rtsb(tp, bp);
|
||||
if (rtsb_bp)
|
||||
xfs_trans_bhold(tp, rtsb_bp);
|
||||
}
|
||||
if (update_rtsb)
|
||||
xfs_log_rtsb(tp, xfs_trans_getsb(tp));
|
||||
xfs_trans_set_sync(tp);
|
||||
error = xfs_trans_commit(tp);
|
||||
if (error)
|
||||
goto out;
|
||||
/*
|
||||
* write out the sb buffer to get the changes to disk
|
||||
*/
|
||||
error = xfs_bwrite(bp);
|
||||
if (!error && rtsb_bp)
|
||||
error = xfs_bwrite(rtsb_bp);
|
||||
out:
|
||||
if (rtsb_bp)
|
||||
xfs_buf_relse(rtsb_bp);
|
||||
xfs_buf_relse(bp);
|
||||
return error;
|
||||
|
||||
/* Re-acquire and write the sb and rtsb to disk. */
|
||||
xfs_buf_lock(mp->m_sb_bp);
|
||||
error = xfs_bwrite(mp->m_sb_bp);
|
||||
xfs_buf_unlock(mp->m_sb_bp);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (update_rtsb && mp->m_rtsb_bp) {
|
||||
xfs_buf_lock(mp->m_rtsb_bp);
|
||||
error = xfs_bwrite(mp->m_rtsb_bp);
|
||||
xfs_buf_unlock(mp->m_rtsb_bp);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "xfs_errortag.h"
|
||||
#include "xfs_error.h"
|
||||
#include "xfs_icache.h"
|
||||
#include "xfs_ioend.h"
|
||||
#include "xfs_zone_alloc.h"
|
||||
#include "xfs_rtgroup.h"
|
||||
#include <linux/bio-integrity.h>
|
||||
@@ -36,15 +37,6 @@ XFS_WPC(struct iomap_writepage_ctx *ctx)
|
||||
return container_of(ctx, struct xfs_writepage_ctx, ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fast and loose check if this write could update the on-disk inode size.
|
||||
*/
|
||||
static inline bool xfs_ioend_is_append(struct iomap_ioend *ioend)
|
||||
{
|
||||
return ioend->io_offset + ioend->io_size >
|
||||
XFS_I(ioend->io_inode)->i_disk_size;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update on-disk file size now that data has been written to disk.
|
||||
*/
|
||||
@@ -80,175 +72,6 @@ xfs_setfilesize(
|
||||
return xfs_trans_commit(tp);
|
||||
}
|
||||
|
||||
static void
|
||||
xfs_ioend_put_open_zones(
|
||||
struct iomap_ioend *ioend)
|
||||
{
|
||||
struct iomap_ioend *tmp;
|
||||
|
||||
/*
|
||||
* Put the open zone for all ioends merged into this one (if any).
|
||||
*/
|
||||
list_for_each_entry(tmp, &ioend->io_list, io_list)
|
||||
xfs_open_zone_put(tmp->io_private);
|
||||
|
||||
/*
|
||||
* The main ioend might not have an open zone if the submission failed
|
||||
* before xfs_zone_alloc_and_submit got called.
|
||||
*/
|
||||
if (ioend->io_private)
|
||||
xfs_open_zone_put(ioend->io_private);
|
||||
}
|
||||
|
||||
/*
|
||||
* IO write completion.
|
||||
*/
|
||||
STATIC void
|
||||
xfs_end_ioend_write(
|
||||
struct iomap_ioend *ioend)
|
||||
{
|
||||
struct xfs_inode *ip = XFS_I(ioend->io_inode);
|
||||
struct xfs_mount *mp = ip->i_mount;
|
||||
bool is_zoned = xfs_is_zoned_inode(ip);
|
||||
xfs_off_t offset = ioend->io_offset;
|
||||
size_t size = ioend->io_size;
|
||||
unsigned int nofs_flag;
|
||||
int error;
|
||||
|
||||
/*
|
||||
* We can allocate memory here while doing writeback on behalf of
|
||||
* memory reclaim. To avoid memory allocation deadlocks set the
|
||||
* task-wide nofs context for the following operations.
|
||||
*/
|
||||
nofs_flag = memalloc_nofs_save();
|
||||
|
||||
/*
|
||||
* Just clean up the in-memory structures if the fs has been shut down.
|
||||
*/
|
||||
if (xfs_is_shutdown(mp)) {
|
||||
error = -EIO;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clean up all COW blocks and underlying data fork delalloc blocks on
|
||||
* I/O error. The delalloc punch is required because this ioend was
|
||||
* mapped to blocks in the COW fork and the associated pages are no
|
||||
* longer dirty. If we don't remove delalloc blocks here, they become
|
||||
* stale and can corrupt free space accounting on unmount.
|
||||
*/
|
||||
error = blk_status_to_errno(ioend->io_bio.bi_status);
|
||||
if (unlikely(error)) {
|
||||
/*
|
||||
* Zoned writes update the in-core open zone accounting before
|
||||
* I/O submission. A failed write leaves that state
|
||||
* inconsistent, so shut down the filesystem instead of letting
|
||||
* later writers wait forever for open zone space to become
|
||||
* available.
|
||||
*/
|
||||
if (is_zoned) {
|
||||
xfs_force_shutdown(mp, SHUTDOWN_META_IO_ERROR);
|
||||
goto done;
|
||||
}
|
||||
if (ioend->io_flags & IOMAP_IOEND_SHARED) {
|
||||
ASSERT(!is_zoned);
|
||||
xfs_reflink_cancel_cow_range(ip, offset, size, true);
|
||||
xfs_bmap_punch_delalloc_range(ip, XFS_DATA_FORK, offset,
|
||||
offset + size, NULL);
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
* Success: commit the COW or unwritten blocks if needed.
|
||||
*/
|
||||
if (is_zoned)
|
||||
error = xfs_zoned_end_io(ip, offset, size, ioend->io_sector,
|
||||
ioend->io_private, NULLFSBLOCK);
|
||||
else if (ioend->io_flags & IOMAP_IOEND_SHARED)
|
||||
error = xfs_reflink_end_cow(ip, offset, size);
|
||||
else if (ioend->io_flags & IOMAP_IOEND_UNWRITTEN)
|
||||
error = xfs_iomap_write_unwritten(ip, offset, size, false);
|
||||
|
||||
if (!error &&
|
||||
!(ioend->io_flags & IOMAP_IOEND_DIRECT) &&
|
||||
xfs_ioend_is_append(ioend))
|
||||
error = xfs_setfilesize(ip, offset, size);
|
||||
done:
|
||||
if (is_zoned)
|
||||
xfs_ioend_put_open_zones(ioend);
|
||||
iomap_finish_ioends(ioend, error);
|
||||
memalloc_nofs_restore(nofs_flag);
|
||||
}
|
||||
|
||||
/*
|
||||
* Finish all pending IO completions that require transactional modifications.
|
||||
*
|
||||
* We try to merge physical and logically contiguous ioends before completion to
|
||||
* minimise the number of transactions we need to perform during IO completion.
|
||||
* Both unwritten extent conversion and COW remapping need to iterate and modify
|
||||
* one physical extent at a time, so we gain nothing by merging physically
|
||||
* discontiguous extents here.
|
||||
*
|
||||
* The ioend chain length that we can be processing here is largely unbound in
|
||||
* length and we may have to perform significant amounts of work on each ioend
|
||||
* to complete it. Hence we have to be careful about holding the CPU for too
|
||||
* long in this loop.
|
||||
*/
|
||||
void
|
||||
xfs_end_io(
|
||||
struct work_struct *work)
|
||||
{
|
||||
struct xfs_inode *ip =
|
||||
container_of(work, struct xfs_inode, i_ioend_work);
|
||||
struct iomap_ioend *ioend;
|
||||
struct list_head tmp;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ip->i_ioend_lock, flags);
|
||||
list_replace_init(&ip->i_ioend_list, &tmp);
|
||||
spin_unlock_irqrestore(&ip->i_ioend_lock, flags);
|
||||
|
||||
iomap_sort_ioends(&tmp);
|
||||
while ((ioend = list_first_entry_or_null(&tmp, struct iomap_ioend,
|
||||
io_list))) {
|
||||
list_del_init(&ioend->io_list);
|
||||
iomap_ioend_try_merge(ioend, &tmp);
|
||||
if (bio_op(&ioend->io_bio) == REQ_OP_READ)
|
||||
iomap_finish_ioends(ioend,
|
||||
blk_status_to_errno(ioend->io_bio.bi_status));
|
||||
else
|
||||
xfs_end_ioend_write(ioend);
|
||||
cond_resched();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
xfs_end_bio(
|
||||
struct bio *bio)
|
||||
{
|
||||
struct iomap_ioend *ioend = iomap_ioend_from_bio(bio);
|
||||
struct xfs_inode *ip = XFS_I(ioend->io_inode);
|
||||
struct xfs_mount *mp = ip->i_mount;
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* For Appends record the actually written block number and set the
|
||||
* boundary flag if needed.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_XFS_RT) && bio_is_zone_append(bio)) {
|
||||
ioend->io_sector = bio->bi_iter.bi_sector;
|
||||
xfs_mark_rtg_boundary(ioend);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&ip->i_ioend_lock, flags);
|
||||
if (list_empty(&ip->i_ioend_list))
|
||||
WARN_ON_ONCE(!queue_work(mp->m_unwritten_workqueue,
|
||||
&ip->i_ioend_work));
|
||||
list_add_tail(&ioend->io_list, &ip->i_ioend_list);
|
||||
spin_unlock_irqrestore(&ip->i_ioend_lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* We cannot cancel the ioend directly on error. We may have already set other
|
||||
* pages under writeback and hence we have to run I/O completion to mark the
|
||||
@@ -631,13 +454,8 @@ xfs_zoned_map_blocks(
|
||||
XFS_BMAPI_REMAP);
|
||||
xfs_iunlock(ip, XFS_ILOCK_EXCL);
|
||||
|
||||
wpc->iomap.type = IOMAP_MAPPED;
|
||||
wpc->iomap.flags = IOMAP_F_DIRTY;
|
||||
wpc->iomap.bdev = mp->m_rtdev_targp->bt_bdev;
|
||||
wpc->iomap.offset = offset;
|
||||
wpc->iomap.length = XFS_FSB_TO_B(mp, count_fsb);
|
||||
wpc->iomap.flags = IOMAP_F_ANON_WRITE;
|
||||
|
||||
xfs_iomap_set_anon_write(ip, &wpc->iomap, offset,
|
||||
XFS_FSB_TO_B(mp, count_fsb));
|
||||
trace_xfs_zoned_map_blocks(ip, offset, wpc->iomap.length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,5 @@ extern const struct address_space_operations xfs_address_space_operations;
|
||||
extern const struct address_space_operations xfs_dax_aops;
|
||||
|
||||
int xfs_setfilesize(struct xfs_inode *ip, xfs_off_t offset, size_t size);
|
||||
void xfs_end_bio(struct bio *bio);
|
||||
|
||||
#endif /* __XFS_AOPS_H__ */
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#include "xfs_iomap.h"
|
||||
#include "xfs_reflink.h"
|
||||
#include "xfs_file.h"
|
||||
#include "xfs_aops.h"
|
||||
#include "xfs_ioend.h"
|
||||
#include "xfs_zone_alloc.h"
|
||||
#include "xfs_error.h"
|
||||
#include "xfs_errortag.h"
|
||||
|
||||
@@ -1174,8 +1174,7 @@ xfs_getfsmap(
|
||||
if (!xfs_getfsmap_check_keys(&head->fmh_keys[0], &head->fmh_keys[1]))
|
||||
return -EINVAL;
|
||||
|
||||
use_rmap = xfs_has_rmapbt(mp) &&
|
||||
has_capability_noaudit(current, CAP_SYS_ADMIN);
|
||||
use_rmap = xfs_has_rmapbt(mp) && capable_noaudit(CAP_SYS_ADMIN);
|
||||
head->fmh_entries = 0;
|
||||
|
||||
/* Set up our device handlers. */
|
||||
|
||||
@@ -82,24 +82,20 @@ static inline xa_mark_t ici_tag_to_mark(unsigned int tag)
|
||||
|
||||
/*
|
||||
* Allocate and initialise an xfs_inode.
|
||||
*
|
||||
* This can happen in context of already dirtied transactions, so the memory
|
||||
* allocations must not fail.
|
||||
*/
|
||||
struct xfs_inode *
|
||||
xfs_inode_alloc(
|
||||
struct xfs_mount *mp,
|
||||
xfs_ino_t ino)
|
||||
{
|
||||
gfp_t gfp = GFP_KERNEL | __GFP_NOFAIL;
|
||||
struct xfs_inode *ip;
|
||||
|
||||
/*
|
||||
* XXX: If this didn't occur in transactions, we could drop GFP_NOFAIL
|
||||
* and return NULL here on ENOMEM.
|
||||
*/
|
||||
ip = alloc_inode_sb(mp->m_super, xfs_inode_cache, GFP_KERNEL | __GFP_NOFAIL);
|
||||
|
||||
if (inode_init_always(mp->m_super, VFS_I(ip))) {
|
||||
kmem_cache_free(xfs_inode_cache, ip);
|
||||
return NULL;
|
||||
}
|
||||
ip = alloc_inode_sb(mp->m_super, xfs_inode_cache, gfp);
|
||||
inode_init_always_gfp(mp->m_super, VFS_I(ip), gfp);
|
||||
|
||||
VFS_I(ip)->i_ino = ino;
|
||||
/* VFS doesn't initialise i_mode! */
|
||||
|
||||
@@ -647,7 +647,7 @@ xfs_ioctl_setattr_get_trans(
|
||||
goto out_error;
|
||||
|
||||
error = xfs_trans_alloc_ichange(ip, NULL, NULL, pdqp,
|
||||
has_capability_noaudit(current, CAP_FOWNER), &tp);
|
||||
capable_noaudit(CAP_FOWNER), &tp);
|
||||
if (error)
|
||||
goto out_error;
|
||||
|
||||
|
||||
184
fs/xfs/xfs_ioend.c
Normal file
184
fs/xfs/xfs_ioend.c
Normal file
@@ -0,0 +1,184 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2016-2025 Christoph Hellwig.
|
||||
* All Rights Reserved.
|
||||
*/
|
||||
#include "xfs_platform.h"
|
||||
#include "xfs_shared.h"
|
||||
#include "xfs_format.h"
|
||||
#include "xfs_log_format.h"
|
||||
#include "xfs_trans_resv.h"
|
||||
#include "xfs_mount.h"
|
||||
#include "xfs_inode.h"
|
||||
#include "xfs_iomap.h"
|
||||
#include "xfs_trace.h"
|
||||
#include "xfs_bmap_util.h"
|
||||
#include "xfs_reflink.h"
|
||||
#include "xfs_zone_alloc.h"
|
||||
#include "xfs_ioend.h"
|
||||
|
||||
static void
|
||||
xfs_ioend_put_open_zones(
|
||||
struct iomap_ioend *ioend)
|
||||
{
|
||||
struct iomap_ioend *tmp;
|
||||
|
||||
/*
|
||||
* Put the open zone for all ioends merged into this one (if any).
|
||||
*/
|
||||
list_for_each_entry(tmp, &ioend->io_list, io_list)
|
||||
xfs_open_zone_put(tmp->io_private);
|
||||
|
||||
/*
|
||||
* The main ioend might not have an open zone if the submission failed
|
||||
* before xfs_zone_alloc_and_submit got called.
|
||||
*/
|
||||
if (ioend->io_private)
|
||||
xfs_open_zone_put(ioend->io_private);
|
||||
}
|
||||
|
||||
static void
|
||||
xfs_end_ioend_write(
|
||||
struct iomap_ioend *ioend)
|
||||
{
|
||||
struct xfs_inode *ip = XFS_I(ioend->io_inode);
|
||||
struct xfs_mount *mp = ip->i_mount;
|
||||
bool is_zoned = xfs_is_zoned_inode(ip);
|
||||
xfs_off_t offset = ioend->io_offset;
|
||||
size_t size = ioend->io_size;
|
||||
unsigned int nofs_flag;
|
||||
int error;
|
||||
|
||||
/*
|
||||
* We can allocate memory here while doing writeback on behalf of
|
||||
* memory reclaim. To avoid memory allocation deadlocks set the
|
||||
* task-wide nofs context for the following operations.
|
||||
*/
|
||||
nofs_flag = memalloc_nofs_save();
|
||||
|
||||
/*
|
||||
* Just clean up the in-memory structures if the fs has been shut down.
|
||||
*/
|
||||
if (xfs_is_shutdown(mp)) {
|
||||
error = -EIO;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clean up all COW blocks and underlying data fork delalloc blocks on
|
||||
* I/O error. The delalloc punch is required because this ioend was
|
||||
* mapped to blocks in the COW fork and the associated pages are no
|
||||
* longer dirty. If we don't remove delalloc blocks here, they become
|
||||
* stale and can corrupt free space accounting on unmount.
|
||||
*/
|
||||
error = blk_status_to_errno(ioend->io_bio.bi_status);
|
||||
if (unlikely(error)) {
|
||||
/*
|
||||
* Zoned writes update the in-core open zone accounting before
|
||||
* I/O submission. A failed write leaves that state
|
||||
* inconsistent, so shut down the filesystem instead of letting
|
||||
* later writers wait forever for open zone space to become
|
||||
* available.
|
||||
*/
|
||||
if (is_zoned) {
|
||||
xfs_force_shutdown(mp, SHUTDOWN_META_IO_ERROR);
|
||||
goto done;
|
||||
}
|
||||
if (ioend->io_flags & IOMAP_IOEND_SHARED) {
|
||||
ASSERT(!is_zoned);
|
||||
xfs_reflink_cancel_cow_range(ip, offset, size, true);
|
||||
xfs_bmap_punch_delalloc_range(ip, XFS_DATA_FORK, offset,
|
||||
offset + size, NULL);
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
* Success: commit the COW or unwritten blocks if needed.
|
||||
*/
|
||||
if (is_zoned)
|
||||
error = xfs_zoned_end_io(ip, offset, size, ioend->io_sector,
|
||||
ioend->io_private, NULLFSBLOCK);
|
||||
else if (ioend->io_flags & IOMAP_IOEND_SHARED)
|
||||
error = xfs_reflink_end_cow(ip, offset, size);
|
||||
else if (ioend->io_flags & IOMAP_IOEND_UNWRITTEN)
|
||||
error = xfs_iomap_write_unwritten(ip, offset, size, false);
|
||||
|
||||
if (!error &&
|
||||
!(ioend->io_flags & IOMAP_IOEND_DIRECT) &&
|
||||
xfs_ioend_is_append(ioend))
|
||||
error = xfs_setfilesize(ip, offset, size);
|
||||
done:
|
||||
if (is_zoned)
|
||||
xfs_ioend_put_open_zones(ioend);
|
||||
iomap_finish_ioends(ioend, error);
|
||||
memalloc_nofs_restore(nofs_flag);
|
||||
}
|
||||
|
||||
/*
|
||||
* Finish all pending IO completions that require transactional modifications.
|
||||
*
|
||||
* We try to merge physical and logically contiguous ioends before completion to
|
||||
* minimise the number of transactions we need to perform during IO completion.
|
||||
* Both unwritten extent conversion and COW remapping need to iterate and modify
|
||||
* one physical extent at a time, so we gain nothing by merging physically
|
||||
* discontiguous extents here.
|
||||
*
|
||||
* The ioend chain length that we can be processing here is largely unbound in
|
||||
* length and we may have to perform significant amounts of work on each ioend
|
||||
* to complete it. Hence we have to be careful about holding the CPU for too
|
||||
* long in this loop.
|
||||
*/
|
||||
void
|
||||
xfs_end_io(
|
||||
struct work_struct *work)
|
||||
{
|
||||
struct xfs_inode *ip =
|
||||
container_of(work, struct xfs_inode, i_ioend_work);
|
||||
struct iomap_ioend *ioend;
|
||||
struct list_head tmp;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ip->i_ioend_lock, flags);
|
||||
list_replace_init(&ip->i_ioend_list, &tmp);
|
||||
spin_unlock_irqrestore(&ip->i_ioend_lock, flags);
|
||||
|
||||
iomap_sort_ioends(&tmp);
|
||||
while ((ioend = list_first_entry_or_null(&tmp, struct iomap_ioend,
|
||||
io_list))) {
|
||||
list_del_init(&ioend->io_list);
|
||||
iomap_ioend_try_merge(ioend, &tmp);
|
||||
if (bio_op(&ioend->io_bio) == REQ_OP_READ)
|
||||
iomap_finish_ioends(ioend,
|
||||
blk_status_to_errno(ioend->io_bio.bi_status));
|
||||
else
|
||||
xfs_end_ioend_write(ioend);
|
||||
cond_resched();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
xfs_end_bio(
|
||||
struct bio *bio)
|
||||
{
|
||||
struct iomap_ioend *ioend = iomap_ioend_from_bio(bio);
|
||||
struct xfs_inode *ip = XFS_I(ioend->io_inode);
|
||||
struct xfs_mount *mp = ip->i_mount;
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* For Appends record the actually written block number and set the
|
||||
* boundary flag if needed.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_XFS_RT) && bio_is_zone_append(bio)) {
|
||||
ioend->io_sector = bio->bi_iter.bi_sector;
|
||||
xfs_mark_rtg_boundary(ioend);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&ip->i_ioend_lock, flags);
|
||||
if (list_empty(&ip->i_ioend_list))
|
||||
WARN_ON_ONCE(!queue_work(mp->m_unwritten_workqueue,
|
||||
&ip->i_ioend_work));
|
||||
list_add_tail(&ioend->io_list, &ip->i_ioend_list);
|
||||
spin_unlock_irqrestore(&ip->i_ioend_lock, flags);
|
||||
}
|
||||
16
fs/xfs/xfs_ioend.h
Normal file
16
fs/xfs/xfs_ioend.h
Normal file
@@ -0,0 +1,16 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef __XFS_IOEND_H
|
||||
#define __XFS_IOEND_H
|
||||
|
||||
/*
|
||||
* Fast and loose check if this write could update the on-disk inode size.
|
||||
*/
|
||||
static inline bool xfs_ioend_is_append(struct iomap_ioend *ioend)
|
||||
{
|
||||
return ioend->io_offset + ioend->io_size >
|
||||
XFS_I(ioend->io_inode)->i_disk_size;
|
||||
}
|
||||
|
||||
void xfs_end_bio(struct bio *bio);
|
||||
|
||||
#endif /* __XFS_IOEND_H */
|
||||
@@ -1083,12 +1083,7 @@ xfs_zoned_direct_write_iomap_begin(
|
||||
return error;
|
||||
}
|
||||
|
||||
iomap->type = IOMAP_MAPPED;
|
||||
iomap->flags = IOMAP_F_DIRTY;
|
||||
iomap->bdev = ip->i_mount->m_rtdev_targp->bt_bdev;
|
||||
iomap->offset = offset;
|
||||
iomap->length = length;
|
||||
iomap->flags = IOMAP_F_ANON_WRITE;
|
||||
xfs_iomap_set_anon_write(ip, iomap, offset, length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,20 @@ int xfs_zero_range(struct xfs_inode *ip, loff_t pos, loff_t len,
|
||||
int xfs_truncate_page(struct xfs_inode *ip, loff_t pos,
|
||||
struct xfs_zone_alloc_ctx *ac, bool *did_zero);
|
||||
|
||||
static inline void
|
||||
xfs_iomap_set_anon_write(
|
||||
struct xfs_inode *ip,
|
||||
struct iomap *iomap,
|
||||
loff_t offset,
|
||||
loff_t length)
|
||||
{
|
||||
iomap->type = IOMAP_MAPPED;
|
||||
iomap->bdev = ip->i_mount->m_rtdev_targp->bt_bdev;
|
||||
iomap->offset = offset;
|
||||
iomap->length = length;
|
||||
iomap->flags = IOMAP_F_ANON_WRITE | IOMAP_F_DIRTY;
|
||||
}
|
||||
|
||||
static inline xfs_filblks_t
|
||||
xfs_aligned_fsb_count(
|
||||
xfs_fileoff_t offset_fsb,
|
||||
|
||||
@@ -834,7 +834,7 @@ xfs_setattr_nonsize(
|
||||
}
|
||||
|
||||
error = xfs_trans_alloc_ichange(ip, udqp, gdqp, NULL,
|
||||
has_capability_noaudit(current, CAP_FOWNER), &tp);
|
||||
capable_noaudit(CAP_FOWNER), &tp);
|
||||
if (error)
|
||||
goto out_dqrele;
|
||||
|
||||
|
||||
@@ -289,15 +289,4 @@ int xfs_rw_bdev(struct block_device *bdev, sector_t sector, unsigned int count,
|
||||
# define PTR_FMT "%p"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Helper for IO routines to grab backing pages from allocated kernel memory.
|
||||
*/
|
||||
static inline struct page *
|
||||
kmem_to_page(void *addr)
|
||||
{
|
||||
if (is_vmalloc_addr(addr))
|
||||
return vmalloc_to_page(addr);
|
||||
return virt_to_page(addr);
|
||||
}
|
||||
|
||||
#endif /* _XFS_PLATFORM_H */
|
||||
|
||||
@@ -445,7 +445,7 @@ xfs_shutdown_devices(
|
||||
blkdev_issue_flush(mp->m_logdev_targp->bt_bdev);
|
||||
invalidate_bdev(mp->m_logdev_targp->bt_bdev);
|
||||
}
|
||||
if (mp->m_rtdev_targp) {
|
||||
if (mp->m_rtdev_targp && mp->m_rtdev_targp != mp->m_ddev_targp) {
|
||||
blkdev_issue_flush(mp->m_rtdev_targp->bt_bdev);
|
||||
invalidate_bdev(mp->m_rtdev_targp->bt_bdev);
|
||||
}
|
||||
|
||||
@@ -521,7 +521,8 @@ xfs_trans_log_buf(
|
||||
{
|
||||
struct xfs_buf_log_item *bip = bp->b_log_item;
|
||||
|
||||
ASSERT(first <= last && last < BBTOB(bp->b_length));
|
||||
ASSERT(first <= last);
|
||||
ASSERT(last < BBTOB(bp->b_length));
|
||||
ASSERT(!(bip->bli_flags & XFS_BLI_ORDERED));
|
||||
|
||||
xfs_trans_dirty_buf(tp, bp);
|
||||
|
||||
@@ -793,17 +793,35 @@ xfs_get_cached_zone(
|
||||
|
||||
rcu_read_lock();
|
||||
oz = VFS_I(ip)->i_private;
|
||||
if (oz) {
|
||||
/*
|
||||
* GC only steals open zones at mount time, so no GC zones
|
||||
* should end up in the cache.
|
||||
*/
|
||||
ASSERT(!oz->oz_is_gc);
|
||||
if (!atomic_inc_not_zero(&oz->oz_ref))
|
||||
oz = NULL;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
if (!oz)
|
||||
goto out_unlock;
|
||||
|
||||
/*
|
||||
* GC only steals open zones at mount time, so no GC zones should end up
|
||||
* in the cache.
|
||||
*/
|
||||
ASSERT(!oz->oz_is_gc);
|
||||
|
||||
/*
|
||||
* Drop the old cached open zone if it is full.
|
||||
*/
|
||||
if (oz->oz_allocated == rtg_blocks(oz->oz_rtg)) {
|
||||
spin_lock(&ip->i_flags_lock);
|
||||
oz = VFS_I(ip)->i_private;
|
||||
if (oz && oz->oz_allocated == rtg_blocks(oz->oz_rtg)) {
|
||||
VFS_I(ip)->i_private = NULL;
|
||||
spin_unlock(&ip->i_flags_lock);
|
||||
xfs_open_zone_put(oz);
|
||||
oz = NULL;
|
||||
goto out_unlock;
|
||||
}
|
||||
spin_unlock(&ip->i_flags_lock);
|
||||
}
|
||||
|
||||
if (!atomic_inc_not_zero(&oz->oz_ref))
|
||||
oz = NULL;
|
||||
out_unlock:
|
||||
rcu_read_unlock();
|
||||
return oz;
|
||||
}
|
||||
|
||||
@@ -818,18 +836,41 @@ xfs_get_cached_zone(
|
||||
* that were every written to, but significantly simplifies the cached zone
|
||||
* lookup. Because the open_zone is clearly marked as full when all data
|
||||
* in the underlying RTG was written, the caching is always safe.
|
||||
*
|
||||
* Called with a reference on @oz held. And returns two references on the
|
||||
* returned zone: one for the caller and one for pinning the zone in
|
||||
* inode->i_private.
|
||||
*/
|
||||
static void
|
||||
static struct xfs_open_zone *
|
||||
xfs_set_cached_zone(
|
||||
struct xfs_inode *ip,
|
||||
struct xfs_open_zone *oz)
|
||||
{
|
||||
struct xfs_open_zone *old_oz;
|
||||
|
||||
/*
|
||||
* If the open zone cached in the inode still has free space, use that
|
||||
* instead of the new open zone just selected. This can happen when
|
||||
* multiple threads race to perform zone selection for an inode.
|
||||
* io_uring worker threads seem to be good way to trigger this.
|
||||
*
|
||||
* We need to grab an extra reference to this open zone as the caller
|
||||
* owns a reference in addition to the i_private pointer.
|
||||
*/
|
||||
spin_lock(&ip->i_flags_lock);
|
||||
old_oz = VFS_I(ip)->i_private;
|
||||
if (old_oz && old_oz->oz_allocated < rtg_blocks(old_oz->oz_rtg) &&
|
||||
atomic_inc_not_zero(&old_oz->oz_ref)) {
|
||||
spin_unlock(&ip->i_flags_lock);
|
||||
xfs_open_zone_put(oz);
|
||||
return old_oz;
|
||||
}
|
||||
VFS_I(ip)->i_private = oz;
|
||||
atomic_inc(&oz->oz_ref);
|
||||
old_oz = xchg(&VFS_I(ip)->i_private, oz);
|
||||
spin_unlock(&ip->i_flags_lock);
|
||||
if (old_oz)
|
||||
xfs_open_zone_put(old_oz);
|
||||
return oz;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -873,14 +914,13 @@ xfs_zone_alloc_and_submit(
|
||||
* the inode is still associated with a zone and use that if so.
|
||||
*/
|
||||
if (!*oz)
|
||||
*oz = xfs_get_cached_zone(ip);
|
||||
|
||||
if (!*oz) {
|
||||
select_zone:
|
||||
*oz = xfs_get_cached_zone(ip);
|
||||
if (!*oz) {
|
||||
*oz = xfs_select_zone(mp, write_hint, pack_tight);
|
||||
if (!*oz)
|
||||
goto out_error;
|
||||
xfs_set_cached_zone(ip, *oz);
|
||||
*oz = xfs_set_cached_zone(ip, *oz);
|
||||
}
|
||||
|
||||
alloc_len = xfs_zone_alloc_blocks(*oz, XFS_B_TO_FSB(mp, ioend->io_size),
|
||||
|
||||
@@ -869,6 +869,11 @@ xfs_zone_gc_write_chunk(
|
||||
WRITE_ONCE(chunk->state, XFS_GC_BIO_NEW);
|
||||
list_move_tail(&chunk->entry, &data->writing);
|
||||
|
||||
/*
|
||||
* If we run on top of stacked block device, the read I/O might have
|
||||
* reset bi_bdev, restore it to the one we want.
|
||||
*/
|
||||
bio_set_dev(&chunk->bio, mp->m_rtdev_targp->bt_bdev);
|
||||
bio_reuse(&chunk->bio, REQ_OP_WRITE);
|
||||
while ((split_chunk = xfs_zone_gc_split_write(data, chunk)))
|
||||
xfs_zone_gc_submit_write(data, split_chunk);
|
||||
|
||||
@@ -145,6 +145,7 @@ extern bool has_capability_noaudit(struct task_struct *t, int cap);
|
||||
extern bool has_ns_capability_noaudit(struct task_struct *t,
|
||||
struct user_namespace *ns, int cap);
|
||||
extern bool capable(int cap);
|
||||
bool capable_noaudit(int cap);
|
||||
extern bool ns_capable(struct user_namespace *ns, int cap);
|
||||
extern bool ns_capable_noaudit(struct user_namespace *ns, int cap);
|
||||
extern bool ns_capable_setid(struct user_namespace *ns, int cap);
|
||||
@@ -167,6 +168,10 @@ static inline bool capable(int cap)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
static inline bool capable_noaudit(int cap)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
static inline bool ns_capable(struct user_namespace *ns, int cap)
|
||||
{
|
||||
return true;
|
||||
|
||||
@@ -527,7 +527,10 @@ struct cgroup {
|
||||
|
||||
int nr_threaded_children; /* # of live threaded child cgroups */
|
||||
|
||||
/* sequence number for cgroup.kill, serialized by css_set_lock. */
|
||||
/*
|
||||
* Sequence number for cgroup.kill. Incremented with both cgroup_mutex
|
||||
* and css_set_lock held. Readers hold either one.
|
||||
*/
|
||||
unsigned int kill_seq;
|
||||
|
||||
struct kernfs_node *kn; /* cgroup kernfs entry */
|
||||
|
||||
@@ -598,9 +598,6 @@ struct mem_ctl_info {
|
||||
int op_state;
|
||||
|
||||
struct dentry *debugfs;
|
||||
u8 fake_inject_layer[EDAC_MAX_LAYERS];
|
||||
bool fake_inject_ue;
|
||||
u16 fake_inject_count;
|
||||
|
||||
/*
|
||||
* Memory Controller hierarchy
|
||||
|
||||
@@ -326,7 +326,6 @@ bool has_capability_noaudit(struct task_struct *t, int cap)
|
||||
{
|
||||
return has_ns_capability_noaudit(t, &init_user_ns, cap);
|
||||
}
|
||||
EXPORT_SYMBOL(has_capability_noaudit);
|
||||
|
||||
static bool ns_capable_common(struct user_namespace *ns,
|
||||
int cap,
|
||||
@@ -416,6 +415,24 @@ bool capable(int cap)
|
||||
return ns_capable(&init_user_ns, cap);
|
||||
}
|
||||
EXPORT_SYMBOL(capable);
|
||||
|
||||
/**
|
||||
* capable_noaudit - Determine if the current task has a superior
|
||||
* capability in effect by checking the process's effective
|
||||
* capabilities (unaudited).
|
||||
* @cap: The capability to be tested for
|
||||
*
|
||||
* This is the same as capable(), except it uses CAP_OPT_NOAUDIT as to prevent
|
||||
* issuing spurious audit messages.
|
||||
*
|
||||
* This sets PF_SUPERPRIV on the task if the capability is available on the
|
||||
* assumption that it's about to be used.
|
||||
*/
|
||||
bool capable_noaudit(int cap)
|
||||
{
|
||||
return ns_capable_noaudit(&init_user_ns, cap);
|
||||
}
|
||||
EXPORT_SYMBOL(capable_noaudit);
|
||||
#endif /* CONFIG_MULTIUSER */
|
||||
|
||||
/**
|
||||
|
||||
@@ -6873,10 +6873,7 @@ static int cgroup_css_set_fork(struct kernel_clone_args *kargs)
|
||||
spin_lock_irq(&css_set_lock);
|
||||
cset = task_css_set(current);
|
||||
get_css_set(cset);
|
||||
if (kargs->cgrp)
|
||||
kargs->kill_seq = kargs->cgrp->kill_seq;
|
||||
else
|
||||
kargs->kill_seq = cset->dfl_cgrp->kill_seq;
|
||||
kargs->kill_seq = cset->dfl_cgrp->kill_seq;
|
||||
spin_unlock_irq(&css_set_lock);
|
||||
|
||||
if (!(kargs->flags & CLONE_INTO_CGROUP)) {
|
||||
@@ -6940,6 +6937,7 @@ static int cgroup_css_set_fork(struct kernel_clone_args *kargs)
|
||||
|
||||
put_css_set(cset);
|
||||
kargs->cgrp = dst_cgrp;
|
||||
kargs->kill_seq = dst_cgrp->kill_seq;
|
||||
return ret;
|
||||
|
||||
err:
|
||||
|
||||
@@ -1259,6 +1259,28 @@ static void reset_partition_data(struct cpuset *cs)
|
||||
cpumask_copy(cs->effective_cpus, parent->effective_cpus);
|
||||
}
|
||||
|
||||
/* Return true if isolated_cpus changes. */
|
||||
static bool isolated_cpu_update(int new_prs, int cpu)
|
||||
{
|
||||
lockdep_assert_held(&callback_lock);
|
||||
lockdep_assert_held(&cpuset_mutex);
|
||||
|
||||
if (new_prs == PRS_ISOLATED) {
|
||||
if (cpumask_test_cpu(cpu, isolated_cpus))
|
||||
return false;
|
||||
cpumask_set_cpu(cpu, isolated_cpus);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* CPUs isolated at boot must remain isolated. */
|
||||
if (!cpumask_test_cpu(cpu,
|
||||
housekeeping_cpumask(HK_TYPE_DOMAIN_BOOT)) ||
|
||||
!cpumask_test_cpu(cpu, isolated_cpus))
|
||||
return false;
|
||||
cpumask_clear_cpu(cpu, isolated_cpus);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* isolated_cpus_update - Update the isolated_cpus mask
|
||||
* @old_prs: old partition_root_state
|
||||
@@ -1267,19 +1289,16 @@ static void reset_partition_data(struct cpuset *cs)
|
||||
*/
|
||||
static void isolated_cpus_update(int old_prs, int new_prs, struct cpumask *xcpus)
|
||||
{
|
||||
bool updated = false;
|
||||
int cpu;
|
||||
|
||||
WARN_ON_ONCE(old_prs == new_prs);
|
||||
lockdep_assert_held(&callback_lock);
|
||||
lockdep_assert_held(&cpuset_mutex);
|
||||
if (new_prs == PRS_ISOLATED) {
|
||||
if (cpumask_subset(xcpus, isolated_cpus))
|
||||
return;
|
||||
cpumask_or(isolated_cpus, isolated_cpus, xcpus);
|
||||
} else {
|
||||
if (!cpumask_intersects(xcpus, isolated_cpus))
|
||||
return;
|
||||
cpumask_andnot(isolated_cpus, isolated_cpus, xcpus);
|
||||
}
|
||||
update_housekeeping = true;
|
||||
for_each_cpu(cpu, xcpus)
|
||||
updated |= isolated_cpu_update(new_prs, cpu);
|
||||
if (updated)
|
||||
update_housekeeping = true;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -876,9 +876,9 @@ struct task_struct *scx_task_iter_next_locked(struct scx_task_iter *iter)
|
||||
* unloading. The init_tasks ("swappers") should be excluded
|
||||
* from the iteration because:
|
||||
*
|
||||
* - It's unsafe to use __setschduler_prio() on an init_task to
|
||||
* determine the sched_class to use as it won't preserve its
|
||||
* idle_sched_class.
|
||||
* - It's unsafe to use __setscheduler_class() on an init_task
|
||||
* to determine the sched_class to use as it won't preserve
|
||||
* its idle_sched_class.
|
||||
*
|
||||
* - ops.init/exit_task() can easily be confused if called with
|
||||
* init_tasks as they, e.g., share PID 0.
|
||||
@@ -2806,6 +2806,8 @@ static void dispatch_to_local_dsq(struct scx_sched *sch, struct rq *rq,
|
||||
* @p: task to finish dispatching
|
||||
* @qseq_at_dispatch: qseq when @p started getting dispatched
|
||||
* @dsq_id: destination DSQ ID
|
||||
* @slice: slice carried by the insert verdict, 0 keeps the current value
|
||||
* @vtime: vtime carried by the insert verdict, committed on PRIQ inserts
|
||||
* @enq_flags: %SCX_ENQ_*
|
||||
*
|
||||
* Dispatching to local DSQs may need to wait for queueing to complete or
|
||||
@@ -5514,7 +5516,7 @@ static const struct kset_uevent_ops scx_uevent_ops = {
|
||||
};
|
||||
|
||||
/*
|
||||
* Used by sched_fork() and __setscheduler_prio() to pick the matching
|
||||
* Used by sched_fork() and __setscheduler_class() to pick the matching
|
||||
* sched_class. dl/rt are already handled.
|
||||
*/
|
||||
bool task_should_scx(int policy)
|
||||
@@ -7694,7 +7696,7 @@ static void scx_root_enable_workfn(struct kthread_work *work)
|
||||
/*
|
||||
* Enable ops for every task. Fork is excluded by scx_fork_rwsem
|
||||
* preventing new tasks from being added. No need to exclude tasks
|
||||
* leaving as sched_ext_free() can handle both prepped and enabled
|
||||
* leaving as sched_ext_dead() can handle both prepped and enabled
|
||||
* tasks. Prep all tasks first and then enable them with preemption
|
||||
* disabled.
|
||||
*
|
||||
@@ -7786,7 +7788,7 @@ static void scx_root_enable_workfn(struct kthread_work *work)
|
||||
|
||||
/*
|
||||
* We're fully committed and can't fail. The task READY -> ENABLED
|
||||
* transitions here are synchronized against sched_ext_free() through
|
||||
* transitions here are synchronized against sched_ext_dead() through
|
||||
* scx_tasks_lock.
|
||||
*/
|
||||
percpu_down_write(&scx_fork_rwsem);
|
||||
@@ -8079,6 +8081,7 @@ static int bpf_scx_check_member(const struct btf_type *t,
|
||||
case offsetof(struct sched_ext_ops, cgroup_init):
|
||||
case offsetof(struct sched_ext_ops, cgroup_exit):
|
||||
case offsetof(struct sched_ext_ops, cgroup_prep_move):
|
||||
case offsetof(struct sched_ext_ops, cgroup_set_bandwidth):
|
||||
#endif
|
||||
case offsetof(struct sched_ext_ops, cpu_online):
|
||||
case offsetof(struct sched_ext_ops, cpu_offline):
|
||||
@@ -9003,12 +9006,6 @@ static bool scx_dsq_move(struct bpf_iter_scx_dsq_kern *kit,
|
||||
if (unlikely(READ_ONCE(sch->aborting)))
|
||||
return false;
|
||||
|
||||
if (unlikely(!scx_task_on_sched(sch, p))) {
|
||||
scx_error(sch, "scx_bpf_dsq_move[_vtime]() on %s[%d] but the task belongs to a different scheduler",
|
||||
p->comm, p->pid);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Can be called from either ops.dispatch() holding the dispatched rq's
|
||||
* lock or any context where no rq lock is held. If latter, lock @p's
|
||||
@@ -9040,6 +9037,17 @@ static bool scx_dsq_move(struct bpf_iter_scx_dsq_kern *kit,
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* @p has been on $src_dsq and can't move anymore. If @p is not on @sch,
|
||||
* the caller didn't have authority over @p at the time of the call.
|
||||
*/
|
||||
if (unlikely(!scx_task_on_sched(sch, p))) {
|
||||
scx_error(sch, "scx_bpf_dsq_move[_vtime]() on %s[%d] but the task belongs to a different scheduler",
|
||||
p->comm, p->pid);
|
||||
raw_spin_unlock(&src_dsq->lock);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* @p is still on $src_dsq and stable, determine the destination */
|
||||
dst_dsq = find_dsq_for_dispatch(sch, locked_rq ?: this_rq(), dsq_id, task_cpu(p));
|
||||
|
||||
@@ -9765,7 +9773,7 @@ __bpf_kfunc struct task_struct *bpf_iter_scx_dsq_next(struct bpf_iter_scx_dsq *i
|
||||
* bpf_iter_scx_dsq_destroy - Destroy a DSQ iterator
|
||||
* @it: iterator to destroy
|
||||
*
|
||||
* Undo scx_iter_scx_dsq_new().
|
||||
* Undo bpf_iter_scx_dsq_new().
|
||||
*/
|
||||
__bpf_kfunc void bpf_iter_scx_dsq_destroy(struct bpf_iter_scx_dsq *it)
|
||||
{
|
||||
@@ -11041,3 +11049,16 @@ static int __init scx_init(void)
|
||||
return 0;
|
||||
}
|
||||
__initcall(scx_init);
|
||||
|
||||
/*
|
||||
* Compatibility markers for userspace. Existence of a marker function
|
||||
* represents that the kernel supports that sched-ext feature.
|
||||
*/
|
||||
|
||||
/*
|
||||
* scx_compat_marker_cgroup_set_bandwidth_may_sleep: advertises that
|
||||
* ops.cgroup_set_bandwidth() may be implemented as a sleepable callback.
|
||||
*/
|
||||
#ifdef CONFIG_EXT_GROUP_SCHED
|
||||
DEFINE_SCX_COMPAT_MARKER(cgroup_set_bandwidth_may_sleep);
|
||||
#endif /* CONFIG_EXT_GROUP_SCHED */
|
||||
|
||||
@@ -442,7 +442,7 @@ struct sched_ext_ops {
|
||||
*
|
||||
* Note that this callback may be called from a CPU other than the
|
||||
* one the task is going to run on. This can happen when a task
|
||||
* property is changed (i.e., affinity), since scx_next_task_scx(),
|
||||
* property is changed (i.e., affinity), since set_next_task_scx(),
|
||||
* which triggers this callback, may run on a CPU different from
|
||||
* the task's assigned CPU.
|
||||
*
|
||||
@@ -753,7 +753,7 @@ struct sched_ext_ops {
|
||||
* @burst_us: bandwidth control burst
|
||||
*
|
||||
* Update @cgrp's bandwidth control parameters. This is from the cpu.max
|
||||
* cgroup interface.
|
||||
* cgroup interface. This operation may block.
|
||||
*
|
||||
* @quota_us / @period_us determines the CPU bandwidth @cgrp is entitled
|
||||
* to. For example, if @period_us is 1_000_000 and @quota_us is
|
||||
@@ -2001,6 +2001,27 @@ struct scx_bstr_buf {
|
||||
char line[SCX_EXIT_MSG_LEN];
|
||||
};
|
||||
|
||||
/* Internal helper for DEFINE_SCX_COMPAT_MARKER(). */
|
||||
#define DECLARE_SCX_COMPAT_MARKER(func) \
|
||||
extern void scx_compat_marker_##func(void)
|
||||
|
||||
/**
|
||||
* DEFINE_SCX_COMPAT_MARKER() - define a userspace capability marker
|
||||
* @func: marker suffix; the defined symbol is scx_compat_marker_@func
|
||||
*
|
||||
* Emit an empty, callerless function that is retained in the kernel's BTF.
|
||||
* Its presence is part of the kernel<->userspace contract: userspace probes
|
||||
* scx_compat_marker_@func (e.g. via BTF) to detect that this kernel supports
|
||||
* the corresponding feature.
|
||||
*
|
||||
* The leading declaration suppresses the missing-prototype warning; the
|
||||
* trailing declaration consumes the semicolon at the use site.
|
||||
*/
|
||||
#define DEFINE_SCX_COMPAT_MARKER(func) \
|
||||
DECLARE_SCX_COMPAT_MARKER(func); \
|
||||
__used __retain void scx_compat_marker_##func(void) {} \
|
||||
DECLARE_SCX_COMPAT_MARKER(func)
|
||||
|
||||
extern struct scx_sched __rcu *scx_root;
|
||||
DECLARE_PER_CPU(struct rq *, scx_locked_rq_state);
|
||||
|
||||
|
||||
@@ -3197,7 +3197,16 @@ __acquires(&pool->lock)
|
||||
#ifdef CONFIG_PREEMPT_RT
|
||||
static void worker_lock_callback(struct worker_pool *pool)
|
||||
{
|
||||
spin_lock(&pool->cb_lock);
|
||||
/*
|
||||
* SINGLE_DEPTH_NESTING is for a dead pool's bh_worker() running from
|
||||
* drain_dead_softirq_workfn() inside a live pool's bh_worker(). The
|
||||
* unlocked read is stable: the flag is only set while @pool's CPU is
|
||||
* dead, inside a serialized hotplug operation. data_race() as the value
|
||||
* only affects the lockdep annotation and the read can be elided when
|
||||
* lockdep is disabled.
|
||||
*/
|
||||
spin_lock_nested(&pool->cb_lock,
|
||||
data_race(pool->flags) & POOL_BH_DRAINING ? SINGLE_DEPTH_NESTING : 0);
|
||||
}
|
||||
|
||||
static void worker_unlock_callback(struct worker_pool *pool)
|
||||
@@ -5285,12 +5294,6 @@ static void pwq_release_workfn(struct kthread_work *work)
|
||||
mutex_unlock(&wq->mutex);
|
||||
}
|
||||
|
||||
if (!is_percpu_pool(pool)) {
|
||||
mutex_lock(&wq_pool_mutex);
|
||||
put_unbound_pool(pool);
|
||||
mutex_unlock(&wq_pool_mutex);
|
||||
}
|
||||
|
||||
if (!list_empty(&pwq->pending_node)) {
|
||||
struct wq_node_nr_active *nna =
|
||||
wq_node_nr_active(pwq->wq, pwq->pool->node);
|
||||
@@ -5300,6 +5303,12 @@ static void pwq_release_workfn(struct kthread_work *work)
|
||||
raw_spin_unlock_irq(&nna->lock);
|
||||
}
|
||||
|
||||
if (!is_percpu_pool(pool)) {
|
||||
mutex_lock(&wq_pool_mutex);
|
||||
put_unbound_pool(pool);
|
||||
mutex_unlock(&wq_pool_mutex);
|
||||
}
|
||||
|
||||
kfree_rcu(pwq, rcu);
|
||||
|
||||
/*
|
||||
@@ -8050,6 +8059,9 @@ static int wq_watchdog_param_set_thresh(const char *val,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (thresh > MAX_JIFFY_OFFSET / HZ)
|
||||
return -ERANGE;
|
||||
|
||||
if (system_percpu_wq)
|
||||
wq_watchdog_set_thresh(thresh);
|
||||
else
|
||||
@@ -8080,12 +8092,12 @@ static inline void wq_watchdog_init(void) { }
|
||||
|
||||
static void bh_pool_kick_normal(struct irq_work *irq_work)
|
||||
{
|
||||
raise_softirq_irqoff(TASKLET_SOFTIRQ);
|
||||
raise_softirq(TASKLET_SOFTIRQ);
|
||||
}
|
||||
|
||||
static void bh_pool_kick_highpri(struct irq_work *irq_work)
|
||||
{
|
||||
raise_softirq_irqoff(HI_SOFTIRQ);
|
||||
raise_softirq(HI_SOFTIRQ);
|
||||
}
|
||||
|
||||
static void __init restrict_unbound_cpumask(const char *name, const struct cpumask *mask)
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
extern int LINUX_KERNEL_VERSION __kconfig;
|
||||
extern const char CONFIG_CC_VERSION_TEXT[64] __kconfig __weak;
|
||||
extern const char CONFIG_LOCALVERSION[64] __kconfig __weak;
|
||||
extern bool CONFIG_PREEMPT_RCU __kconfig __weak;
|
||||
|
||||
/*
|
||||
* Earlier versions of clang/pahole lost upper 32bits in 64bit enums which can
|
||||
@@ -97,6 +98,7 @@ s32 scx_bpf_pick_any_cpu_node(const cpumask_t *cpus_allowed, int node, u64 flags
|
||||
s32 scx_bpf_pick_any_cpu(const cpumask_t *cpus_allowed, u64 flags) __ksym;
|
||||
bool scx_bpf_task_running(const struct task_struct *p) __ksym;
|
||||
s32 scx_bpf_task_cpu(const struct task_struct *p) __ksym;
|
||||
struct rq *scx_bpf_cpu_rq(s32 cpu) __ksym __weak;
|
||||
struct rq *scx_bpf_locked_rq(void) __ksym;
|
||||
struct task_struct *scx_bpf_cpu_curr(s32 cpu) __ksym __weak;
|
||||
struct task_struct *scx_bpf_tid_to_task(u64 tid) __ksym __weak;
|
||||
@@ -527,32 +529,103 @@ static __always_inline const struct cpumask *cast_mask(struct bpf_cpumask *mask)
|
||||
return (const struct cpumask *)mask;
|
||||
}
|
||||
|
||||
/*
|
||||
* True if the non-sleepable BPF trampoline prolog (__bpf_prog_enter) calls
|
||||
* migrate_disable() for the current task. Recorded once by
|
||||
* scx_lib_init_probe, an fentry program on bpf_scx_reg() that fires during
|
||||
* the natural scheduler-attach call chain (auto-attached by scx_ops_attach!).
|
||||
*
|
||||
* Defaults to true (conservative). Over-reporting in is_migration_disabled()
|
||||
* causes local-only dispatch, which is safe. Under-reporting can crash the
|
||||
* scheduler, so we err high if the probe somehow fails to run.
|
||||
*/
|
||||
bool __scx_prolog_disables_migration __weak = true;
|
||||
|
||||
/*
|
||||
* scx_lib_init_probe - non-sleepable prolog probe.
|
||||
*
|
||||
* Attached to bpf_scx_reg(), the .reg callback in bpf_sched_ext_ops
|
||||
* (kernel/sched/ext.c). The kernel's struct_ops machinery invokes
|
||||
* bpf_scx_reg when userspace creates the scheduler link, before
|
||||
* ops.init() fires. Its address is taken in the vtable, so the symbol
|
||||
* is non-inlinable and has been stable since introduction.
|
||||
*
|
||||
* Entering via fentry runs us through __bpf_prog_enter -- the
|
||||
* non-sleepable prolog that consumers of is_migration_disabled() live
|
||||
* under.
|
||||
*
|
||||
* Loud warning: the prolog adds at most 1 to migration_disabled.
|
||||
* Reading > 1 means something upstream in the
|
||||
* bpf_struct_ops_link_create -> bpf_scx_reg path disabled migration
|
||||
* before the prolog ran, invalidating the probe; audit and adjust.
|
||||
*/
|
||||
SEC("fentry/bpf_scx_reg") __weak
|
||||
int scx_lib_init_probe(void *ctx)
|
||||
{
|
||||
if (bpf_core_field_exists(((struct task_struct *)0)->migration_disabled)) {
|
||||
const struct task_struct *p = bpf_get_current_task_btf();
|
||||
unsigned int md = p->migration_disabled;
|
||||
|
||||
if (md > 1)
|
||||
bpf_printk("scx_lib_init_probe: unexpected migration_disabled=%u "
|
||||
"upstream of BPF prolog; probe result unreliable",
|
||||
md);
|
||||
|
||||
__scx_prolog_disables_migration = md > 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true if task @p cannot migrate to a different CPU, false
|
||||
* otherwise.
|
||||
*
|
||||
* IMPORTANT: designed for NON-SLEEPABLE BPF contexts only. Sleepable
|
||||
* contexts (BPF_STRUCT_OPS_SLEEPABLE, SEC("syscall"),
|
||||
* SEC("fentry.s/...")) enter via __bpf_prog_enter_sleepable() or
|
||||
* __bpf_prog_enter_sleepable_recur(), both of which unconditionally
|
||||
* call migrate_disable(); this helper can yield a false negative for
|
||||
* p == current there, which can crash the scheduler.
|
||||
*/
|
||||
static inline bool is_migration_disabled(const struct task_struct *p)
|
||||
{
|
||||
/*
|
||||
* Testing p->migration_disabled in a BPF code is tricky because the
|
||||
* migration is _always_ disabled while running the BPF code.
|
||||
* The prolog (__bpf_prog_enter) and epilog (__bpf_prog_exit) for BPF
|
||||
* code execution disable and re-enable the migration of the current
|
||||
* task, respectively. So, the _current_ task of the sched_ext ops is
|
||||
* always migration-disabled. Moreover, p->migration_disabled could be
|
||||
* two or greater when a sched_ext ops BPF code (e.g., ops.tick) is
|
||||
* executed in the middle of the other BPF code execution.
|
||||
* Testing p->migration_disabled in BPF is tricky because the BPF prolog
|
||||
* (__bpf_prog_enter) may call migrate_disable() for the current task,
|
||||
* making migration_disabled == 1 even for tasks that are not truly
|
||||
* migration-disabled.
|
||||
*
|
||||
* Therefore, we should decide that the _current_ task is
|
||||
* migration-disabled only when its migration_disabled count is greater
|
||||
* than one. In other words, when p->migration_disabled == 1, there is
|
||||
* an ambiguity, so we should check if @p is the current task or not.
|
||||
* Since commit 8e4f0b1ebcf2 ("bpf: use rcu_read_lock_dont_migrate() for
|
||||
* trampoline.c"), the BPF prolog calls migrate_disable() only when
|
||||
* CONFIG_PREEMPT_RCU is enabled. Two fast paths cover the common cases:
|
||||
*
|
||||
* 1) CONFIG_PREEMPT_RCU: prolog always calls migrate_disable(), so
|
||||
* migration_disabled == 1 for the current task is ambiguous.
|
||||
* Disambiguate by checking p == current.
|
||||
*
|
||||
* 2) v6.18+ without CONFIG_PREEMPT_RCU: prolog never calls
|
||||
* migrate_disable(), so migration_disabled == 1 is unambiguously
|
||||
* a real migrate_disable() call.
|
||||
*
|
||||
* A slow path handles pre-v6.18 kernels without CONFIG_PREEMPT_RCU,
|
||||
* where the prolog historically called migrate_disable() unconditionally
|
||||
* but a cherry-picked downstream kernel may not. The runtime-probed flag
|
||||
* __scx_prolog_disables_migration (set by scx_lib_init_probe) distinguishes
|
||||
* the two cases without relying on the kernel version alone.
|
||||
*/
|
||||
if (bpf_core_field_exists(p->migration_disabled)) {
|
||||
if (p->migration_disabled == 1)
|
||||
return bpf_get_current_task_btf() != p;
|
||||
else
|
||||
return p->migration_disabled;
|
||||
if (p->migration_disabled == 1) {
|
||||
/* Fast path: prolog always disables migration */
|
||||
if (CONFIG_PREEMPT_RCU)
|
||||
return bpf_get_current_task_btf() != p;
|
||||
/* Fast path: prolog never disables migration */
|
||||
if (LINUX_KERNEL_VERSION >= KERNEL_VERSION(6, 18, 0))
|
||||
return true;
|
||||
/* Slow path: pre-v6.18, !PREEMPT_RCU - use runtime flag */
|
||||
return __scx_prolog_disables_migration ?
|
||||
bpf_get_current_task_btf() != p : true;
|
||||
}
|
||||
return p->migration_disabled;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -1021,7 +1094,20 @@ static inline u64 scx_clock_task(u32 cpu)
|
||||
{
|
||||
struct rq___local *rq = get_current_rq(cpu);
|
||||
|
||||
/* Equivalent to the kernel's rq_clock_task(). */
|
||||
/*
|
||||
* Equivalent to the kernel's rq_clock_task(): wall-clock time minus
|
||||
* cumulative IRQ time (CONFIG_IRQ_TIME_ACCOUNTING) and hypervisor
|
||||
* steal time (CONFIG_PARAVIRT_TIME_ACCOUNTING). Without those configs,
|
||||
* it equals rq->clock.
|
||||
*
|
||||
* Conceptually this clock advances during idle (the idle task counts
|
||||
* as a running task), but rq->clock_task is only updated on scheduling
|
||||
* events. With NO_HZ_IDLE (the default), the periodic tick is stopped
|
||||
* on idle CPUs, so rq->clock_task is not refreshed while a CPU is
|
||||
* idle. Reading this clock for a remote idle CPU from a BPF timer
|
||||
* callback returns the value from when the CPU last went idle, making
|
||||
* the delta over an idle interval effectively zero.
|
||||
*/
|
||||
return rq ? rq->clock_task : 0;
|
||||
}
|
||||
|
||||
@@ -1032,9 +1118,23 @@ static inline u64 scx_clock_pelt(u32 cpu)
|
||||
/*
|
||||
* Equivalent to the kernel's rq_clock_pelt(): subtracts
|
||||
* lost_idle_time from clock_pelt to absorb the jump that occurs
|
||||
* when clock_pelt resyncs with clock_task at idle exit. The result
|
||||
* is a continuous, capacity-invariant clock safe for both task
|
||||
* execution time stamping and cross-idle measurements.
|
||||
* when clock_pelt resyncs with clock_task at idle exit. The intent
|
||||
* is a continuous, capacity- and frequency-invariant clock that is
|
||||
* frozen during idle, IRQ, and hypervisor steal.
|
||||
*
|
||||
* However, like scx_clock_task(), this clock has a stale-read issue
|
||||
* for remote idle CPUs with NO_HZ_IDLE (the default). clock_pelt
|
||||
* itself advances at wall-clock rate (hardware-clock based), but
|
||||
* lost_idle_time is only updated via update_rq_clock_pelt(), which
|
||||
* requires update_rq_clock() to be called. With NO_HZ_IDLE, the
|
||||
* periodic tick is stopped on idle CPUs, so lost_idle_time is not
|
||||
* refreshed during idle. Reading this clock for a remote idle CPU
|
||||
* from a BPF timer callback therefore returns a value that drifts
|
||||
* at wall-clock rate -- the same stale behaviour as scx_clock_task().
|
||||
*
|
||||
* Without NO_HZ_IDLE, periodic ticks keep lost_idle_time nearly in
|
||||
* sync (stale by at most one tick period, ~1 ms), so the result is
|
||||
* accurate.
|
||||
*/
|
||||
return rq ? (rq->clock_pelt - rq->lost_idle_time) : 0;
|
||||
}
|
||||
|
||||
@@ -92,15 +92,20 @@ int bpf_cpumask_populate(struct bpf_cpumask *dst, void *src, size_t src__sz) __k
|
||||
|
||||
/*
|
||||
* v6.19: Introduce lockless peek API for user DSQs.
|
||||
* v7.1: Fix scx_bpf_dsq_peek() spuriously returning NULL on non-empty
|
||||
* FIFO DSQs (2f2ea7709266).
|
||||
*
|
||||
* Preserve the following macro until v6.21.
|
||||
* The kfunc exists from v6.19 but can return NULL for a non-empty FIFO DSQ
|
||||
* before the v7.1 fix. Require kernel version >= 7.1.0 before calling it;
|
||||
* otherwise fall through to the bpf_iter_scx_dsq fallback below.
|
||||
*/
|
||||
static inline struct task_struct *__COMPAT_scx_bpf_dsq_peek(u64 dsq_id)
|
||||
{
|
||||
struct task_struct *p = NULL;
|
||||
struct bpf_iter_scx_dsq it;
|
||||
|
||||
if (bpf_ksym_exists(scx_bpf_dsq_peek))
|
||||
if (bpf_ksym_exists(scx_bpf_dsq_peek) &&
|
||||
LINUX_KERNEL_VERSION >= KERNEL_VERSION(7, 1, 0))
|
||||
return scx_bpf_dsq_peek(dsq_id);
|
||||
if (!bpf_iter_scx_dsq_new(&it, dsq_id, 0))
|
||||
p = bpf_iter_scx_dsq_next(&it);
|
||||
@@ -238,6 +243,26 @@ static inline bool __COMPAT_is_enq_cpu_selected(u64 enq_flags)
|
||||
scx_bpf_pick_any_cpu_node(cpus_allowed, node, flags) : \
|
||||
scx_bpf_pick_any_cpu(cpus_allowed, flags))
|
||||
|
||||
/*
|
||||
* v6.18: Add a helper to retrieve the current task running on a CPU.
|
||||
*
|
||||
* The kernel tree dropped this helper and scx_bpf_cpu_rq(), but schedulers in
|
||||
* this tree still support pre-v6.18 kernels where scx_bpf_cpu_curr() doesn't
|
||||
* resolve and the scx_bpf_cpu_rq() fallback still exists. Keep it until
|
||||
* pre-v6.18 kernels fall out of the support window.
|
||||
*/
|
||||
static inline struct task_struct *__COMPAT_scx_bpf_cpu_curr(int cpu)
|
||||
{
|
||||
struct rq *rq;
|
||||
|
||||
if (bpf_ksym_exists(scx_bpf_cpu_curr))
|
||||
return scx_bpf_cpu_curr(cpu);
|
||||
|
||||
rq = scx_bpf_cpu_rq(cpu);
|
||||
|
||||
return rq ? rq->curr : NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* v6.19: To work around BPF maximum parameter limit, the following kfuncs are
|
||||
* replaced with variants that pack scalar arguments in a struct. Wrappers are
|
||||
@@ -378,6 +403,17 @@ static inline void scx_bpf_task_set_dsq_vtime(struct task_struct *p, u64 vtime)
|
||||
p->scx.dsq_vtime = vtime;
|
||||
}
|
||||
|
||||
/*
|
||||
* v7.1: New scx_bpf_dsq_reenq() that allows re-enqueues on more DSQs. This
|
||||
* will eventually deprecate scx_bpf_reenqueue_local().
|
||||
*/
|
||||
void scx_bpf_dsq_reenq___compat(u64 dsq_id, u64 reenq_flags) __ksym __weak;
|
||||
|
||||
static inline bool __COMPAT_has_generic_reenq(void)
|
||||
{
|
||||
return bpf_ksym_exists(scx_bpf_dsq_reenq___compat);
|
||||
}
|
||||
|
||||
/*
|
||||
* v6.19: The new void variant can be called from anywhere while the older v1
|
||||
* variant can only be called from ops.cpu_release(). The double ___ prefixes on
|
||||
@@ -395,21 +431,31 @@ static inline bool __COMPAT_scx_bpf_reenqueue_local_from_anywhere(void)
|
||||
|
||||
static inline void scx_bpf_reenqueue_local(void)
|
||||
{
|
||||
if (__COMPAT_scx_bpf_reenqueue_local_from_anywhere())
|
||||
if (__COMPAT_has_generic_reenq())
|
||||
scx_bpf_dsq_reenq___compat(SCX_DSQ_LOCAL, 0);
|
||||
else if (__COMPAT_scx_bpf_reenqueue_local_from_anywhere())
|
||||
scx_bpf_reenqueue_local___v2___compat();
|
||||
else
|
||||
scx_bpf_reenqueue_local___v1();
|
||||
}
|
||||
|
||||
/*
|
||||
* v7.1: New scx_bpf_dsq_reenq() that allows re-enqueues on more DSQs. This
|
||||
* will eventually deprecate scx_bpf_reenqueue_local().
|
||||
*/
|
||||
void scx_bpf_dsq_reenq___compat(u64 dsq_id, u64 reenq_flags) __ksym __weak;
|
||||
|
||||
static inline bool __COMPAT_has_generic_reenq(void)
|
||||
static inline int scx_bpf_reenqueue_local_from_anywhere(void)
|
||||
{
|
||||
return bpf_ksym_exists(scx_bpf_dsq_reenq___compat);
|
||||
/*
|
||||
* The generic reenq kfunc and the v2 reenqueue-local variant can both be
|
||||
* called from anywhere; v1 cannot. Test each ksym in its own branch with a
|
||||
* distinct call: combining them with || would fold into a bitwise OR of the
|
||||
* two ksym addresses, which the verifier rejects.
|
||||
*/
|
||||
if (__COMPAT_has_generic_reenq()) {
|
||||
scx_bpf_dsq_reenq___compat(SCX_DSQ_LOCAL, 0);
|
||||
return 0;
|
||||
}
|
||||
if (__COMPAT_scx_bpf_reenqueue_local_from_anywhere()) {
|
||||
scx_bpf_reenqueue_local___v2___compat();
|
||||
return 0;
|
||||
}
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline void scx_bpf_dsq_reenq(u64 dsq_id, u64 reenq_flags)
|
||||
|
||||
@@ -10,9 +10,14 @@
|
||||
#include <bpf/btf.h>
|
||||
#include <bpf/libbpf.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "enums_abi.autogen.h"
|
||||
|
||||
struct btf *__COMPAT_vmlinux_btf __attribute__((weak));
|
||||
|
||||
static inline void __COMPAT_load_vmlinux_btf(void)
|
||||
@@ -23,6 +28,85 @@ static inline void __COMPAT_load_vmlinux_btf(void)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Recover the true value of a 64-bit enum enumerator whose kernel BTF entry
|
||||
* was truncated to its low 32 bits.
|
||||
*
|
||||
* Kernels whose BTF was generated without BTF_KIND_ENUM64 support encode
|
||||
* 64-bit enums as 8-byte BTF_KIND_ENUM entries whose enumerator values only
|
||||
* carry the low 32 bits. This happens with pahole < 1.24, which predates
|
||||
* ENUM64, and with pahole passing --skip_encoding_btf_enum64 (e.g. Google's
|
||||
* Container-Optimized OS / GKE kernels deliberately pass it for backward
|
||||
* compatibility with older BTF consumers). The high bits
|
||||
* can't be recovered from kernel BTF, so substitute the value from the
|
||||
* vmlinux.h this tree was built against, cross-checked against the low 32
|
||||
* bits the kernel did provide.
|
||||
*
|
||||
* Note that this is a best-effort recovery, not a ground truth. The
|
||||
* substitution assumes the running kernel agrees with this tree's vmlinux.h
|
||||
* on the high 32 bits, but only the low 32 bits can actually be verified.
|
||||
* The cross-check is vacuous for enumerators whose value has no low bits
|
||||
* set (e.g. SCX_DSQ_FLAG_BUILTIN, __SCX_ENQ_INTERNAL_MASK,
|
||||
* SCX_ENQ_CLEAR_OPSS, SCX_ECODE_*): their lo32 is 0 and matches anything,
|
||||
* so those substitutions rest entirely on the high bits never moving. An
|
||||
* enumerator missing from the table (a kernel newer than this tree's
|
||||
* vmlinux.h, or a stale autogen table) can't be recovered at all. If a
|
||||
* substitution is ever wrong, the scheduler operates on bogus values (e.g.
|
||||
* dispatching to nonexistent DSQ ids or silently dropping flags) and can
|
||||
* wildly malfunction, which is why the mismatch and table-miss paths refuse
|
||||
* instead of guessing.
|
||||
*/
|
||||
static inline bool __COMPAT_recover_truncated_enum64(const char *type,
|
||||
const char *name,
|
||||
u32 lo32, u64 *v)
|
||||
{
|
||||
static bool warned;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < sizeof(__scx_enum_abi_vals) / sizeof(__scx_enum_abi_vals[0]); i++) {
|
||||
const struct __scx_enum_abi_val *e = &__scx_enum_abi_vals[i];
|
||||
|
||||
if (strcmp(e->type, type) || strcmp(e->name, name))
|
||||
continue;
|
||||
|
||||
if (e->val <= (u64)UINT32_MAX) {
|
||||
*v = lo32;
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((u32)e->val != lo32) {
|
||||
fprintf(stderr, "ERROR: kernel BTF value of %s::%s (0x%x) doesn't match the low 32 bits of the vmlinux.h value (0x%llx); refusing to substitute\n",
|
||||
type, name, lo32, (unsigned long long)e->val);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!warned) {
|
||||
fprintf(stderr,
|
||||
"WARNING: kernel BTF lacks BTF_KIND_ENUM64 encoding (generated by\n"
|
||||
"WARNING: pahole < 1.24 or with --skip_encoding_btf_enum64), so 64-bit\n"
|
||||
"WARNING: scx enum values are truncated to their low 32 bits in kernel\n"
|
||||
"WARNING: BTF. Substituting the full 64-bit values from the vmlinux.h\n"
|
||||
"WARNING: this binary was built against, cross-checked against the low\n"
|
||||
"WARNING: 32 bits the kernel does provide. The high 32 bits cannot be\n"
|
||||
"WARNING: verified: if the running kernel's actual values differ from\n"
|
||||
"WARNING: the build-time vmlinux.h (e.g. an enum that moved in a newer\n"
|
||||
"WARNING: kernel), the scheduler will operate on bogus values, such as\n"
|
||||
"WARNING: dispatching to nonexistent DSQ ids, and can wildly malfunction.\n");
|
||||
warned = true;
|
||||
}
|
||||
*v = e->val;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unknown enumerator (likely a stale autogen table). Fail
|
||||
* pessimistically to avoid returning an invalid value.
|
||||
*/
|
||||
fprintf(stderr, "ERROR: kernel BTF truncates 64-bit enum %s::%s to 0x%x; 64-bit variant not found in vmlinux.h\n",
|
||||
type, name, lo32);
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool __COMPAT_read_enum(const char *type, const char *name, u64 *v)
|
||||
{
|
||||
const struct btf_type *t;
|
||||
@@ -46,6 +130,19 @@ static inline bool __COMPAT_read_enum(const char *type, const char *name, u64 *v
|
||||
n = btf__name_by_offset(__COMPAT_vmlinux_btf, e[i].name_off);
|
||||
SCX_BUG_ON(!n, "btf__name_by_offset()");
|
||||
if (!strcmp(n, name)) {
|
||||
/*
|
||||
* Try to recover a 64-bit enum from an 8-byte
|
||||
* BTF_KIND_ENUM that was encoded without ENUM64
|
||||
* support (old pahole or
|
||||
* --skip_encoding_btf_enum64). Only scx_*
|
||||
* types are covered by the substitution table;
|
||||
* non-scx types fall through to the raw value
|
||||
* so this generic utility keeps working for
|
||||
* them.
|
||||
*/
|
||||
if (t->size == 8 && !strncmp(type, "scx_", 4))
|
||||
return __COMPAT_recover_truncated_enum64(type, name,
|
||||
(u32)e[i].val, v);
|
||||
*v = e[i].val;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -56,6 +56,10 @@
|
||||
#define HAVE_SCX_DEQ_SLEEP
|
||||
#define HAVE_SCX_DEQ_CORE_SCHED_EXEC
|
||||
#define HAVE_SCX_DEQ_SCHED_CHANGE
|
||||
#define HAVE_SCX_DSP_NONE
|
||||
#define HAVE_SCX_DSP_LOCAL
|
||||
#define HAVE_SCX_DSP_PREV
|
||||
#define HAVE_SCX_DSP_RETRY
|
||||
#define HAVE_SCX_DSQ_FLAG_BUILTIN
|
||||
#define HAVE_SCX_DSQ_FLAG_LOCAL_ON
|
||||
#define HAVE_SCX_DSQ_INVALID
|
||||
@@ -188,7 +192,6 @@
|
||||
#define HAVE_SCX_RQ_SUB_IDLE_RENOTIFY
|
||||
#define HAVE_SCX_RQ_ROOT_IDLE_RENOTIFY
|
||||
#define HAVE_SCX_RQ_IN_WAKEUP
|
||||
#define HAVE_SCX_RQ_IN_BALANCE
|
||||
#define HAVE_SCX_RQ_IN_DISPATCH
|
||||
#define HAVE_SCX_SCHED_PCPU_BYPASSING
|
||||
#define HAVE_SCX_SLICE_OOB_DUR_BITS
|
||||
|
||||
223
tools/sched_ext/include/scx/enums_abi.autogen.h
Normal file
223
tools/sched_ext/include/scx/enums_abi.autogen.h
Normal file
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* WARNING: This file is autogenerated from gen_enum_defs.py [1].
|
||||
*
|
||||
* scx enumerator values from the vmlinux.h this tree is built against.
|
||||
* Used as the substitution source when the running kernel's BTF lacks
|
||||
* BTF_KIND_ENUM64 encoding and 64-bit enum values are truncated.
|
||||
*
|
||||
* [1] https://github.com/sched-ext/scx/blob/main/scripts/gen_enum_defs.py
|
||||
*/
|
||||
|
||||
#ifndef __ENUMS_ABI_AUTOGEN_H__
|
||||
#define __ENUMS_ABI_AUTOGEN_H__
|
||||
|
||||
struct __scx_enum_abi_val {
|
||||
const char *type;
|
||||
const char *name;
|
||||
u64 val;
|
||||
};
|
||||
|
||||
static const struct __scx_enum_abi_val __scx_enum_abi_vals[]
|
||||
__attribute__((unused)) = {
|
||||
{ "scx_arena_consts", "SCX_ARENA_MIN_ORDER", 0x3LLU },
|
||||
{ "scx_arena_consts", "SCX_ARENA_GROW_PAGES", 0x4LLU },
|
||||
{ "scx_cap_flags", "__SCX_CAP_ENQ_IMMED", 0x0LLU },
|
||||
{ "scx_cap_flags", "__SCX_CAP_ENQ", 0x1LLU },
|
||||
{ "scx_cap_flags", "__SCX_CAP_PREEMPT", 0x2LLU },
|
||||
{ "scx_cap_flags", "__SCX_CAP_PERF", 0x3LLU },
|
||||
{ "scx_cap_flags", "__SCX_NR_CAPS", 0x4LLU },
|
||||
{ "scx_cap_flags", "__SCX_CAP_ALL", 0xfLLU },
|
||||
{ "scx_cap_flags", "SCX_CAP_ENQ_IMMED", 0x1LLU },
|
||||
{ "scx_cap_flags", "SCX_CAP_ENQ", 0x2LLU },
|
||||
{ "scx_cap_flags", "SCX_CAP_PREEMPT", 0x4LLU },
|
||||
{ "scx_cap_flags", "SCX_CAP_PERF", 0x8LLU },
|
||||
{ "scx_cap_flags", "SCX_CAP_BASE", 0x1LLU },
|
||||
{ "scx_cap_flags", "SCX_CAPS_REENQ_ON_LOSS", 0x3LLU },
|
||||
{ "scx_cid_consts", "SCX_CID_SHARD_SIZE_DFL", 0x18LLU },
|
||||
{ "scx_cid_consts", "SCX_CID_SHARD_MAX_CPUS", 0x200LLU },
|
||||
{ "scx_consts", "SCX_DSP_DFL_MAX_BATCH", 0x20LLU },
|
||||
{ "scx_consts", "SCX_DSP_MAX_LOOPS", 0x20LLU },
|
||||
{ "scx_consts", "SCX_WATCHDOG_MAX_TIMEOUT", 0x7530LLU },
|
||||
{ "scx_consts", "SCX_RESCUE_DFL_BW_PPT", 0x14LLU },
|
||||
{ "scx_consts", "SCX_RESCUE_MAX_BW_PPT", 0xfaLLU },
|
||||
{ "scx_consts", "SCX_RESCUE_DISABLE", 0xffffffffLLU },
|
||||
{ "scx_consts", "SCX_RESCUE_DFL_QUANTUM_US", 0x1388LLU },
|
||||
{ "scx_consts", "SCX_RESCUE_MIN_QUANTUM_US", 0x3e8LLU },
|
||||
{ "scx_consts", "SCX_RESCUE_MAX_QUANTUM_US", 0x186a0LLU },
|
||||
{ "scx_consts", "SCX_RESCUE_MIN_SLICE_US", 0x3e8LLU },
|
||||
{ "scx_consts", "SCX_RESCUE_OVERLOAD_MULT", 0x10LLU },
|
||||
{ "scx_consts", "SCX_RESCUE_MIN_OVERLOAD_MS", 0x3e8LLU },
|
||||
{ "scx_consts", "SCX_RESCUE_MAX_OVERLOAD_MS", 0x3a98LLU },
|
||||
{ "scx_consts", "SCX_TID_CHUNK", 0x400LLU },
|
||||
{ "scx_consts", "SCX_EXIT_BT_LEN", 0x40LLU },
|
||||
{ "scx_consts", "SCX_EXIT_MSG_LEN", 0x400LLU },
|
||||
{ "scx_consts", "SCX_EXIT_DUMP_DFL_LEN", 0x8000LLU },
|
||||
{ "scx_consts", "SCX_CPUPERF_ONE", 0x400LLU },
|
||||
{ "scx_consts", "SCX_TASK_ITER_BATCH", 0x20LLU },
|
||||
{ "scx_consts", "SCX_BYPASS_HOST_NTH", 0x2LLU },
|
||||
{ "scx_consts", "SCX_BYPASS_LB_DFL_INTV_US", 0x7a120LLU },
|
||||
{ "scx_consts", "SCX_BYPASS_LB_DONOR_PCT", 0x7dLLU },
|
||||
{ "scx_consts", "SCX_BYPASS_LB_MIN_DELTA_DIV", 0x4LLU },
|
||||
{ "scx_consts", "SCX_BYPASS_LB_BATCH", 0x100LLU },
|
||||
{ "scx_consts", "SCX_REENQ_MAX_REPEAT", 0x100LLU },
|
||||
{ "scx_consts", "SCX_SUB_MAX_DEPTH", 0x4LLU },
|
||||
{ "scx_cpu_preempt_reason", "SCX_CPU_PREEMPT_RT", 0x0LLU },
|
||||
{ "scx_cpu_preempt_reason", "SCX_CPU_PREEMPT_DL", 0x1LLU },
|
||||
{ "scx_cpu_preempt_reason", "SCX_CPU_PREEMPT_STOP", 0x2LLU },
|
||||
{ "scx_cpu_preempt_reason", "SCX_CPU_PREEMPT_UNKNOWN", 0x3LLU },
|
||||
{ "scx_deq_flags", "SCX_DEQ_SLEEP", 0x1LLU },
|
||||
{ "scx_deq_flags", "SCX_DEQ_CORE_SCHED_EXEC", 0x100000000LLU },
|
||||
{ "scx_deq_flags", "SCX_DEQ_SCHED_CHANGE", 0x200000000LLU },
|
||||
{ "scx_dsp_verdict", "SCX_DSP_NONE", 0x0LLU },
|
||||
{ "scx_dsp_verdict", "SCX_DSP_LOCAL", 0x1LLU },
|
||||
{ "scx_dsp_verdict", "SCX_DSP_PREV", 0x2LLU },
|
||||
{ "scx_dsp_verdict", "SCX_DSP_RETRY", 0x3LLU },
|
||||
{ "scx_dsq_id_flags", "SCX_DSQ_FLAG_BUILTIN", 0x8000000000000000LLU },
|
||||
{ "scx_dsq_id_flags", "SCX_DSQ_FLAG_LOCAL_ON", 0x4000000000000000LLU },
|
||||
{ "scx_dsq_id_flags", "SCX_DSQ_INVALID", 0x8000000000000000LLU },
|
||||
{ "scx_dsq_id_flags", "SCX_DSQ_GLOBAL", 0x8000000000000001LLU },
|
||||
{ "scx_dsq_id_flags", "SCX_DSQ_LOCAL", 0x8000000000000002LLU },
|
||||
{ "scx_dsq_id_flags", "SCX_DSQ_BYPASS", 0x8000000000000003LLU },
|
||||
{ "scx_dsq_id_flags", "SCX_DSQ_REJECT", 0x8000000000000004LLU },
|
||||
{ "scx_dsq_id_flags", "SCX_DSQ_RESCUE", 0x8000000000000005LLU },
|
||||
{ "scx_dsq_id_flags", "SCX_DSQ_LOCAL_ON", 0xc000000000000000LLU },
|
||||
{ "scx_dsq_id_flags", "SCX_DSQ_LOCAL_CPU_MASK", 0xffffffffLLU },
|
||||
{ "scx_dsq_iter_flags", "SCX_DSQ_ITER_REV", 0x10000LLU },
|
||||
{ "scx_dsq_iter_flags", "__SCX_DSQ_ITER_HAS_SLICE", 0x40000000LLU },
|
||||
{ "scx_dsq_iter_flags", "__SCX_DSQ_ITER_HAS_VTIME", 0x80000000LLU },
|
||||
{ "scx_dsq_iter_flags", "__SCX_DSQ_ITER_USER_FLAGS", 0x10000LLU },
|
||||
{ "scx_dsq_iter_flags", "__SCX_DSQ_ITER_ALL_FLAGS", 0xc0010000LLU },
|
||||
{ "scx_dsq_lnode_flags", "SCX_DSQ_LNODE_ITER_CURSOR", 0x1LLU },
|
||||
{ "scx_dsq_lnode_flags", "__SCX_DSQ_LNODE_PRIV_SHIFT", 0x10LLU },
|
||||
{ "scx_enable_state", "SCX_ENABLING", 0x0LLU },
|
||||
{ "scx_enable_state", "SCX_ENABLED", 0x1LLU },
|
||||
{ "scx_enable_state", "SCX_DISABLING", 0x2LLU },
|
||||
{ "scx_enable_state", "SCX_DISABLED", 0x3LLU },
|
||||
{ "scx_enq_flags", "SCX_ENQ_WAKEUP", 0x1LLU },
|
||||
{ "scx_enq_flags", "SCX_ENQ_HEAD", 0x10000LLU },
|
||||
{ "scx_enq_flags", "SCX_ENQ_CPU_SELECTED", 0x100000LLU },
|
||||
{ "scx_enq_flags", "SCX_ENQ_PREEMPT", 0x100000000LLU },
|
||||
{ "scx_enq_flags", "SCX_ENQ_IMMED", 0x200000000LLU },
|
||||
{ "scx_enq_flags", "SCX_ENQ_RESCUE", 0x400000000LLU },
|
||||
{ "scx_enq_flags", "SCX_ENQ_REENQ", 0x10000000000LLU },
|
||||
{ "scx_enq_flags", "SCX_ENQ_LAST", 0x20000000000LLU },
|
||||
{ "scx_enq_flags", "__SCX_ENQ_INTERNAL_MASK", 0xff00000000000000LLU },
|
||||
{ "scx_enq_flags", "SCX_ENQ_CLEAR_OPSS", 0x100000000000000LLU },
|
||||
{ "scx_enq_flags", "SCX_ENQ_DSQ_PRIQ", 0x200000000000000LLU },
|
||||
{ "scx_enq_flags", "SCX_ENQ_NESTED", 0x400000000000000LLU },
|
||||
{ "scx_enq_flags", "SCX_ENQ_GDSQ_FALLBACK", 0x800000000000000LLU },
|
||||
{ "scx_enq_flags", "SCX_ENQ_IGNORE_CAPS", 0x1000000000000000LLU },
|
||||
{ "scx_enq_flags", "SCX_ENQ_APPLY_SLICE", 0x2000000000000000LLU },
|
||||
{ "scx_enq_flags", "SCX_ENQ_SLICE_DFL", 0x4000000000000000LLU },
|
||||
{ "scx_ent_dsq_flags", "SCX_TASK_DSQ_ON_PRIQ", 0x1LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_QUEUED", 0x1LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_IN_CUSTODY", 0x2LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_RESET_RUNNABLE_AT", 0x4LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_DEQD_FOR_SLEEP", 0x8LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_SUB_INIT", 0x10LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_IMMED", 0x20LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_PROTECTED", 0x40LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_STATE_SHIFT", 0x8LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_STATE_BITS", 0x3LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_STATE_MASK", 0x700LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_NONE", 0x0LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_INIT_BEGIN", 0x100LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_INIT", 0x200LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_READY", 0x300LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_ENABLED", 0x400LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_DEAD", 0x500LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_REENQ_REASON_SHIFT", 0xcLLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_REENQ_REASON_BITS", 0x3LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_REENQ_REASON_MASK", 0x7000LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_REENQ_NONE", 0x0LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_REENQ_KFUNC", 0x1000LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_REENQ_IMMED", 0x2000LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_REENQ_PREEMPTED", 0x3000LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_REENQ_CAP", 0x4000LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_CURSOR", 0xffffffff80000000LLU },
|
||||
{ "scx_exit_code", "SCX_ECODE_RSN_HOTPLUG", 0x100000000LLU },
|
||||
{ "scx_exit_code", "SCX_ECODE_RSN_CGROUP_OFFLINE", 0x200000000LLU },
|
||||
{ "scx_exit_code", "SCX_ECODE_ACT_RESTART", 0x1000000000000LLU },
|
||||
{ "scx_exit_flags", "SCX_EFLAG_INITIALIZED", 0x1LLU },
|
||||
{ "scx_exit_kind", "SCX_EXIT_NONE", 0x0LLU },
|
||||
{ "scx_exit_kind", "SCX_EXIT_DONE", 0x1LLU },
|
||||
{ "scx_exit_kind", "SCX_EXIT_UNREG", 0x40LLU },
|
||||
{ "scx_exit_kind", "SCX_EXIT_UNREG_BPF", 0x41LLU },
|
||||
{ "scx_exit_kind", "SCX_EXIT_UNREG_KERN", 0x42LLU },
|
||||
{ "scx_exit_kind", "SCX_EXIT_SYSRQ", 0x43LLU },
|
||||
{ "scx_exit_kind", "SCX_EXIT_PARENT", 0x44LLU },
|
||||
{ "scx_exit_kind", "SCX_EXIT_PARENT_KILL", 0x45LLU },
|
||||
{ "scx_exit_kind", "SCX_EXIT_ERROR", 0x400LLU },
|
||||
{ "scx_exit_kind", "SCX_EXIT_ERROR_BPF", 0x401LLU },
|
||||
{ "scx_exit_kind", "SCX_EXIT_ERROR_STALL", 0x402LLU },
|
||||
{ "scx_exit_kind", "SCX_EXIT_ERROR_REENQ", 0x403LLU },
|
||||
{ "scx_exit_kind", "SCX_EXIT_ERROR_RESCUE", 0x404LLU },
|
||||
{ "scx_kf_allow_flags", "SCX_KF_ALLOW_UNLOCKED", 0x1LLU },
|
||||
{ "scx_kf_allow_flags", "SCX_KF_ALLOW_INIT_CIDS", 0x2LLU },
|
||||
{ "scx_kf_allow_flags", "SCX_KF_ALLOW_CPU_RELEASE", 0x4LLU },
|
||||
{ "scx_kf_allow_flags", "SCX_KF_ALLOW_DISPATCH", 0x8LLU },
|
||||
{ "scx_kf_allow_flags", "SCX_KF_ALLOW_ENQUEUE", 0x10LLU },
|
||||
{ "scx_kf_allow_flags", "SCX_KF_ALLOW_SELECT_CPU", 0x20LLU },
|
||||
{ "scx_kick_flags", "SCX_KICK_IDLE", 0x1LLU },
|
||||
{ "scx_kick_flags", "SCX_KICK_PREEMPT", 0x2LLU },
|
||||
{ "scx_kick_flags", "SCX_KICK_WAIT", 0x4LLU },
|
||||
{ "scx_opi", "SCX_OPI_BEGIN", 0x0LLU },
|
||||
{ "scx_opi", "SCX_OPI_NORMAL_BEGIN", 0x0LLU },
|
||||
{ "scx_opi", "SCX_OPI_NORMAL_END", 0x21LLU },
|
||||
{ "scx_opi", "SCX_OPI_CPU_HOTPLUG_BEGIN", 0x21LLU },
|
||||
{ "scx_opi", "SCX_OPI_CPU_HOTPLUG_END", 0x23LLU },
|
||||
{ "scx_opi", "SCX_OPI_END", 0x23LLU },
|
||||
{ "scx_ops_flags", "SCX_OPS_KEEP_BUILTIN_IDLE", 0x1LLU },
|
||||
{ "scx_ops_flags", "SCX_OPS_ENQ_LAST", 0x2LLU },
|
||||
{ "scx_ops_flags", "SCX_OPS_ENQ_EXITING", 0x4LLU },
|
||||
{ "scx_ops_flags", "SCX_OPS_SWITCH_PARTIAL", 0x8LLU },
|
||||
{ "scx_ops_flags", "SCX_OPS_ENQ_MIGRATION_DISABLED", 0x10LLU },
|
||||
{ "scx_ops_flags", "SCX_OPS_ALLOW_QUEUED_WAKEUP", 0x20LLU },
|
||||
{ "scx_ops_flags", "SCX_OPS_BUILTIN_IDLE_PER_NODE", 0x40LLU },
|
||||
{ "scx_ops_flags", "SCX_OPS_ALWAYS_ENQ_IMMED", 0x80LLU },
|
||||
{ "scx_ops_flags", "SCX_OPS_TID_TO_TASK", 0x100LLU },
|
||||
{ "scx_ops_flags", "SCX_OPS_ALL_FLAGS", 0x1ffLLU },
|
||||
{ "scx_ops_flags", "__SCX_OPS_INTERNAL_MASK", 0xff00000000000000LLU },
|
||||
{ "scx_ops_flags", "SCX_OPS_HAS_CPU_PREEMPT", 0x100000000000000LLU },
|
||||
{ "scx_ops_state", "SCX_OPSS_NONE", 0x0LLU },
|
||||
{ "scx_ops_state", "SCX_OPSS_QUEUEING", 0x1LLU },
|
||||
{ "scx_ops_state", "SCX_OPSS_QUEUED", 0x2LLU },
|
||||
{ "scx_ops_state", "SCX_OPSS_DISPATCHING", 0x3LLU },
|
||||
{ "scx_ops_state", "SCX_OPSS_QSEQ_SHIFT", 0x2LLU },
|
||||
{ "scx_pick_idle_cpu_flags", "SCX_PICK_IDLE_CORE", 0x1LLU },
|
||||
{ "scx_pick_idle_cpu_flags", "SCX_PICK_IDLE_IN_NODE", 0x2LLU },
|
||||
{ "scx_public_consts", "SCX_OPS_NAME_LEN", 0x80LLU },
|
||||
{ "scx_public_consts", "SCX_SLICE_DFL", 0x1312d00LLU },
|
||||
{ "scx_public_consts", "SCX_SLICE_BYPASS", 0x4c4b40LLU },
|
||||
{ "scx_public_consts", "SCX_SLICE_INF", 0xffffffffffffffffLLU },
|
||||
{ "scx_reenq_flags", "SCX_REENQ_ANY", 0x1LLU },
|
||||
{ "scx_reenq_flags", "SCX_REENQ_CAP_REVOKE", 0x2LLU },
|
||||
{ "scx_reenq_flags", "__SCX_REENQ_FILTER_MASK", 0xffffLLU },
|
||||
{ "scx_reenq_flags", "__SCX_REENQ_USER_MASK", 0x1LLU },
|
||||
{ "scx_reenq_flags", "SCX_REENQ_TSR_RQ_OPEN", 0x100000000LLU },
|
||||
{ "scx_reenq_flags", "SCX_REENQ_TSR_NOT_FIRST", 0x200000000LLU },
|
||||
{ "scx_reenq_flags", "__SCX_REENQ_TSR_MASK", 0xf00000000LLU },
|
||||
{ "scx_rq_flags", "SCX_RQ_ONLINE", 0x1LLU },
|
||||
{ "scx_rq_flags", "SCX_RQ_CAN_STOP_TICK", 0x2LLU },
|
||||
{ "scx_rq_flags", "SCX_RQ_CLK_VALID", 0x20LLU },
|
||||
{ "scx_rq_flags", "SCX_RQ_BAL_CB_PENDING", 0x40LLU },
|
||||
{ "scx_rq_flags", "SCX_RQ_SUB_IDLE_RENOTIFY", 0x80LLU },
|
||||
{ "scx_rq_flags", "SCX_RQ_ROOT_IDLE_RENOTIFY", 0x100LLU },
|
||||
{ "scx_rq_flags", "SCX_RQ_IN_WAKEUP", 0x10000LLU },
|
||||
{ "scx_rq_flags", "SCX_RQ_IN_DISPATCH", 0x20000LLU },
|
||||
{ "scx_sched_pcpu_flags", "SCX_SCHED_PCPU_BYPASSING", 0x1LLU },
|
||||
{ "scx_slice_oob_consts", "SCX_SLICE_OOB_DUR_BITS", 0x2bLLU },
|
||||
{ "scx_slice_oob_consts", "SCX_SLICE_OOB_ID_BITS", 0x14LLU },
|
||||
{ "scx_slice_oob_consts", "SCX_SLICE_OOB_DUR_MASK", 0x7ffffffffffLLU },
|
||||
{ "scx_slice_oob_consts", "SCX_SLICE_OOB_ID_SHIFT", 0x2bLLU },
|
||||
{ "scx_slice_oob_consts", "SCX_SLICE_OOB_ID_MASK", 0xfffffLLU },
|
||||
{ "scx_slice_oob_consts", "SCX_SLICE_OOB_PENDING", 0x8000000000000000LLU },
|
||||
{ "scx_tg_flags", "SCX_TG_ONLINE", 0x1LLU },
|
||||
{ "scx_tg_flags", "SCX_TG_INITED", 0x2LLU },
|
||||
{ "scx_tg_flags", "SCX_TG_SUB_INIT", 0x4LLU },
|
||||
{ "scx_wake_flags", "SCX_WAKE_FORK", 0x4LLU },
|
||||
{ "scx_wake_flags", "SCX_WAKE_TTWU", 0x8LLU },
|
||||
{ "scx_wake_flags", "SCX_WAKE_SYNC", 0x10LLU },
|
||||
};
|
||||
|
||||
#endif /* __ENUMS_ABI_AUTOGEN_H__ */
|
||||
@@ -299,6 +299,7 @@ static int central_timerfn(void *map, int *key, struct bpf_timer *timer)
|
||||
u64 now = scx_bpf_now();
|
||||
u64 nr_to_kick = nr_queued;
|
||||
s32 i, curr_cpu;
|
||||
int ret;
|
||||
|
||||
curr_cpu = bpf_get_smp_processor_id();
|
||||
if (timer_pinned && (curr_cpu != central_cpu)) {
|
||||
@@ -332,7 +333,10 @@ static int central_timerfn(void *map, int *key, struct bpf_timer *timer)
|
||||
scx_bpf_kick_cpu(cpu, SCX_KICK_PREEMPT);
|
||||
}
|
||||
|
||||
bpf_timer_start(timer, TIMER_INTERVAL_NS, BPF_F_TIMER_CPU_PIN);
|
||||
ret = bpf_timer_start(timer, TIMER_INTERVAL_NS,
|
||||
timer_pinned ? BPF_F_TIMER_CPU_PIN : 0);
|
||||
if (ret)
|
||||
scx_bpf_error("bpf_timer_start failed (%d)", ret);
|
||||
__sync_fetch_and_add(&nr_timers, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -937,7 +937,7 @@ void BPF_STRUCT_OPS(fcg_cgroup_move, struct task_struct *p,
|
||||
if (!(from_cgc = find_cgrp_ctx(from)) || !(to_cgc = find_cgrp_ctx(to)))
|
||||
return;
|
||||
|
||||
delta = time_delta(p->scx.dsq_vtime, from_cgc->tvtime_now);
|
||||
delta = (s64)(p->scx.dsq_vtime - from_cgc->tvtime_now);
|
||||
scx_bpf_task_set_dsq_vtime(p, to_cgc->tvtime_now + delta);
|
||||
}
|
||||
|
||||
|
||||
@@ -1246,7 +1246,8 @@ static int monitor_timerfn(void *map, int *key, struct bpf_timer *timer)
|
||||
scx_read_event(&events, SCX_EV_BYPASS_ACTIVATE));
|
||||
}
|
||||
|
||||
bpf_timer_start(timer, ONE_SEC_IN_NS, 0);
|
||||
if (bpf_timer_start(timer, ONE_SEC_IN_NS, 0))
|
||||
scx_bpf_error("failed to re-arm stats timer");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1268,7 +1269,8 @@ struct {
|
||||
static int lowpri_timerfn(void *map, int *key, struct bpf_timer *timer)
|
||||
{
|
||||
scx_bpf_dsq_reenq(LOWPRI_DSQ, 0);
|
||||
bpf_timer_start(timer, LOWPRI_INTV_NS, 0);
|
||||
if (bpf_timer_start(timer, LOWPRI_INTV_NS, 0))
|
||||
scx_bpf_error("failed to re-arm lowpri timer");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1747,7 +1749,8 @@ static void rr_advance(void)
|
||||
static int round_robin_timerfn(void *map, int *key, struct bpf_timer *timer)
|
||||
{
|
||||
rr_advance();
|
||||
bpf_timer_start(timer, round_robin_ns, 0);
|
||||
if (bpf_timer_start(timer, round_robin_ns, 0))
|
||||
scx_bpf_error("failed to re-arm round-robin timer");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -919,7 +919,6 @@ int main(int argc, char *argv[])
|
||||
int i;
|
||||
|
||||
ksft_print_header();
|
||||
ksft_set_plan(ARRAY_SIZE(tests));
|
||||
if (cg_find_unified_root(root, sizeof(root), &nsdelegate)) {
|
||||
if (setup_named_v1_root(root, sizeof(root), CG_NAMED_NAME))
|
||||
ksft_exit_skip("cgroup v2 isn't mounted and could not setup named v1 hierarchy\n");
|
||||
@@ -932,6 +931,7 @@ int main(int argc, char *argv[])
|
||||
ksft_exit_skip("Failed to set memory controller\n");
|
||||
|
||||
post_v2_setup:
|
||||
ksft_set_plan(ARRAY_SIZE(tests));
|
||||
for (i = 0; i < ARRAY_SIZE(tests); i++) {
|
||||
switch (tests[i].fn(root)) {
|
||||
case KSFT_PASS:
|
||||
|
||||
@@ -832,7 +832,6 @@ int main(int argc, char *argv[])
|
||||
int i;
|
||||
|
||||
ksft_print_header();
|
||||
ksft_set_plan(ARRAY_SIZE(tests));
|
||||
if (cg_find_unified_root(root, sizeof(root), NULL))
|
||||
ksft_exit_skip("cgroup v2 isn't mounted\n");
|
||||
|
||||
@@ -840,6 +839,7 @@ int main(int argc, char *argv[])
|
||||
if (cg_write(root, "cgroup.subtree_control", "+cpu"))
|
||||
ksft_exit_skip("Failed to set cpu controller\n");
|
||||
|
||||
ksft_set_plan(ARRAY_SIZE(tests));
|
||||
for (i = 0; i < ARRAY_SIZE(tests); i++) {
|
||||
switch (tests[i].fn(root)) {
|
||||
case KSFT_PASS:
|
||||
|
||||
@@ -497,7 +497,6 @@ int main(int argc, char *argv[])
|
||||
int i;
|
||||
|
||||
ksft_print_header();
|
||||
ksft_set_plan(ARRAY_SIZE(tests));
|
||||
if (cg_find_unified_root(root, sizeof(root), NULL))
|
||||
ksft_exit_skip("cgroup v2 isn't mounted\n");
|
||||
|
||||
@@ -505,6 +504,7 @@ int main(int argc, char *argv[])
|
||||
if (cg_write(root, "cgroup.subtree_control", "+cpuset"))
|
||||
ksft_exit_skip("Failed to set cpuset controller\n");
|
||||
|
||||
ksft_set_plan(ARRAY_SIZE(tests));
|
||||
for (i = 0; i < ARRAY_SIZE(tests); i++) {
|
||||
switch (tests[i].fn(root)) {
|
||||
case KSFT_PASS:
|
||||
|
||||
@@ -797,7 +797,6 @@ check_isolcpus()
|
||||
EXPECTED_ISOLCPUS=$1
|
||||
ISCPUS=${CGROUP2}/cpuset.cpus.isolated
|
||||
ISOLCPUS=$(cat $ISCPUS)
|
||||
HKICPUS=$(cat /sys/devices/system/cpu/isolated)
|
||||
LASTISOLCPU=
|
||||
SCHED_DOMAINS=/sys/kernel/debug/sched/domains
|
||||
if [[ $EXPECTED_ISOLCPUS = . ]]
|
||||
@@ -835,11 +834,6 @@ check_isolcpus()
|
||||
ISOLCPUS=
|
||||
EXPECTED_ISOLCPUS=$EXPECTED_SDOMAIN
|
||||
|
||||
#
|
||||
# The inverse of HK_TYPE_DOMAIN cpumask in $HKICPUS should match $ISOLCPUS
|
||||
#
|
||||
[[ "$ISOLCPUS" != "$HKICPUS" ]] && return 1
|
||||
|
||||
#
|
||||
# Use the sched domain in debugfs to check isolated CPUs, if available
|
||||
#
|
||||
@@ -1161,6 +1155,63 @@ test_isolated()
|
||||
pause 0.05
|
||||
}
|
||||
|
||||
#
|
||||
# Select an online CPU isolated from scheduler domains at boot.
|
||||
# $1: test name used in the skip message
|
||||
#
|
||||
get_boot_isolated_cpu()
|
||||
{
|
||||
TEST_NAME=$1
|
||||
BOOT_ISOLATED_FILE=/sys/devices/system/cpu/isolated
|
||||
|
||||
[[ -r $BOOT_ISOLATED_FILE ]] || {
|
||||
echo "$TEST_NAME test SKIPPED: boot isolation state unavailable"
|
||||
return 1
|
||||
}
|
||||
BOOT_CPUS=$(cat $BOOT_ISOLATED_FILE)
|
||||
[[ -n "$BOOT_CPUS" ]] || {
|
||||
echo "$TEST_NAME test SKIPPED: no boot-isolated CPU"
|
||||
return 1
|
||||
}
|
||||
|
||||
BOOT_CPU=$(echo "$BOOT_CPUS" | sed -e 's/[,-].*//')
|
||||
CPU_ONLINE=/sys/devices/system/cpu/cpu${BOOT_CPU}/online
|
||||
[[ ! -e $CPU_ONLINE || $(cat $CPU_ONLINE) -eq 1 ]] || {
|
||||
echo "$TEST_NAME test SKIPPED: CPU $BOOT_CPU is offline"
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# A CPU isolated at boot must stay isolated after it is released by a dynamic
|
||||
# isolated partition.
|
||||
#
|
||||
test_boot_isolated()
|
||||
{
|
||||
TEST_NAME="Boot-isolated CPU partition release"
|
||||
get_boot_isolated_cpu "$TEST_NAME" || return 0
|
||||
echo "Running $TEST_NAME test ..."
|
||||
|
||||
cd $CGROUP2/test
|
||||
echo member > cpuset.cpus.partition
|
||||
echo $BOOT_CPU > cpuset.cpus
|
||||
[[ $(cat cpuset.cpus.effective) = "$BOOT_CPU" ]] || {
|
||||
echo "$TEST_NAME test SKIPPED: CPU $BOOT_CPU is unavailable"
|
||||
echo "" > cpuset.cpus
|
||||
cd $CGROUP2
|
||||
return 0
|
||||
}
|
||||
test_partition isolated
|
||||
test_partition member
|
||||
check_isolcpus "." || {
|
||||
echo "Boot-isolated CPU $BOOT_CPU was lost after partition release"
|
||||
exit 1
|
||||
}
|
||||
echo "" > cpuset.cpus
|
||||
cd $CGROUP2
|
||||
echo "$TEST_NAME test PASSED."
|
||||
}
|
||||
|
||||
#
|
||||
# Wait for inotify event for the given file and read it
|
||||
# $1: cgroup file to wait for
|
||||
@@ -1232,5 +1283,6 @@ trap cleanup 0 2 3 6
|
||||
run_state_test TEST_MATRIX
|
||||
run_remote_state_test REMOTE_TEST_MATRIX
|
||||
test_isolated
|
||||
test_boot_isolated
|
||||
test_inotify
|
||||
echo "All tests PASSED."
|
||||
|
||||
@@ -1491,9 +1491,9 @@ int main(int argc, char *argv[])
|
||||
int i;
|
||||
|
||||
ksft_print_header();
|
||||
ksft_set_plan(ARRAY_SIZE(tests));
|
||||
if (cg_find_unified_root(root, sizeof(root), NULL))
|
||||
ksft_exit_skip("cgroup v2 isn't mounted\n");
|
||||
ksft_set_plan(ARRAY_SIZE(tests));
|
||||
for (i = 0; i < ARRAY_SIZE(tests); i++) {
|
||||
switch (tests[i].fn(root)) {
|
||||
case KSFT_PASS:
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "kselftest.h"
|
||||
@@ -261,6 +262,59 @@ static int test_cgkill_forkbomb(const char *root)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Test that a cgroup that was killed in the past can still be the target
|
||||
* of clone3(CLONE_INTO_CGROUP): writing cgroup.kill must only kill the
|
||||
* tasks in the cgroup at the time of the write, not tasks cloned into
|
||||
* it afterwards.
|
||||
*/
|
||||
static int test_cgkill_clone_into_killed(const char *root)
|
||||
{
|
||||
pid_t pid;
|
||||
int cgroup_fd = -EBADF;
|
||||
int ret = KSFT_FAIL;
|
||||
char *cgroup = NULL;
|
||||
|
||||
cgroup = cg_name(root, "cg_test_clone_into_killed");
|
||||
if (!cgroup)
|
||||
goto cleanup;
|
||||
|
||||
if (cg_create(cgroup))
|
||||
goto cleanup;
|
||||
|
||||
/* Kill the cgroup while it is still empty. */
|
||||
if (cg_write(cgroup, "cgroup.kill", "1"))
|
||||
goto cleanup;
|
||||
|
||||
cgroup_fd = dirfd_open_opath(cgroup);
|
||||
if (cgroup_fd < 0)
|
||||
goto cleanup;
|
||||
|
||||
pid = clone_into_cgroup(cgroup_fd);
|
||||
if (pid < 0) {
|
||||
if (errno == ENOSYS)
|
||||
ret = KSFT_SKIP;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (pid == 0)
|
||||
exit(EXIT_SUCCESS);
|
||||
|
||||
/* The child must not be SIGKILLed; it has to exit cleanly. */
|
||||
if (clone_reap(pid, WEXITED) != EXIT_SUCCESS)
|
||||
goto cleanup;
|
||||
|
||||
ret = KSFT_PASS;
|
||||
|
||||
cleanup:
|
||||
if (cgroup_fd >= 0)
|
||||
close(cgroup_fd);
|
||||
if (cgroup)
|
||||
cg_destroy(cgroup);
|
||||
free(cgroup);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define T(x) { x, #x }
|
||||
struct cgkill_test {
|
||||
int (*fn)(const char *root);
|
||||
@@ -269,6 +323,7 @@ struct cgkill_test {
|
||||
T(test_cgkill_simple),
|
||||
T(test_cgkill_tree),
|
||||
T(test_cgkill_forkbomb),
|
||||
T(test_cgkill_clone_into_killed),
|
||||
};
|
||||
#undef T
|
||||
|
||||
@@ -278,9 +333,9 @@ int main(int argc, char *argv[])
|
||||
int i;
|
||||
|
||||
ksft_print_header();
|
||||
ksft_set_plan(ARRAY_SIZE(tests));
|
||||
if (cg_find_unified_root(root, sizeof(root), NULL))
|
||||
ksft_exit_skip("cgroup v2 isn't mounted\n");
|
||||
ksft_set_plan(ARRAY_SIZE(tests));
|
||||
for (i = 0; i < ARRAY_SIZE(tests); i++) {
|
||||
switch (tests[i].fn(root)) {
|
||||
case KSFT_PASS:
|
||||
|
||||
@@ -145,7 +145,7 @@ static int cg_run_in_subcgroups(const char *parent,
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (cg_run(child, fn, NULL)) {
|
||||
if (cg_run(child, fn, arg)) {
|
||||
cg_destroy(child);
|
||||
free(child);
|
||||
return -1;
|
||||
@@ -426,7 +426,6 @@ int main(int argc, char **argv)
|
||||
int i;
|
||||
|
||||
ksft_print_header();
|
||||
ksft_set_plan(ARRAY_SIZE(tests));
|
||||
if (cg_find_unified_root(root, sizeof(root), NULL))
|
||||
ksft_exit_skip("cgroup v2 isn't mounted\n");
|
||||
|
||||
@@ -441,6 +440,7 @@ int main(int argc, char **argv)
|
||||
if (cg_write(root, "cgroup.subtree_control", "+memory"))
|
||||
ksft_exit_skip("Failed to set memory controller\n");
|
||||
|
||||
ksft_set_plan(ARRAY_SIZE(tests));
|
||||
for (i = 0; i < ARRAY_SIZE(tests); i++) {
|
||||
switch (tests[i].fn(root)) {
|
||||
case KSFT_PASS:
|
||||
|
||||
@@ -1798,7 +1798,6 @@ int main(int argc, char **argv)
|
||||
page_size = BUF_SIZE;
|
||||
|
||||
ksft_print_header();
|
||||
ksft_set_plan(ARRAY_SIZE(tests));
|
||||
if (cg_find_unified_root(root, sizeof(root), NULL))
|
||||
ksft_exit_skip("cgroup v2 isn't mounted\n");
|
||||
|
||||
@@ -1823,6 +1822,7 @@ int main(int argc, char **argv)
|
||||
ksft_exit_skip("Failed to query cgroup mount option\n");
|
||||
has_localevents = proc_status;
|
||||
|
||||
ksft_set_plan(ARRAY_SIZE(tests));
|
||||
for (i = 0; i < ARRAY_SIZE(tests); i++) {
|
||||
switch (tests[i].fn(root)) {
|
||||
case KSFT_PASS:
|
||||
|
||||
@@ -148,7 +148,6 @@ int main(int argc, char **argv)
|
||||
char root[PATH_MAX];
|
||||
|
||||
ksft_print_header();
|
||||
ksft_set_plan(ARRAY_SIZE(tests));
|
||||
if (cg_find_unified_root(root, sizeof(root), NULL))
|
||||
ksft_exit_skip("cgroup v2 isn't mounted\n");
|
||||
|
||||
@@ -163,6 +162,7 @@ int main(int argc, char **argv)
|
||||
if (cg_write(root, "cgroup.subtree_control", "+pids"))
|
||||
ksft_exit_skip("Failed to set pids controller\n");
|
||||
|
||||
ksft_set_plan(ARRAY_SIZE(tests));
|
||||
for (int i = 0; i < ARRAY_SIZE(tests); i++) {
|
||||
switch (tests[i].fn(root)) {
|
||||
case KSFT_PASS:
|
||||
|
||||
@@ -819,7 +819,6 @@ int main(int argc, char **argv)
|
||||
page_size = BUF_SIZE;
|
||||
|
||||
ksft_print_header();
|
||||
ksft_set_plan(ARRAY_SIZE(tests));
|
||||
if (cg_find_unified_root(root, sizeof(root), NULL))
|
||||
ksft_exit_skip("cgroup v2 isn't mounted\n");
|
||||
|
||||
@@ -836,6 +835,7 @@ int main(int argc, char **argv)
|
||||
if (cg_write(root, "cgroup.subtree_control", "+memory"))
|
||||
ksft_exit_skip("Failed to set memory controller\n");
|
||||
|
||||
ksft_set_plan(ARRAY_SIZE(tests));
|
||||
for (i = 0; i < ARRAY_SIZE(tests); i++) {
|
||||
switch (tests[i].fn(root)) {
|
||||
case KSFT_PASS:
|
||||
|
||||
@@ -78,6 +78,12 @@ def cpumask_str(cpumask):
|
||||
|
||||
wq_type_len = 9
|
||||
|
||||
def wq_attrs(wq):
|
||||
try:
|
||||
return wq.attrs
|
||||
except AttributeError:
|
||||
return wq.unbound_attrs
|
||||
|
||||
def wq_type_str(wq):
|
||||
if wq.flags & WQ_BH:
|
||||
return f'{"bh":{wq_type_len}}'
|
||||
@@ -85,7 +91,7 @@ def wq_type_str(wq):
|
||||
if wq.flags & WQ_ORDERED:
|
||||
return f'{"ordered":{wq_type_len}}'
|
||||
else:
|
||||
if wq.attrs.affn_strict:
|
||||
if wq_attrs(wq).affn_strict:
|
||||
return f'{"unbound,S":{wq_type_len}}'
|
||||
else:
|
||||
return f'{"unbound":{wq_type_len}}'
|
||||
@@ -206,7 +212,7 @@ for wq in list_for_each_entry('struct workqueue_struct', workqueues.address_of_(
|
||||
|
||||
print(f'{wq.name.string_().decode():{WQ_NAME_LEN}}', end='')
|
||||
if wq.flags & WQ_UNBOUND:
|
||||
print(f' {cpumask_str(wq.attrs.cpumask):{ucpus_len}}', end='')
|
||||
print(f' {cpumask_str(wq_attrs(wq).cpumask):{ucpus_len}}', end='')
|
||||
else:
|
||||
print(f' {"":{ucpus_len}}', end='')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user