Domain: Computer Systems & Operating Systems

# Operating Systems / Storage (RAID): Deterministic Layout, Parity Update, Rebuild, and Reliability

## Global constants (used everywhere)

- Disks are named: `D0, D1, D2, D3, D4` (5 disks total).
- Sector size is irrelevant except for parity; for this task **each sector stores exactly 1 byte**.
- A stripe unit (chunk) is **4 sectors**.
- All XOR parity is byte-wise XOR (8-bit).

### Common stripe indexing
Let logical address be in **sectors**:
- RAID5 has **4 data disks per stripe** ⇒ logical stripe width:
  \[
  W_{5}=4\cdot 4=16 \text{ logical sectors per stripe}
  \]
- Stripe number:
  \[
  s = \left\lfloor \frac{\text{LBA}}{16}\right\rfloor
  \]
- Position within stripe:
  \[
  p = \text{LBA}\bmod 16
  \]
- Data-chunk index within stripe:
  \[
  j = \left\lfloor \frac{p}{4}\right\rfloor \in \{0,1,2,3\}
  \]
- Offset within chunk:
  \[
  o = p \bmod 4 \in \{0,1,2,3\}
  \]
- Physical sector number on a disk for stripe `s` at offset `o` is:
  \[
  \text{physSector} = 4s + o
  \]

---

## Part A — RAID 5 (distributed parity, **layout is fixed and must be used**)

### RAID5 layout rule (left-asymmetric, deterministic)
For stripe number `s`:
- Parity disk:
  \[
  pDisk = s \bmod 5
  \]
- The 4 data chunks `j=0..3` are placed on disks:
  \[
  dDisk(j) = (pDisk + 1 + j) \bmod 5
  \]
- For any stripe `s` and offset `o`, parity byte stored on `pDisk` at sector `4s+o` is:
  \[
  P[s,o] = D[s,0,o]\oplus D[s,1,o]\oplus D[s,2,o]\oplus D[s,3,o]
  \]
  where `D[s,j,o]` is the data byte for chunk `j` offset `o`.

### RAID5 read requests (must map to physical disk sector reads)
Compute the minimal set of physical read operations for each request, using this rule:

**Read-I/O grouping rule:** within the same disk, merge any reads that touch **contiguous sector ranges** into one `(disk,start,len)` tuple.

Output tuples must be sorted by disk number ascending and, within a disk, by `start` ascending.

#### RAID5 Read Request R1
Read `len=22` logical sectors starting at `LBA=14` (i.e., LBAs 14..35).

#### RAID5 Read Request R2
Read `len=18` logical sectors starting at `LBA=7` (i.e., LBAs 7..24).

---

## Part B — RAID5 write W (parity update) and I/O counts

### Deterministic write W
Write `len=11` logical sectors starting at `LBA=23` (LBAs 23..33).
The new sector bytes (in order of increasing LBA) are:

`A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA` (hex bytes, uppercase, no `0x`).

### RAID5 initial on-disk bytes (only stripes used by W are provided)

Stripe `s=1` (so sectors `4..7` on each disk for offsets `o=0..3`):
- Parity disk is `D1`. Data disks are `D2 (j=0), D3 (j=1), D4 (j=2), D0 (j=3)`.

Given **data bytes** (offsets `o=0,1,2,3`):
- `D2: 10 11 12 13`
- `D3: 20 21 22 23`
- `D4: 30 31 32 33`
- `D0: 40 41 42 43`

Stripe `s=2` (sectors `8..11` on each disk):
- Parity disk is `D2`. Data disks are `D3 (j=0), D4 (j=1), D0 (j=2), D1 (j=3)`.

Given **data bytes**:
- `D3: 55 56 57 58`
- `D4: 65 66 67 68`
- `D0: 75 76 77 78`
- `D1: 05 06 07 08`

### RAID5 write-parity update rule (must follow exactly)
Write uses **read-modify-write** with these exact rules:

For each stripe `s` touched by the write:
1. Read the **entire parity chunk** for that stripe: 4 contiguous sectors on `pDisk` → counts as **1 read I/O**.
2. For each data disk that has any sectors overwritten in that stripe, read the overwritten sectors on that disk; within that disk, merge contiguous reads → counts as read I/O(s).
3. Write the new data sectors (merged per disk into contiguous runs) → counts as write I/O(s).
4. Write back the **entire parity chunk** for that stripe (4 contiguous sectors) → counts as **1 write I/O**.

**Important:** Reads/writes on the same disk may be merged into a single I/O if they are contiguous *even if they belong to different stripes* (because physical sectors are contiguous). Use the same merge rule as in Part A.

### What you must compute for RAID5 write W
- `WRITE_W_IO_RAID5`: total read I/Os and total write I/Os for the entire write W.
- `RAID5_PARITY_STRIPE1`: the 4 parity bytes on `D1` sectors `4..7` after W completes, as comma-separated hex bytes.
- `RAID5_PARITY_STRIPE2`: the 4 parity bytes on `D2` sectors `8..11` after W completes.

---

## Part C — RAID 4 (dedicated parity) on the same 5 disks

RAID4 uses:
- Data disks: `D0..D3`
- Dedicated parity disk: `D4`

Chunk size is still 4 sectors, and logical stripe width is also \(16\) sectors.

### RAID4 layout rule (deterministic)
For stripe `s`:
- Chunk `j` is stored on disk `Dj` for `j=0..3` (so `j=0→D0`, `j=1→D1`, etc.).
- Parity is stored on `D4`, and for each offset `o`:
  \[
  P4[s,o] = D0[s,o]\oplus D1[s,o]\oplus D2[s,o]\oplus D3[s,o]
  \]
- Physical sector on any disk for stripe `s` offset `o` is `4s+o`.

### RAID4 initial bytes (only stripes used by W are provided)

Stripe `s=1` (sectors `4..7`):
- `D0: 01 02 03 04`
- `D1: 05 06 07 08`
- `D2: 09 0A 0B 0C`
- `D3: 0D 0E 0F 10`

Stripe `s=2` (sectors `8..11`):
- `D0: 21 22 23 24`
- `D1: 25 26 27 28`
- `D2: 29 2A 2B 2C`
- `D3: 2D 2E 2F 30`

(Parity on `D4` is implied by XOR; you may compute it.)

### RAID4 write behavior for W
Apply the same **read-modify-write I/O rules** as RAID5, except:
- parity disk is always `D4`.

### What you must compute for RAID4 under the same W
- `WRITE_W_IO_RAID4`: total read I/Os and total write I/Os.
- `RAID4_PARITY`: after W completes, output:
  - stripe1 parity bytes on `D4` sectors `4..7`
  - stripe2 parity bytes on `D4` sectors `8..11`

---

## Part D — RAID5 rebuild check (coupled to your parity result)
After RAID5 write W completes, assume **disk D2 fails** completely.

Compute the reconstructed byte that should be stored at:
- stripe `s=1`, chunk `j=0`, offset `o=1`

Output that reconstructed byte as **two hex digits**.

---

## Part E — Reliability (numeric, deterministic rounding)

Let per-disk failure probability over the period be:
\[
p = 0.03
\]
Assume independent disk failures.

Compute data-loss probability (rounded to **exactly 10 digits after the decimal point**):

1. `P_LOSS_RAID0_5DISKS_p0.03`: RAID0 on 5 disks (loss if **any** disk fails).
2. `P_LOSS_RAID5_5DISKS_p0.03`: RAID5 on 5 disks (loss if **2 or more** disks fail).
3. `P_LOSS_RAID1_2DISKS_p0.03`: RAID1 mirrored pair (2 disks; loss if **both** fail).

---

## Required Output Format

Provide your reasoning, then output a final answer block containing **exactly 11 lines** in this exact order:

1. `RAID5_READ_REQ1: ...`
2. `RAID5_READ_REQ2: ...`
3. `WRITE_W_IO_RAID5: reads=N writes=N`
4. `WRITE_W_IO_RAID4: reads=N writes=N`
5. `RAID5_PARITY_STRIPE1: D1[4..7]=..,..,..,..`
6. `RAID5_PARITY_STRIPE2: D2[8..11]=..,..,..,..`
7. `RAID4_PARITY: stripe1 D4[4..7]=..,..,..,.. | stripe2 D4[8..11]=..,..,..,..`
8. `REBUILD_RAID5_D2_s1_j0_o1: ..`
9. `P_LOSS_RAID0_5DISKS_p0.03: 0.xxxxxxxxxx`
10. `P_LOSS_RAID5_5DISKS_p0.03: 0.xxxxxxxxxx`
11. `P_LOSS_RAID1_2DISKS_p0.03: 0.xxxxxxxxxx`


Grading rubric:
criterion: Line 1 is exactly `RAID5_READ_REQ1: (D0,4,4) (D2,4,4) (D3,4,8) (D4,2,6)`.
weight: 10
criterion: Line 2 is exactly `RAID5_READ_REQ2: (D2,3,5) (D3,0,8) (D4,0,5)`.
weight: 10
criterion: Line 3 is exactly `WRITE_W_IO_RAID5: reads=5 writes=5`.
weight: 10
criterion: Line 4 is exactly `WRITE_W_IO_RAID4: reads=5 writes=5`.
weight: 10
criterion: Line 5 is exactly `RAID5_PARITY_STRIPE1: D1[4..7]=34,34,34,BF`.
weight: 20
criterion: Line 6 is exactly `RAID5_PARITY_STRIPE2: D2[8..11]=BC,BC,40,40`.
weight: 20
criterion: Line 7 is exactly `RAID4_PARITY: stripe1 D4[4..7]=00,00,00,A8 | stripe2 D4[8..11]=88,88,00,10`.
weight: 20
criterion: Line 8 is exactly `REBUILD_RAID5_D2_s1_j0_o1: 11`.
weight: 15
criterion: Line 9 is exactly `P_LOSS_RAID0_5DISKS_p0.03: 0.1412659743`.
weight: 10
criterion: Line 10 is exactly `P_LOSS_RAID5_5DISKS_p0.03: 0.0084720528`.
weight: 10
criterion: Line 11 is exactly `P_LOSS_RAID1_2DISKS_p0.03: 0.0009000000`.
weight: 10
criterion: Does the final answer block contain exactly 11 lines in the specified order and format, regardless of any preceding reasoning?
weight: 5

Answer:
### Request R1: read LBAs 14..35 (len=22)

- Stripe 0 covers LBA 0..15. Need LBA 14..15.
  - Stripe 0: \(pDisk=0\). For \(p=14,15\Rightarrow j=3\), disk is \(dDisk(3)=(0+1+3)\bmod5=4\Rightarrow D4\).
  - Offsets \(o=2,3\) ⇒ D4 sectors 2..3 ⇒ \((D4,2,2)\).

- Stripe 1 covers LBA 16..31. Need entire stripe.
  - Stripe 1: \(pDisk=1\). Data disks:
    - \(j=0\to D2\) sectors 4..7
    - \(j=1\to D3\) sectors 4..7
    - \(j=2\to D4\) sectors 4..7
    - \(j=3\to D0\) sectors 4..7

- Stripe 2 covers LBA 32..47. Need LBA 32..35.
  - Stripe 2: \(pDisk=2\). \(p=0..3\Rightarrow j=0\to D3\) sectors 8..11 ⇒ \((D3,8,4)\).

Merge contiguous reads per disk:
- D4: (2,2) and (4,4) merge ⇒ \((D4,2,6)\)
- D3: (4,4) and (8,4) merge ⇒ \((D3,4,8)\)
- D0: \((D0,4,4)\)
- D2: \((D2,4,4)\)

**RAID5_READ_REQ1:** `(D0,4,4) (D2,4,4) (D3,4,8) (D4,2,6)`

### Request R2: read LBAs 7..24 (len=18)

- Stripe 0 need LBA 7..15:
  - LBA 7: \(j=1,o=3\) on \(D2\) sector 3 ⇒ \((D2,3,1)\)
  - LBA 8..11: \(j=2\) on \(D3\) sectors 0..3 ⇒ \((D3,0,4)\)
  - LBA 12..15: \(j=3\) on \(D4\) sectors 0..3 ⇒ \((D4,0,4)\)

- Stripe 1 need LBA 16..24 (p=0..8):
  - p=0..3: \(j=0\) on D2 sectors 4..7 ⇒ \((D2,4,4)\)
  - p=4..7: \(j=1\) on D3 sectors 4..7 ⇒ \((D3,4,4)\)
  - p=8: \(j=2\) on D4 sector 4 ⇒ \((D4,4,1)\)

Merge per disk:
- D2: sector 3 and 4..7 merge ⇒ \((D2,3,5)\)
- D3: 0..3 and 4..7 merge ⇒ \((D3,0,8)\)
- D4: 0..3 and 4 merge ⇒ \((D4,0,5)\)

**RAID5_READ_REQ2:** `(D2,3,5) (D3,0,8) (D4,0,5)`

---

## Part B — RAID5 write W (LBA 23..33): parity + I/O counts

Write bytes (in LBA order): `A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA`.

Mapping:
- Stripe 1 (LBA 16..31), \(pDisk=1\):
  - LBA 23 (p=7): \(j=1,o=3\) ⇒ D3 sector 7 gets A0
  - LBA 24..27 (p=8..11): \(j=2\) ⇒ D4 sectors 4..7 get A1..A4
  - LBA 28..31 (p=12..15): \(j=3\) ⇒ D0 sectors 4..7 get A5..A8
- Stripe 2 (LBA 32..47), \(pDisk=2\):
  - LBA 32..33 (p=0..1): \(j=0\) ⇒ D3 sectors 8..9 get A9,AA

Overwritten per disk:
- D3 sectors 7..9 (contiguous) ⇒ one run
- D4 sectors 4..7
- D0 sectors 4..7

### RAID5 I/O counts (read-modify-write)
Parity reads: stripe1 on D1 (4..7) + stripe2 on D2 (8..11) ⇒ 2 read I/Os.  
Data reads (merged even across stripes): D3 (7..9) + D4 (4..7) + D0 (4..7) ⇒ 3 read I/Os.  
Total reads = 5.

Data writes: D3 + D4 + D0 ⇒ 3 write I/Os.  
Parity writes: D1 (4..7) + D2 (8..11) ⇒ 2 write I/Os.  
Total writes = 5.

So: `WRITE_W_IO_RAID5: reads=5 writes=5`.

### RAID5 parity after W

Stripe 1 parity (on D1[4..7]):
- Data after write:
  - D2: 10 11 12 13
  - D3: 20 21 22 A0
  - D4: A1 A2 A3 A4
  - D0: A5 A6 A7 A8
Parity (XOR per offset o=0..3):
- o0: 10⊕20⊕A1⊕A5 = 34
- o1: 11⊕21⊕A2⊕A6 = 34
- o2: 12⊕22⊕A3⊕A7 = 34
- o3: 13⊕A0⊕A4⊕A8 = BF

Thus `RAID5_PARITY_STRIPE1: D1[4..7]=34,34,34,BF`.

Stripe 2 parity (on D2[8..11]):
- Data after write:
  - D3: A9 AA 57 58
  - D4: 65 66 67 68
  - D0: 75 76 77 78
  - D1: 05 06 07 08
Parity:
- o0: A9⊕65⊕75⊕05 = BC
- o1: AA⊕66⊕76⊕06 = BC
- o2: 57⊕67⊕77⊕07 = 40
- o3: 58⊕68⊕78⊕08 = 40

Thus `RAID5_PARITY_STRIPE2: D2[8..11]=BC,BC,40,40`.

---

## Part C — RAID4 under the same W

Mapping under RAID4 (j on Dj; parity on D4):
- Stripe1: D1 sector7 = A0; D2 sectors4..7 = A1..A4; D3 sectors4..7 = A5..A8
- Stripe2: D0 sectors8..9 = A9,AA

### RAID4 I/O counts
Parity disk D4 needs full parity chunks for stripe1 (4..7) and stripe2 (8..11), which are contiguous ⇒ 1 read I/O and 1 write I/O on D4.
Data disks overwritten: D1, D2, D3, D0 ⇒ 4 reads and 4 writes.
Totals: reads=5, writes=5.

So: `WRITE_W_IO_RAID4: reads=5 writes=5`.

### RAID4 parity after W
Stripe1 parity D4[4..7]:
- D0: 01 02 03 04
- D1: 05 06 07 A0
- D2: A1 A2 A3 A4
- D3: A5 A6 A7 A8
Parity: `00,00,00,A8`

Stripe2 parity D4[8..11]:
- D0: A9 AA 23 24
- D1: 25 26 27 28
- D2: 29 2A 2B 2C
- D3: 2D 2E 2F 30
Parity: `88,88,00,10`

---

## Part D — RAID5 rebuild byte (after W), D2 failed
Need stripe1, j=0, o=1 (this is D2 offset1). Reconstruct:
missing = parity ⊕ other three data bytes.
At stripe1 offset1: parity=34, other data: D3=21, D4=A2, D0=A6.
34⊕21=15; 15⊕A2=B7; B7⊕A6=11 ⇒ reconstructed byte `11`.

---

## Part E — Reliability (p=0.03), 10 decimals
Let q=1-p=0.97.

- RAID0 (5 disks): P(loss)=1-q^5.
  \(0.97^5 = 0.8587340257\) ⇒ P = 0.1412659743.
- RAID5 (5 disks): loss if ≥2 fail:
  P = 1 - [q^5 + 5 p q^4].
  \(q^4=0.8852928100\),
  \(5pq^4 = 5*0.03*0.8852928100 = 0.1327939215\).
  Sum = 0.9915279472 ⇒ P = 0.0084720528.
- RAID1 (2 disks): P(loss)=p^2=0.0009000000.

---

## Final 11-line Output

```
RAID5_READ_REQ1: (D0,4,4) (D2,4,4) (D3,4,8) (D4,2,6)
RAID5_READ_REQ2: (D2,3,5) (D3,0,8) (D4,0,5)
WRITE_W_IO_RAID5: reads=5 writes=5
WRITE_W_IO_RAID4: reads=5 writes=5
RAID5_PARITY_STRIPE1: D1[4..7]=34,34,34,BF
RAID5_PARITY_STRIPE2: D2[8..11]=BC,BC,40,40
RAID4_PARITY: stripe1 D4[4..7]=00,00,00,A8 | stripe2 D4[8..11]=88,88,00,10
REBUILD_RAID5_D2_s1_j0_o1: 11
P_LOSS_RAID0_5DISKS_p0.03: 0.1412659743
P_LOSS_RAID5_5DISKS_p0.03: 0.0084720528
P_LOSS_RAID1_2DISKS_p0.03: 0.0009000000
```