diff --git a/site/assets/css/default.css b/site/assets/css/default.css index b95be3b..4f6b6f8 100644 --- a/site/assets/css/default.css +++ b/site/assets/css/default.css @@ -171,7 +171,7 @@ html, body { @apply wrap-anywhere; } - &:not(:has(> code:only-child)) { + &:not(:has(> code:only-child)):not(:has(> img)) { @apply px-1; &:not(.font-mono) { @@ -230,8 +230,8 @@ html, body { code:not(pre > code) { @apply border-1 border-dotted px-2 py-0.5 border-black dark:border-white; - a:hover &:not(:is(h1, h2, h3, h4, h5, h6) *), - a:active &:not(:is(h1, h2, h3, h4, h5, h6) *) { + a:hover &:not(:only-child, :is(h1, h2, h3, h4, h5, h6) *), + a:active &:not(:only-child, :is(h1, h2, h3, h4, h5, h6) *) { @apply border-transparent; } } @@ -252,9 +252,7 @@ html, body { } & li::before { - @apply pr-1; - - content: "[" counter(item) "]"; + content: counter(item) ". "; } } diff --git a/site/assets/images/cpu-dumb.webp b/site/assets/images/cpu-dumb.webp new file mode 100644 index 0000000..44e37f1 Binary files /dev/null and b/site/assets/images/cpu-dumb.webp differ diff --git a/site/blog/test.md b/site/blog/2024-01-01-test.md similarity index 99% rename from site/blog/test.md rename to site/blog/2024-01-01-test.md index 2b9257e..abb6da3 100644 --- a/site/blog/test.md +++ b/site/blog/2024-01-01-test.md @@ -1,7 +1,7 @@ --- -date: 2024-01-01 title: Test description: "Testing" + draft: true --- diff --git a/site/blog/htmnix.md b/site/blog/2024-03-04-htmnix.md similarity index 99% rename from site/blog/htmnix.md rename to site/blog/2024-03-04-htmnix.md index eb6d0ff..847b0ec 100644 --- a/site/blog/htmnix.md +++ b/site/blog/2024-03-04-htmnix.md @@ -2,8 +2,6 @@ title: HTMNIX description: How the absolutely cursed HTMNIX project works. -date: 2024-03-04 - keywords: - html - nix diff --git a/site/blog/cosmic-drift.md b/site/blog/2024-04-02-cosmic-drift.md similarity index 99% rename from site/blog/cosmic-drift.md rename to site/blog/2024-04-02-cosmic-drift.md index 9f6dab4..862d7cb 100644 --- a/site/blog/cosmic-drift.md +++ b/site/blog/2024-04-02-cosmic-drift.md @@ -2,8 +2,6 @@ title: Cosmic Drift description: Or how I missed the school bus because of a cosmic ray. -date: 2024-04-02 - keywords: - time - unix-timestamps diff --git a/site/blog/nix-iceberg.md b/site/blog/2024-04-15-nix-iceberg.md similarity index 79% rename from site/blog/nix-iceberg.md rename to site/blog/2024-04-15-nix-iceberg.md index 847b205..59218c4 100644 --- a/site/blog/nix-iceberg.md +++ b/site/blog/2024-04-15-nix-iceberg.md @@ -2,7 +2,6 @@ title: Explaining the Nix iceberg description: And revealing how cursed Nix is. -date: 2024-04-15 draft: true keywords: @@ -16,12 +15,10 @@ it a reputation of being arcane and hard to use. I'll be explaining the contents of the following iceberg chart, which includes some truly arcane examples of Nix code. -Some knowledge of Nix is required, you may get confused in the terminology if -you've never used it. +Some knowledge of Nix is required, you may get confused with the terminology if +you've never used Nix. -![The Nix Iceberg](/assets/images/nix-iceberg.webp) - -[Here's the original source for this image, on cohost.](https://cohost.org/leftpaddotpy/post/3885451-the-nix-iceberg) +[![The Nix Iceberg](/assets/images/nix-iceberg.webp)](https://cohost.org/leftpaddotpy/post/3885451-the-nix-iceberg) Let's start: @@ -54,7 +51,7 @@ So, what are we doing in this snippet? 2. Creating a derivation that runs an echo command, which writes a Nix expression to the output file. 3. Then we import the output file, forcing the derivation to be realized as we - accessed the contents of it. + just accessed the contents of it. > Wait, what does _realization_ mean? @@ -519,14 +516,14 @@ known. # Tier 3: `assigned nix hacker at employment` -

+

```sh #!/usr/bin/env nix-shell #!nix-shell -i python3 -p python3 ``` -

+ _(taken verbatim from `man nix-shell`)_ @@ -565,7 +562,16 @@ print t ## `--accept-flake-config` more like `--pwn-me-mommy` -TODO +The +[`accept-flake-config`](https://nix.dev/manual/nix/2.29/command-ref/conf-file#conf-accept-flake-config) +Nix configuration variable or `--accept-flake-config` flag in Nix commands +allows Nix to unconditionally accept flake `nixConfig`'s. + +This is dangerous, because this can enable `builtins.importNative` by enabling +the +[`allow-unsafe-native-code-during-evaluation`](https://nix.dev/manual/nix/2.29/command-ref/conf-file#conf-allow-unsafe-native-code-during-evaluation) +option, which then allows Nix expresions to load arbitrary dynamic libraries, +which can do anything as they are not confined to the Nix evaluation sandbox. ## Zilch @@ -717,8 +723,7 @@ But if two attribute sets that are compared have the same memory location, Nix ignores this and does a pointer comparison, totally ignoring all members. This is a hack. -[Link to code that does this.](https://github.com/NixOS/nix/blob/aa165301d1ae3b306319a6a834dc1d4e340a7112/src/libexpr/eval.cc#L2525-L2528) -Here's the snippet: +[Here's the snippet:](https://github.com/NixOS/nix/blob/aa165301d1ae3b306319a6a834dc1d4e340a7112/src/libexpr/eval.cc#L2525-L2528) ```cpp bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_view errorCtx) @@ -739,7 +744,7 @@ want with this information. As surprising as it sounds, Nix does indeed supports plugins. You can load plugins using the -[`plugin-files`](https://nix.dev/manual/nix/2.22/command-ref/conf-file#conf-plugin-files) +[`plugin-files`](https://nix.dev/manual/nix/2.29/command-ref/conf-file.html#conf-plugin-files) configuration option. From the configuration reference: @@ -771,7 +776,7 @@ Some example plugins are [`nix-doc`](https://github.com/lf-/nix-doc) and ## `/bin/sh` and sandbox impurity By setting the -[`sandbox-paths`](https://nix.dev/manual/nix/2.22/command-ref/conf-file#conf-sandbox-paths) +[`sandbox-paths`](https://nix.dev/manual/nix/2.29/command-ref/conf-file#conf-sandbox-paths) option to `/bin/sh=/bin/sh`, Nix will bind the `/bin/sh` path in the build sandbox (left) to the `/bin/sh` path in the host (right). This is of course impure, but is useful for bootstrapping from absolute scratch without copying @@ -779,10 +784,11 @@ impure binaries to the Nix store. ## `rec { a = 5; b = a + 1; __overrides.a = 6; }` -There is a special field named `__overrides` in recursive attrset expressions, -which simply overrides the parent attribute set with the keys inside it. This is -different from the update operator (`//`) because that will not override the -self-references in the recursive attribute set. +There is a special field named `__overrides` in keyed expressions (attribute +sets, `let-in`'s and as secret third thing), which simply overrides the parent +attribute set with the keys inside it. This is different from the update +operator (`//`) because that will not override the self-references in the +recursive attribute set. `rec { a = 5; b = a + 1; __overrides.a = 6; }.b` will evaluate to 7, while `(rec { a = 5; b = a + 1; } // { a = 6; }).b` will evaluate to 6. @@ -814,7 +820,58 @@ TODO ## `let a = _: -1; or = 6; in [ a or 9 ]` -TODO +The Nix parser is weird. + +Normally, `or` is used for attribute path selection defaults: + +```nix +{ foo = 123; }.not-here.not-here-either or 123 +``` + +That above evaluates to `123`. + +But when parsing an expression that is not an attribute-select, `or` is treated +as an identifier. This means that in the following `let-in`, we are passing `or` +to `a`. + +```nix +let + a = _: -1; + or = 6; +in a or +``` + +But there is another piece of weirdness. Function applications that use the +literal `or` have higher precedence than the spaces when parsing lists, so these +two codeblocks are not equivalent: + +```nix +let + a = _: -1; + or = 6; +in [ a or ] +``` + +> This evaluates to `[ -1 ]` + +```nix +let + a = _: -1; + foo = 6; +in [ a foo ] +``` + +> This evaluates to `[ 6 ]` + +However, this behaviour might get removed in the future. But currently, in the +Nix version that I am using which is `2.28.3`, it prints this warning instead: + +```text +warning: at «string»:4:6: This expression uses `or` as an identifier in a way that will change in a future Nix release. +Wrap this entire expression in parentheses to preserve its current meaning: + (a or) +Give feedback at https://github.com/NixOS/nix/pull/11121 +``` ## eelco's home address is in nixpkgs @@ -822,16 +879,25 @@ TODO ## `restrict-eval` -TODO +[From the Nix manual:](https://nix.dev/manual/nix/2.29/command-ref/conf-file.html?highlight=restrict-eval#conf-restrict-eval) + +> If set to true, the Nix evaluator will not allow access to any files outside +> of `builtins.nixPath`, or to URIs outside of `allowed-uris`. ## nix2 ## `__noChroot` -TODO +When the +[`sandbox`](https://nix.dev/manual/nix/2.29/command-ref/conf-file.html?highlight=__nos#conf-sandbox) +Nix configuration value is set to `relaxed`, fixed-output derivations (FODs) +that have the `__noChroot` attribute set to `true` will not run in the Nix +sandbox. ## cloud scale hydra +TODO + ## `(_:_) != (_:_)` but `(a:a) == (a:a)` Evaluating `(_:_) == (_:_)`, we see that it is `false`, which means the two @@ -855,3 +921,149 @@ TODO This is the legacy `let` syntax. Equivalent to `let huh = "?"; in huh`. ## Tier 6: `has meowed before` + +### `let { body = 1; __overrides.body = 2; }` + +This is a combination of [`__override`](#rec-a-5-b-a-1-overridesa-6-) for keyed +experessions and the [`legacy let syntax`](#let-huh-body-huh-). + +### function identity is load bearing on importing nixpkgs + +Since +[attribute sets with function members compare function identities (memory locations)](#let-f-a-a-s-ff-in-f-f-s-s), +comparing any attribute set that contains a function is load-bearing on the +function's identity. + +The way this affects importing nixpkgs is that nixpkgs internally compares +stdenvs, which contain functions, to determine whether if we are +cross-compiling.nixpkgs internally compares stdenvs, which contain functions, to +determine whether if we are cross-compiling. + +Therefore, function identity really **is** load bearing on importing nixpkgs. + +### `import ` + +This looks like we are importing , and getting the `fetchurl.nix` file in +it. + +Let's see if that is true: + +```nix +nix-repl> builtins.readDir +{ + ".clang-format" = "regular"; + ".clang-tidy" = "regular"; + ".dir-locals.el" = "regular"; + ".editorconfig" = "regular"; + ".github" = "directory"; + ".gitignore" = "regular"; + ".mergify.yml" = "regular"; + ".shellcheckrc" = "regular"; + ".version" = "regular"; + ".version-determinate" = "regular"; + "CITATION.cff" = "regular"; + "CONTRIBUTING.md" = "regular"; + COPYING = "regular"; + "HACKING.md" = "symlink"; + "README.md" = "regular"; + contrib = "directory"; + doc = "directory"; + "docker.nix" = "regular"; + "flake.lock" = "regular"; + "flake.nix" = "regular"; + maintainers = "directory"; + "meson.build" = "regular"; + "meson.options" = "regular"; + misc = "directory"; + nix-meson-build-support = "directory"; + packaging = "directory"; + "precompiled-headers.h" = "regular"; + scripts = "directory"; + src = "directory"; + tests = "directory"; +} +``` + +There doesn't seem to be a `fetchurl.nix` file here. + +This is because +[`` actually falls back to `corepkgs`](https://github.com/NixOS/nix/blob/2afc84fddf463b22196aeb70587bc0c9259e330f/src/libexpr/eval.cc#L3117-L3118), +which is a Nix path +[defined inside Nix itself.](https://github.com/NixOS/nix/blob/2afc84fddf463b22196aeb70587bc0c9259e330f/src/libexpr/eval.cc#L321) + +[Later, the `fetchurl.nix` path is defined in `corepkgs`](https://github.com/NixOS/nix/blob/2afc84fddf463b22196aeb70587bc0c9259e330f/src/libexpr/eval.cc#L363) +and its contents are set to a +[generated C++ header.](https://github.com/NixOS/nix/blob/2afc84fddf463b22196aeb70587bc0c9259e330f/src/libexpr/meson.build#L129-L135) + +You do not need to be in impure evaluation mode to use `corepkgs`, aka +``. + +### test suite of nix wasn't run + +TODO + +### fixed-output derivation sandboxing + +TODO + +### `importNative` + +[`builtins.importNative`](https://nix.dev/manual/nix/2.29/command-ref/conf-file.html#conf-allow-unsafe-native-code-during-evaluation) +allows Nix expressions to import arbitrary dynamic libraries to produce Nix +expressions. + +Of course, this is turned off by default as it is a security risk. You probably +shouldn't use this. + +### `chromium recompressTarball` + +TODO + +### more than 1 million chars of indents breaks things + +The weird Nix parser +[hard codes `1000000`](https://github.com/NixOS/nix/blob/2afc84fddf463b22196aeb70587bc0c9259e330f/src/libexpr/include/nix/expr/parser-state.hh#L250) +instead of `SIZE_MAX` when determining the minimum indent to strip in strings +spanning multiple lines. + +So when you have a line with more than a million spaces for the indent, it is +ignored and not included in the minimum indent calculation. + +## Tier 7: `wears animal ears to NixCon` + +

+ +```nix +nix-repl> builtins.fromJSON ''{"uwu\u0000": 1, "uwu": 2}'' +{ uwu = 2; "uwu" = 1; } +``` + +

+ +TODO + +## `(_: builtins.break _)` + +TODO + +## multiplayer tic-tac-toe in nix repl + +TODO + +## `let e="e"; in [001.2e01e.30.4]` + +TODO + +## `/__corepkgs__/` + +[Already explained previously.](#import-nixfetchurlnix) + +## `some-expr` + +TODO + +## `__darwinAllowLocalNetworking` + +TODO + +## `builtins.derivationStrict` diff --git a/site/blog/swap-i.md b/site/blog/2024-05-20-swap-i.md similarity index 99% rename from site/blog/swap-i.md rename to site/blog/2024-05-20-swap-i.md index 4f27fa3..7d3522b 100644 --- a/site/blog/swap-i.md +++ b/site/blog/2024-05-20-swap-i.md @@ -2,8 +2,6 @@ title: Swap the `ı` and `i` key on your keyboard for faster modal editing description: How to swap the ı and i key on your Turkish keyboard on Linux. -date: 2024-05-20 - keywords: - localisation - modal-editors diff --git a/site/blog/gitignore.md b/site/blog/2024-09-30-gitignore.md similarity index 99% rename from site/blog/gitignore.md rename to site/blog/2024-09-30-gitignore.md index 27a507d..65ea639 100644 --- a/site/blog/gitignore.md +++ b/site/blog/2024-09-30-gitignore.md @@ -5,8 +5,6 @@ description: And how to roll the rock over the edge. color: "#A5804C" thumbnail: /assets/images/sisyphus-ds-store.webp -date: 2024-09-30 - keywords: - vcs --- diff --git a/site/blog/2025-06-05-why-cores.md b/site/blog/2025-06-05-why-cores.md new file mode 100644 index 0000000..8ca2bdb --- /dev/null +++ b/site/blog/2025-06-05-why-cores.md @@ -0,0 +1,269 @@ +--- +title: "Why more `/sys/class/hwmon/*/temp*_label` than CPU cores?" + +color: "#99CF9F" +thumbnail: /assets/images/cpu-dumb.webp + +keywords: +- hardware +--- + +So, I am currently working on a Linux tool+daemon to replace +[`auto-cpufreq`](https://github.com/AdnanHodzic/auto-cpufreq) with a more +efficient, tunable and observable alternative. + +And obviously, to tune a CPU well, knowing how hot it is is a requirement. + +Then, how do you actually see how hot your CPU is in Linux? Using +`/sys/class/hwmon` of course. + +This path contains hardware monitoring devices. In the laptop I'm testing this +on, `hwmon4` under this directory corresponds to the CPU. + +Let's run a `tree`: + +```text +/sys/class/hwmon/hwmon4 -> ../../devices/platform/coretemp.0/hwmon/hwmon4 +├── device -> ../../../coretemp.0 +│ ├── driver_override +│ ├── hwmon +│ ├── modalias +│ ├── power +│ ├── subsystem -> ../../../bus/platform +│ └── uevent +├── name +├── power +│ ├── autosuspend_delay_ms +│ ├── control +│ ├── runtime_active_time +│ ├── runtime_status +│ └── runtime_suspended_time +├── subsystem -> ../../../../../class/hwmon [recursive, not followed] +├── temp10_crit +├── temp10_crit_alarm +├── temp10_input +├── temp10_label +├── temp10_max +├── temp14_crit +├── temp14_crit_alarm +├── temp14_input +├── temp14_label +├── temp14_max +├── temp18_crit +├── temp18_crit_alarm +├── temp18_input +├── temp18_label +├── temp18_max +├── temp1_crit +├── temp1_crit_alarm +├── temp1_input +├── temp1_label +├── temp1_max +├── temp22_crit +├── temp22_crit_alarm +├── temp22_input +├── temp22_label +├── temp22_max +├── temp26_crit +├── temp26_crit_alarm +├── temp26_input +├── temp26_label +├── temp26_max +├── temp2_crit +├── temp2_crit_alarm +├── temp2_input +├── temp2_label +├── temp2_max +├── temp30_crit +├── temp30_crit_alarm +├── temp30_input +├── temp30_label +├── temp30_max +├── temp34_crit +├── temp34_crit_alarm +├── temp34_input +├── temp34_label +├── temp34_max +├── temp35_crit +├── temp35_crit_alarm +├── temp35_input +├── temp35_label +├── temp35_max +├── temp36_crit +├── temp36_crit_alarm +├── temp36_input +├── temp36_label +├── temp36_max +├── temp37_crit +├── temp37_crit_alarm +├── temp37_input +├── temp37_label +├── temp37_max +├── temp38_crit +├── temp38_crit_alarm +├── temp38_input +├── temp38_label +├── temp38_max +├── temp39_crit +├── temp39_crit_alarm +├── temp39_input +├── temp39_label +├── temp39_max +├── temp40_crit +├── temp40_crit_alarm +├── temp40_input +├── temp40_label +├── temp40_max +├── temp41_crit +├── temp41_crit_alarm +├── temp41_input +├── temp41_label +├── temp41_max +├── temp42_crit +├── temp42_crit_alarm +├── temp42_input +├── temp42_label +├── temp42_max +├── temp43_crit +├── temp43_crit_alarm +├── temp43_input +├── temp43_label +├── temp43_max +├── temp44_crit +├── temp44_crit_alarm +├── temp44_input +├── temp44_label +├── temp44_max +├── temp45_crit +├── temp45_crit_alarm +├── temp45_input +├── temp45_label +├── temp45_max +├── temp46_crit +├── temp46_crit_alarm +├── temp46_input +├── temp46_label +├── temp46_max +├── temp47_crit +├── temp47_crit_alarm +├── temp47_input +├── temp47_label +├── temp47_max +├── temp48_crit +├── temp48_crit_alarm +├── temp48_input +├── temp48_label +├── temp48_max +├── temp49_crit +├── temp49_crit_alarm +├── temp49_input +├── temp49_label +├── temp49_max +├── temp6_crit +├── temp6_crit_alarm +├── temp6_input +├── temp6_label +├── temp6_max +└── uevent +``` + +Let's `cat` all the `_label` files: + +```text +/sys/class/hwmon/hwmon4/temp1_label: +Package id 0 +/sys/class/hwmon/hwmon4/temp2_label: +Core 0 +/sys/class/hwmon/hwmon4/temp6_label: +Core 4 +/sys/class/hwmon/hwmon4/temp10_label: +Core 8 +/sys/class/hwmon/hwmon4/temp14_label: +Core 12 +/sys/class/hwmon/hwmon4/temp18_label: +Core 16 +/sys/class/hwmon/hwmon4/temp22_label: +Core 20 +/sys/class/hwmon/hwmon4/temp26_label: +Core 24 +/sys/class/hwmon/hwmon4/temp30_label: +Core 28 +/sys/class/hwmon/hwmon4/temp34_label: +Core 32 +/sys/class/hwmon/hwmon4/temp35_label: +Core 33 +/sys/class/hwmon/hwmon4/temp36_label: +Core 34 +/sys/class/hwmon/hwmon4/temp37_label: +Core 35 +/sys/class/hwmon/hwmon4/temp38_label: +Core 36 +/sys/class/hwmon/hwmon4/temp39_label: +Core 37 +/sys/class/hwmon/hwmon4/temp40_label: +Core 38 +/sys/class/hwmon/hwmon4/temp41_label: +Core 39 +/sys/class/hwmon/hwmon4/temp42_label: +Core 40 +/sys/class/hwmon/hwmon4/temp43_label: +Core 41 +/sys/class/hwmon/hwmon4/temp44_label: +Core 42 +/sys/class/hwmon/hwmon4/temp45_label: +Core 43 +/sys/class/hwmon/hwmon4/temp46_label: +Core 44 +/sys/class/hwmon/hwmon4/temp47_label: +Core 45 +/sys/class/hwmon/hwmon4/temp48_label: +Core 46 +/sys/class/hwmon/hwmon4/temp49_label: +Core 47 +``` + +Notice something? I do: + +- `temp1_label` is `Package id 0` - what is that? +- The core numbers make no sense. This device only has 32 cores, there shouldn't + be any gaps within numbers and the numbers shouldn't go that high. + +The explanation for the first point is simple, looking at the kernel +[`coretemp.c`](https://github.com/torvalds/linux/blob/ec7714e4947909190ffb3041a03311a975350fe0/drivers/hwmon/coretemp.c#L348) +implementation, we can see that it is the temperature of the CPU as a whole: + +```c +static ssize_t show_label( + struct device *dev, + struct device_attribute *devattr, + char *buf +) { + struct platform_data *pdata = dev_get_drvdata(dev); + struct temp_data *tdata = container_of(devattr, struct temp_data, sd_attrs[ATTR_LABEL]); + + if (is_pkg_temp_data(tdata)) + return sprintf(buf, "Package id %u\n", pdata->pkg_id); + + return sprintf(buf, "Core %u\n", tdata->cpu_core_id); +} +``` + +That leaves us the second question. Why do we have CPUs 1, 2, 6, 10, 14, 18, 22, +26, 30, 34-49, instead of the expected 0-31? + +It turns out that CPU It's common for chip manufacturers to disable faulty or +degraded cores before shipping. Or if a lower tier SKU[^Stock Keeping Unit] is +selling more, cores of higher tier SKUs are disabled to match expectations. + +This process is called `binning`, and it exists to not waste silicon. Since +silicon manufacturing is not perfect, some CPUs are more faulty, thus slower +than others. The process determintes how faulty a CPU is and sorts them into +"bins". This is also why lower tier CPUs who are almost exactly the same as +higher tier CPUs exist. + +The likelyhood of faulty silicon also increases with the smaller the +architechture size gets (the Apple M4 is 4m, which is crazy), so this method of +recycling worse chips is becoming much more valuable by the day. + +So, in summary the 32 core CPU I was testing this on was most likely just the 64 +core version with a bunch of the cores disabled. diff --git a/site/blog/_data.ts b/site/blog/_data.ts index d50cb78..bd76e4f 100644 --- a/site/blog/_data.ts +++ b/site/blog/_data.ts @@ -1,4 +1,6 @@ export default { layout: "text.vto", type: "article", + + description: null, };