Repurposing HPE 3PAR Storage: Getting 520-Byte SAS Drives Working on Linux
A few months ago, we won an auction for some server equipment we needed. As is usually the case with large equipment lots, the servers we actually wanted came with quite a bit of additional enterprise hardware that we had no immediate use for.
Part of that equipment was an HPE 3PAR storage system.
The lot included the original HPE 3PAR StoreServ 7200 equipment along with eight 2U, 24-bay SAS disk shelves. The shelves were populated with roughly 1 TB-class enterprise SAS drives.
They sat around for a few months before I finally decided it was time to see what I could do with them.
I already had two Proxmox servers in the office, so my initial thought was pretty simple: if I could repurpose a few of these shelves, they could make excellent bulk backup storage, or possibly provide additional storage for virtual machines.
There was just one problem: 3PAR hardware isn’t exactly designed around the assumption that somebody is going to pull it out of its original storage system and plug it into a random Linux server.
Finding a SAS Controller
Before buying anything, I went through the rest of the auction equipment looking for a SAS controller that had an external Mini-SAS connection.
Eventually I found a 3ware/LSI 9750-24i4e RAID controller.
The 24i4e version has external SAS connectivity, making it at least physically capable of connecting to the 3PAR disk shelf.
It wasn’t the controller I would have chosen if I were designing this from scratch. A modern IT-mode HBA would have made this project considerably easier. But the 9750 was already sitting in the pile, so I figured it was worth seeing how far I could get with it.
I installed it in one of the Proxmox servers, connected one of the 3PAR shelves, and headed home to continue the experiment remotely.
Initial Assessment
The first thing I checked was whether Linux could see any new disks.
lsblk
Nothing.
That immediately raised the question of whether the controller itself was working.
I checked PCI discovery:
lspci
and then looked at which driver Linux had attached to it:
lspci -nnk | grep -A3 -i 3ware
The controller was there and the Linux 3w-sas driver had loaded correctly.
I also checked the kernel logs:
dmesg | grep -iE '3w-sas|3ware|9750|sas'
Everything looked normal.
At this point I had someone in the office move the external Mini-SAS cable to another port on the shelf while I watched the logs remotely.
Linux still didn’t gain any disks, but something interesting happened: the controller logged the disconnect and reconnect.
That told me the SAS connection wasn’t completely dead. The controller was clearly communicating with something on the other end of that cable.
Finding tw_cli
The next step was figuring out how to talk directly to the 9750.
The 9750 was originally part of 3ware’s controller lineup, and its management utility is called tw_cli.
Once I got tw_cli running, the picture changed considerably.
For example:
/usr/local/bin/tw_cli /c0 show
could interrogate controller 0 directly.
The enclosure and physical drives were actually there.
The controller could enumerate the drives, identify their positions, and retrieve information about them. But instead of presenting them as usable disks, every drive was reported as something along the lines of:
NOT-SUPPORTED
0 MB
So we now had a strange situation:
SAS connection Working
Controller Working
Linux driver Working
Shelf discovery Working
Physical disks Detected
Linux block devices Missing
The hardware could communicate. The controller simply refused to make the drives usable.
The 520-Byte Sector Problem
At this point my leading theory was the logical sector format.
Drives used in enterprise storage arrays are sometimes formatted differently from normal server or desktop disks.
The familiar logical sector size is:
512 bytes
but storage arrays have historically also used formats such as:
520 bytes
528 bytes
The extra bytes can be used for protection or integrity information managed by the storage system.
A drive can therefore be a completely functional SAS disk and still be unusable by another controller because that controller doesn’t support the drive’s current logical block format.
Given that these drives came out of a 3PAR system, 520-byte sectors were a very plausible explanation.
There was an obvious solution in theory: reformat the disks to 512-byte sectors.
In practice, there was a catch.
The Chicken-and-Egg Problem
If one of these drives had appeared in Linux as:
/dev/sde
this would have been a relatively ordinary SCSI-disk problem. Utilities such as sg_format could communicate directly with the device and, assuming the drive supported it, change its logical block size.
But Linux didn’t have a /dev/sdX device for these disks.
The 9750 wouldn’t expose the disk because it didn’t support its format.
So:
Need to format the disk
↓
Need to send SCSI commands to the disk
↓
Need access to the physical disk
↓
Controller refuses to expose the disk
↓
Need to format the disk
That was the actual problem.
I looked for a supported way to make tw_cli reformat an unsupported physical disk and couldn’t find one. Buying another controller would probably have ended the project fairly quickly, and I also had the original 3PAR controller hardware available if I really wanted to rebuild the original environment.
But tw_cli bothered me.
It was already retrieving a substantial amount of information from drives that the controller supposedly didn’t support.
If the controller could communicate with those drives well enough to identify them, there had to be a lower-level communication path somewhere.
That is where this stopped being a normal storage project.
Bringing in AI
I had already been using AI to research the 3PAR hardware, the 9750, and the various tools surrounding it.
Eventually I decided to give OpenAI Codex running GPT-6-Astra the problem directly.
The instruction was essentially:
Figure out how this controller is communicating with these physical drives and find a safe way to send the SCSI commands necessary to reformat them. If necessary, investigate the controller firmware itself.
I was willing to let it go fairly deep, including reverse engineering, because there wasn’t any valuable data on these disks.
But I didn’t want an experimental storage command touching the disks that actually ran my Proxmox server.
Building an Isolated Test Environment
Rather than giving Codex free rein on the Proxmox host itself, I created a dedicated LXC for the experiment.
One small terminology correction here: because an LXC shares the host’s Linux kernel, this wasn’t conventional VM-style VFIO PCI passthrough. I exposed the controller/device interface needed by the tooling into the container and kept the research environment isolated from the rest of the network.
The container was also placed on its own VLAN.
The important part was that Codex could access the 3ware controller interface while the host’s existing storage lived on an entirely different controller.
I explicitly told it that if it discovered anything that looked like one of the server’s existing disks, it was to stop rather than attempt any destructive operation.
Inside the research environment, the interesting controller interface was:
/dev/twl0
That character device became the key to the entire project.
Reverse Engineering the 9750
Astra started by gathering everything it could about the controller.
That included:
- the Linux
3w-sasdriver source; - the 3ware management utilities;
- diagnostic utilities;
- a controller firmware image;
- strings and symbols extracted from the tools;
- a disassembly of the firmware;
- and traces of the communication between
tw_cliand the kernel driver.
The workspace eventually contained files such as:
3w-sas.c
3w-sas.h
fw/prom0011.img
fw.asm
fw-map.py
fw-strings.txt
os_linux.cpp
trace-twl.c
trace-twl
probe-scsi.py
probe-physical.py
That wasn’t just theoretical research. The tracing work generated more than a hundred captured command/response pairs from the controller interface.
Importantly, we never ended up flashing modified firmware to the controller.
The firmware investigation helped map out what was happening, but the breakthrough came from understanding the existing communication path well enough to use it.
Watching tw_cli Talk to the Driver
Astra created a small tracing utility called:
trace-twl
with its source in:
trace-twl.c
The idea was to watch what happened when legitimate tw_cli operations interacted with the 3ware driver and /dev/twl0.
That generated captures such as:
twl-0001-in.bin
twl-0001-out.bin
twl-0002-in.bin
twl-0002-out.bin
...
Instead of guessing how the controller worked, Astra could compare requests with responses and begin reconstructing the controller’s command structure.
The important realization was that the 9750 had a path for addressing physical drives behind the controller, even when those drives had not been turned into Linux-visible logical units.
Conceptually:
Userspace tool
│
▼
/dev/twl0
│
▼
3w-sas driver
│
▼
9750 controller
│
▼
Physical SAS port
│
▼
SAS disk
If that path could carry a SCSI command, we no longer needed Linux to expose the disk as /dev/sdX first.
Starting With Read-Only Commands
This is the part of the project where being conservative mattered.
Before sending anything capable of changing a disk, Astra built small probing tools and tested read-only SCSI operations.
Those became:
probe-scsi.py
probe-physical.py
The first real test target was physical controller port:
p28
Instead of formatting anything, the first goal was simply to ask:
Are we actually talking to the physical disk we think we’re talking to?
The probes began issuing standard read-only SCSI queries such as:
- INQUIRY;
- READ CAPACITY;
- MODE information;
- REQUEST SENSE;
- serial-number queries;
- WWN/identifier queries.
The workspace contains separate captures for those operations, including physical capacity, inquiry, mode, sense, serial and WWN requests.
And they worked.
That was the first major breakthrough.
The controller wasn’t just vaguely aware that something occupied a slot. We could send a SCSI request through the 9750 to an individual physical drive and get a valid response back.
Confirming 520 Bytes Instead of Guessing
Until this point, 520-byte formatting had been an educated guess.
Now we could actually ask the disk.
A SCSI READ CAPACITY response includes the logical block length.
The test drive reported:
0x208
which is:
520 bytes
That was the moment the original problem finally made sense.
These weren’t bad drives.
The SAS connection wasn’t broken.
The enclosure wasn’t incompatible at the electrical level.
The controller was communicating with perfectly functional enterprise SAS disks that happened to be formatted with a logical block size it didn’t want to expose.
Converting the First Drive
At this point I ran out of credits for Astra on Codex. I had to review the files and code left behind to finish the project myself. Fortunately it already was very close to actually formatting the drive, we had logs showing successful tests and all we really needed to do was go for the format.
One drive.
One known physical port.
No batch processing yet.
Warning: Everything from this point forward is destructive. Changing a disk’s logical block format destroys the data on it. Low-level SCSI commands sent to the wrong target can also destroy perfectly good storage. Don’t copy this process onto hardware containing data you care about.
The first conversion tooling eventually became:
format-p28-512.py
The process used the SCSI commands required to move the drive to a 512-byte logical format and perform the format operation. I still used an older version of GPT to write this python script but I was limited to the browser edition.
At a high level the code does this:
Identify physical disk
↓
Read existing capacity/block size
↓
Prepare 512-byte block format
↓
Issue FORMAT UNIT
↓
Wait for completion
↓
Read capacity again
↓
Verify 512-byte blocks
So I decided enough testing had been done and I ran the script. The research directory still contains the actual before/after captures:
preformat-capacity-in.bin
preformat-capacity-out.bin
mode-select-test-520-in.bin
mode-select-test-520-out.bin
mode-select-512-in.bin
mode-select-512-out.bin
format-unit-512-in.bin
format-unit-512-out.bin
Those artifacts were later retained specifically as part of the verified conversion work.
And then we checked the disk again.
Instead of:
520 bytes
it reported:
512 bytes
We did it! The disks are now formatted to 512 bytes.
Getting the Disk Into Linux
By the time I had gotten done reviewing all the work from Astra, generating a python script to run the format and actually completing the format hours had passed. When I came back to the computer Astra was again available to me and I deployed it to finish the job.
Formatting the physical drive solved only half the problem.
The 3ware/LSI 9750-24i4e is still a RAID controller, not a transparent IT-mode HBA.
So even after the physical disk became usable, Linux wasn’t simply going to see the raw disk appear directly behind an HBA.
The controller needed to expose the converted disk as an individual unit.
That produced another important distinction in this project.
My original idea was:
Turn the 3PAR shelf into a JBOD.
What I actually built with this particular controller was closer to:
Convert every physical disk to 512-byte sectors and create an individual single-disk unit for each disk.
From Linux’s perspective that ultimately looks very similar—you get an individual /dev/sdX device for each physical drive—but the 9750 remains in the path.
The first converted drive was exposed and verified before we attempted the rest of the shelf. The workspace contains the p28 unit-creation and Linux-device verification artifacts from that test.
And for the first time:
lsblk
showed a disk from the 3PAR shelf.
From One Drive to Twenty-Four
Once one disk worked, the engineering problem changed again.
I wasn’t interested in manually repeating an experimental series of low-level SCSI commands 23 more times.
More importantly, doing that manually would be an excellent way to eventually type the wrong port number.
So Astra turned the proof-of-concept into a batch workflow.
The automation added safeguards such as:
- discovering the shelf before making changes;
- recording drive identity;
- mapping controller ports and enclosure slots;
- checking current logical sector size;
- skipping drives already formatted to 512 bytes;
- refusing to format drives already assigned to controller units;
- requiring confirmation;
- supporting dry runs;
- verifying the result after a format;
- refreshing controller discovery;
- exposing converted disks individually;
- and logging every operation.
The research version evolved through tools such as:
shelf_batch.py
expose_remaining.py
verify_linux_units.py
and eventually became something I could actually reuse.
sas-shelf
The finished utility became:
sas-shelf
Instead of remembering low-level controller commands, I can now start with a completely read-only inventory:
sas-shelf inventory
Before changing anything, I can preview exactly what the utility intends to touch:
sas-shelf format --all --expose --dry-run
To convert all eligible disks and expose them individually:
sas-shelf format --all --expose
Or I can target one enclosure slot:
sas-shelf format --slot 0 --expose
or one physical controller port:
sas-shelf format --port 28 --expose
Formatting and exposing can also be separated:
sas-shelf format --all
and later:
sas-shelf expose --all
Long-running operations can be checked with:
sas-shelf status
The public project is intentionally designed as both a usable tool for similar hardware and a reference implementation for people trying to solve the same problem on a different controller.
The controller-specific communication is separated from the higher-level workflow so that someone willing to dig into another RAID controller has a reasonable starting point rather than having to repeat this project from zero.
The Result
After converting and exposing the entire test shelf, I ran:
lsblk -d -o NAME,HCTL,SIZE,MODEL,SERIAL,WWN,LOG-SEC,PHY-SEC
and got exactly what I had been trying to get from the beginning.
The shelf appeared as 24 individual Linux block devices:
sde through sdab
The disk population in this shelf turned out to be:
16 × ~900 GB SAS drives
8 × ~1.2 TB SAS drives
or approximately:
24 TB raw decimal capacity
Every exposed drive showed:
LOG-SEC 512
PHY-SEC 512
A shortened version of the final result looks like:
NAME HCTL SIZE MODEL LOG-SEC PHY-SEC
sde 0:0:0:0 838G 9750-24i4e DISK 512 512
sdf 0:0:1:0 838G 9750-24i4e DISK 512 512
sdg 0:0:2:0 838G 9750-24i4e DISK 512 512
...
sdz 0:0:21:0 1.1T 9750-24i4e DISK 512 512
sdaa 0:0:22:0 1.1T 9750-24i4e DISK 512 512
sdab 0:0:23:0 1.1T 9750-24i4e DISK 512 512
There is also an interesting detail in that output.
Linux reports the model as:
9750-24i4e DISK
rather than the underlying physical drive model.
That is because these are being presented through individual units created by the 9750. This isn’t the same thing as connecting the shelf to a transparent IT-mode SAS HBA.
For my experiment, that was enough to prove the shelf and all 24 disks could be repurposed with hardware I already had.
Conclusion: What AI Actually Did Here
This ended up being a much better AI experiment than I expected.
I didn’t ask a chatbot:
How do I convert a 3PAR drive to 512 bytes?
There wasn’t a useful copy-and-paste answer available.
Instead, the workflow looked more like:
Find hardware limitation -> Research existing documentation and tools -> build env for Astra to directly troubleshoot the issue with slight guidance along the way.
Astra generated source code, analyzed binary command structures, compared real controller responses, wrote probing tools, tested them against physical hardware, and iterated based on what the hardware actually returned.
The workspace eventually became pretty substantial. By the time the process was automated it included controller captures, SCSI request/response pairs, a shelf manifest, batch logs, Linux-device mappings, verification output and the final packaged sas-shelf utility.
And yes, I burned through my Codex allowance remarkably quickly doing it.
But the result was something I probably wouldn’t have bothered attempting manually. It’s very likely I would have gotten the original 3Par controller set up and ended up just using that but I would have needed to make an extra trip to the office on a weekend. If I’m being honest I might set up the original controller anyway depending on what I end up doing with these drives. This workflow and specific result only exists because AI made it practical to pursue. Doing this type of work for me personally would have taken at least a few weeks to a month of troubleshooting with no guarantee of success.
A Word of Warning
I published sas-shelf, but I do not consider it a universal SAS formatting utility.
It was developed and tested against a very specific configuration:
HPE 3PAR enterprise SAS storage
3ware/LSI 9750-24i4e
Linux 3w-sas driver
tw_cli
/dev/twl0 controller interface
520-byte SAS disks
Different controllers expose physical disks differently. Firmware versions can behave differently. Different disks can implement SCSI formatting differently.
A low-level storage command is a particularly bad place to assume that “close enough” means compatible.
If your configuration differs, I recommend treating the project primarily as a reference implementation.
Read the code.
Read the documentation for your hardware.
Start with discovery and read-only commands.
Use dry-run mode.
Verify physical serial numbers and WWNs before touching anything.
And if you’re using something like Codex to adapt the project to different hardware, let the AI inspect the actual controller, driver documentation and hardware responses rather than simply asking it to change some constants and hoping for the best.
What’s Next
This solved the hard part: the shelf isn’t locked to its original 3PAR environment, and all 24 disks can now be used by Linux through the 9750.
The next step is deciding what I actually want to do with them.
Given that the first shelf alone provides around 24 TB of raw storage—and there are several more sitting in the rack—I have considerably more potential backup storage than I expected when we bought the original auction lot. In today’s market this much storage is difficult to get and it’s even weirder to have it spread across all 1TB drives. In total I have around ~190TB in these 3Par shelfs to work with and zero projects or reason to have it right now.
For now, though, I’m calling the experiment a success.
That’s good enough for one weekend project.
Project
The sas-shelf source code, documentation, and safety notes are available on GitHub: