Skip to content

Commit 2e491bb

Browse files
committed
Merge origin/main into row/ENG-RESIDENCY-CONFIG-W2
The base moved while W2 was under review and repair, and #1200 landed edits to `.agents/specs/expert-streaming.md` two rows above this row's own entry. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [Claude Code] # Conflicts: # .agents/specs/expert-streaming.md
2 parents 86e8939 + c802dba commit 2e491bb

38 files changed

Lines changed: 5463 additions & 221 deletions

.agents/issue-index.md

Lines changed: 3 additions & 0 deletions
Large diffs are not rendered by default.

.agents/kernel-matrix.md

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

.agents/specs/expert-streaming.md

Lines changed: 7 additions & 2 deletions
Large diffs are not rendered by default.

.agents/specs/gdn-replayssm.md

Lines changed: 336 additions & 44 deletions
Large diffs are not rendered by default.

.agents/specs/model-fp8-block-weight.md

Lines changed: 474 additions & 0 deletions
Large diffs are not rendered by default.

docs/ENVIRONMENT.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ These change how the engine runs and have no CLI flag (or complement one).
7878
| `VT_VULKAN_DEVICE` | first suitable device | Forces the Vulkan physical device index. Required on a multi-GPU host to pin the intended device |
7979
| `VT_KV_CACHE_F32` | off (native KV dtype) | Forces the KV cache to fp32. A precision/diagnostic lever, at the cost of double the KV memory |
8080
| `VLLM_CPP_VOCODER_DEVICE` | `cpu` | Which device the shared 1-D BigVGAN vocoder core (`vllm::vocoder1d` — MiniMax-Music3, MiniMax-H3's audio VAE, LTX-2.5's audio VAE, IndexTTS-2.5) runs its convolutions on. It takes any device name `vt` knows (`vt::DeviceTypeName` — `cpu`, `cuda`, `metal`, `vulkan`, `xpu`, `rocm`, `tenstorrent`) and resolves it through `vt::DeviceTypeFromName`, so a provider registered for a new backend becomes reachable here with no edit. A name `vt` does not know, or one whose device has no registered `vt::Conv1d` / `vt::ConvTranspose1d` provider in this build, is REFUSED by name — never silently downgraded to the host, because a silent fallback means an operator who asked for a device never learns they did not get one. The transposed convolution is 88.5 % of MiniMax-Music3's acoustic-half profile and on scalar host loops a 45 s clip is a multi-hour decode, so this is the knob that decides whether that stage runs on the GPU. The two providers are BYTE-IDENTICAL — one f64 accumulator per output element walked in the same order, the host pinned `-ffp-contract=off` and the device kernel pinned with `__dmul_rn`/`__dadd_rn` — and `tests/vt/test_ops_conv1d_general.cpp` gates that with `memcmp`, not a tolerance. It still defaults to `cpu`: flipping four shipped audio models onto a device arm is not a default the row that ADDED the arm is entitled to set, and the flip is owed to the wiring row named in [.agents/specs/minimax-music3.md](../.agents/specs/minimax-music3.md) §11.4. Requires a CUDA build — asking for `cuda` without one throws rather than falling back silently ([#672](https://github.com/mudler/vllm.cpp/issues/672)) |
81+
| `VLLM_CPP_MUSIC3_PROFILE` | unset (off) | Prints a per-STAGE wall-clock and RSS split for a MiniMax-Music3 request to **stderr** when it finishes. `examples/minimax_music3_gen/main.cpp` times only the checkpoint load and the whole of `vllm_synthesize`, and at a real duration those two numbers cannot say which of the six stages spent the time — four of the six are still host reference loops ([.agents/specs/minimax-music3.md](../.agents/specs/minimax-music3.md) §11.1, §14.2), so the split is the question. Rows are LEAVES (they partition the request and are summed) or SPANS (they enclose leaves and are printed but never added), plus pure counters; `unattributed` is the glue between the leaves and is printed rather than spread silently over the measured stages. `calls` on `denoise.dit_device` counts denoise STEPS, so the DiT forward count is twice it. Accepts `1`, `true`, `on`, `yes` (case-insensitive) — **any other value, including a near miss like `y`, leaves it off**, because an operator who mistypes should get a run with no table rather than a run whose meaning quietly changed. With it off no clock is read and no `/proc` file is opened. It is an attribution instrument and takes no GPU clock window ([.agents/benchmarking.md](../.agents/benchmarking.md)), so its rows are a within-run split and must not be quoted as per-kernel or cross-box figures ([#672](https://github.com/mudler/vllm.cpp/issues/672)) |
8182
| `VT_ENABLE_JUMP_FORWARD` | off | Opt-in to jump-forward constrained decoding (SGLang parity SW3): when a grammar/structured-output request reaches a state with exactly one valid next token, that token is emitted without a model step. Currently drives only the standalone driver (`DrainForcedTokens`); output-identical by construction (it fires only where the constrained sampler already has a single valid token), so it changes speed, never tokens. Off by default until the production scheduler splice (jumped-token KV recompute) lands. Set `1`/`true`/`on` to enable |
8283
| `VT_SERVER_MAX_PROMPT_CHARS` | `200000` | Rejects larger `/v1/chat/completions` prompts before scheduling. `0` disables the guard. This is a character count after chat-template rendering, not a token limit |
8384
| `VT_SERVER_MAX_NEW_TOKENS` | `4096` | Caps the request's `max_tokens` value for `/v1/chat/completions`. `0` disables the cap |

docs/FEATURES.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ are our reading of their documented behavior, not measurements.
7979
| GPTQ | ◐ CPU dequant ||||
8080
| MXFP4 compressed-tensors | ◐ W4A16 Marlin, mem 2.63x less. gate_up FUSION + decode-graph default-ON; #44 3/3, 32B 6/6. **`VT_MARLIN_DENSE` DEFAULT-ON** (`KERNEL-MARLIN-DENSE-EXEC`): dense marlin 48-CTA, byte-faithful, beats MoE (c8 0.969) ||||
8181
| fp8 weights, per-tensor scale |||||
82-
| Block-wise (fine-grained 128x128) FP8, the `weight_scale_inv` layout | ☐ REFUSED BY NAME at load (#1166): `Qwen/Qwen3.8-27B-FP8` declares `weight_block_size` [128, 128] and this build is per-tensor FP8 only ([spec](../.agents/specs/fp8-blockwise-refusal.md)) ||||
82+
| Block-wise (fine-grained 128x128) FP8, the `weight_scale_inv` layout | ◐ LOADS, cannot run (#1189 M3): weight + `cdiv` scale rung + config/tensor cross-check; BF16 scale widened to f32. Linear method is M4, so `Prepare` refuses by name ([spec](../.agents/specs/model-fp8-block-weight.md)) ||||
8383
| Per-tensor FP8 W8A8 linear is a shared seam any model can bind |`models/dense_fp8_gemm.h` + `layers::Fp8W8A8LinearMethod` (#940), bound via `layers::MakeLinearMethod`. One definition, CUDA only ([spec](../.agents/specs/vt-fp8-shared-seam.md)) |`Fp8LinearMethod` |||
8484
| FP8 W8A8 works on a CUDA arch without `cutlass-fp8` |`vt::QuantFp8Static` registers from an unconditional TU (#960); sm_110 measured ([spec](../.agents/specs/vt-fp8-quant-arch-gate.md)) ||||
8585
| fp8-tower GDN `in_proj` emits bf16, unlocking packed GDN decode |`VT_GDN_FP8_IN_BF16` + `VT_GDN_PACKED_DECODE_FP8_TOWER` (inert alone), both default **OFF**, ungated (#339) ([spec](../.agents/specs/perf-fp8-alpha-fold.md)) | ✅ bf16 `out_dtype` |||

docs/USAGE.md

Lines changed: 61 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -596,32 +596,41 @@ quantizes the activation once; a checkpoint whose scales differ keeps the two
596596
separate GEMMs automatically. `VT_GDN_MERGED_QKVZ_FP8=0` restores the two GEMMs
597597
in the same binary.
598598

599-
### Block-wise FP8 is refused at load
599+
### Block-wise FP8 loads and does not run yet
600600

601-
This build reads per-tensor FP8, where one scale covers a whole weight. It does
602-
not read block-wise FP8, also called fine-grained FP8, where one scale covers
603-
each 128x128 block of the weight. A block-wise checkpoint declares
604-
`quantization_config.weight_block_size` in its `config.json`, and it stores its
605-
scales under `weight_scale_inv` rather than under `weight_scale`.
601+
Block-wise FP8, also called fine-grained FP8, keeps one scale for each 128x128
602+
block of a weight rather than one scale for the whole weight. A block-wise
603+
checkpoint declares `quantization_config.weight_block_size` in its
604+
`config.json` and stores its scales under `weight_scale_inv` rather than under
605+
`weight_scale`.
606606

607607
`Qwen/Qwen3.8-27B-FP8` is such a checkpoint. At revision
608608
`017b9c7af6b5689d5dd426a76e0bc077eb5ca20a` it declares `weight_block_size`
609609
`[128, 128]` with `activation_scheme` `dynamic`, and it stores
610610
`self_attn.q_proj.weight` as `F8_E4M3` `[12288, 5120]` beside
611611
`self_attn.q_proj.weight_scale_inv` as `BF16` `[96, 40]`.
612612

613-
Loading it stops with a message that names the key:
613+
That checkpoint now LOADS. The weights are read into a block-wise FP8 weight,
614+
the `BF16` scale is widened to `F32` by value the way vLLM widens it, and the
615+
config is cross-checked against the tensors so a disagreement is named rather
616+
than guessed at. Nothing can execute the weight yet, so the model refuses to
617+
finish preparing:
614618

615619
```text
616-
quantization_config.weight_block_size [128, 128] selects block-wise
617-
(fine-grained) FP8, which is not implemented. This build implements per-tensor
618-
FP8 only.
620+
block-wise (fine-grained) 128x128 FP8 weights LOADED for
621+
model.layers.0.self_attn.q_proj and nothing in this build can execute them
619622
```
620623

621-
The refusal is deliberate. Nothing is wrong with that checkpoint, and the
622-
missing arm is in this project. To run the same model here, use a per-tensor
623-
FP8, BF16, NVFP4, or GGUF checkpoint of it. Issue
624-
[#1166](https://github.com/mudler/vllm.cpp/issues/1166) tracks the port.
624+
Two block-wise configurations are refused earlier, at load, because no build
625+
here implements them: an `activation_scheme` other than `dynamic`, and a
626+
`weight_block_size` other than `[128, 128]`. Both messages name the key and the
627+
value your `config.json` declares.
628+
629+
Nothing is wrong with those checkpoints; the missing arm is in this project. To
630+
run the same model today, use a per-tensor FP8, BF16, NVFP4, or GGUF checkpoint
631+
of it. Issue [#1189](https://github.com/mudler/vllm.cpp/issues/1189) tracks the
632+
remaining milestones, and
633+
[#1166](https://github.com/mudler/vllm.cpp/issues/1166) is the original report.
625634

626635
### A per-tensor scale has to be one F32 number
627636

@@ -2192,6 +2201,44 @@ arm — `VLLM_CPP_MUSIC3_DEVICE=1` selects the device one, unset is the CPU one
21922201
at the same bounds, with the same negative control. Numbers for both are in
21932202
[BENCHMARKS](BENCHMARKS.md).
21942203

2204+
#### Where the time actually goes: `VLLM_CPP_MUSIC3_PROFILE`
2205+
2206+
The table above says which stage runs where. It does not say what each one
2207+
*costs*, and at a real duration that is the only question anyone asks. Set
2208+
2209+
```sh
2210+
VLLM_CPP_MUSIC3_PROFILE=1 minimax-music3-gen --model ... --duration 20 --steps 30 --device 1
2211+
```
2212+
2213+
and the engine prints a `MUSIC3_PROFILE` table to **stderr** when the request
2214+
finishes: one row per stage with seconds, a call count, and its share of the
2215+
request, then the resident-set size at each stage boundary.
2216+
2217+
Read it as follows.
2218+
2219+
* `leaf` rows partition the request and are the ones that add up. `span` rows
2220+
enclose leaves — `ar.TOTAL_loop`, `denoise.TOTAL` — and are printed for
2221+
context but never summed, so the table cannot claim more work than the run
2222+
contained.
2223+
* `cnt` rows carry no time at all. They are the counts a split has to state to
2224+
be readable: frames, windows, requested steps.
2225+
* `unattributed` is the glue between the leaves — chunk slicing, the overlap
2226+
blend, the Euler step, the WAV assembly. It is printed rather than spread
2227+
silently over the measured stages, so a bracket in the wrong place shows up as
2228+
a number instead of as a plausible share somewhere else.
2229+
* the `calls` column on `denoise.dit_device` counts *steps*, not forwards: one
2230+
bracket covers both classifier-free-guidance branches, so the forward count is
2231+
twice it.
2232+
2233+
It is **off unless the variable is set to `1`, `true`, `on` or `yes`**. Any
2234+
other value, including a near miss like `y`, leaves it off — an operator who
2235+
mistypes gets a run with no table rather than a run whose meaning quietly
2236+
changed. With it off, no clock is read and no `/proc` file is opened.
2237+
2238+
This is an attribution instrument, not a benchmark harness: it takes no GPU
2239+
clock window, so its rows are a within-run **split** and must not be quoted as
2240+
per-kernel or cross-box figures.
2241+
21952242
**Measured, so expectations are calibrated rather than hoped for.** On a Jetson
21962243
Thor (sm_110, 14 cores) the device arm was *slower* on a two-frame request
21972244
(846.6 s vs 835.1 s) and 5.4 % faster on a ten-frame one (1430.4 s vs 1512.1 s).
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
ARCH=sm_121a
2+
ARCH_HITS_IN_RES_USAGE=2
3+
COMPILE_RC=0
4+
CUOBJDUMP_RC=0
5+
LINEINFO_COMPILE_RC=0
6+
OBJECT_BYTES=435184
7+
NVCC_VERSION=Cuda compilation tools, release 13.0, V13.0.88 Build cuda_13.0.r13.0/compiler.36424714_0
8+
PTXAS_VERSION=Cuda compilation tools, release 13.0, V13.0.88 Build cuda_13.0.r13.0/compiler.36424714_0
9+
COMPILE_CMD=nvcc -std=c++20 -O3 -arch=sm_121a -Xptxas -v -c /tmp/replayssm-w0b/probe.cu -o /tmp/replayssm-w0b/probe.o
10+
PROBE_SHA256=f7d323651cf1a5720f0ce712b802a10248278566e1abb9ecdbae8e62fcf0f4b3
11+
SCRIPT_SHA256=c5218d087949e94647e28d6799b2f870e34643bc754dfaec084b562fd979ef55
12+
--- ptxas -v ---
13+
ptxas info : 0 bytes gmem
14+
ptxas info : Compiling entry function '_Z25ProbeReplaySsmFusedKernelI13__nv_bfloat16fLi128ELi16ELi32ELi64EEvPT_PKS1_S4_S4_PKvN45_GLOBAL__N__f6ac47b2_8_probe_cu_24629ce9_31385DTypeES6_S8_PT0_SA_SA_PfPKiSD_lllllllllf' for 'sm_121a'
15+
ptxas info : Function properties for _Z25ProbeReplaySsmFusedKernelI13__nv_bfloat16fLi128ELi16ELi32ELi64EEvPT_PKS1_S4_S4_PKvN45_GLOBAL__N__f6ac47b2_8_probe_cu_24629ce9_31385DTypeES6_S8_PT0_SA_SA_PfPKiSD_lllllllllf
16+
0 bytes stack frame, 0 bytes spill stores, 0 bytes spill loads
17+
ptxas info : Used 95 registers, used 0 barriers, 1224 bytes smem
18+
ptxas info : Compile time = 203.592 ms
19+
ptxas info : Compiling entry function '_Z28ProbeReplaySsmFlushSeqKernelI13__nv_bfloat16fLi128ELi16ELi64EEvPT_PKS1_S4_S4_PKvN45_GLOBAL__N__f6ac47b2_8_probe_cu_24629ce9_31385DTypeES6_S8_PT0_SA_SA_PfPKiSD_lllllllllf' for 'sm_121a'
20+
ptxas info : Function properties for _Z28ProbeReplaySsmFlushSeqKernelI13__nv_bfloat16fLi128ELi16ELi64EEvPT_PKS1_S4_S4_PKvN45_GLOBAL__N__f6ac47b2_8_probe_cu_24629ce9_31385DTypeES6_S8_PT0_SA_SA_PfPKiSD_lllllllllf
21+
0 bytes stack frame, 0 bytes spill stores, 0 bytes spill loads
22+
ptxas info : Used 94 registers, used 0 barriers, 1156 bytes smem
23+
ptxas info : Compile time = 178.604 ms
24+
ptxas info : Compiling entry function '_Z25ProbeReplaySsmFlushKernelI13__nv_bfloat16fLi128ELi16ELi64EEvPT_PKS1_S4_S4_PKvN45_GLOBAL__N__f6ac47b2_8_probe_cu_24629ce9_31385DTypeES6_S8_PT0_SA_SA_PfPKiSD_lllllllllf' for 'sm_121a'
25+
ptxas info : Function properties for _Z25ProbeReplaySsmFlushKernelI13__nv_bfloat16fLi128ELi16ELi64EEvPT_PKS1_S4_S4_PKvN45_GLOBAL__N__f6ac47b2_8_probe_cu_24629ce9_31385DTypeES6_S8_PT0_SA_SA_PfPKiSD_lllllllllf
26+
0 bytes stack frame, 0 bytes spill stores, 0 bytes spill loads
27+
ptxas info : Used 94 registers, used 0 barriers, 1156 bytes smem
28+
ptxas info : Compile time = 178.771 ms
29+
ptxas info : Compiling entry function '_Z28ProbeReplaySsmNonFlushKernelI13__nv_bfloat16fLi128ELi16EEvPT_PKS1_S4_S4_PKvN45_GLOBAL__N__f6ac47b2_8_probe_cu_24629ce9_31385DTypeES6_S8_PT0_SA_SA_PfPKiSD_lllllllllf' for 'sm_121a'
30+
ptxas info : Function properties for _Z28ProbeReplaySsmNonFlushKernelI13__nv_bfloat16fLi128ELi16EEvPT_PKS1_S4_S4_PKvN45_GLOBAL__N__f6ac47b2_8_probe_cu_24629ce9_31385DTypeES6_S8_PT0_SA_SA_PfPKiSD_lllllllllf
31+
0 bytes stack frame, 0 bytes spill stores, 0 bytes spill loads
32+
ptxas info : Used 42 registers, used 0 barriers, 1224 bytes smem
33+
ptxas info : Compile time = 123.199 ms
34+
ptxas info : Compiling entry function '_Z27ProbeReplaySsmRegTileKernelI13__nv_bfloat16fLi128ELi16EEvPT_PKS1_S4_S4_PKvN45_GLOBAL__N__f6ac47b2_8_probe_cu_24629ce9_31385DTypeES6_S8_PT0_SA_SA_PfPKiSD_lllllllllf' for 'sm_121a'
35+
ptxas info : Function properties for _Z27ProbeReplaySsmRegTileKernelI13__nv_bfloat16fLi128ELi16EEvPT_PKS1_S4_S4_PKvN45_GLOBAL__N__f6ac47b2_8_probe_cu_24629ce9_31385DTypeES6_S8_PT0_SA_SA_PfPKiSD_lllllllllf
36+
96 bytes stack frame, 96 bytes spill stores, 96 bytes spill loads
37+
ptxas info : Used 255 registers, used 0 barriers, 96 bytes cumulative stack size, 1224 bytes smem
38+
ptxas info : Compile time = 269.512 ms
39+
ptxas info : Compiling entry function '_Z25ProbeControlRegTileKernelI13__nv_bfloat16fLi128EEvPT_PKS1_S4_S4_PKvN45_GLOBAL__N__f6ac47b2_8_probe_cu_24629ce9_31385DTypeES6_S8_PT0_PKilllllllllf' for 'sm_121a'
40+
ptxas info : Function properties for _Z25ProbeControlRegTileKernelI13__nv_bfloat16fLi128EEvPT_PKS1_S4_S4_PKvN45_GLOBAL__N__f6ac47b2_8_probe_cu_24629ce9_31385DTypeES6_S8_PT0_PKilllllllllf
41+
56 bytes stack frame, 56 bytes spill stores, 56 bytes spill loads
42+
ptxas info : Used 255 registers, used 0 barriers, 56 bytes cumulative stack size, 1024 bytes smem
43+
ptxas info : Compile time = 187.856 ms
44+
--- cuobjdump -res-usage ---
45+
46+
Fatbin ptx code:
47+
================
48+
arch = sm_121
49+
code version = [9,0]
50+
host = linux
51+
compile_size = 64bit
52+
compressed
53+
ptxasOptions = -v
54+
55+
Fatbin elf code:
56+
================
57+
arch = sm_121a
58+
code version = [1,8]
59+
host = linux
60+
compile_size = 64bit
61+
62+
Resource usage:
63+
Common:
64+
GLOBAL:0
65+
Function _Z25ProbeReplaySsmFusedKernelI13__nv_bfloat16fLi128ELi16ELi32ELi64EEvPT_PKS1_S4_S4_PKvN45_GLOBAL__N__f6ac47b2_8_probe_cu_24629ce9_31385DTypeES6_S8_PT0_SA_SA_PfPKiSD_lllllllllf:
66+
REG:95 STACK:0 SHARED:2248 LOCAL:0 CONSTANT[0]:1084 TEXTURE:0 SURFACE:0 SAMPLER:0
67+
Function _Z28ProbeReplaySsmFlushSeqKernelI13__nv_bfloat16fLi128ELi16ELi64EEvPT_PKS1_S4_S4_PKvN45_GLOBAL__N__f6ac47b2_8_probe_cu_24629ce9_31385DTypeES6_S8_PT0_SA_SA_PfPKiSD_lllllllllf:
68+
REG:94 STACK:0 SHARED:2180 LOCAL:0 CONSTANT[0]:1084 TEXTURE:0 SURFACE:0 SAMPLER:0
69+
Function _Z25ProbeReplaySsmFlushKernelI13__nv_bfloat16fLi128ELi16ELi64EEvPT_PKS1_S4_S4_PKvN45_GLOBAL__N__f6ac47b2_8_probe_cu_24629ce9_31385DTypeES6_S8_PT0_SA_SA_PfPKiSD_lllllllllf:
70+
REG:94 STACK:0 SHARED:2180 LOCAL:0 CONSTANT[0]:1084 TEXTURE:0 SURFACE:0 SAMPLER:0
71+
Function _Z28ProbeReplaySsmNonFlushKernelI13__nv_bfloat16fLi128ELi16EEvPT_PKS1_S4_S4_PKvN45_GLOBAL__N__f6ac47b2_8_probe_cu_24629ce9_31385DTypeES6_S8_PT0_SA_SA_PfPKiSD_lllllllllf:
72+
REG:42 STACK:0 SHARED:2248 LOCAL:0 CONSTANT[0]:1084 TEXTURE:0 SURFACE:0 SAMPLER:0
73+
Function _Z27ProbeReplaySsmRegTileKernelI13__nv_bfloat16fLi128ELi16EEvPT_PKS1_S4_S4_PKvN45_GLOBAL__N__f6ac47b2_8_probe_cu_24629ce9_31385DTypeES6_S8_PT0_SA_SA_PfPKiSD_lllllllllf:
74+
REG:255 STACK:96 SHARED:2248 LOCAL:0 CONSTANT[0]:1084 TEXTURE:0 SURFACE:0 SAMPLER:0
75+
Function _Z25ProbeControlRegTileKernelI13__nv_bfloat16fLi128EEvPT_PKS1_S4_S4_PKvN45_GLOBAL__N__f6ac47b2_8_probe_cu_24629ce9_31385DTypeES6_S8_PT0_PKilllllllllf:
76+
REG:255 STACK:56 SHARED:2048 LOCAL:0 CONSTANT[0]:1052 TEXTURE:0 SURFACE:0 SAMPLER:0
77+
78+
Fatbin ptx code:
79+
================
80+
arch = sm_121a
81+
code version = [9,0]
82+
host = linux
83+
compile_size = 64bit
84+
compressed
85+
ptxasOptions = -v

0 commit comments

Comments
 (0)