Search logs:

channel logs for 2004 - 2010 are archived at http://tunes.org/~nef/logs/old/ ·· can't be searched

#osdev2 = #osdev @ Libera from 23may2021 to present

#osdev @ OPN/FreeNode from 3apr2001 to 23may2021

all other channels are on OPN/FreeNode from 2004 to present


http://bespin.org/~qz/search/?view=1&c=osdev2&y=24&m=4&d=20

Saturday, 20 April 2024

00:30:00 <geist> it's uncouth, but what's wrong with php?
00:30:00 <geist> it's close to pdp! my favorite line of computers
00:34:00 <childlikempress> pdp is short for programmers despise pdp
00:36:00 <heat> the p in pdp is short for ping pong
00:36:00 <childlikempress> the d in pdp is short for ding dong
00:36:00 <heat> pdp stands for penis dick penis
00:36:00 <heat> subscribe for more mature takes on technology
00:38:00 <childlikempress> so pdp is gay and z80 is dead. what about the rest of the ancient technology?
00:44:00 <geist> heh i was just pondering if i should order a z80 or two off digikey before they're gone
00:44:00 <geist> probably will, though they're like $10 a pop
00:45:00 <childlikempress> that is extremely bad flops/$
00:45:00 <mjg> php stands for programmes hate php
00:46:00 <geist> pretty hate peripheral
00:46:00 <mjg> z80 huh?
00:46:00 <mjg> no love for atari?
00:46:00 <heat> pretty hot programmer
00:46:00 * mjg <-- biased
00:46:00 <geist> well zilog just announced like yesterday that they're stopping manufacturing
00:46:00 <geist> thats the news, 48 years!
00:47:00 <vdamewood> Programming Hell Programatically
00:47:00 <mjg> :O
00:48:00 <vdamewood> That means that I'll have to use ther 65C02 for my 8-bit computer needs
00:52:00 <geist> but yeah i'm somewhat biased towards 6502 based stuff
00:52:00 <geist> i have a stash of those though
00:53:00 <kof673> > and z80 is dead well if there is a stork then...the phoenix will birth it into the underworld. it'll be fine.
00:54:00 <kof673> z80-en
00:54:00 <vdamewood> I want to build a system with a 65C816
00:54:00 <geist> yeah neat thing is it's basically drop in compatible
00:55:00 <geist> *basically* just a few pins different
00:55:00 <vdamewood> Just have to worry about the higher memory address pins
00:55:00 <geist> well, to use it without 24 bit memory access, you need to obviously deal with higher addresses being plonked on the D bus and then latch it during the off cycle
00:55:00 <geist> yah they reused D0-D7 for that
00:56:00 <heat> the D in D0-D7 stands for deeznutz
00:56:00 <geist> you like imagine dragons?
00:57:00 <heat> no
00:57:00 <vdamewood> I like imagining dragons
00:57:00 <heat> imagine dragons are like
00:57:00 <heat> kinda cheesy
00:57:00 <geist> imagine dragon deez nutz on your chin
00:57:00 <heat> lol
00:58:00 <mjg> is this geist
00:58:00 <mjg> or is this 16 y/o geist
00:58:00 <vdamewood> I want a system with a dragon drop interface
00:58:00 <childlikempress> geist was always secretly a 16 year old
00:59:00 <vdamewood> Not always. There was a time when he was 15
00:59:00 <childlikempress> lies!
00:59:00 <geist> for some reason the whole deeznutz thing just cracks me up so much
00:59:00 <childlikempress> you can crack up deez nuts
00:59:00 <geist> i was losing it the first time i saw dr ligma video
00:59:00 <childlikempress> (where by deez i actually mean mjg's)
01:01:00 <heat> mjg, mofo do you have some trick to debug ref counts
01:01:00 <heat> or it the trick to be permanently sad
01:01:00 <heat> is the*
01:02:00 <mjg> geist thinks dnd means deez nuts dragons
01:02:00 <vdamewood> Both
01:02:00 <mjg> heat: there is no magic trick
01:02:00 <mjg> heat: there are however people who add stack tracking to them
01:02:00 <heat> yeah i just got some but i feel like i'm looking at sadness
01:02:00 <mjg> heat: well there is one trick: you get things right first time, then when there is a bug you can bisect
01:03:00 <heat> i'm thinking about doing a flamegraph of this shite
01:03:00 <mjg> you overflow or underflow
01:03:00 <heat> i'm getting leaking a reference somewhere
01:03:00 <geist> all problems are solved with flamegraphs
01:03:00 <heat> unironically yes
01:03:00 <mjg> what obj is i
01:03:00 <mjg> t
01:03:00 * vdamewood bisects heat
01:03:00 <heat> dentries
01:03:00 <mjg> you can plausibly narrow it down to the bad syscall
01:04:00 <mjg> so you have it fully reproducible?
01:04:00 <heat> yes, ALL TEH PATH LOOKUPS
01:04:00 <mjg> dude
01:04:00 <heat> yep
01:04:00 <mjg> you are plaing refcount debug on easy mode
01:04:00 <mjg> the fuck are you even asking on the channel for
01:04:00 <heat> am i?
01:04:00 <mjg> real shit happens when the leak is unreliable
01:04:00 <mjg> and you can't even hope for a reproducer
01:05:00 <vdamewood> I hate it when my leaks are unreliable.
01:05:00 <mjg> you can add printk to all ref and unref ops
01:05:00 <mjg> if (strcmp(current->comm, "lulrepro") == 0) printk("dentry %p ref %d", ...);
01:06:00 <heat> i have a trace
01:06:00 <heat> but again, i feel like i'm looking at sad
01:06:00 <heat> 300 lines of crap
01:06:00 <mjg> i'm saying you can print out all spots which take and remove the ref
01:06:00 <mjg> inline the op, use __FILE__ + __LINE__
01:06:00 <heat> i guess i can write a C repro and be less sad that way
01:06:00 <mjg> and use a simple prog to trigger
01:06:00 <heat> i'm using shell commands which are basically my repro atm
01:07:00 <mjg> they are doing fuckton of lookups
01:07:00 <mjg> have a prog ready
01:07:00 <mjg> you can even gate these printks not only behind the prog name, but also a sysctl
01:07:00 <mjg> so you spawn the prog, it wait in read() to do the first lookup
01:07:00 <mjg> then you flip the sysctl and input something in the prog
01:07:00 <mjg> you get a clean report of only *that* lookup
01:08:00 <heat> i dont have sysctl yet
01:08:00 <mjg> then add a lul mechanism to flip the var
01:08:00 <mjg> or evne maybe do it from gdb in qemu monitor?
01:09:00 <mjg> you can even hack it as follows: you do the lookup against some lul path like /deez/nuts
01:09:00 <mjg> you detect that's what's going on in namei and that flips tracing
01:10:00 <mjg> getting debug from only that one lookup for only that one proc is a non-problem
01:13:00 <mjg> geist: btw https://www.youtube.com/watch?v=G_zhkXLviAw
01:13:00 <bslsk05> โ€‹'Garglon? (Funny Donation)' by Invisible Men Production (00:00:31)
01:16:00 <geist> ahaha
01:17:00 <heat> thanks for the ideas
01:20:00 <geist> i got a bunch of them from a similar video: https://youtu.be/CAqKu6oEZxk
01:20:00 <bslsk05> โ€‹'๐—œ๐˜ ๐˜๐—ผ๐—ผ๐—ธ ๐—•๐—ถ๐—ฏ๐—ผ๐—ผ'๐˜€ ๐—–๐—ต๐—ฎ๐˜ ๐—ฎ๐—น๐—บ๐—ผ๐˜€๐˜ ๐Ÿฏ ๐—บ๐—ผ๐—ป๐˜๐—ต๐˜€ ๐˜๐—ผ ๐—ฎ๐—ฐ๐—ฐ๐—ผ๐—บ๐—ฝ๐—น๐—ถ๐˜€๐—ต ๐˜๐—ต๐—ถ๐˜€... |Hololive|' by Gomi Simpington Ch. (00:07:02)
01:24:00 <mjg> i'm going to unsee that
01:25:00 <geist> unpossible!
01:32:00 <geist> oh forgot to link this the other day: https://chipsandcheese.com/2024/04/01/inside-control-data-corporations-cdc-6600/
01:32:00 <bslsk05> โ€‹chipsandcheese.com: Inside Control Data Corporation’s CDC 6600 – Chips and Cheese
02:32:00 <heat> fixed dem issues
02:32:00 <heat> perfetto's sql thingy is pretty darn useful
03:18:00 <geist> huh just discovered libcapstone: https://github.com/capstone-engine/capstone
03:18:00 <bslsk05> โ€‹capstone-engine/capstone - Capstone disassembly/disassembler framework for ARM, ARM64 (ARMv8), BPF, Ethereum VM, M68K, M680X, Mips, MOS65XX, PPC, RISC-V(rv32G/rv64G), SH, Sparc, SystemZ, TMS320C64X, TriCore, Webassembly, XCore and X86. (1497 forks/7022 stargazers/NOASSERTION)
03:18:00 <geist> pretty neat
03:19:00 <heat> https://www.twitch.tv/theneedledrop
03:19:00 <bslsk05> โ€‹'theneedledrop (live)' by theneedledrop (live)
03:20:00 <heat> forget libcapstone, lets listen to taylor swift's new album together
03:22:00 <kof673> yes, i thought someday i might try to c89 it
03:22:00 <kof673> taylor swift i mean, not capstone
03:22:00 <kof673> but that too
03:33:00 <kof673> > MOS65XX :)
03:35:00 <vdamewood> I need to send an adventurer to fetch the disc of Tay'lor for me.
03:35:00 <vdamewood> Having surgery on ones foot really sucks.
03:41:00 <geist-sdf> :(
03:41:00 <geist-sdf> yeah i bet
03:42:00 <kazinsal> friend of mine just had surgery on his dominant hand, incision was through the palm. poor dude's got basically negative grip strength right now
03:43:00 <geist-sdf> oh a fun thing i discovered the other day: telnet mapscii.me
03:52:00 <vdamewood> I should be mobile again some time in June.
04:14:00 <vaihome> morning
04:46:00 <kazinsal> man the Rocky 9 equivalent of build-essentials is missing a LOT of important shit
04:46:00 <kazinsal> I want to do a nice simple `time ./doit i386 x86_64 aarch64` but nooooo I keep having to install shit that's missing...
05:47:00 <kazinsal> 28 minutes to do three toolchains on four cores. not bad. much better than the old sandy bridge box
07:39:00 <geist> not bad
07:40:00 <geist> i've been playing with proxmox this evening
07:40:00 <geist> leaning towards repaving my main server box as a proxmox box and then among other things having a large VM in it
07:40:00 <geist> instead of the other way around where i have a server box with a bunch of qemu instances running via screen
07:42:00 <kazinsal> I probably shoulda done the same since I had to repave anyways
07:42:00 <kazinsal> went with ESXi 7.0 which has a nicer interface than 6.5 but is deprecated as of next april
07:42:00 <kazinsal> and broadcom did a broadcom and decided 8.0 doesn't get a free release
07:44:00 <kazinsal> I think my next go will be a proxmox host and then do some fuckery to mount the VMFS partition hosting my VMs on that and convert them to QCOW
08:54:00 <mjg> geist: for few years now libvirt is good enough to not be a problem
08:54:00 <mjg> geist: you can use virt-manager to connect to it over ssh and manage everything from a gui
08:55:00 <mjg> geist: as in you can just install a run of the mill distro and not have to fuck with qemu by hand
08:55:00 <mjg> that is to say, if you insist on proxmox, more power to you, but it's not necessary for comfortable vm hosting
09:27:00 <sortie> https://irc.sortix.org/forum/index.php ← You ever just wake up and feel hung over and realize you accidentally ported phpBB to your OS last night
09:27:00 <bslsk05> โ€‹irc.sortix.org: irc.sortix.org #osdev - Index page
09:29:00 <heat_> hey sortie i tried to register it didn't work
09:29:00 <heat_> did you fix that yet
09:29:00 <heat_> i want to say linox kornal on your forum
09:29:00 <sortie> heat_, hi, are you using the provided Sortix client?
09:29:00 <sortie> oh it signed me out
09:29:00 <sortie> I forgot to copy down the admin password
09:29:00 <heat_> lol
09:30:00 <sortie> Firefox was like 'do you want to save this password' and I was like 'nah' and then I accidentally closed the terminal with the randomly generated password
09:31:00 <mjg> password is: sortix > onyx
09:31:00 <mjg> innit
09:31:00 <sortie> no that can't be ... it worked how did you know
09:31:00 <sortie> I was pleasantly surprised to find out sqlite was enough to run this beast
09:32:00 <sortie> Porting something like MySQL sounded like a beast and also the word Orcacle kept popping up way too much and then I realized I actually don't have cmake yet
09:32:00 <mjg> sortie: because i'm logged in as an admin
09:32:00 <heat_> hi sortie do you not want to run an Oracle Database instance
09:33:00 <sortie> If anyone hacked this thing, congrats
09:33:00 <heat_> on Oracle Sparc?
09:33:00 <heat_> you could have Oracle Numbers
09:33:00 <mjg> is mariadb still a thing?
09:33:00 <heat_> sure
09:33:00 <mjg> it was a mysql fork after oracle takevoer
09:33:00 <heat_> 'tis what distros package
09:33:00 <sortie> I realized with sqllite, it's actually a SNSP server
09:33:00 <heat_> SNSP sounds like a scottish party
09:34:00 <sortie> SNNP?
09:34:00 <heat_> sooo i switched isps and i might not drop randomly anymore
09:34:00 <sortie> I guess Sortix is now the official operating system of getting Scotland back in the EU.. just think of the exposure from angry people
09:34:00 <heat_> otoh, can't wait to find out what exciting new router bugs there are
09:34:00 <sortie> heat_, oh hey they rolled out Sortix net?
09:35:00 <zid> heat_: bad news, your new ISP has onyx installed on the router
09:35:00 * heat_ googles the nmap os detection command
09:35:00 <mjg> is it -s
09:35:00 <mjg> does it even work anymore?
09:35:00 <heat_> it's -O
09:35:00 <heat_> i think... it's a mess
09:36:00 <mjg> it used to be decent 15 years ago, but i would *suspect* things evened out a bit between systems since then
09:36:00 <heat_> yeah it works pretty well i think
09:36:00 <mjg> i got a bunch of vms, lemme try
09:36:00 <mjg> let's try to detect open
09:37:00 <heat_> oh haha this is running linux
09:37:00 <mjg> it probably is, but plausibly it is not AND nmap thinks it is linux
09:37:00 <heat_> it's a huawei router
09:37:00 <mjg> that is linux
09:37:00 <heat_> it's plausible they stole linux's network stack and are infringing on the GPL
09:38:00 <mjg> huawei actively contributes to the linux kernel itself
09:38:00 <mjg> all over the place
09:38:00 <mjg> wut
09:38:00 <mjg> Running: OpenBSD 6.X
09:38:00 <mjg> OS CPE: cpe:/o:openbsd:openbsd:6
09:38:00 <mjg> OS details: OpenBSD 6.0 - 6.4
09:38:00 <heat_> yes sure to get your GPL source code you need to come to guangzhou china and wait on this dark dark alley right next to the "internment camp" thingy, don't worry about it lol
09:38:00 <mjg> it is 7.5, but i already give points for spotting the type
09:39:00 <mjg> infringing on gpl is liek the norm
09:40:00 <mjg> Running: FreeBSD 12.X|13.X
09:40:00 <mjg> aight
09:40:00 <mjg> i guess it does work
09:43:00 <zid> The man you meet will say "Left or right?"
09:43:00 <zid> You won't understand, then you wake up in a bath of ice, but with a flash drive in your pocket
09:45:00 <mjg> it failed to recognize illumos :(
09:45:00 <mjg> 1/10 would not use again
10:05:00 <nikolar> What does it think about illumos
10:06:00 <mjg> unknown
10:08:00 <nikolar> :(
10:10:00 <geist> mjg: yeah i've known about libvirt for a while, last time i messed with it i bounced off it pretty hard
10:11:00 <geist> though it was a few years back, maybe it's in better shape now
10:11:00 <zid> nikolapdp: 3.997?
10:12:00 <mjg> geist: it works fine
10:23:00 <nikolar> Not yet zid
10:24:00 <nikolar> geist I can tell you that I'm running macos with gpu passthrough through qemu and it works without issue
10:27:00 <heat_> wow i'm very negatively surprised wrt huawei's legal department
10:27:00 <heat_> they thought it was a great idea to list like... all copyright holders in linux
10:28:00 <heat_> not sure if this is all of them or just the code they built into the kernel
10:28:00 <heat_> in any case it's a huge fuckin list
10:29:00 <mjg> it's what's normally odne
10:30:00 <mjg> i even got copyright statement in some lul product because they copied a file
10:30:00 <heat_> wtf they have uclibc AND musl
10:31:00 <nikolar> In the same product?
10:31:00 <mjg> maybe just some files coiped
10:32:00 <heat_> yep same product
10:32:00 <heat_> musl 1.1.22
10:32:00 <mjg> or just a piece of code from a file with given copyright
10:32:00 <heat_> also, consider this: there's an EOL'd version of openssl on it too
10:32:00 <mjg> what do you expect them to d ofrom legal standpoint
10:33:00 <heat_> i'm /fairly/ sure you don't need to list copyright holders to comply with FOSS licenses
10:33:00 <heat_> but again, IANAL
10:38:00 <heat_> the kernel is also EOL'd, fucking brilliant
10:38:00 <mjg> it always is
10:38:00 <mjg> i have an ebook reader with liek a 3.x kernel
10:38:00 <mjg> bought 5 months ago, new
10:39:00 <heat_> a router with an EOL'd kernel and openssl does not fill me with confidence
10:39:00 <heat_> i would rather use theodore de raadt's software than EOL'd linux
10:40:00 <mjg> wait till you find out what kind of software is being run by the biggest companies out there
10:40:00 <mjg> even merely in terms of age
10:40:00 <heat_> i trust google or oracle or ibm or whatever to maintain EOL software far past expiry date
10:41:00 <zid> heat_: Is it the footballers that you IANAL?
10:41:00 <heat_> ianal everyone
12:50:00 <leg7> hey how can I interpret the interrupt lines in the qemu dump?
12:50:00 <leg7> https://0x0.st/XoWg.txt
12:52:00 <leg7> Ig v is the interrupt number `e` and `i` I have no clue. `cpl` must be the current privilege level
12:52:00 <zid> vector 35
12:53:00 <zid> exception number 0 (it's not an exception)
12:53:00 <zid> i=1 is an interrupt
12:53:00 <zid> but the one you care about is the v=0d one
12:53:00 <zid> which is an exception with code 0x1aa
12:53:00 <zid> and 0xd is.. #GP?
12:55:00 <zid> you tried to load segment selector 0x1aa, which is why it faulted, anyway
12:55:00 <leg7> yes interrupt 35 I call manually
12:56:00 <leg7> Where do you see I loaded that?
12:57:00 <zid> I.. just told you?
12:57:00 <zid> >> which is an exception with code 0x1aa and 0xd is GP
12:57:00 <zid> 1: v=0d e=01aa
12:58:00 <zid> https://wiki.osdev.org/Exceptions#General_Protection_Fault "The General Protection Fault sets an error code, which is the segment selector index when the exception is segment related. Otherwise, 0. "
12:58:00 <bslsk05> โ€‹wiki.osdev.org: Exceptions - OSDev Wiki
12:58:00 <leg7> ok I seethe line now
12:58:00 <leg7> ty
13:21:00 <leg7> The segment selector of IdtGates should point to the kernel code segment in the Gdt right?
13:29:00 <zid> if you want the idt entry to run as kernel code, yea
13:29:00 <zid> you could make it run in ring 2 with a weird base and limit if you wanted too ofc
13:32:00 <leg7> Well I fixed my code and I'm still getting a GP fault even though the segment selector is now correct (selector for the kernel code segment)
13:32:00 <zid> what e= do you ge?
13:33:00 <leg7> 0400
13:33:00 <leg7> in hex
13:33:00 <zid> then you loaded selector 0x400
13:33:00 <zid> and it isn't as fixed as you thought
13:33:00 <leg7> oh
13:33:00 <leg7> my bad
13:33:00 <leg7> 0040
13:33:00 <leg7> 1: v=0d e=0040 i=0 cpl=0 IP=0008:00203460 pc=00203460 SP=0010:0020a7ec env->regs[R_EAX]=00000017
13:34:00 <leg7> so it is fixed
13:34:00 <leg7> I think
13:34:00 <leg7> according to the wiki the GP could only be due to:
13:34:00 <leg7> Writing a 1 in a reserved register field or writing invalid value combinations (e.g. CR0 with PE=0 and PG=1).
13:35:00 <leg7> Segment error (privilege, type, limit, read/write rights).
13:35:00 <leg7> by elimination
13:35:00 <zid> why do you think
13:35:00 <zid> 0x40 is your kernel selector
13:35:00 <zid> kernel code*
13:36:00 <leg7> in binary it would be: 0b0000000001_000_0_00
13:36:00 <leg7> the first two bits are the ring
13:36:00 <zid> no?
13:36:00 <zid> I mean, yes, it would be that binary pattern
13:36:00 <zid> but that isn't what I asked
13:37:00 <zid> why 0x40, why not 0x27
13:37:00 <leg7> the third is ldt/gdt
13:37:00 <zid> why not 3
13:37:00 <zid> where did you get 0x40 from
13:37:00 <leg7> Well I'm trying to explain it to you
13:37:00 <zid> sounds like you have no idea then
13:37:00 <zid> 1: v=0d e=0040 i=0 cpl=0 IP=0008:00203460 pc=00203460 SP=0010:0020a7ec env->regs[R_EAX]=00000017
13:37:00 <zid> do you know what the 0008: and 0010: here mean?
13:37:00 <leg7> now
13:37:00 <leg7> no*
13:38:00 <zid> ...
13:38:00 <zid> They're the selectors.
13:38:00 <zid> Which are 8 and 16.. because those are the selectors you made, and loaded, and used
13:38:00 <leg7> code sector of eip
13:38:00 <zid> so I'm very confused why you suddenly think your code selector is 40, not 8
13:38:00 <zid> your gdt isn't even 0x47 long
13:38:00 <zid> in order for 0x40 to be valid
13:38:00 <leg7> well according the wiki the upper bits of the binary pattern I sent you are the index of the segment in the gdt
13:39:00 <leg7> or ldt
13:39:00 <leg7> and my kernel code segment has index 1
13:39:00 <zid> each segment is 8 bytes big
13:39:00 <zid> so they're 0, 8, 16, 24, 32, ..
13:39:00 <leg7> yes but the wiki says index not offset
13:39:00 <leg7> that's why I used the index
13:39:00 <zid> 0*8, 1*8, 2*8, ...
13:39:00 <zid> the index would b e 1
13:39:00 <zid> so you used neither
13:39:00 <zid> the 1th element in your array is at offset 0x8
13:40:00 <leg7> yes I know that but since the wiki said index I thought those bits encoded the index
13:40:00 <leg7> not the offset in the gdt
13:40:00 <zid> the reason there are some flag bits packed onto the bottom is because they're irrelevent, given that they're 8 bytes long
13:40:00 <zid> that means the bottom couple of bits are always 0
13:40:00 <leg7> yes
13:40:00 <leg7> that's why I added a _ to separate them
13:41:00 <zid> 8 in binary is 1000
13:41:00 <zid> not 64
13:42:00 <leg7> yes I didn't say it was 64
13:42:00 <zid> in binary it would be: 0b0000000001_000_0_00
13:42:00 <zid> <leg7> that's why I added a _ to separate them]
13:42:00 <zid> one of those two statements is false
13:43:00 <leg7> give me a sec I'll write it out
13:43:00 <zid> You don't need to prove anything to me, I already know what 8 is in binary
13:43:00 <zid> You need to figure out why *you* thought 8 was 1000000
13:44:00 <leg7> the first two bits from the right are the privilege, the third is a boolean flag to look it up the ldt instead, the next 3 bits are always 0, the rest is the "index" in the gdt/ldt
13:44:00 <zid> the next 3 bits are not always 0
13:44:00 <zid> You've *added* the flag bits
13:44:00 <zid> to the end of the number
13:44:00 <zid> not treated the bottom bits of the number as flag bits
13:45:00 <leg7> the value of Index is never unaligned and contains all zeros in the lowest 3 bits.
13:45:00 <leg7> https://osdev.org/Segment_Selector
13:45:00 <bslsk05> โ€‹osdev.org: Segment Selector - OSDev Wiki
13:45:00 <zid> you've seen "0bx00000000 bit x is status, 0x0y0000000 bit y is control" and gone "control and status is 0x1000000001000000"
13:46:00 <zid> yes, that page is bad and confusing
13:46:00 <zid> you were told multiple times to use the manual instead
13:46:00 <zid> and also, you *already knew* how to do this
13:46:00 <zid> because you loaded 8 and 10 into cs and ss, not 40
13:47:00 <leg7> I thought the segment selector for the gdt/ldt was differrent from segment registers
13:47:00 <zid> the registers hold selector values
13:47:00 <leg7> ok
13:47:00 <zid> which is.. why you loaded the registers with values
13:47:00 <zid> what did you think the 8 in cs was for?
13:47:00 <zid> magic you stole?
13:48:00 <zid> "The magic make it work value to load into cs is 8"
13:48:00 <zid> "What does cs do?" "No idea"
13:48:00 <zid> This is what it is sounding like
13:49:00 <leg7> No I read to load the segment registers with offsets of the GDT
13:50:00 <leg7> At the time I didn't know of "segment selectors" in the sense that privilege was enccoded in it
14:22:00 <offlinemark_> How much lockfree programming knowledge is required for basic SMP osdev? By which I mean - deep understanding of memory models, atomics, barriers, data/address/control dependencies, etc. Asking because I was trying to understand Linux's READ_ONCE() etc macros, and now find myself totally confused and intimidated.
14:22:00 <offlinemark_> Or to rephrase — what would you consider the minimum amount of knowledge needed in this area for productive osdev, if you're willing to sacrifice performance for simplicity and just "getting things going"? thx
14:22:00 <zid> depends if you're doing an easy arch or not
14:23:00 <zid> x86 is strong-strong-strong memory model so it's really easy
14:23:00 <offlinemark_> Let's say plain vanilla x86 :)
14:23:00 <nikolar> But if you want portability (to arm or riscv presumably) you need to be very careful
14:24:00 <GeDaMo> https://research.swtch.com/mm
14:24:00 <bslsk05> โ€‹research.swtch.com: research!rsc: Memory Models
14:24:00 <zid> you need to understand the difference between a memory barrier and a compiler barrier, beyond that
14:24:00 <zid> a lot of people get confused by that
14:24:00 <zid> and end up writing code that doesn't work
14:35:00 <offlinemark_> Ok, so you're saying knowing how and when to use barriers is important mandatory knowledge?
14:35:00 <zid> That's pretty much the only control you DO have
14:36:00 <zid> The order of the instructions in the binary (compiler barriers) and the order things happen in memory (memory barriers) covers pretty much everything
14:38:00 <offlinemark_> Hm, I thought there was more nuance.. such as when to use atomics (and which memory order to use with them)
14:38:00 <zid> atomics are just 'don't write things to memory only partially, in case someone sneaks a read into the middle'
14:39:00 <zid> you don't actually need them
14:39:00 <zid> they're just handy
14:39:00 <zid> there's no "atomically update this entire sql record" or "atomically update this entire struct" or whatever, you use a lock (implemented with barriers)
14:40:00 <nikolar> And/or atomics
14:40:00 <zid> cpu atomics are "atomically update this u32" and things at most
14:40:00 <zid> so useful, but not required, you could just treat it as a struct with 4 chars in
14:40:00 <zid> and use normal locking algo
14:42:00 <mjg> offlinemark_: you don't need almost squat
14:43:00 <mjg> offlinemark_: a low scalability but perfectly correct kernel will get away with locked access to everything
14:43:00 <mjg> offlinemark_: in order to implement that correctly you only need to know about acquire and release fences for locking primitives
14:43:00 <mjg> writing code which *does* scale is a fundamentlly different matter
14:44:00 <mjg> and a lot of it boils down to avoiding any writes to shared areas to begin with
14:44:00 <mjg> and comes with actual expertise requirements, as far as multicore issues are concerned
14:45:00 <mjg> well you will also need to know about things like tlb invalidation for other cpus, but that's not strictly memory model related
14:45:00 <offlinemark_> Thanks, what does "scale" look like, just out of curiosity. I assume it is measured in number of cores
14:46:00 <mjg> lemme find an example real quick
14:46:00 <zid> I prefer centimeters
14:46:00 <mjg> https://www.usenix.org/legacy/event/atc11/tech/final_files/Triplett.pdf page 11
14:46:00 <mjg> close to linear scaling of ops/s as number of cores goes up vs flat line
14:47:00 <mjg> with locking you are "aiming" for the latter ;)
14:47:00 <mjg> (well not strictly true, but locked access notoriously suffers significant scalability problems)
14:48:00 <mjg> if you want to learn more about multicore aspects this is THE book: https://mirrors.edge.kernel.org/pub/linux/kernel/people/paulmck/perfbook/perfbook.2023.06.11a.pdf
14:49:00 <offlinemark_> Great, will read. Thanks all. Then I won't get super deep into the topic and just stick with locks :)
14:49:00 <mjg> :]
14:49:00 <mjg> you do you mate
14:50:00 <mjg> here is a great overview of spinlocks https://www.cs.rochester.edu/~scott/papers/1991_TOCS_synch.pdf
14:50:00 <mjg> you will be fine with most primitive variant
14:59:00 <heat_> dosent scale literaly unusable
14:59:00 <zid> let me guess
14:59:00 <mjg> just like onyx innit
14:59:00 <zid> mjg had an insane rant to the guy asking how smp works
14:59:00 <zid> about scaling
14:59:00 <heat_> not really
15:00:00 <zid> whitequark says I won the jackpot
15:00:00 <mjg> i'm pretty sure i gave concise and actionable advice, no rant that i can see
15:00:00 <mjg> but zid would know better not knowing the content
15:12:00 <heat_> mjg, something i've come to appreciate these last few months is how rcu can give you a nice existence guarantee
15:12:00 <heat_> like holding a really big lock would, but without holding a really big lock
15:14:00 <mjg> well yes, as i noted, rcu or an equivalent is mandatory for real-world scalability
15:14:00 <mjg> fearlessly concurrent locked access all over is the webdev's idea of utilizing multicore
15:14:00 <mjg> (is this an insane rant yet?)
15:14:00 <heat_> i'm not even talking about scalability
15:15:00 <heat_> just general code correctness and simplicity
15:15:00 <nikolar> Are we shitting on rust
15:15:00 <nikolar> FEARLESS CONCURRENCY
15:15:00 <heat_> knowing the thing is Just There (despite being valid or not) is really powerful
15:15:00 <mjg> if you don't care about performance, you may as well take the lock
15:15:00 <mjg> so i don't think you should be raving about this aspect
15:15:00 <heat_> no
15:15:00 <heat_> you can care about performance and not care enough to make it super performant or super scalable
15:16:00 <mjg> the one thing correctness-wise which rcu does help is avoiding establishing certain lock orderings
15:16:00 <heat_> 'tis why most new code in e.g linux uses locks and not rcu
15:16:00 <mjg> in turn avoiding numerous deadlocks
15:16:00 <zid> There's only ONE valid measure of performance heat
15:16:00 <zid> and that's how many simultaneous clients can hammer on it before it falls over
15:16:00 <zid> all other things are irrelevent
15:16:00 <zid> how many more times do we need to teach you
15:17:00 <mjg> this is *probably* why even openbsd has an equivalent
15:17:00 <mjg> they don't use it much, but it is there
15:17:00 <heat_> huh, they do?
15:17:00 <heat_> is it quiescent state based or ebr?
15:17:00 <mjg> i dont' remember, something turbo primitive
15:17:00 <mjg> git grep SMR
15:18:00 <mjg> it is used in the network stack, prsumably specifically to dodge deadlocks
15:19:00 <heat_> that would involve checking out openbsd, and im not doing that
15:19:00 <mjg> theo sent you an email
15:19:00 <mjg> and you refuse to read the code?
15:19:00 <mjg> show some gratitude
15:19:00 <heat_> i didn't consent
15:19:00 <mjg> my apologies
15:20:00 <mjg> but on the note of rcu and "correctness", while it does prevent the object itself from becoming freed
15:20:00 <heat_> yeah that reminds me sortie talked with me about some really awkward lock inversion that can happen in a naive tcp stack with accepted connections and their parent sock
15:20:00 <heat_> which could use some rcu i think
15:20:00 <mjg> as in the backing struct
15:20:00 <mjg> the obj itself may get destroyed as you try to use it
15:20:00 <heat_> yes
15:20:00 <heat_> which is why cmpxchg or taking a lock or whatever
15:20:00 <mjg> ye network stack is the primary lock order reversal driver
15:21:00 <mjg> and this is where openbsd uses smr
15:22:00 <mjg> well then perhaps you can appreciate that if the obj was protected with a global, you could inspect more than one without any fearzzz
15:22:00 <mjg> or seqlock trickery
15:22:00 <heat_> yeah it'd be great for single threaded
15:22:00 <mjg> rcu makes it harder to reason about correctness (modulo deadlock avoidance), but it is worth it in plenty of cases
15:22:00 <heat_> or !SMP
15:23:00 <mjg> like the famed path lookup
15:23:00 <heat_> what i was thinking of was actually related to path lookup
15:24:00 <heat_> getting a parent of a dentry without taking 1) a big lock 2) a really weird complex mix of locks with different ordering; is super hard
15:24:00 <mjg> instead of talking shit better tell me if you got to a point where you can sruvive creating more than a handful of files
15:24:00 <heat_> without 3) rcu
15:24:00 <mjg> ye the classic of wakling upstream is trylocking
15:24:00 <mjg> it's slow af, error prone and you are likely to lose forward progress guarantee
15:25:00 <mjg> but again, this here is deadlock avoided by no establishing an ordering, which i already mentioned
15:26:00 <mjg> now that i mention it, do you have something like lockdep?
15:26:00 <mjg> or are you rusting it
15:26:00 <heat_> i don't have lockdep
15:26:00 <mjg> even openbsd ported an equivalent from freebsd
15:26:00 <mjg> so they can check for things like that
15:27:00 <mjg> sounds like your kernel sucks
15:27:00 <heat_> i want to but i dont really grok the theory and stealing an impl from someone else is hard
15:27:00 <mjg> well the good news is that you can give yourself poor man's verification with moderate effort
15:28:00 <mjg> the idea is that you predefine a bunch of orderings
15:28:00 <heat_> there's a funnier idea i'm yet to implement
15:28:00 <heat_> linux has a hung task timeout system
15:28:00 <mjg> so does freebsd (no, really, named deadlock detection or similar)
15:29:00 <heat_> if uninterruptible sleeps go over a certain amount of time, they splat the task
15:29:00 <mjg> i'm aware, one sees it a lot with any non-trivial linux deployment
15:30:00 <mjg> so where do you stand with surviving file creation
15:36:00 <heat_> it survives file creation
15:36:00 <mjg> at SCALE
15:37:00 <heat_> what scale do you want?
15:37:00 <mjg> the one i tested on
15:37:00 <heat_> your fs tree LRU bench?
15:37:00 <mjg> s/LRU//
15:38:00 <heat_> well, reclamation bench
15:38:00 <heat_> but no i can't survive that yet
15:38:00 <mjg> i'm tempted to check if sortix can do it
15:39:00 <heat_> i need an LRU, and one that doesn't shaft the scalability
15:39:00 <mjg> as i noted you can start with just having a global list of all inodes
15:39:00 <heat_> >global list
15:39:00 <mjg> that does not affect scalability
15:39:00 <heat_> global lock, performance shafted, mjg triggered
15:40:00 <mjg> you add inodes to the list in the constructor
15:40:00 <mjg> in the slab allocator
15:40:00 <heat_> i dont implement ctors
15:40:00 <mjg> they don't move around on mere alloc/free, only when new pages are imported or exported
15:41:00 <heat_> though i do already have a per-superblock inode list
15:41:00 <heat_> i could use that
15:42:00 <mjg> ther you go
15:43:00 <mjg> you can even go full bsd and literally whack one inode
15:43:00 <mjg> to make room for a new one
15:43:00 <mjg> boo, sortix still does not have smp
15:44:00 <mjg> wait, the wevpage even says no networking ,but that can't be correct
15:44:00 <heat_> it depends on the build
15:44:00 <heat_> i think nightly has networking
15:49:00 <heat> mjg: anyway the real problem is handling correctness at all
15:49:00 <heat> i can have a nice list of all inodes, i'll still need to find a way to correctly yank that inode without racing with anything else
15:49:00 <mjg> that's becuase you are not using rust
15:49:00 <heat> i can have inode yanking done properly, doesn't matter if memory reclamation is busted
15:49:00 <heat> etc
15:49:00 <mjg> yanking is trivial, you lock it, check the ref and unlink from stuff
15:50:00 <heat> ok. now imagine someone else concurrently looks up that inode. what do?
15:50:00 <mjg> for that to be possible you ar either incorrectly using rcu
15:50:00 <heat> i have an answer for what do, but it's not trivial
15:50:00 <mjg> or incorrectly locking stuff
15:50:00 <heat> no?
15:51:00 <heat> first lets establish the idea that you can't have two struct inodes referring to the same on-disk inode
15:51:00 <mjg> do you rcu it or not
15:51:00 <heat> for safety reasons
15:51:00 <mjg> of course
15:51:00 <heat> i do not
15:51:00 <mjg> do you know of the "inode hash" thing?
15:51:00 <heat> yes
15:51:00 <mjg> both bsds and linux have one
15:51:00 <mjg> there you go
15:51:00 <heat> it doesn't help
15:51:00 <mjg> how so
15:52:00 <heat> if i yank it off the lists (sb list and hash) it'll still be alive, in a dying state, doing whatever needs to be done to kill it
15:52:00 <heat> in the meanwhile, someone can look that inode up, create a new struct inode, and start fucking around with that same on-disk inode. yay, i have two struct inodes for the same on-disk inode
15:52:00 <mjg> anyone trying to allocate it will have to wait for this to finish
15:52:00 <heat> yes
15:52:00 <mjg> they find it on the hash
15:52:00 <mjg> and wait for destruction if it already started
15:53:00 <mjg> and then pretend they did not find it
15:53:00 <heat> >i have an answer for what do, but it's not trivial
15:53:00 <heat> which is my point, there are tiny edge cases that need to be thought of and solved
15:53:00 <mjg> maybe i'm biased, ultimatley all this just requires some proficiency in handling locking
15:53:00 <heat> it's not unsolveable, it's just... harder
15:53:00 <mjg> and mostly not being afraid of it
15:54:00 <mjg> not that there is 0 corner cases to worry about
15:54:00 <heat> it is solveable, it's just annoying and needs thought and testing
15:55:00 <heat> i've done this for long enough to know that a lot of issues only come up when testing and stressing
15:55:00 <mjg> what are you planning to stress this with
15:56:00 <heat> it also turns out that reclaiming cached up shit is the one part of the kernel that i have the least experience with
15:56:00 <heat> uhh, no clue, maybe your script
15:56:00 <mjg> well you will definitely want to survive that, but it does not test the particular scenario you are worried about
15:56:00 <heat> it is very hard to set up any particular scenario cuz i need just the ideal memory pressure
15:57:00 <heat> so, in-kernel unit tests maybe?
15:57:00 <mjg> xfstests should be able to help
15:57:00 <heat> xfstests has other deps
15:58:00 <mjg> 100% seroius comment: i would give it a shot to port subset of stress2 from freebsd
15:58:00 <heat> just as a good mkfs, fsck, mount and umount system
15:58:00 <heat> what's stress2?
15:58:00 <heat> i have a stress-ng port i'm yet to put into good use
15:58:00 <mjg> as the name suggests a stress test suite
15:58:00 <mjg> has tons of scenarios, including highly parallel fs acccess
15:59:00 <mjg> creation, destruction, renames 'n shit
15:59:00 <mjg> by efault it creates md-backed ufs partitions, but you can just whack that bit
15:59:00 <mjg> and have it operate on a bare directory
16:00:00 <mjg> this thing finds a metric fuckton of bugs
16:01:00 <mjg> i would say before you go there you want to make sure fsx.c does not mess you up
16:01:00 <mjg> that and dirconc
16:04:00 <heat> fsx.c works well
16:04:00 <heat> dirconc renames still fuck with the fs
16:12:00 <heat> oh i also have fsstress and that one's messing my ext2 up too
16:17:00 <leg7> When writing the stdio part of the libc do you bother trying to make it independant of your os?
16:17:00 <leg7> Or do you assume stuff (like every libc I've seen assumes linux)
16:22:00 <nortti> how do you mean, assume stuff?
16:23:00 <mjg> presumably syscall numbers, calling convention and whatnot
16:24:00 <mjg> i doubt people care to even try to make it portable
16:24:00 <heat> glibc is portable
16:24:00 <mjg> yourt ypical libc is not
20:47:00 <zid> https://computerhistory.org/blog/fifty-years-of-the-personal-computer-operating-system/
20:47:00 <bslsk05> โ€‹computerhistory.org: Fifty Years of the Personal Computer Operating System - CHM
20:49:00 <mjg> that was quick
20:53:00 <heat> mjg, fyi stress-ng works pretty well
20:58:00 <heat> dunno if you've ever used it, but they have stressers of various parts of the kernel and you can tell it to spawn N of each instance
21:04:00 <zid> stressors shorten your lifespan
21:04:00 <geist> scissors do too
21:05:00 <geist> well, ordered a few z80s off mouser. dunno why, i'm sure there's enough of them floating around on ebay and whatnot to still be around for 100 years
21:05:00 <geist> but i had to have my own, maybe i'll put them in a glass case with a hammer to break in case of emergency
21:06:00 <geist> also happy 4/20 day
21:07:00 <heat> wasn't expecting this from you geist
21:07:00 <heat> smh
21:07:00 <mjg> is this how midlife crisis looks like?
21:07:00 <mjg> deez nutz jokes, weed jokes
21:07:00 <mjg> next thing he is gonna post a pdp tiktok
21:07:00 <heat> 420 blaze it swag yolo
21:11:00 <mjg> you know what ip octet did my vm got on my 192.168.122.0/24 subnet?
21:11:00 <mjg> .69
21:11:00 <mjg> you heard that geist
21:13:00 <geist> nah i dont really like to blaze up, i have asthma anyway
21:13:00 <geist> and honestly i just get sleepy if i do
21:13:00 <geist> (it's totally legal here)
21:13:00 <zid> sec I have a gif for this
21:13:00 <geist> but folks that can, blaze up!
21:13:00 <Griwes> edibles are much nicer anyway
21:13:00 <zid> https://cdn.discordapp.com/attachments/277229032310046729/1227637217623801940/ju9vjnm8eltc1.webp?ex=66252cc1&is=6623db41&hm=7ae88cbd8962a1ab3e0b7a3cc848449166f8266572aaef576e2bef34273e833e&
21:13:00 <geist> yeah edibles occasionally. they delay a bit too much, but honestly if i do need to sleep they're darn good for taht if i plan far ahead
21:14:00 <heat> i had a nice pint of guinness
21:15:00 <heat> it's nice
21:15:00 <zid> I have smiths, it's like guinness but doesn't taste of black
21:15:00 <heat> what's the problem with the taste of black
21:15:00 <heat> are you a racist
21:16:00 <zid> Too ethnic for me, comes from ireland
21:16:00 <Griwes> there's been some edibles that settle in very quickly that I've seen recently
21:16:00 <zid> even john smiths is pushing it, that's from yorkshire
21:16:00 <Griwes> well, very quickly compared to edibles in general
21:18:00 <heat> i need help
21:19:00 <heat> (i think) i'm getting a race here where waiters gets unset spuriously
21:19:00 <heat> can anyone spot a problem? https://github.com/search?q=repo%3Aheatd%2FOnyx%20page_clear_waiters&type=code
21:19:00 <bslsk05> โ€‹github.com: Code search results · GitHub
21:19:00 <mjg> hm....
21:20:00 <heat> everything *looks* okay, with the proper lock and all
21:21:00 <mjg> why are you using different conditoins to clear it
21:22:00 <heat> wdym
21:25:00 <heat_> if you're getting woken you can try and unset WAITERS if the wq is clear (to avoid the wait queue traversal looking for same-page waiters)
21:26:00 <mjg> your logic in the while loop looks very suspicious
21:26:00 <heat_> if you're waking you can know if you see the page while waking something up
21:26:00 <mjg> i can take a closer look in about 1h
21:26:00 <heat_> oh yeah?
21:26:00 <heat_> that'd be much appreciated <3
21:27:00 <mjg> i would start with adding an assert over there that the waiters bit is set
21:28:00 <heat_> yeah, my problem rn is that i have no way to repro
21:28:00 <mjg> the going to sleep side should be never clearing this anyway
21:28:00 <mjg> ye i think i see the race
21:28:00 <heat_> oh yeah?
21:29:00 <mjg> consider 2 threads waiking up in page_wait_bit
21:29:00 <mjg> oh wait, they only remove themselves from the list
21:29:00 <mjg> for a moment there i thought one guy could clear the flag and bail, while the other one waits on line 232
21:30:00 <mjg> i'm gonna tgake a closer look after i'm done with some stuff here
21:31:00 <heat_> waaaait now i think i see it
21:32:00 <heat_> __wait_queue_wake also dequeues waiters. consider a wait for WRITEBACK and !LOCKED. both conditions can be transiently satisfied, the queue can be clear, and the race you describe just happens
21:33:00 <mjg> why the fuck do you have 2 spots to do it
21:33:00 <heat_> well, not LOCKED, cuz we grab it immediately, but maybe WRITEBACK, where it can be WRITEBACK for a bit, then clear, wakes up 2 waiters, one sees !WRITEBACK, one sees WRITEBACK and sleeps again
21:34:00 <heat_> i have two spots because it looked(looks?) like a blatant missed optimization
21:34:00 <heat_> i don't want to always have a spurious page_wake_bit trip after every wait
21:38:00 <mjg> that code is indeed PESSIMAL
21:38:00 <mjg> but also written in a way which suggests you are trying to fuck yourself over
21:39:00 <mjg> it should be an invariant that if you are woken up, you are not on any fucking list
21:39:00 <heat_> that's usually true except signals
21:40:00 <heat_> it is non-trivial to do that with signals
21:40:00 <mjg> ye modulo that one case which should be handled separately
21:40:00 <heat_> oh, and timers if i'm waiting with a timeout
21:40:00 <mjg> in which case you may or may not happen to be there
21:40:00 <mjg> but for the usual case..
21:41:00 <mjg> i'm saying you might received both regular wakeup AND a signal
21:41:00 <mjg> while the condition you were checking for might have flipped back and forth
21:46:00 <geist> heat: why are you doing it twice?
21:47:00 <mcrod> hi
21:47:00 <geist> ah i see mjg asked
21:47:00 <geist> mcrod: 'lo!
21:47:00 <heat> i *think* it makes some sense
21:48:00 <geist> just to double check, make sure the compiler doesn't elide the two ands
21:48:00 <geist> though i doubt it, unless they're release
21:48:00 <geist> er relaxed i mean
21:51:00 <heat> https://gist.github.com/heatd/739d1775ec380d0247d63362ff8a3208
21:51:00 <bslsk05> โ€‹gist.github.com: 0001-mm-Re-set-WAITERS-after-wakeup.patch · GitHub
21:51:00 <heat> i think this makes sense
21:52:00 <mjg> you are doing the webdev
21:52:00 <heat> actually i'm not sure if i should have the diagram in the code, probably not
21:52:00 <heat> why am i doing the webdev?
21:52:00 <mjg> the actual thing to do is to add assertions
21:52:00 <mjg> and remove the problem, not paper around it
21:52:00 <heat> and the problem is?
21:53:00 <mjg> per your description there is a case where the bit gets cleare dwhen it should not
21:53:00 <mjg> or at least the code does not cope with it
21:53:00 <mjg> if you have cases where it legitimately can persist, you need to assert that it did
21:53:00 <mjg> when it should have
21:53:00 <mjg> and that it did not when it should not have
21:54:00 <heat> ok lets see reasons why waiters might be cleared
21:54:00 <mjg> but really looks like most of the problem is you handling queue removals in 2 different places
21:54:00 <heat> 1) the queue is empty
21:54:00 <heat> 2) we iterated through the queue, no one is waiting for the page
21:56:00 <heat> it might be that 2) is useless because 1) already covers it
21:56:00 <heat> but again im not sure
22:42:00 <heat_> what if i, just for a joke, binge-wrote kcsan tonight
22:45:00 <nikolar> Do it
22:47:00 <zid> ketamine coma sanitiser?
22:53:00 <heat_> yes, im also fixing the opioid epidemic tonight
22:55:00 <zid> ketamine.. is not an opiod heat
22:55:00 <heat_> thats why i said also
22:55:00 <zid> clearly
22:56:00 <heat_> yes
23:11:00 <mjg> pulling an all nighter is a young nerd's game
23:11:00 <mjg> and is not recommended
23:27:00 <kazinsal> yep. had to pull one recently after screwing up my sleep schedule with an unexpectedly long nap. would not recommend either
23:43:00 <heat_> i said screw it and now i'm taking linux's
23:53:00 <geist> yah i was taking too many naps a few months ago due to medication and it was really really messing up my sleep
23:53:00 <geist> had to start not napping, just drinking caffeiene to keep going
23:53:00 <geist> actualy regulated things better that way
23:53:00 <geist> despite the naps feeling fantastic
23:54:00 <mjg> geist: https://www.pinterest.com/pin/207024914093163726/