Skip to content

Conversation

qingwangrh
Copy link
Contributor

@qingwangrh qingwangrh commented Sep 10, 2025

ID:4056

Summary by CodeRabbit

  • Tests
    • Added a Linux-only test validating unaligned discard behavior when a host-attached 4K-sector disk is exposed to a guest using 512-byte sectors.
    • Runs discard-based I/O inside the guest and verifies success on virtio/scsi-hd and expected failure for pass-through-style devices.
    • Includes variants covering virtio, scsi-hd, scsi-block, and scsi-generic with 512B and 4K sector scenarios.
    • Automates device setup/teardown, discovery, VM lifecycle, and parameter-driven expected outcomes.

Copy link

coderabbitai bot commented Sep 10, 2025

Walkthrough

Adds a new QEMU test and config to validate unaligned discard behavior for a host scsi_debug 4K disk exposed to guests as 512B or 4K sectors; the test discovers scsi_debug devices, boots a VM with the disk, runs parameterized blkdiscard/lsblk guest IOs, checks exit codes, and cleans up.

Changes

Cohort / File(s) Change summary
New test: 4K discard validation
qemu/tests/block_4k_discard.py
New test implementing scsi_debug discovery via lsscsi, selection of device (path/sg/WWN), optional preprocessing, VM boot with extra data disk, guest login and execution of parameterized guest IO commands (blkdiscard/lsblk), verification of expected exit codes, and VM teardown. Adds run(test, params, env) decorated with @error_context.context_aware.
Config for test variants
qemu/tests/cfg/block_4k_discard.cfg
Adds block_4k_discard configuration: image and raw-device settings, pre_command/post_command to load/unload scsi_debug (sector_size=4096), host and guest IO command parameters, five guest_io commands plus host discovery, and four variants (virtio, scsi-hd, scsi-block, scsi-generic) with expected exit-code vectors.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Runner as Test Runner
    participant Host
    participant scsi_debug as scsi_debug (host)
    participant VM as Guest VM
    participant Guest as Guest Session

    Host->>scsi_debug: modprobe scsi_debug sector_size=4096 (pre_command)
    Runner->>Host: (optional) env_process.process(preprocess_image, preprocess_vm)
    Runner->>VM: Boot VM with scsi_debug-backed data disk
    VM-->>Runner: VM alive
    Runner->>Guest: Open login session
    Runner->>Host: lsscsi -> discover scsi_debug device(s)
    Host-->>Runner: return {path, sg, wwn, size}
    Runner->>Guest: Resolve drive path (serial_stg1 or discovered WWN/sg)
    loop For each guest_io_cmdN
        Runner->>Guest: Execute formatted blkdiscard/lsblk command
        Guest-->>Runner: Exit status
        Runner->>Runner: Compare exit status to expected_result
    end
    Runner->>VM: Destroy VM (force if needed)
    Host->>scsi_debug: rmmod scsi_debug (post_command)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Pre-merge checks (2 passed, 1 warning)

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "Add block 4k device unaligned discard test" accurately summarizes the primary change (adding a test for unaligned discard behavior on 4K block devices) and matches the added qemu/tests/block_4k_discard.py and its configuration, so it is concise and relevant for reviewers scanning history.

Poem

I hopped to a disk with four-K delight,
Probed paths and WWNs by moonlight.
I tossed blkdiscard with a careful paw,
Checked each exit code, noted the law.
A carrot for logs — tidy and bright.

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 81b6484 and c677377.

📒 Files selected for processing (2)
  • qemu/tests/block_4k_discard.py (1 hunks)
  • qemu/tests/cfg/block_4k_discard.cfg (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • qemu/tests/block_4k_discard.py
  • qemu/tests/cfg/block_4k_discard.cfg
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (6)
qemu/tests/cfg/block_4k_discard.cfg (2)

23-23: Duplicate guest_io_cmd0; last one wins and changes semantics.

You define guest_io_cmd0 twice; the latter (with grep) overrides the first. This is confusing and can skew expectations.

Apply either (remove the trailing duplicate):

-    guest_io_cmd0 = "${lsblk_cmd}|egrep '${sec_size}.*${sec_size}'"

or (if the filtered output is desired) remove the earlier unfiltered one:

-    guest_io_cmd0 = "${lsblk_cmd}"

Also applies to: 50-50


34-35: Typo in parameter name “expected_resultes”.

Prefer “expected_results” for clarity; keep backward-compat in the Python to avoid breaking existing cfgs.

If you adopt the rename, update these lines:

-            expected_resultes = 0 0 0 0 0
+            expected_results = 0 0 0 0 0

(and similarly for other variants), and accept the Python-side fallback in the companion change below.

Also applies to: 40-40, 44-44, 49-49

qemu/tests/block_4k_discard.py (4)

4-7: Remove unused imports.

-import os
-import re
-import time
+import re

72-85: Guard against missing WWN when serial_stg1 is unset.

If serial isn’t provided (block/generic variants), ensure we found a WWN; otherwise the guest disk lookup will fail.

-        if scsi_debug_devs:
+        if scsi_debug_devs:
             dev = scsi_debug_devs[0]
-            disk_wwn = dev["wwn"]
+            disk_wwn = dev.get("wwn")
             if params["drive_format_stg1"] == "scsi-generic":
                 params["image_name_stg1"] = dev["sg"]
             else:
                 params["image_name_stg1"] = dev["path"]
         else:
             test.fail("Can not find scsi_debug devices")
+        if not params.get("serial_stg1") and not disk_wwn:
+            test.cancel("No serial_stg1 and no WWN found; cannot reliably locate disk in guest")

87-96: Preprocess toggle is non-intuitive; add a brief comment.

Pattern is fine (call preprocess when not_preprocess=yes), but it reads backwards.

Add a comment above explaining that this test self-preprocesses when the framework is instructed to skip preprocessing.


111-111: Add trailing newline.

-    
+\n
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 39966b3 and a6cfffb.

📒 Files selected for processing (2)
  • qemu/tests/block_4k_discard.py (1 hunks)
  • qemu/tests/cfg/block_4k_discard.cfg (1 hunks)
🧰 Additional context used
🪛 Ruff (0.12.2)
qemu/tests/block_4k_discard.py

111-111: No newline at end of file

Add trailing newline

(W292)

🔇 Additional comments (1)
qemu/tests/cfg/block_4k_discard.cfg (1)

1-1: Revert—leading dashes on cfg section headers are the norm
tp-qemu cfg files predominantly declare top-level sections as - name: (3629 vs 2641 without) so - block_4k_discard: is correct.

Likely an incorrect or invalid review comment.

@qingwangrh qingwangrh force-pushed the 4056_4k_unaligned_discard branch from a6cfffb to 81f06b3 Compare September 11, 2025 13:00
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
qemu/tests/block_4k_discard.py (1)

72-79: Null-safe WWN parsing; avoid AttributeError and support common formats.

wwn_tok.split("0x", 1)[1] will crash if no 0x* token exists; lsscsi -w -s often prints wwn:0x… or naa.…. Parse robustly and allow missing WWN.

-            wwn_tok = next((t for t in tokens if t.startswith("0x")), None)
-            wwn = wwn_tok.split("0x", 1)[1]
+            # WWN may appear as 'wwn:0x...' (preferred) or as a bare '0x...'/ 'naa.*'
+            wwn = None
+            wwn_tok = next((t for t in tokens if t.startswith("wwn:")), None)
+            if wwn_tok and ":" in wwn_tok:
+                wwn = wwn_tok.split(":", 1)[1]
+            else:
+                alt = next((t for t in tokens if re.fullmatch(r"(?:0x[0-9a-fA-F]+|naa\..+)", t)), None)
+                wwn = alt
🧹 Nitpick comments (2)
qemu/tests/block_4k_discard.py (2)

55-58: Drop redundant '|| true'.

ignore_status=True already suppresses non-zero exit codes.

-        cmd = "lsscsi -g -w -s | grep scsi_debug || true"
+        cmd = "lsscsi -g -w -s | grep scsi_debug"

92-92: Remove stray print; use logger.

-            print(disk_wwn)
+            logger.debug("Selected scsi_debug WWN: %s", disk_wwn)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a6cfffb and 81f06b3.

📒 Files selected for processing (2)
  • qemu/tests/block_4k_discard.py (1 hunks)
  • qemu/tests/cfg/block_4k_discard.cfg (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • qemu/tests/cfg/block_4k_discard.cfg

@qingwangrh qingwangrh force-pushed the 4056_4k_unaligned_discard branch from 81f06b3 to 81b6484 Compare September 12, 2025 09:01
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (3)
qemu/tests/block_4k_discard.py (3)

35-40: Clarify mismatch message; use correct param name.

Message says io_cmd_number; the param is guest_io_cmd_number. Also helpful to include the raw list for debugging.

-            test.cancel(
-                "Mismatch: io_cmd_number={} but expected_results has {} items".format(
-                    io_cmd_num, len(results_raw)
-                )
-            )
+            test.cancel(
+                "Mismatch: guest_io_cmd_number={} but expected_results has {} items: {}"
+                .format(io_cmd_num, len(results_raw), results_raw)
+            )

77-84: Fix WWN parsing; current code can crash and misses wwn: token.

wwn_tok looks for tokens starting with "0x" and then unconditionally calls .split, which will raise when no match is found. lsscsi -w emits wwn:0x...; parse that and guard for None.

Apply:

-            wwn_tok = next((t for t in tokens if t.startswith("0x")), None)
-            wwn = wwn_tok.split("0x", 1)[1]
+            wwn_tok = next((t for t in tokens if t.startswith("wwn:")), None)
+            wwn = wwn_tok.split(":", 1)[1] if wwn_tok and ":" in wwn_tok else None

105-113: Invert the not_preprocess gate; currently runs when it should skip.

Preprocessing executes when not_preprocess=yes. Only run when it’s not “yes”.

-        if params.get("not_preprocess", "no") == "yes":
-            logger.debug("Ready boot VM : %s", params["images"])
+        if params.get("not_preprocess", "no") != "yes":
+            logger.debug("Preprocessing and booting VM: %s", params.get("images"))
             env_process.process(
                 test,
                 params,
                 env,
                 env_process.preprocess_image,
                 env_process.preprocess_vm,
             )
🧹 Nitpick comments (4)
qemu/tests/block_4k_discard.py (4)

60-63: Drop redundant || true; ignore_status=True already suppresses errors.

Simplifies the host command while keeping behavior.

-        cmd = "lsscsi -g -w -s | grep scsi_debug || true"
+        cmd = "lsscsi -g -w -s | grep scsi_debug"

97-97: Remove stray print; use logger if needed.

print(disk_wwn) is a leftover debug.

-            print(disk_wwn)
+            logger.debug("Selected scsi_debug WWN: %s", disk_wwn)

64-66: Unify wording and logger usage; minor copy edits.

Prefer logger.warning(...) over test.log.warning(...) for consistency, and use “Cannot …” phrasing.

-        logger.info("Host cmd output '%s'", out)
-        if not out:
-            test.log.warning("Can not find scsi_debug disk")
+        logger.info("Host cmd output '%s'", out)
+        if not out:
+            logger.warning("Cannot find scsi_debug disk")
@@
-                logger.warning("Unable to parse scsi_debug line: %s", line)
+                logger.warning("Unable to parse scsi_debug line: %s", line)
@@
-            test.fail("Can not find disk by {}".format(serial))
+            test.fail("Cannot find disk by {}".format(serial))
@@
-            test.fail("Can not find scsi_debug devices")
+            test.fail("Cannot find scsi_debug devices")

Also applies to: 80-81, 29-30, 103-103


1-2: Tighten docstring language.

Use explicit units and clearer phrasing.

-"""Attach the host 4k disk but QEMU is exposing a 512 to the Guest.
-Test the unaligned discard operation on the disk."""
+"""Attach a host 4K disk while QEMU exposes 512B sectors to the guest.
+Test unaligned discard behavior on that disk."""
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 81f06b3 and 81b6484.

📒 Files selected for processing (2)
  • qemu/tests/block_4k_discard.py (1 hunks)
  • qemu/tests/cfg/block_4k_discard.cfg (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • qemu/tests/cfg/block_4k_discard.cfg
🧰 Additional context used
🧬 Code graph analysis (1)
qemu/tests/block_4k_discard.py (1)
provider/storage_benchmark.py (1)
  • session (83-90)
🔇 Additional comments (2)
qemu/tests/block_4k_discard.py (2)

41-58: Good hardening of result parsing and logging.

Safe int conversion, structured logging, and detailed failure messages look solid.


121-124: Good guard on missing identifier before guest lookup.

Prevents passing an empty identifier to get_linux_drive_path.

@qingwangrh
Copy link
Contributor Author

@XueqiangWei @fbq815 Please help to review, thanks.

@qingwangrh qingwangrh force-pushed the 4056_4k_unaligned_discard branch from 81b6484 to c677377 Compare September 12, 2025 11:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant